/* ============================================================
   发卡商城后台 样式
   风格：白底 · 单一橙红强调 · 清晰边框 · 扁平整洁 + 轻科技感（2026-09-22 升级）
   ============================================================ */
/* 通用隐藏类（2026-10-06 补）：
   ⚠️ 这个类以前**没有定义**，但 trial.html / enter.html / signup.html 都在用它切显隐
   （`class="box bad hide"`）→ 表现是"错误框永远显示"（只读试看页就中过这个招 ✗）。
   加 !important 是为了压过页面内联 style。 */
.hide { display: none !important; }
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  /* ⭐ 2026-10-20 用户要求（方案 A）：**后台视觉语言对齐前台**（前台 = React 商城，主色蓝 #2E5BFF）
     只改"品牌观感"：主色/中性色/线条/圆角/阴影 —— 布局与 HTML 结构一律不动 ✓（守卫不受影响 ✓）
     对照表（前台 tailwind.config.ts ↔ 后台变量）：
       accent #2E5BFF ↔ --brand ／ accent-dark #1E44D6 ↔ --brand-deep
       accent-glow rgba(46,91,255,.12) ↔ 焦点环 / --brand-glow
       text-primary #0F172A ↔ --ink ／ text-secondary #475569 ↔ --ink-3 ／ text-muted #64748B ↔ --ink-4
       border #E2E8F0 ↔ --line（略加深，见下）／ background #F8FAFC ↔ --bg-gray（略加深，见下）
       rounded-card 12px ↔ --radius（后台用 10px，表格/徽章不至于过圆）／ shadow soft·card ↔ --shadow-sm·md */
  --brand: #2E5BFF;
  --brand-deep: #1E44D6;
  --brand-soft: #EEF3FF;      /* 选中态浅蓝底（原来是浅橙 #FFF1E8） */
  --brand-line: #C3D0FF;      /* 品牌色描边（原来是浅橙边） */
  --brand-glow: rgba(46, 91, 255, .14);   /* 焦点/发光统一走这个 */
  --link-blue: #2E5BFF;
  --ok: #00B578; --ok-ink: #00694A; --ok-bg: #DCF5ED;
  --danger: #FF4400; --danger-ink: #C93300; --danger-bg: #FFE7DF;
  --warn: #FFA000; --warn-ink: #A05E00; --warn-bg: #FFF0D6;
  --info-ink: #2B6FD4; --info-bg: #E1EDFB;

  /* 文字：改用前台的 slate 色阶（对比度：白底 15:1 / 9:1 / 7.5:1 / 4.8:1 ✓ 都达标） */
  --ink: #0F172A;
  --ink-2: #334155;
  --ink-3: #475569;
  --ink-4: #64748B;

  /* 线条：带一点冷调（原来纯灰），比前台 #E2E8F0 稍深一档，保证灰底上仍看得见 */
  --line: #D9E1EC;
  --line-soft: #E9EEF6;
  --bg: #FFFFFF;
  /* 内容区底色：贴近前台 #F8FAFC 的冷调灰，但**比 #F7F7F7 深**（theme 守卫要求 ✓） */
  --bg-gray: #EEF2F7;
  --bg-gray-2: #E5EBF3;       /* 需要更深时的第二档（表头、内嵌块） */

  /* 阴影：换成前台的 soft / card 两档（多层低透明度，比单层更细腻） */
  --shadow-sm: 0 1px 2px rgba(15, 23, 42, .04), 0 1px 3px rgba(15, 23, 42, .03);
  --shadow-md: 0 1px 2px rgba(15, 23, 42, .04), 0 4px 12px -4px rgba(15, 23, 42, .06);
  --radius: 10px;

  /* 链接蓝 / 天蓝色：并入品牌蓝（前台没有第二套蓝） */
  --sky: #2E5BFF;
}

/* 输入框提示文字：浏览器默认太浅，这里显式指定（2026-09-21 提升可读性） */
input::placeholder,
textarea::placeholder {
  color: #8A8A8A;
  opacity: 1;
}

/* ------------------------------------------------------------------
   表单控件的「字体」与「字号」统一（2026-09-22 用户要求）
   ⚠️ 关键坑：HTML 的 input/select/textarea 默认**不继承**父级字体 ——
      浏览器强制用 `font: 400 13.33px Arial`，所以输入框里会出现
      「Arial 且字号和旁边标签不一致」。必须显式 font-family: inherit。
   ⚠️ 字号也要统一：原来字段输入框 14px、工具栏输入框 13px，两种混着。
   ------------------------------------------------------------------ */
input, select, textarea, button {
  font-family: inherit;   /* 关键：让输入框用思源黑体，而不是 Arial */
}
.field input, .field select, .field textarea { font-size: 13.5px; }
.toolbar input, .toolbar select { font-size: 13.5px; }

body {
  /* 2026-10-02 用户要求（与 2026-09-22 同一条规则）：
   全后台**只有表格表头**加粗，<b>/<strong> 一律按常规字重渲染 —— 要强调请用颜色/字号。
   例外（更具体的选择器，会覆盖本行）：邮件模板预览(.mt-preview)保真、编辑器工具条标题(.tb-head)、
   表格表头里的 <b>（.tbl th b / .tbl th strong 跟随表头）。 */
b, strong { font-weight: 400; }

/* 2026-09-22：改用思源黑体（Noto Sans SC 是思源黑体的官方同源版，本机已安装）；
     没有该字体的机器自动回落到微软雅黑/苹方，不影响显示。 */
/* 2026-10-20：字体栈**保持思源黑体优先**（中文两边的实际显示字体本来就是思源黑体/系统黑体 ✓）。
   ⚠️ 不要把 Inter 排到最前：本机没装 Inter → 只会白白回落成系统字体，而且会撞坏
   `platform-ui` 守卫的断言「平台端字体仍以思源为先」✗（2026-10-20 试过，退回）。
   要严格对齐拉丁字形，得先把 Inter 装进系统或自托管，再改这一行。 */
  font-family: "Source Han Sans SC", "Noto Sans SC", "Source Han Sans CN", "Noto Sans CJK SC",
    "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--ink-2); -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---------------- 布局 ---------------- */
.layout { display: flex; min-height: 100vh; }
.sidebar {
  width: 200px; flex-shrink: 0;
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
  background: linear-gradient(180deg, #FFFFFF 0%, #FBFCFE 100%);
  border-right: 1px solid var(--line);
  box-shadow: 1px 0 0 rgba(16, 24, 40, .02);
}
.side-logo {
  padding: 19px 18px; font-size: 14.5px; font-weight: 400; color: var(--brand);
  border-bottom: 1px solid var(--line-soft); letter-spacing: .3px;
  display: flex; align-items: center; gap: 9px;
  /* 科技感：LOGO 区加一层极淡的品牌色光晕 */
  background: linear-gradient(135deg, rgba(46, 91, 255, .06), transparent 62%);
}
/* ⭐ 2026-10-21 用户要求：后台 LOGO **换成前台那个 LOGO**，并且**跟着租户自己的 Logo 一起变**
   · `--side-logo` 由 `admin.js` 的 `loadAdminMeta()` 按 `GET /api/site` 的 `header.logoUrl` 写进来
     （租户在「页面管理 → 页头页脚」上传过就用那张 ✓ 没上传就用下面这个默认图 ✓）
   · 默认图 = `public/images/logo-umo.png`（从 frontend/public/images 复制过来，与前台兜底一致 ✓）
   · ⚠️ 图片自带蓝色底 + 圆角 → 不要再叠渐变底色，否则边缘会出现"方角内套圆角"的脏边 ✗ */
.side-logo::before {
  content: ""; width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
  background: var(--side-logo, url("/images/logo-umo.png")) center/contain no-repeat;
  box-shadow: 0 2px 8px rgba(46, 91, 255, .28);
}
/* 导航项增多后（媒体库/文章库/导航管理/页头页脚，2026-09-22）：
   .side-nav 是 flex:1 的列，屏幕高度不够时要把菜单本身变成可滚动区域，
   否则底部（修改密码 / 退出）会被挤出视口且滚不到。min-height:0 是 flex 子项能滚的前提。 */
.side-nav { flex: 1; min-height: 0; overflow-y: auto; padding: 8px 0; }
.side-nav a {
  display: block; padding: 12px 20px; color: var(--ink-2); text-decoration: none; font-size: 14px; font-weight: 400;
  border-left: 3px solid transparent; transition: all .15s ease;
  display: flex; gap: 9px; align-items: center;
}
.side-nav a:hover { background: #F1F4F9; color: var(--ink); }
/* 选中项：左侧品牌色竖条 + 右侧渐隐底色，比纯色块更有层次 */
.side-nav a.active {
  background: linear-gradient(90deg, var(--brand-soft), rgba(238, 243, 255, .35));
  color: var(--brand-deep);
  border-left-color: var(--brand); font-weight: 400;
}

/* 商品表单里的会员等级勾选区（2026-09-22）
   2026-09-23 用户反馈「格子太大没必要」→ 改小：每格约 140px、字 12.5px、内边距收紧，
   数量多时自动换行（auto-fill），不再硬撑 2/4 列把每格拉到 270px 宽 */
.member-level-box {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 6px 8px;
  margin-top: 2px;
}
.member-level-item {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 9px; border: 1px solid var(--line); border-radius: 6px;
  font-size: 12.5px; background: #fff; cursor: pointer; line-height: 1.4;
}
.member-level-item:hover { border-color: var(--brand-line); background: var(--brand-soft); }

/* 表格「操作」列按钮分两排（2026-09-22 用户要求节省横向空间）
   ⚠️ 踩过的两个坑：
     ① 不要用 grid 固定 2 列 —— 每格被压低后「下架/删除」会折成两行；
     ② 只给 max-width 不够：表格 auto 布局下这一列可能只分到 ~54px，
        按钮会变成一排一个（实测 1440px 宽时）
   正解：flex-wrap + min-width（保证一行放得下 2 个）+ max-width（保证第 3 个换行）。 */
.row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  min-width: 112px;   /* 两个两字按钮 + 间距；逼表格把这一列撑开 */
  /* 2026-10-30 用户要求：宽度够的时候按钮排成一行（原来写死 max-width:180px → 第 3 个必然换行）
     flex-wrap:wrap 仍然保留 —— 屏幕/列太窄时会自动折行，不会溢出 ✓ */
  max-width: none;
}
.row-actions > .btn { white-space: nowrap; }

/* 2026-10-30 用户要求：操作列按钮在一行显示 —— 订单表 13 列太挤，单独给它最后一列保底宽度 */
/* 2026-10-30 用户要求：订单表操作列**永远一行** ——
   只加 min-width 不够（列宽不够时 .row-actions 的 flex-wrap:wrap 照样折行 ✗），
   这里直接把这个表格里的按钮组设成 nowrap，宁可横向滚动也不折行 ✓ */
#oTable th:last-child, #oTable td:last-child { min-width: 300px; white-space: nowrap; }
#oTable td:last-child .row-actions { flex-wrap: nowrap; }

/* 2026-10-30 用户要求：订单管理每行上下间距缩小一点（原来 td padding 11px → 5px，
   多行副文本行高 1.75 → 1.3；状态徽章间距 3px → 2px）。只影响订单表，别的表不动 ✓ */
#oTable td { padding-top: 5px; padding-bottom: 5px; }
#oTable td .muted { line-height: 1.3; }
#oTable td .stage-cell { gap: 2px; }

/* ============================================================
   侧边栏一级分组（2026-09-22）
   用原生 <details>/<summary> 做展开收起：无 JS 也能用，键盘可达，
   展开状态由 admin.js 持久化到 localStorage。
   父项在「子项被选中」时也高亮（.active），一眼看出在哪一栏。
   ============================================================ */
.side-group { border: 0; }
/* 一级分组标题：2026-09-22 放大到 14.5px（原来 13.5px 加粗偏小） */
.side-group > summary {
  display: flex; align-items: center; gap: 9px; cursor: pointer; list-style: none;
  padding: 12px 20px; color: var(--ink); font-size: 14px; font-weight: 400;
  border-left: 3px solid transparent; transition: background .15s ease, color .15s ease;
}
/* 去掉 Chrome/Safari 默认的三角标记，改用自绘的箭头 */
.side-group > summary::-webkit-details-marker { display: none; }
.side-group > summary::after {
  content: '▸'; margin-left: auto; font-size: 11px; color: var(--ink-4);
  transition: transform .18s ease;
}
.side-group[open] > summary::after { transform: rotate(90deg); }
.side-group > summary:hover { background: #F1F4F9; }
/* 子项选中 → 父项也高亮（加渐隐底色，与子项选中呼应） */
.side-group.active > summary {
  color: var(--brand-deep);
  background: linear-gradient(90deg, var(--brand-soft), rgba(238, 243, 255, .30));
  border-left-color: var(--brand);
}
/* 子项：缩进一层（左内距 42px vs 一级 20px），字体略小、颜色略浅
   ⚠️ 选择器要带 .side-group 前缀，才压得过上面 `.side-nav a` 的 padding
   ⚠️ 不要用 inset box-shadow 当指示线：会和 .side-nav a 的 border-left:3px 打架，
      统一用 border-left（一级/二级同一条辅助线，选中都变品牌色） */
.side-group > a {
  padding: 10px 20px 10px 42px; font-size: 13.5px;
  color: var(--ink-3); border-left-color: var(--line-soft);
}
.side-group > a:hover { color: var(--ink); }
.side-group > a.active { color: var(--brand-deep); border-left-color: var(--brand); font-weight: 400; }
.side-user {
  padding: 13px 18px; border-top: 1px solid var(--line-soft);
  font-size: 13px; display: flex; justify-content: space-between; align-items: center; color: var(--ink-3);
  background: linear-gradient(0deg, rgba(16, 24, 40, .02), transparent);
}
.side-user a { color: var(--ink-2); cursor: pointer; font-size: 13px; }
.side-user a:hover { color: var(--brand); }

/* 内容区底色：2026-09-22 按用户要求加深。
   只把整块涂深容易显"闷"，所以在顶部叠一层极淡的冷色光，
   配合白色卡片形成"有光源的浅灰台面"，是常见的后台科技感做法。 */
.main {
  flex: 1; padding: 24px; min-width: 0;
  background: var(--bg-gray);
  /* 顶部一层极淡的冷色光（偏蓝、很轻）：让浅灰台面有"光源"而不是一块死灰。
     2026-09-22 调整：第一版暖色偏重会发粉，改成以冷调为主、暖色只留一点点。 */
  background-image:
    radial-gradient(900px 360px at 88% -10%, rgba(43, 111, 212, .06), transparent 60%),
    radial-gradient(700px 300px at 8% -6%, rgba(46, 91, 255, .022), transparent 58%);
  background-repeat: no-repeat;
  background-attachment: fixed;
}
.page { max-width: 100%; }   /* 2026-10-30 用户要求：自动适配分辨率（原来写死 1400px → 宽屏右侧留白） */

h2.page-title {
  /* 2026-09-22：按需求取消加粗；只取消加粗会让大标题显得空，所以字号从 19.5 收到 17 */
  font-size: 17px; margin-bottom: 16px; font-weight: 400; color: var(--ink); letter-spacing: .1px;
  /* 标题左侧一小段品牌色竖条，页面层级一眼可辨 */
  display: flex; align-items: baseline; gap: 10px;
}
h2.page-title::before {
  content: ""; width: 3px; height: 16px; border-radius: 2px; flex-shrink: 0;
  background: linear-gradient(180deg, #4C7DFF, #2E5BFF);
  align-self: center;
}
.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar .spacer { flex: 1; }
.toolbar input, .toolbar select {
  border: 1px solid var(--line); border-radius: 6px; padding: 8px 12px;
  outline: none; background: #fff; color: var(--ink); transition: all .15s;
}
.toolbar input:focus, .toolbar select:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(46, 91, 255, .14); }

/* ---------------- 统计卡片 ---------------- */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); gap: 14px; margin-bottom: 18px; }
/* 2026-09-22 质感：白卡 + 柔和投影 + 顶部一条品牌色细线（像仪表盘的卡） */
.stat {
  background: #fff; border-radius: var(--radius); padding: 16px 18px;
  border: 1px solid var(--line-soft);
  box-shadow: var(--shadow-sm);
  position: relative; overflow: hidden;
  transition: box-shadow .18s ease, transform .18s ease;
}
.stat::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: linear-gradient(90deg, var(--brand), #7FA0FF 55%, transparent);
  opacity: .9;
}
.stat:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.stat .num { font-size: 23px; font-weight: 400; margin-top: 5px; letter-spacing: .3px; color: var(--brand); }
.stat .label { font-size: 13px; color: var(--ink-3); }
.stat.blue .num { color: var(--brand); }
.stat.green .num { color: var(--brand); }
.stat.orange .num { color: var(--brand); }
.stat.red .num { color: var(--brand); }

/* ---------------- 面板 ---------------- */
/* 2026-09-22 质感：圆角加大 + 柔和投影 + 表头细描边（原来是 4px 圆角无阴影的"大灰块"） */
.panel {
  background: #fff; border-radius: var(--radius); border: 1px solid var(--line-soft);
  margin-bottom: 18px; overflow: hidden;
  box-shadow: var(--shadow-sm);
}
/* 面板标题：本来是 14.5px + 800，偏小又过重；改为 15.5px + 700 更舒展 */
/* 面板标题：2026-09-22 与字段标签、说明文字统一到 13.5px
   （用户要求「标题/标签/说明 字体和字号都一样」，这三者现在是同一套字） */
.panel-head {
  padding: 15px 20px; border-bottom: 1px solid var(--line-soft);
  font-size: 13.5px; font-weight: 400; color: var(--ink);
  display: flex; justify-content: space-between; align-items: center;
  /* 极淡的冷调渐变，比纯白更有层次 */
  background: linear-gradient(180deg, #FCFDFF, #F7F9FC);
}
.panel-head .muted { font-weight: 400; font-size: 13.5px; color: var(--sky); }
.panel-body { padding: 14px 20px; }
/* 桌面端兜底（2026-09-28）：窗口较窄时，列表表格仍可能比容器宽。
   `.panel` 是 overflow:hidden（为了圆角）→ 多出来的部分会被**直接裁掉、内容凭空消失**，最坑。
   这里让面板内部可以横向滚动：宽屏看不出区别，窄屏能滑出来看。
   ⚠️ 不能只靠"把表格做窄"：列多 + 用户可能填超长名称，窄窗口下总会溢出。 */
.panel-body { overflow-x: auto; }

/* ---------------- 表格 ---------------- */
/* 2026-09-22：表头原来 12.5px + 700 又小又重，看着发糊；
   改为 13px + 600（略大、略减重，反而更清楚） */
table.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th {
  text-align: left; padding: 11px 13px; background: var(--bg-gray-2); color: var(--ink-2);
  /* 2026-09-22 用户指定：全后台只有表头加粗 */
  font-weight: 700; white-space: nowrap; border-bottom: 1px solid var(--line); letter-spacing: .2px; font-size: 13px;
}
.tbl td { padding: 11px 13px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; transition: background .12s; color: var(--ink-2); }
/* 订单管理整表收紧左右内边距（2026-09-29 用户要求「商品列再宽点」）
   13px → 10px，9 列一共省 54px。**内容盒子大小不变，不会多折一行** ——
   比继续压某几列的宽度上限更划算（那些会把字挤成更多行）。
   ⚠️ 必须排在下面那批按列的规则（.money-cell/.time-cell/.acct-cell…）**之前**，
      否则同特异度下后写的会被这条盖掉。 */
.tbl.tbl-tight th, .tbl.tbl-tight td { padding-left: 10px; padding-right: 10px; }
/* ============ 表格单元格「内容宽度约束」（2026-09-28）============
   背景：表格是 auto 布局，列宽由内容决定。用户实测两种反向的毛病：
   ① **撑破**：分类名 59 个字（没有空格）→「分类」列被撑到 401px → 表格 1322px，
      超出容器 147px（桌面端 .panel 是 overflow:hidden，超出部分直接看不见）；
   ② **被挤扁**：同一张表里，「名称」列被挤到 97px，档位列表变成一行一个字、30 多行。

   ⚠️ 只加 `overflow-wrap` **没用** —— auto 布局会把列直接变宽，永远触发不了换行
      （第一版就是这么修的，用户在页面上实测打回来了）。
   ✅ 正确做法：给"可能很长"的列同时加**宽度上下限** + 任意断行：
      · max-width = 再长也到此为止，超了就换行（这才是真正让它断行的开关）
      · min-width = 再挤也不低于这个宽度（防止一字一行）
   实测（1440 视口 / 商品管理）：改前 表 1322 vs 容器 1175（溢出 147）；
      给「名称/分类/会员等级」三列加上后 → 1190 vs 1190 ✔

   ⚠️ 只加在**确实可能很长**的列上，别全局加到 `.tbl td`：
      全局加会把 ID / 销量这种短列也撑成 min-width 那么宽。
   档位参考：xs=账号/编号  sm=联系方式/短名称  md=名称/备注/订单号  lg=商品名/库名（带附属徽章） */
.tbl td.ln-xs { min-width: 62px;  max-width: 110px; overflow-wrap: anywhere; word-break: break-word; }
.tbl td.ln-sm { min-width: 86px;  max-width: 160px; overflow-wrap: anywhere; word-break: break-word; }
.tbl td.ln-md { min-width: 110px; max-width: 220px; overflow-wrap: anywhere; word-break: break-word; }
.tbl td.ln-lg { min-width: 150px; max-width: 320px; overflow-wrap: anywhere; word-break: break-word; }
/* xl（2026-09-29）：给「商品名称」这种要吃掉全部富余宽度的列用 */
.tbl td.ln-xl { min-width: 220px; max-width: 620px; overflow-wrap: anywhere; word-break: break-word; }
/* 库存列（2026-09-29 用户要求）：收窄 + 固定 2 行（徽章一行、「按规格分桶」一行） */
.tbl td.stock-cell { min-width: 92px; max-width: 124px; white-space: normal; }
.stock-cell .muted { font-size: 12px; line-height: 1.5; }
/* 订单管理的时间列（2026-09-29 用户要求）：收窄但保证 2 行（日期一行、时间一行），
   多出来的宽度给「商品」列。
   ⚠️ 光靠自动换行凑不出稳定的两行：列一窄，"2026-09-28" 自己就可能再断成两截，
      所以 markup 里就拆成两个 div（.t-date / .t-time）并各自 nowrap。
   左右内边距比常规 td（13px）小一点，纯粹为了再省几个 px。 */
.tbl td.time-cell { min-width: 84px; max-width: 94px; padding-left: 9px; padding-right: 9px; white-space: normal; }
/* ⭐ 2026-11-09：**表头**也要限宽 —— admin-colw 量列宽时表还在 table-layout:auto 阶段，
   列宽由表头内容一起参与分配；只在 td 上限宽管不住，实测「付款时间」被撑到 300px（商品列被压到 150px ✗） */
.tbl th.time-cell { max-width: 94px; }
/* ⭐ 2026-11-09 用户要求：订单管理加「使用 / 赠送」两列 —— 这两列只有数字，让它按内容收缩（width:1%），
   否则 table-layout:auto 时的富余宽度会被塞给最后几列（实测付款时间被撑到 300px、商品列被压到 150px ✗） */
.tbl td.pt-col, .tbl th.pt-col { width: 1%; white-space: nowrap; }
.tbl td.time-cell > div { white-space: nowrap; }
.time-cell .t-time { color: var(--ink-3); }
/* 订单管理「商品」列：装得下主商品名 + 规格数量 + 赠品行，富余宽度都给它 */
.tbl td.goods-cell { min-width: 150px; max-width: 560px; overflow-wrap: anywhere; word-break: break-word; }
/* 订单管理：「金额」「买家账号」两列再收一收，腾出的宽度给「商品」列（2026-09-29 用户要求）
   ⚠️ 表头必须一起收：`.tbl th` 是 nowrap 的，表头文字 + 26px 内边距就是这两列的下限
      （「买家账号」= 4 个字 + 26 = 78px），只压单元格内边距是没用的。
   金额列的内容是「￥10.00」+ 小字「单价 ￥1.00」，后者才是宽度下限 → 顺手用 12px 小字。 */
.tbl th.th-tight { padding-left: 9px; padding-right: 9px; }
.tbl td.money-cell { min-width: 74px; max-width: 96px; padding-left: 9px; padding-right: 9px; white-space: normal; }
.money-cell .muted { font-size: 12px; line-height: 1.5; }
.tbl td.acct-cell { min-width: 62px; max-width: 140px; padding-left: 9px; padding-right: 9px; overflow-wrap: anywhere; word-break: break-word; }
/* 订单号 / 支付方式：把这两列的「宽度上限」压低，是让「商品」列真正变宽的关键 ——
   auto 布局是按各列的**内容需求**分配富裕宽度的，只要 订单号/支付方式 还在喊"我要 160/174"，
   腾出来的宽度就会被它们分走（实测：光收内边距那 54px，商品只拿到 4px）。
   订单号本来就是 25 位编码、无论多宽都断成两行 → 压它**零代价**；
   支付方式压到 152 会让长邮箱多折一行，属可控范围。 */
.tbl td.orderno-cell { min-width: 96px; max-width: 118px; }
/* ⭐ 2026-10-20 用户要求：「支付方式」列要**自动换行** ✓
   起因：联系方式是长邮箱（`loadd05m5@load.test` 这种没空格）→ 以前不换行 → 整串溢出，
         直接盖到隔壁「下单时间」列上（用户截图能看到两列文字叠一起 ✗）
   做法：允许换行 + 长串任意断点（overflow-wrap:anywhere）+ 词内断开（word-break:break-word）
   ⚠️ 不能只加 white-space:normal（邮箱没有空格 → 没用 ✗ 必须配 overflow-wrap） */
.tbl td.pay-cell {
  min-width: 120px; max-width: 152px;
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
}
/* 单元格里如果还塞着别的块（如商品名下面的档位徽章），只约束名称那一行 */
.tbl .ln-inline { overflow-wrap: anywhere; word-break: break-word; }
.tbl tr:last-child td { border-bottom: none; }
.tbl tbody tr:hover td, .tbl tr:hover td { background: #FFF6F1; }
/* ⭐⭐ 2026-10-20 用户要求：「合计 / 汇总」行要有底色 ✓
   ⚠️ 底色必须画在 **td** 上 ✗ 不能只给 tr 设 background ——
     悬停规则 `.tbl tr:hover td { background:#FFF6F1 }` 和单元格自己的背景是画在 td 上的，
     只给 tr 设底色时，td 会把 tr 的颜色整个盖掉（实际看起来还是白的 ✓ 2026-10-20 踩到）
   配色：用 --bg-gray（#EDEFF3）—— 比表头的 --bg-gray-2 浅一档，层次是「表头 > 合计 > 数据行」
   悬停不改色（汇总行不该跟着鼠标变橙），保持稳定 ✓ */
.tbl tr.dw-sum td,
.tbl tr.sum-row td { background: var(--bg-gray); }
.tbl tr.dw-sum:hover td,
.tbl tr.sum-row:hover td { background: var(--bg-gray); }
.tbl tr.dw-sum td, .tbl tr.sum-row td { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }

.tbl input[type=checkbox] { accent-color: var(--brand); width: 15px; height: 15px; }
/* 订单号的等宽长串：允许在单元格内换行，避免把表格撑出边框（2026-09-22） */
.tbl td.mono { white-space: normal; word-break: break-all; }
/* 订单列表「操作」列：两个中文按钮一排，最多 2 排（配合 .row-actions 的宽度限制）
   ⚠️ min-width 必须 ≥ 两个按钮 + 间距（实测按钮 76px×2 + gap 6 = 158px）：
      状态列内容变多（邮件/赠品徽章）会把操作列挤窄，min-width 不够就会变成 3 排 */
.op-cell { white-space: normal; }
.op-cell .row-actions { min-width: 158px; max-width: 190px; }
/* 状态列：固定两排（2026-09-24 用户要求）
   第一排 = 订单状态 + 邮件状态；第二排 = 主商品够/缺货 + 赠品够/等补货
   徽章压小一点（11px / padding 1px 6px）让行高降下来 —— 之前徽章偏大导致整行偏高 */
.stage-cell { display: flex; flex-direction: column; gap: 3px; }
.stage-cell .badge-row { display: flex; flex-wrap: wrap; gap: 3px 4px; align-items: center; }
.stage-cell .badge { font-size: 11px; line-height: 17px; padding: 1px 6px; }
/* 可点徽章（跳到卡密库）：hover 时给个明确反馈，鼠标变手型 */
.badge-jump { cursor: pointer; border: none; font-family: inherit; }
.badge-jump:hover { filter: brightness(1.08); box-shadow: 0 0 0 2px rgba(46, 91, 255, .25); }

/* ---- 卡密库排序（2026-09-24）----
   ① 可点表头：三态循环（不排 → 升序 → 降序 → 不排），当前列加粗 + 箭头
   ② 序号列：数字输入框 + ↑↓ 两个小按钮，单元格不能被表格挤到换行 */
.tbl th.th-sort { cursor: pointer; user-select: none; }
.tbl th.th-sort:hover { background: #E4EAF2; color: var(--brand); }
.tbl th.th-sort.on { color: var(--brand); }
/* 2026-10-30 用户要求：序号列（输入框 + ↑↓）排一行 —— 给这一列保底宽度，否则 flex-wrap 会折行 */
.st-sort-cell { white-space: nowrap; min-width: 155px; }
.st-sort-cell input.st-sort,
.st-sort-cell input.sc-sort {
  width: 64px; padding: 4px 6px; border: 1px solid var(--line); border-radius: 6px;
  font-size: 12.5px; text-align: center; background: #fff; color: var(--ink); vertical-align: middle;
}
.st-sort-cell input.st-sort:focus,
.st-sort-cell input.sc-sort:focus { border-color: var(--brand); outline: none; }
/* 迷你按钮（↑↓ 用）：比 .btn-sm 再小一圈，别把行高撑起来 */
.btn-xs { padding: 2px 7px; font-size: 12px; line-height: 1.55; border-radius: 5px; }
.st-sort-cell .btn-xs { margin-left: 0; }   /* row 布局用 gap 控制间距（2026-10-30） */
.st-sort-cell .btn-xs[disabled] { opacity: .45; cursor: not-allowed; }
/* ---- 序号列改成「一行输入框 + 一行 ↑↓」并收窄（2026-09-29 用户要求）----
   原来 input + ↑↓ 挤在一行：商品管理「序号」列被测到 237px（比「名称」列 188px 还宽）、
   分类管理更夸张到 356px —— auto 布局把富余宽度塞给了这一列。
   改成上下两行后 max-content 只剩 ~74px，省下来的宽度自然给了「名称」列。
   ⚠️ 用 td 里的 <div class="st2"> 包一层，别直接把 td 设成 flex —— td 的 display 一改，
      浏览器要给它套一层匿名 table-cell，列宽计算会变得不可预期。 */
/* 2026-10-30 用户要求：序号输入框和 ↑↓ 排成一行（原来 flex-direction:column 强制两行）
   ⚠️ 2026-11-04 修溢出：一行需要 input 56 + 2×按钮 30 + 间隙 ≈ 123px，而本列
      被 `.tbl td.st-sort-cell { max-width: 100px }`（2026-09-29 用户要求「收窄」）卡在 100px
      → ↑↓ 溢出 36px 侵入「ID」列（真机量出来的）。两个要求都不让步的做法：**控件瘦身**，
      内容压到 ~92px 装进 100px 列（列宽上限仍是 100px ✓ 不出横向滚动条 ✓ 仍是一行 ✓）。
      守卫 `check-admin-theme` 会断言「内容不越出单元格」防回退。 */
.st-sort-cell .st2 { display: inline-flex; flex-direction: row; align-items: center; gap: 2px; width: auto; flex-wrap: nowrap; }  /* 2026-10-30：nowrap 保证输入框和 ↑↓ 同一行（原来 wrap → 列窄时折行）*/
.st-sort-cell .st2 .mv-row { display: flex; gap: 2px; }
.st-sort-cell .st2 input { width: 46px; padding: 0 2px; font-size: 12px; }
.st-sort-cell .st2 .btn-xs { margin-left: 0; min-width: 21px; padding: 2px 2px; font-size: 12px; line-height: 1.1; }
/* 2026-10-30 用户要求：序号列的「输入框 + ↑↓」排成一行。
   踩坑：卡密库/商品/分类表的 .st2 **不一定有 .st-sort-cell 父级**，
   只写 `.st-sort-cell .st2` 会有页面不生效 ✗ → 必须再来一条不依赖父级的规则 ✓
   用 nowrap：宁可溢出也不折行（列宽由需要决定）*/
.st2 { display: inline-flex; flex-direction: row; align-items: center; gap: 4px; flex-wrap: nowrap; white-space: nowrap; }
.st2 .mv-row { display: flex; gap: 3px; flex-shrink: 0; }
.st2 input { width: 56px; }
.st2 .btn-xs { margin-left: 0; }
/* 收窄序号列：min/max 都要卡住，否则表格有富余宽度时 auto 布局又会把它撑大 */
/* 2026-11-04：左右内边距 13→3，把内容盒从 74px 让到 94px —— 一行控件（~92px）才装得下 */
.tbl td.st-sort-cell { min-width: 74px; max-width: 100px; padding-left: 3px; padding-right: 3px; text-align: center; }
/* ---- 悬停气泡：默认藏着的长内容，鼠标移上去弹卡片（2026-09-29）---- */
.hover-tip { cursor: help; border-bottom: 1px dashed rgba(0, 0, 0, .28); }
.hover-tip-box {
  position: fixed; z-index: 9999; display: none;
  /* ⚠️ 必须写死宽度：fixed 元素默认 shrink-to-fit，而内容里带强制换行（pre-line），
     于是"最大内容宽度"= 最长那一行 ≈ 117px → 气泡会变成一根又窄又高的条子。 */
  width: 460px; max-width: calc(100vw - 16px);
  /* 档位那种一列很长的清单，分 3 栏摆，比一条长条子好读得多 */
  columns: 3; column-gap: 18px;
  padding: 9px 12px; border-radius: 8px; background: #23262B; color: #fff;
  font-size: 12.5px; line-height: 1.75; letter-spacing: .2px;
  white-space: pre-line; overflow-wrap: anywhere;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .22); pointer-events: none;
}
.hover-tip-box.on { display: block; }
/* 2026-09-22：表格内容里的 <b>（商品名/用户名等）取消加粗，
   让"表头是页面上唯一最重的文字"成立 */
.tbl td b, .tbl td strong { font-weight: 400; }

/* ============================================================
   全局取消加粗（2026-09-22 用户要求：除表格表头外一律不加粗）
   ⚠️ <b>/<strong> 即使这里不写 font-weight，浏览器也会默认加粗，
      所以要显式压成 400；否则它们会和表头一样重，"唯一加粗"就不成立。
   ⚠️ 提示文字（.hint）里的 <b> 也一并压平（用户复核后再次确认要全部取消）；
      支付配置、手动提卡等处的「⚠️ …」靠 emoji 前缀和加粗前的语气词承担强调，
      不再依赖字重。
   ============================================================ */
.sidebar b, .sidebar strong,
.main b, .main strong,
.modal b, .modal strong,
.toolbar b, .toolbar strong,
.stat b, .stat strong,
.panel-head b, .panel-head strong,
.hint b, .hint strong,
.field > label b, .field > label strong {
  font-weight: 400;
}
/* 表头里的 <b> 跟随表头字重（保证表头仍是唯一加粗处） */
.tbl th b, .tbl th strong { font-weight: inherit; }

/* ---------------- 徽章 ---------------- */
.badge { display: inline-block; padding: 2px 11px; border-radius: 4px; font-size: 12px; font-weight: 400; letter-spacing: .2px; white-space: nowrap; }
.badge.gray { background: #E6E8EC; color: var(--ink-2); }
.badge.green { background: var(--ok-bg); color: var(--ok-ink); }
.badge.orange { background: var(--warn-bg); color: var(--warn-ink); }
.badge.red { background: var(--danger-bg); color: var(--danger-ink); }
.badge.blue { background: var(--info-bg); color: var(--info-ink); }
/* 强提示徽章（2026-09-21：订单列表「邮件失败」用，比浅色徽章显眼得多） */
.badge-solid-red { background: #FF4400; color: #fff; }
.badge-solid-green { background: #00B578; color: #fff; }
.email-err { display: block; margin-top: 3px; font-size: 10px; color: var(--danger-ink); max-width: 150px; word-break: break-all; }

/* ---------------- 表单 ---------------- */
.field { margin-bottom: 13px; }
/* 2026-09-22：字段标签 13px + 600 在小字里偏挤，放大到 13.5px */
/* 字段标签：2026-09-22 与面板标题、说明文字统一到 13.5px */
.field label { display: block; font-size: 13.5px; color: var(--ink-2); margin-bottom: 6px; font-weight: 400; }
.field input, .field select, .field textarea {
  width: 100%; border: 1px solid var(--line); border-radius: 6px; padding: 9px 12px;
  outline: none; background: #fff; transition: all .15s; color: var(--ink);
}
/* 赠品试算条（2026-09-24）：让配置当场就能看到"一笔订单会送几个"以及赠品库够不够 */
.gift-try {
  border: 1px dashed #D6DCE4; border-radius: 8px; padding: 8px 10px;
  margin: 4px 0 10px; background: #FBFCFE;
}
.gift-try-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 13px; }
.gift-try-row input {
  padding: 4px 8px; border: 1px solid var(--line); border-radius: 6px; font-size: 13px;
  background: #fff; color: var(--ink); width: auto;
}
.gift-try-row b { color: var(--brand); font-size: 13.5px; }
.gift-try-warn {
  margin-top: 6px; font-size: 12.5px; line-height: 1.65;
  color: var(--danger-ink, #B91C1C); background: var(--danger-bg, #FEF2F2);
  border-radius: 6px; padding: 6px 8px;
}
.gift-try-warn.ok { color: #047857; background: #ECFDF5; }

/* 勾选类控件（复选框/单选框）不能被上面的 width:100% 撑满整行 —— 2026-09-23 用户反馈：
   勾选框被推到行尾、后面的文案被挤到下一行（会员等级格子尤其明显）。
   与「.sale-mode 的 radio」是同一类坑，这里一次性兜住所有 .field 里的勾选控件。 */
.field input[type=checkbox], .field input[type=radio] {
  width: auto; height: auto; flex: none; padding: 0; border: 0; background: none;
  accent-color: var(--brand); margin: 0; vertical-align: -2px;
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--brand); box-shadow: 0 0 0 3px rgba(46, 91, 255, .14);
}
.form-row { display: flex; gap: 12px; }
.form-row .field { flex: 1; }
/* 说明文字（2026-09-22 按需求）：
   · 颜色改成天蓝色 --sky（原来是灰色 #555）
   · 字号与面板标题、字段标签统一到 13.5px，整块看起来是同一套字 */
.hint { font-size: 13.5px; color: var(--sky); margin-top: 5px; line-height: 1.75; }

/* 邮件模板预览（2026-09-22）：模拟收件人看到的邮件正文，白底卡片 + 细边框 */
.mt-preview {
  border: 1px solid var(--line); border-radius: var(--radius); background: #fff;
  padding: 14px 16px; font-size: 13.5px; line-height: 1.7; color: var(--ink-2);
  box-shadow: var(--shadow-sm);
}
.mt-preview-head {
  font-size: 13.5px; font-weight: 400; color: var(--ink);
  padding-bottom: 8px; border-bottom: 1px dashed var(--line);
}
.mt-preview table { color: var(--ink-2); }
.mt-preview a { color: var(--sky); }
.mt-preview b, .mt-preview strong { font-weight: 700; } /* 邮件正文里的 **加粗** 要真的加粗（收件人看到的效果） */
.mt-preview code { font-family: Consolas, Monaco, monospace; font-size: 13px; }
.mt-preview-text {
  margin: 8px 0 0; white-space: pre-wrap; word-break: break-word;
  font-family: Consolas, Monaco, monospace; font-size: 12.5px; color: var(--ink-3);
  background: var(--bg-gray); border: 1px solid var(--line-soft); border-radius: 6px; padding: 10px 12px;
}

/* ---------------- 按钮 ---------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  border: none; border-radius: 6px; padding: 8px 17px; font-size: 13px; cursor: pointer;
  background: linear-gradient(180deg, #4C7DFF, #2E5BFF); color: #fff; text-decoration: none;
  transition: filter .15s ease, box-shadow .15s ease;
  font-weight: 400; box-shadow: 0 1px 2px rgba(46, 91, 255, .28);
}
.btn:hover { filter: brightness(1.07); box-shadow: 0 3px 10px rgba(46, 91, 255, .26); }
.btn:disabled { opacity: .4; cursor: not-allowed; }
.btn-block { width: 100%; }
/* 2026-09-22：小按钮 12px + 600 又小又重，改为 12.5px + 600（表格操作按钮也跟着变大些） */
.btn-sm { padding: 5px 12px; font-size: 12.5px; font-weight: 400; }
.btn-ghost { background: #fff; color: var(--ink-2); border: 1px solid var(--line); font-weight: 400; }
.btn-ghost:hover { border-color: var(--brand); color: var(--brand); background: #fff; }
.btn-danger { background: var(--danger); }
.btn-danger:hover { background: var(--danger-ink); }
.btn-green { background: var(--ok); }
.btn-green:hover { background: var(--ok-ink); }
.btn-warn { background: var(--warn); }
.btn-warn:hover { background: var(--warn-ink); }
.btn-orange { background: var(--brand); }
.btn-orange:hover { background: var(--brand-deep); }

/* ---------------- 弹窗 ---------------- */
.mask {
  position: fixed; inset: 0; background: rgba(0,0,0,.45);
  z-index: 100; display: flex; align-items: flex-start; justify-content: center;
  padding: 40px 16px; overflow-y: auto;
}
.modal {
  background: #fff; border-radius: 6px; width: 100%; max-width: 580px; padding: 22px;
  animation: pop .18s cubic-bezier(.2,.9,.3,1.1); margin: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,.15);
}
@keyframes pop { from { transform: scale(.97) translateY(6px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }
.modal h3 { font-size: 15px; font-weight: 400; margin-bottom: 16px; color: var(--ink); }
.modal .close {
  float: right; border: none; background: var(--bg-gray); color: var(--ink-3); font-size: 15px;
  cursor: pointer; width: 28px; height: 28px; border-radius: 4px; transition: all .15s; line-height: 1;
}
.modal .close:hover { background: var(--danger-bg); color: var(--danger); }

/* ---------------- 分页 ---------------- */
.pager { display: flex; gap: 8px; align-items: center; justify-content: flex-end; padding: 12px 20px; font-size: 13px; color: var(--ink-3); }

/* ---------------- 登录页 ---------------- */
.login-body {
  display: flex; align-items: center; justify-content: center; min-height: 100vh;
  background: var(--bg-gray);
}
.login-box {
  background: #fff; border: 1px solid var(--line);
  border-radius: 6px; padding: 34px; width: 380px;
}
.login-box h2 { text-align: center; margin-bottom: 24px; font-size: 16px; font-weight: 400; color: var(--ink); letter-spacing: .3px; }
.login-msg { color: var(--danger-ink); font-size: 13px; margin-top: 10px; text-align: center; font-weight: 400; }

/* ---------------- 其他 ---------------- */
.notice {
  border-radius: 4px; padding: 11px 15px; font-size: 12.5px; line-height: 1.7;
  border: 1px solid var(--line); background: var(--bg-gray); color: var(--ink-2); white-space: pre-wrap;
}
.toast {
  position: fixed; left: 50%; bottom: 40px; transform: translateX(-50%);
  background: rgba(0,0,0,.78); color: #fff;
  border-radius: 4px; padding: 11px 22px; font-size: 13px; z-index: 200;
  box-shadow: 0 6px 20px rgba(0,0,0,.15); animation: pop .18s ease;
}
.mt10 { margin-top: 10px; }
.mt16 { margin-top: 16px; }
.text-center { text-align: center; }
.muted { color: var(--ink-4); font-size: 12px; }
/* 表格里的 .muted（如文章列表的发布时间/更新时间）跟表格正文同字号（2026-09-22 用户要求一致） */
.tbl td.muted, .tbl td .muted { font-size: 13px; }
.mono { font-family: Consolas, Monaco, monospace; font-size: 12px; word-break: break-all; }
.empty-tip { text-align: center; color: var(--ink-4); padding: 32px 0; }
.radio-row { display: flex; gap: 20px; align-items: center; }
.radio-row label { display: flex; gap: 6px; align-items: center; font-size: 14px; color: var(--ink); margin: 0; cursor: pointer; }
.radio-row input[type=radio] { accent-color: var(--brand); }

/* 销售方式：两个可选卡片，避免长文案被挤成竖排 */
/* 注意：.field label 的优先级更高，这里必须写成 .field .sale-mode 才能覆盖 */
.sale-modes { display: grid; gap: 8px; }
.field .sale-mode, .sale-mode {
  display: flex; gap: 10px; align-items: flex-start; margin: 0; cursor: pointer;
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; background: #fff;
  font-size: 13.5px; line-height: 1.55; color: var(--ink); font-weight: 400; margin-bottom: 0;
}
.field .sale-mode:hover, .sale-mode:hover { border-color: var(--brand); }
.field .sale-mode input[type=radio], .sale-mode input[type=radio] {
  /* 必须显式定宽：后台全局有 .field input { width: 100% }，会把单选按钮撑满整行、把文字挤成竖排 */
  width: 15px; height: 15px; flex: 0 0 15px; margin: 3px 0 0 0; accent-color: var(--brand);
}
.field .sale-mode > span, .sale-mode > span { display: block; min-width: 0; }
.field .sale-mode b, .sale-mode b { display: block; font-size: 13.5px; line-height: 1.4; color: var(--ink); }
.field .sale-mode .muted, .sale-mode .muted { display: block; font-size: 12px; line-height: 1.5; color: var(--ink-4); font-weight: 400; margin-top: 2px; }
.field .sale-mode:has(input:checked), .sale-mode:has(input:checked) { border-color: var(--brand); background: #f5f9ff; }

/* 支付渠道卡片（支付设置页；当前只有 ANTOM，后续可扩展多渠道） */
.channels { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.channel-card { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; background: #fff; }
.channel-card.active { border-color: var(--brand); background: #f5f9ff; }
.channel-card.add { border-style: dashed; color: var(--ink-4); }
.channel-card .ch-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.channel-card .ch-name { font-weight: 400; font-size: 14px; color: var(--ink); }
.channel-card .ch-desc { font-size: 12px; color: var(--ink-4); margin-top: 4px; line-height: 1.6; }

/* 固定档位：每个类型一个区块；每行两行式布局（输入一行、单价提示一行），窄屏也不会挤成竖排 */
.pkg-block { padding: 6px 0; }
.pkg-block.grouped { border: 1px solid #eef1f5; border-radius: 8px; padding: 10px 12px; margin: 8px 0; }
.pkg-block-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }
.pkg-block-head .muted { font-size: 12px; }
.pkg-list, .pkgList { display: block; }
.pkg-row { padding: 8px 0; border-bottom: 1px dashed #eef1f5; }
.pkg-row:last-child { border-bottom: 0; }
.pkg-line { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; white-space: nowrap; }
.pkg-line .pkg-no { min-width: 40px; }
.pkg-line .pkg-price-label { margin-left: 6px; }
.pkg-line input { width: 108px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px; font-size: 13px; }
.pkg-line input.pkPrice { width: 120px; }
.pkg-line .muted { font-size: 12px; }
.pkg-row .pkCalc { margin-top: 4px; font-size: 12px; color: var(--ink-4); }
.pkg-row .pkCalc b { color: var(--ink-2); }
.pkg-block .pkgAddRow { margin-top: 8px; }
textarea.mono { font-family: Consolas, Monaco, monospace; }
.status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }

/* ============================================================
   移动端适配（2026-09-20 新增，纯 CSS，桌面端行为完全不变）
   · ≤900px：侧边栏变抽屉（顶部 ☰ 展开 / 点遮罩或点菜单收起）
   · ≤640px：表格横向滚动、弹窗底部全宽、按钮加大、输入框 16px（防 iOS 聚焦缩放）
   ============================================================ */

/* 桌面端隐藏的移动端元素 */
.topbar { display: none; }
.side-backdrop { display: none; }

@media (max-width: 900px) {
  body { -webkit-text-size-adjust: 100%; }

  .topbar {
    display: flex; align-items: center; gap: 10px;
    position: sticky; top: 0; z-index: 60;
    min-height: 52px; padding: 0 max(14px, env(safe-area-inset-left)) 0 max(14px, env(safe-area-inset-left));
    background: #fff; border-bottom: 1px solid var(--line);
  }
  .topbar .burger {
    width: 40px; height: 40px; flex: 0 0 40px; border-radius: 8px;
    border: 1px solid var(--line); background: #fff; color: var(--ink);
    font-size: 18px; line-height: 1; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
  }
  .topbar .burger:active { background: var(--bg-gray); }
  .topbar .topbar-title { font-size: 14.5px; font-weight: 400; color: var(--brand); letter-spacing: .3px; }

  /* 侧边栏 → 抽屉 */
  .layout { min-height: auto; }
  .sidebar {
    position: fixed; left: 0; top: 0; bottom: 0; height: 100%;
    width: 78vw; max-width: 300px; z-index: 80;
    transform: translateX(-100%); transition: transform .22s ease;
    box-shadow: 0 0 30px rgba(0,0,0,.18); overflow-y: auto;
  }
  .sidebar.open { transform: translateX(0); }
  .side-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 70; }
  .side-backdrop.show { display: block; }
  .side-nav a { padding: 14px 20px; font-size: 14.5px; }
  .side-user { padding: 15px 20px; }

  .main { padding: 14px; }
  .page { max-width: none; }
}

@media (max-width: 640px) {
  h2.page-title { font-size: 16.5px; margin-bottom: 12px; }
  .main { padding: 12px 10px calc(24px + env(safe-area-inset-bottom)); }

  /* 工具栏：整行堆叠、输入框占满 */
  .toolbar { gap: 8px; }
  .toolbar input, .toolbar select { flex: 1 1 100%; width: 100%; padding: 10px 12px; }
  .toolbar .btn { flex: 1 1 auto; }
  .toolbar .spacer { display: none; }

  /* 输入框统一 16px：iOS 聚焦时不会自动放大页面 */
  input, select, textarea { font-size: 16px !important; }
  .field input, .field select, .field textarea { padding: 11px 12px; }
  .form-row { flex-wrap: wrap; gap: 10px; }
  .form-row .field { flex: 1 1 100%; }

  /* 按钮加大点击区域 */
  .btn { padding: 10px 16px; min-height: 42px; }
  .btn-sm { padding: 8px 12px; min-height: 34px; }

  /* 表格：在面板内横向滚动，不再撑破页面 */
  .panel { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  table.tbl { min-width: 660px; }
  .tbl th, .tbl td { padding: 10px; white-space: nowrap; }
  /* ⚠️ 手机上别把「专门用来换行」的那些列也 nowrap（2026-09-29 探针查出来的）：
     white-space:nowrap 会盖掉 overflow-wrap:anywhere —— 长分类名 / 长商品名 / 长联系方式
     在手机上一行排不下就直接溢出单元格、压到隔壁列上（390px 实测：商品管理分类列溢出 79px、
     订单管理 28 个单元格溢 56~103px）→ 这几类列在窄屏下恢复换行。 */
  .tbl td.ln-xs, .tbl td.ln-sm, .tbl td.ln-md, .tbl td.ln-lg, .tbl td.ln-xl,
  .tbl td.stock-cell, .tbl td.mono { white-space: normal; }

  /* 弹窗：底部全宽 + 内部滚动（margin:0 覆盖基础样式的 margin:auto，否则会垂直居中而不是贴底） */
  .mask { padding: 0; align-items: flex-end; }
  .modal {
    max-width: 100%; width: 100%; margin: 0; border-radius: 14px 14px 0 0;
    max-height: 92vh; overflow-y: auto; padding: 18px 16px calc(22px + env(safe-area-inset-bottom));
  }
  .modal h3 { font-size: 16px; }
  .modal .close { width: 34px; height: 34px; }

  /* 统计卡两列 */
  .cards { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
  .stat { padding: 13px 14px; }
  .stat .num { font-size: 21px; }

  /* 固定档位输入窄一点 */
  .pkg-line input { width: 84px; }
  .pkg-line input.pkPrice { width: 92px; }

  .pager { justify-content: center; flex-wrap: wrap; padding: 10px 12px; }

  /* 登录页 */
  .login-box { width: 100%; max-width: 380px; padding: 24px 18px; margin: 16px; }

  /* 触控细节 */
  * { -webkit-tap-highlight-color: transparent; }
  .btn, .side-nav a, .modal .close, .topbar .burger { touch-action: manipulation; }
}

/* ============================================================
   子账号管理：权限勾选区（2026-09-21 新增）
   注意：这里的选择器要写成 .perm-box .xxx 才压得过 admin.css 里
   `.field label` / `.field input` 的全局样式（同 .sale-mode 的处理方式）
   ============================================================ */
.perm-tools { flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.perm-tools .btn { margin: 0; }
.perm-box {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px 16px;
  border: 1px solid var(--line); border-radius: 6px; padding: 12px 14px;
  background: #fbfcfe; max-height: 40vh; overflow: auto;
}
.perm-box .perm-group-head { font-size: 13px; font-weight: 400; color: var(--ink-2); margin-bottom: 6px; }
.perm-box .perm-items { display: flex; flex-direction: column; gap: 6px; }
.perm-box .perm-item {
  display: flex; align-items: center; gap: 6px; margin: 0; cursor: pointer;
  font-size: 13px; font-weight: 500; color: var(--ink); line-height: 1.4;
}
.perm-box .perm-item input[type=checkbox] {
  width: auto; height: auto; margin: 0; padding: 0; flex: none;
  border: 0; accent-color: var(--brand);
}

@media (max-width: 640px) {
  .perm-box { grid-template-columns: 1fr; max-height: 46vh; }
  .perm-tools .btn { flex: 1; }
}

/* ============================================================
   媒体库（2026-09-22 内容管理第 1 轮）
   缩略图网格 + 上传拖拽区；选图弹窗复用同一套 .media-grid
   ============================================================ */
.media-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
  gap: 14px; min-height: 60px;
}
/* 拖拽上传高亮（由 js 加/去 class） */
.panel-body.media-drop-active { outline: 2px dashed var(--brand); outline-offset: -6px; background: var(--brand-soft); }

.media-card {
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
  background: #fff; display: flex; flex-direction: column; transition: border-color .12s, box-shadow .12s;
}
.media-card:hover { border-color: var(--brand-line); box-shadow: 0 2px 10px rgba(15,23,42,.06); }
.media-thumb {
  height: 124px; background: var(--bg-gray); cursor: zoom-in;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  /* 棋盘格底纹：透明 PNG 也看得出边界 */
  background-image:
    linear-gradient(45deg, #f0f0f0 25%, transparent 25%, transparent 75%, #f0f0f0 75%),
    linear-gradient(45deg, #f0f0f0 25%, transparent 25%, transparent 75%, #f0f0f0 75%);
  background-size: 16px 16px; background-position: 0 0, 8px 8px;
}
.media-thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.media-meta { padding: 8px 10px 6px; min-width: 0; }
.media-title {
  font-size: 13px; font-weight: 400; color: var(--ink); line-height: 1.4;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.media-sub { font-size: 11.5px; color: var(--ink-4); margin-top: 3px; }
.media-acts { display: flex; gap: 6px; padding: 0 10px 10px; flex-wrap: wrap; }
.media-acts .btn { flex: 1; min-width: 62px; padding: 4px 8px; font-size: 12px; }

/* 选图弹窗内的可点缩略图 */
.media-grid button.media-item {
  border: 1px solid var(--line); border-radius: 8px; background: #fff; cursor: pointer;
  padding: 0; overflow: hidden; text-align: left; display: flex; flex-direction: column;
  transition: border-color .12s, box-shadow .12s;
}
.media-grid button.media-item:hover { border-color: var(--brand); box-shadow: 0 2px 10px rgba(46, 91, 255, .10); }
.media-grid button.media-item img { width: 100%; height: 96px; object-fit: contain; background: var(--bg-gray); display: block; }
.media-grid button.media-item .media-name {
  font-size: 12px; color: var(--ink-2); padding: 6px 8px; line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}

@media (max-width: 640px) {
  .media-grid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 10px; }
  .media-thumb { height: 100px; }
  .media-acts .btn { flex: 1 1 40%; }
}

/* ============================================================
   文章库（2026-09-22 内容管理第 2 轮）
   左写右预览的 Markdown 编辑器；预览区样式与前台文章页保持一致的阅读感
   ============================================================ */
.editor-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: stretch;
}
.editor-input {
  width: 100%; min-height: 46vh; resize: vertical; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
  font-family: Consolas, Monaco, "Courier New", monospace; font-size: 13px; line-height: 1.75; color: var(--ink);
}
.editor-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 2px rgba(46, 91, 255, .10); }
.editor-preview {
  min-height: 46vh; max-height: 70vh; overflow: auto; padding: 14px 18px;
  border: 1px solid var(--line); border-radius: 6px; background: #fbfcfe;
  font-size: 14px; line-height: 1.8; color: var(--ink-2); word-wrap: break-word;
}
/* 预览区排版（尽量贴近前台文章页） */
.editor-preview > :first-child { margin-top: 0; }
.editor-preview h1 { font-size: 21px; font-weight: 400; color: var(--ink); margin: 18px 0 10px; }
.editor-preview h2 { font-size: 18px; font-weight: 400; color: var(--ink); margin: 18px 0 9px; }
.editor-preview h3 { font-size: 16px; font-weight: 400; color: var(--ink); margin: 16px 0 8px; }
.editor-preview h4, .editor-preview h5, .editor-preview h6 { font-size: 14px; font-weight: 400; color: var(--ink); margin: 14px 0 7px; }
.editor-preview p { margin: 10px 0; }
.editor-preview ul, .editor-preview ol { margin: 10px 0; padding-left: 24px; }
.editor-preview ul { list-style: disc; }
.editor-preview ol { list-style: decimal; }
.editor-preview li { margin: 4px 0; }
.editor-preview blockquote {
  margin: 12px 0; padding: 8px 14px; border-left: 3px solid var(--brand-line);
  background: var(--brand-soft); color: var(--ink-3); border-radius: 0 6px 6px 0;
}
.editor-preview blockquote p { margin: 4px 0; }
.editor-preview code {
  background: #eef1f6; padding: 1px 5px; border-radius: 3px;
  font-family: Consolas, Monaco, monospace; font-size: 12.5px; color: #b1360a;
}
.editor-preview pre {
  background: #0F172A; color: #e2e8f0; padding: 12px 14px; border-radius: 6px;
  overflow-x: auto; margin: 12px 0;
}
.editor-preview pre code { background: none; color: inherit; padding: 0; font-size: 12.5px; }
.editor-preview img { max-width: 100%; border-radius: 6px; margin: 10px 0; display: block; }
.editor-preview a { color: var(--link-blue); text-decoration: underline; }
.editor-preview hr { border: none; border-top: 1px solid var(--line); margin: 18px 0; }
/* 表格（与前台 .article-body table 同一套观感）；对齐用每列内联 style，不在这里写 class 规则 */
.editor-preview .table-wrap { margin: 12px 0; overflow-x: auto; }
.editor-preview table { width: 100%; border-collapse: collapse; font-size: 13px; border: 1px solid var(--line); }
.editor-preview thead th {
  background: #f1f5f9; color: var(--ink); font-weight: 700; text-align: left; font-size: 12.5px;
  padding: 8px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
.editor-preview tbody td { padding: 8px 10px; border-bottom: 1px solid var(--line-soft); color: var(--ink-2); vertical-align: top; }
.editor-preview tbody tr:last-child td { border-bottom: none; }
.editor-preview tbody tr:nth-child(even) td { background: #fafcff; }
.editor-preview table.align-center th, .editor-preview table.align-center td { text-align: center; }
.editor-preview table.align-right th, .editor-preview table.align-right td { text-align: right; }

@media (max-width: 900px) {
  .editor-grid { grid-template-columns: 1fr; }
  .editor-input, .editor-preview { min-height: 34vh; }
}

/* ============================================================
   页头页脚配置（2026-09-22 内容管理第 3 轮）
   页脚链接列的动态行
   ============================================================ */
.panel .foot-col {
  border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px;
  margin-bottom: 10px; background: #fbfcfe;
}
.foot-col .fc-link { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.foot-col .fc-title,
.foot-col .fc-link input {
  padding: 6px 9px; border: 1px solid var(--line); border-radius: 5px;
  font-size: 13px; color: var(--ink); background: #fff;
}
.foot-col .fc-link input:focus, .foot-col .fc-title:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 2px rgba(46, 91, 255, .10);
}
.foot-col .btn { flex: none; }

@media (max-width: 640px) {
  .foot-col .fc-link { flex-wrap: wrap; }
  .foot-col .fc-link input { width: 100% !important; flex: 1 1 100%; }
}

/* 二维码卡片行（页头页脚配置页，2026-09-22） */
.qr-card {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px;
  margin-bottom: 10px; background: #fbfcfe;
}
.qr-card .qr-img {
  width: 64px; height: 64px; flex: none; object-fit: contain;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
}
.qr-card input {
  padding: 6px 9px; border: 1px solid var(--line); border-radius: 5px; font-size: 13px;
}
.qr-card input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 2px rgba(46, 91, 255, .10); }
@media (max-width: 640px) {
  .qr-card { flex-wrap: wrap; }
  .qr-card .qr-img { width: 52px; height: 52px; }
}

/* ============================================================
   宽弹窗 + 商品编辑「左表单 / 右预览」两栏（2026-09-23）
   需求原话：弹窗卡片横向铺满屏幕，最右侧留一个预览框，
             实时预览这个商品页面（买家看到的样子）。
   设计：铺满 ≠ 输入框拉满 —— 表单内部走 6 列栅格（字段按重要性跨列），
         右栏固定 384px 放预览，窄屏自动折到底部（点按钮展开）。
   ============================================================ */
/* 左右各留 16px 边距（.mask 原本就有 padding:40px 16px；
   这里只压上下的空白，让铺满后的编辑区能多用一点高度）
   ⚠️ 必须限定 min-width:901px：900px 以下 .mask 要变成贴底的抽屉式弹窗，
      若在此处无条件覆盖 padding，会按「后写的赢」把移动端布局顶掉 */
@media (min-width: 901px) {
  .mask-wide { padding: 16px; }
}
.modal-wide { max-width: none; position: relative; }
.modal-wide .close { position: absolute; right: 22px; top: 22px; }

/* 宽弹窗的通用骨架（所有宽度都套用）：纵向 flex —— 头部固定、底部操作条固定、
   中间 .pe-wrap 自己滚。
   ⚠️ 为什么不能再用 position:sticky 做底部按钮：sticky 是"浮"在内容上方的，
      滚动时会把下面那几行字段挡住（2026-09-23 用户反馈：手机端「取消/保存」遮挡页面内容）。
      改成 flex 固定的底部条后，内容只会被"裁在可视区里"，永远不会被盖住。 */
.modal-wide { display: flex; flex-direction: column; overflow: hidden; max-height: calc(100vh - 32px); }
.modal-wide .pe-head { flex: none; }
.modal-wide .pe-foot { flex: none; position: static; margin-top: 12px; }
.pe-wrap { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

.pe-head { display: flex; align-items: baseline; gap: 10px; padding-right: 44px; margin-bottom: 14px; }
.pe-head h3 { margin: 0; }
.pe-head .pe-head-tip { font-size: 12.5px; color: var(--ink-4); }
.pe-wrap { display: flex; gap: 18px; align-items: flex-start; }

/* 表单区：6 列栅格，字段自己声明跨几列 */
.pe-form {
  flex: 1 1 auto; min-width: 0;
  display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 0 16px;
}
.pe-form > .span2 { grid-column: span 2; }
.pe-form > .span3 { grid-column: span 3; }
.pe-form > .span4 { grid-column: span 4; }
.pe-form > .span6 { grid-column: 1 / -1; }
/* 铺开后这两块内部也铺开：销售方式两张卡片并排；会员等级格子保持紧凑（限宽 760px，
   auto-fill 自动排 5 列左右，不因为"铺满"就把每个等级格子拉到 270px 宽） */
.pe-form .sale-modes { grid-template-columns: 1fr 1fr; }
.pe-form .member-level-box { max-width: 760px; }
.pe-form .radio-row { flex-wrap: wrap; row-gap: 4px; }
.pe-form .radio-row label { white-space: nowrap; }
/* 卡密库选择框别拉太长（它是 span3，但不该占满半屏） */
.pe-form #fCardStore { max-width: 420px; }

/* ---------------- 商品表单：类型配置卡片（2026-09-23 重构）----------------
   所有商品都是多规格（「类型」必填 ≥1），每个类型一张卡片；
   卡片内部随销售方式变：
     · 自由输入数量：基础单价 · 卡密库 · 广告语 一排（广告语最宽）+ 优惠档位
     · 固定档位：    左栏档位（宽）/ 右栏卡密库 + 广告语
   ============================================================ */
/* 「必填」小标记 */
.req-tag {
  font-size: 11px; color: var(--brand); background: var(--brand-soft);
  border: 1px solid var(--brand-line); border-radius: 4px; padding: 0 5px;
  margin-left: 6px; vertical-align: 1px; font-weight: 400;
}
.type-block {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 12px; background: #fff;
}
.tb-head { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; flex-wrap: wrap; }
.tb-head b { font-size: 13.5px; font-weight: 700; color: var(--ink); }
.tb-hot {
  display: flex; align-items: center; gap: 6px; margin: 0;
  font-size: 12.5px; font-weight: 400; color: var(--ink-3); cursor: pointer; user-select: none;
}
/* 自由输入数量：一排三个字段，广告语吃掉剩余宽度 */
.tb-row3 { display: flex; gap: 14px; align-items: flex-start; }
.tb-row3 .tb-price { flex: 0 0 160px; }
.tb-row3 .tb-store { flex: 0 0 260px; }
.tb-row3 .tb-slogan { flex: 1 1 auto; min-width: 0; }
/* 固定档位：左右两栏 */
.tb-cols { display: flex; gap: 16px; align-items: flex-start; }
.tb-main { flex: 1 1 auto; min-width: 0; }
.tb-side { flex: 0 0 320px; }
.tb-side .field:last-child { margin-bottom: 0; }
/* 档位列表（含「+ 添加档位」）用虚线框包起来，像后台的"一组配置" */
.pkg-box { border: 1px dashed #D6DCE4; border-radius: 8px; padding: 6px 12px 10px; }
.pkg-box .pkg-row { padding: 6px 0; border-bottom: 1px dashed #EEF1F5; }
.pkg-box .pkg-row:last-child { border-bottom: 0; }
.pkg-box .pkgAddRow { margin-top: 8px; }
/* 优惠档位行（原来是内联样式，现在统一成类，方便窄屏换行） */
.tier-row { display: flex; gap: 8px; align-items: center; margin: 6px 0; flex-wrap: wrap; }
.tier-row input, .tier-row select {
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px;
  font-size: 13px; background: #fff; color: var(--ink);
}
.tier-row input.tMin { width: 90px; }
.tier-row input.tValue { width: 110px; }
.tier-row select { width: auto; }
.tier-row .muted { font-size: 12px; }
@media (max-width: 1024px) {
  .tb-cols { flex-direction: column; }
  .tb-side { flex: 1 1 auto; width: 100%; }
  .tb-row3 { flex-wrap: wrap; }
  .tb-row3 .tb-price, .tb-row3 .tb-store { flex: 1 1 160px; }
  .tb-row3 .tb-slogan { flex: 1 1 100%; }
}
@media (max-width: 640px) {
  .tb-row3 { flex-direction: column; }
  .tb-row3 .tb-price, .tb-row3 .tb-store, .tb-row3 .tb-slogan { flex: 1 1 auto; width: 100%; }
}

/* ---------------- 左右两栏各自滚动（2026-09-23 用户要求）----------------
   为什么：铺满后一屏装不下，原来整页一根滚动条，往下看「档位配置」时
   右侧预览会一起滚走 —— 想对照着改就看不到了。
   怎么改：弹窗是「视口高 - 32px」的纵向 flex（见上面通用骨架），头部/底部不动，
   中间 .pe-wrap 里的左表单与右预览各占一半高度、各自 overflow-y:auto。
   ⚠️ .pe-wrap 必须 min-height:0，否则 flex 子项会被内容撑高、内部滚动条失效。
   ⚠️ 只有 ≥1025px 才把 .pe-wrap 拆成两栏各自滚；更窄时两栏是上下堆叠的，
      由 .pe-wrap 自己滚一根（同样是"内容不被底部按钮盖住"）。 */
@media (min-width: 1025px) {
  .mask-wide { overflow: hidden; }
  /* ⚠️ 必须 align-items: stretch（覆盖基础规则的 flex-start）：
     flex-start 会让两栏各自「按内容撑高」→ 滚动条不生效、弹窗照样超屏（实测 form 高 4040px）。
     stretch 之后两栏高度 = 容器高度，再由各自的 overflow-y:auto 裁切。 */
  .pe-wrap { overflow: visible; align-items: stretch; }
  .pe-form { overflow-y: auto; min-height: 0; padding-right: 8px; }
  .pe-side { position: static; overflow-y: auto; min-height: 0; padding-right: 8px; }
  /* 细滚动条：两条滚动条按系统默认会很粗、抢视线 */
  .pe-form, .pe-side { scrollbar-width: thin; scrollbar-color: #C6CCD6 transparent; }
  .pe-form::-webkit-scrollbar, .pe-side::-webkit-scrollbar { width: 8px; }
  .pe-form::-webkit-scrollbar-thumb, .pe-side::-webkit-scrollbar-thumb { background: #C6CCD6; border-radius: 4px; }
  .pe-form::-webkit-scrollbar-thumb:hover, .pe-side::-webkit-scrollbar-thumb:hover { background: #A9B2BF; }
  .pe-form::-webkit-scrollbar-track, .pe-side::-webkit-scrollbar-track { background: transparent; }
}
/* 窄屏（手机/平板）：整体保留贴底抽屉外观，高度同样受控、底部按钮固定不遮挡内容 */
@media (max-width: 1024px) {
  .pe-wrap { padding-right: 6px; scrollbar-width: thin; scrollbar-color: #C6CCD6 transparent; }
  .pe-wrap::-webkit-scrollbar { width: 8px; }
  .pe-wrap::-webkit-scrollbar-thumb { background: #C6CCD6; border-radius: 4px; }
  .pe-wrap::-webkit-scrollbar-track { background: transparent; }
}
@media (max-width: 900px) {
  /* 手机端：.mask 变贴底抽屉（原有规则），这里只把宽弹窗的高度与圆角对齐过去，
     并确保它不再自己滚（改由 .pe-wrap 滚，底部按钮就不会浮在内容上） */
  .mask-wide { padding: 0; }
  .modal-wide { max-height: 92vh; border-radius: 14px 14px 0 0; }
}

/* 右栏：预览 */
.pe-side { flex: 0 0 384px; position: sticky; top: 16px; }
.pe-pv-toggle { display: none; width: 100%; margin-bottom: 8px; }
.pe-preview {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-gray-2);
  padding: 12px; font-size: 13px;
}
.pe-preview-tip { font-size: 11.5px; color: var(--ink-4); line-height: 1.6; margin-bottom: 8px; }
.pe-preview-tip b { font-weight: 700; font-size: 11.5px; }

/* 底部操作条：铺满后按钮离字段很远，固定在底部更顺手。
   ⚠️ 不要改回 position:sticky —— sticky 是浮在内容上方的，会遮挡它下面那几行字段
      （2026-09-23 用户反馈手机端「取消/保存」遮挡内容）。位置由 .modal-wide 的 flex 骨架决定。 */
.pe-foot {
  flex: none; margin-top: 16px; padding: 12px 0 2px;
  display: flex; justify-content: flex-end; gap: 10px;
  background: #fff; border-top: 1px solid var(--line-soft);
}

/* ---------------- 预览内容：仿前台商城样式 ----------------
   ⚠️ 这里是**照着前台写的一份近似样式**（配色取自 frontend/tailwind.config.ts：
      accent #2E5BFF / border #E2E8F0 / text-primary #0F172A / 促销橙 #FF5722）。
      前台改版后要回来同步一次，否则预览会和真站点有细微出入。 */
.pv-pane {
  background: #fff; border: 1px solid #E2E8F0; border-radius: 12px;
  padding: 14px; box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
}
.pv-pane + .pv-pane { margin-top: 10px; }
.pv-pane-head { font-size: 11.5px; color: #64748B; margin-bottom: 8px; display: flex; justify-content: space-between; }
.pv-card-ribbon {
  position: absolute; left: -34px; top: 12px; transform: rotate(-45deg);
  background: #FF5722; color: #fff; font-size: 9.5px; font-weight: 700;
  letter-spacing: .5px; padding: 1px 34px;
}
.pv-card { position: relative; overflow: hidden; }
.pv-name-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.pv-name { font-size: 15px; font-weight: 600; color: #0F172A; line-height: 1.4; }
.pv-tag { flex: none; font-size: 10.5px; font-weight: 600; color: #2E5BFF; background: #EAF0FF; border-radius: 6px; padding: 1px 6px; }
.pv-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.pv-badge { font-size: 10.5px; border-radius: 6px; padding: 1px 6px; line-height: 18px; }
.pv-badge.promo { background: #FF5722; color: #fff; }
.pv-badge.low { background: #FFF7ED; color: #C2410C; border: 1px solid #FED7AA; }
.pv-desc {
  font-size: 12.5px; color: #475569; line-height: 1.65; margin-top: 8px; min-height: 41px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  /* 与前台的 break-words 对齐：一长串没空格的描述也要折行，否则只显示 1 行还横向溢出 */
  overflow-wrap: break-word; word-break: break-word;
}
.pv-tierhint {
  display: inline-block; margin-top: 10px; font-size: 12px; color: #C2410C;
  background: #FFF7ED; border-radius: 6px; padding: 3px 8px;
}
.pv-pricebar { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; margin-top: 12px; padding-top: 12px; border-top: 1px solid #E2E8F0; }
.pv-price { display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
.pv-price .pre, .pv-price .post { font-size: 12.5px; color: #64748B; }
.pv-price .num { font-size: 23px; font-weight: 700; color: #0F172A; letter-spacing: -.5px; }
.pv-listprice { font-size: 11.5px; color: #64748B; margin-top: 4px; }
.pv-listprice s { opacity: .8; }
.pv-listprice .lp { color: #C2410C; margin-left: 4px; }
.pv-stock { font-size: 11.5px; color: #64748B; margin-top: 4px; }
.pv-stock.low { color: #C2410C; font-weight: 500; }
.pv-buybtn {
  flex: none; background: #2E5BFF; color: #fff; border: none; border-radius: 8px;
  padding: 9px 17px; font-size: 13px; font-weight: 600; cursor: default;
}
.pv-label { font-size: 11.5px; color: #475569; font-weight: 500; margin: 10px 0 6px; }
/* 规格按钮：与前台一致 —— 宽度随名字长短自适应、自动换行、名字完整显示不截断
   （原来是固定 3 列网格 + 文字省略号，长名字会撑破预览框：2026-09-24 用户反馈） */
.pv-types { display: flex; flex-wrap: wrap; gap: 8px; }
.pv-type {
  position: relative; border: 1px solid #E2E8F0; border-radius: 8px; padding: 8px 12px;
  font-size: 12.5px; color: #475569; background: #fff; cursor: pointer; text-align: center;
  white-space: nowrap; overflow: visible;
}
.pv-type span { display: block; }
.pv-type.on { border-color: #2E5BFF; background: rgba(46, 91, 255, .12); color: #2E5BFF; font-weight: 500; }
/* 规格区与「选择档位」之间的分隔线（跟前台弹窗那条 border-t 对应） */
.pv-sep { border-top: 1px solid #E2E8F0; margin-top: 4px; }
.pv-hot {
  position: absolute; right: -5px; top: -6px; background: #FF5722; color: #fff;
  border-radius: 8px; font-size: 11px; line-height: 15px; padding: 0 4px;
}
.pv-pkgs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pv-pkg { position: relative; border: 1px solid #E2E8F0; border-radius: 8px; padding: 10px; background: #fff; cursor: pointer; text-align: left; }
.pv-pkg.on { border-color: #2E5BFF; box-shadow: 0 0 0 2px rgba(46, 91, 255, .18); }
.pv-pkg .qty { font-size: 12.5px; color: #0F172A; font-weight: 500; }
.pv-pkg .amt { font-size: 16px; font-weight: 700; color: #0F172A; margin-top: 3px; }
.pv-pkg .unit { font-size: 11px; color: #64748B; margin-top: 2px; }
.pv-pkg .unit .off { color: #C2410C; font-weight: 500; }
.pv-pkg .save { font-size: 11px; color: #047857; font-weight: 500; margin-top: 2px; }
.pv-best { position: absolute; right: 7px; top: 7px; background: #FF5722; color: #fff; font-size: 9.5px; border-radius: 6px; padding: 0 5px; }
.pv-empty { border: 1px dashed #E2E8F0; border-radius: 8px; background: #F8FAFC; padding: 16px; text-align: center; font-size: 11.5px; color: #64748B; }
.pv-qty { display: flex; align-items: center; gap: 8px; }
.pv-qty button { width: 34px; height: 34px; border: 1px solid #E2E8F0; border-radius: 8px; background: #fff; font-size: 16px; color: #0F172A; cursor: pointer; }
.pv-qty input { width: 72px; text-align: center; border: 1px solid #E2E8F0; border-radius: 8px; padding: 7px 8px; font-size: 13px; }
.pv-pass { border: 1px solid rgba(46, 91, 255, .3); background: rgba(46, 91, 255, .05); border-radius: 8px; padding: 10px; }
.pv-pass .lbl { font-size: 11.5px; color: #0F172A; font-weight: 500; }
.pv-pass input { width: 100%; border: 1px solid #E2E8F0; border-radius: 8px; padding: 7px 9px; font-size: 12.5px; margin-top: 6px; background: #fff; }
.pv-pass .hint { font-size: 10.5px; color: #475569; margin-top: 6px; line-height: 1.55; }
.pv-total { border: 1px solid #E2E8F0; background: #F8FAFC; border-radius: 8px; padding: 10px; margin-top: 10px; }
.pv-total-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 12.5px; color: #475569; }
.pv-total-row .amt { font-size: 16px; font-weight: 700; color: #0F172A; }
.pv-total-row .brk { font-size: 11px; color: #64748B; margin-left: 6px; }
.pv-note { font-size: 11px; border-radius: 6px; padding: 4px 8px; margin-top: 6px; }
.pv-note.tier { background: #FFF7ED; color: #C2410C; }
.pv-note.save { background: #ECFDF5; color: #047857; font-weight: 500; }
.pv-note.unlock { background: #FFF7ED; color: #C2410C; border: 1px dashed #FED7AA; cursor: pointer; }
.pv-slogan { font-size: 11.5px; color: #2E5BFF; font-weight: 500; margin-top: 6px; }
.pv-pay { width: 100%; background: #2E5BFF; color: #fff; border: none; border-radius: 8px; padding: 11px; font-size: 13px; font-weight: 500; margin-top: 10px; cursor: default; }
.pv-footnote { text-align: center; font-size: 10.5px; color: #64748B; margin-top: 6px; }

/* 窄屏：预览折到底部（默认收起，点按钮展开） */
@media (max-width: 1024px) {
  .pe-wrap { flex-direction: column; }
  .pe-side { flex: 1 1 auto; width: 100%; position: static; }
  .pe-pv-toggle { display: inline-flex; }
  .pe-preview { display: none; }
  .pe-preview.open { display: block; }
}
@media (max-width: 640px) {
  .pe-form { grid-template-columns: 1fr; }
  .pe-form > .span2, .pe-form > .span3, .pe-form > .span4 { grid-column: 1 / -1; }
  .pe-form .sale-modes { grid-template-columns: 1fr; }
  .pe-form .member-level-box { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pv-pkgs { grid-template-columns: 1fr; }
  .pe-head { flex-direction: column; gap: 2px; }
}

/* 「🏬 查看前台店铺」入口（2026-10-03 用户要求）：桌面端在侧栏 logo 下面，手机端在顶栏右侧。
   ⚠️ 字重 400、字体继承（全后台规矩：只有表格表头加粗）。 */
.side-shop {
  display: block; margin: 10px 14px 4px; padding: 7px 10px;
  border: 1px solid var(--line); border-radius: 8px;
  text-align: center; text-decoration: none; font-size: 12.5px; font-weight: 400;
  color: var(--ink-2); background: #fff;
}
.side-shop:hover { border-color: var(--brand); color: var(--brand); }
.topbar-shop {
  margin-left: auto; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px;
  text-decoration: none; font-size: 12.5px; font-weight: 400; color: var(--ink-2); background: #fff;
  white-space: nowrap;
}

/* ============================================================================
   ⭐⭐⭐ 手机端深度适配（2026-10-21 用户要求：后台适配手机端，完全适配，
        不要左右拉动，参考淘宝卖家 APP 千牛）
   ----------------------------------------------------------------------------
   放在文件**最后**：同权重下压过前面所有规则 ✓
   三块：
     ① 取消「表格内左右滚动」，改成「一单一张卡、字段竖排」
     ② 底部 Tab 栏（首页/订单/商品/我的）
     ③ 卡片里的字段排版（label 左 / 值右，长文本换行不溢出）
   ⚠️ 卡片化用的 data-label 由 js/admin-mobile.js 的 cardsify() 写进每个 td ✓
   ============================================================================ */

/* ⭐ 2026-10-26：手机端底部 Tab（.mtab）在桌面宽度不该出现 ——
   它的定位/样式全在下面的 @media(max-width:900px) 里，桌面宽度没命中就成了页面最底部一行素文字 ✗ */
@media (min-width: 901px) {
  .mtab { display: none !important; }
}

@media (max-width: 900px) {
  /* ---------- ② 底部 Tab 栏（千牛式） ---------- */
  .mtab {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 76;
    display: flex; align-items: stretch;
    background: #fff; border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -2px 14px rgba(15, 23, 42, .07);
  }
  .mtab a {
    flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; height: 54px; text-decoration: none;
    color: #8A94A6; font-size: 11.5px; letter-spacing: .2px;
  }
  .mtab a .ic { font-size: 19px; line-height: 1; filter: grayscale(1) opacity(.75); transition: filter .12s, transform .12s; }
  .mtab a.active { color: var(--brand); }
  .mtab a.active .ic { filter: none; transform: translateY(-1px) scale(1.06); }
  .mtab a:active { background: var(--brand-soft); }
  body.has-mtab .main { padding-bottom: calc(70px + env(safe-area-inset-bottom)) !important; }
  body.has-mtab .mask { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }
}

@media (max-width: 640px) {
  /* ---------- ① 先取消面板内的横向滚动 ---------- */
  .panel { overflow-x: visible; }
  .panel-body { overflow-x: visible; }
  table.tbl { min-width: 0 !important; width: 100%; }
  .tbl .colwGrip, .tbl .colwGripCell { display: none !important; }
  .tbl th, .tbl td { white-space: normal !important; }

  /* ---------- 表格 → 卡片 ---------- */
  table.tbl[data-mcard] { display: block; border: 0; }
  table.tbl[data-mcard] thead { display: none; }
  table.tbl[data-mcard] tbody { display: block; }
  table.tbl[data-mcard] tr.mc-row {
    display: block; position: relative;
    background: #fff; border: 1px solid var(--line); border-radius: 12px;
    margin: 0 0 10px; padding: 12px 13px 10px;
    box-shadow: 0 1px 3px rgba(15, 23, 42, .05);
  }
  table.tbl[data-mcard] tr.mc-row:hover { background: #fff; }
  table.tbl[data-mcard] tr.mc-row > td {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
    border: 0 !important; background: none !important;
    padding: 4px 0 !important; font-size: 13.5px; line-height: 1.55;
    text-align: right; min-height: 24px;
  }
  table.tbl[data-mcard] tr.mc-row > td::before {
    content: attr(data-label);
    flex: 0 0 auto; max-width: 38%; text-align: left;
    color: #8A94A6; font-size: 12.5px; line-height: 1.6;
  }
  table.tbl[data-mcard] tr.mc-row > td > * { min-width: 0; }
  table.tbl[data-mcard] tr.mc-row > td.ln-lg,
  table.tbl[data-mcard] tr.mc-row > td.ln-xl { word-break: break-word; }

  /* 卡片标题行：主标识（单号 / 商品名 / 卡密）—— 大字、左对齐、虚线分隔 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head {
    display: block; text-align: left;
    font-size: 14.5px; font-weight: 500; color: var(--ink);
    padding: 0 0 9px !important; margin-bottom: 7px;
    border-bottom: 1px dashed var(--line) !important;
    padding-right: 74px !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-head::before { display: none; }

  /* 操作列：按钮铺满一行、每个 ≥40px */
  table.tbl[data-mcard] tr.mc-row > td.mc-ops {
    gap: 8px;
    padding: 10px 0 0 !important; margin-top: 8px;
    border-top: 1px solid var(--line-soft) !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-ops::before { display: none; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ops .btn,
  table.tbl[data-mcard] tr.mc-row > td.mc-ops button { flex: 1 1 calc(50% - 8px); min-height: 40px; margin: 0; }

  /* 空态行居中 */
  table.tbl[data-mcard] td.mc-empty { display: block; text-align: center; color: #8A94A6; border: 0 !important; }
  table.tbl[data-mcard] td.mc-empty::before { display: none; }
  table.tbl[data-mcard] tr.mc-row:nth-child(2n) { background: #fff; }
}

/* ---------- 卡片显示策略（2026-10-21 追加）：表头行/合计行/折叠/优先级 ---------- */
@media (max-width: 640px) {
  /* 表头行藏掉（卡片每格自带列名） */
  table.tbl[data-mcard] tr.mc-headrow { display: none !important; }
  /* 被判定为"空白/低价值"的格子不占位置 */
  table.tbl[data-mcard] tr.mc-row > td.mc-hide { display: none !important; }
  /* 折叠字段：默认不显示，展开后显示 */
  table.tbl[data-mcard] tr.mc-row > td.mc-more { display: none !important; }
  table.tbl[data-mcard] tr.mc-row.mc-open > td.mc-more { display: flex !important; }

  /* 「展开更多」开关 */
  table.tbl[data-mcard] tr.mc-row .mc-toggle {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    margin: 8px -13px -10px; padding: 9px 13px;
    border-top: 1px solid var(--line-soft);
    background: #FAFBFD; color: var(--brand);
    font-size: 12.5px; border-radius: 0 0 12px 12px; cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  table.tbl[data-mcard] tr.mc-row .mc-toggle .arrow { transition: transform .15s; }
  table.tbl[data-mcard] tr.mc-row.mc-open .mc-toggle .arrow { transform: rotate(180deg); }

  /* 合计/汇总行：紧凑一点，别占一屏 */
  table.tbl[data-mcard] tr.mc-sum {
    background: var(--brand-soft) !important; border-color: var(--brand-line) !important;
    padding: 10px 13px !important;
  }
  table.tbl[data-mcard] tr.mc-sum > td { font-size: 12.5px !important; padding: 2px 0 !important; min-height: 0 !important; }
  table.tbl[data-mcard] tr.mc-sum > td.mc-head { font-size: 13.5px !important; padding-right: 0 !important; }
}

/* ---------- 筛选区折叠（手机端） ---------- */
@media (max-width: 640px) {
  .mfilter { width: 100%; border: 1px solid var(--line); border-radius: 10px; background: #fff; overflow: hidden; }
  .mfilter > summary {
    display: flex; align-items: center; justify-content: space-between;
    padding: 12px 14px; font-size: 14px; color: var(--ink); cursor: pointer;
    list-style: none; background: #fff;
  }
  .mfilter > summary::-webkit-details-marker { display: none; }
  .mfilter > summary .arrow { transition: transform .15s; color: #8A94A6; }
  .mfilter[open] > summary .arrow { transform: rotate(180deg); }
  .mfilter[open] > summary { border-bottom: 1px solid var(--line-soft); }
  .mfilter .mfilter-body { display: flex; flex-wrap: wrap; gap: 8px; padding: 12px 14px; }
  .mfilter .mfilter-body > * { flex: 1 1 100%; margin: 0 !important; }
  .mfilter .mfilter-body > .btn { flex: 1 1 calc(50% - 8px); }
  .mfilter .mfilter-body .spacer { display: none; }
}

/* ---------- 卡片内清掉桌面端的「列宽上限」（否则单号 118px 被拆 3 行、
   时间格 94px 两个 div 挤成一行叠字 ✗）2026-10-21 ---------- */
@media (max-width: 640px) {
  table.tbl[data-mcard] tr.mc-row > td {
    min-width: 0 !important; max-width: none !important; width: auto !important;
  }
  /* 单号 / 时间 / 金额 / 账号 这些桌面端带 cap 的格子，卡片里一律占满可用宽度 */
  table.tbl[data-mcard] tr.mc-row > td.orderno-cell,
  table.tbl[data-mcard] tr.mc-row > td.time-cell,
  table.tbl[data-mcard] tr.mc-row > td.money-cell,
  table.tbl[data-mcard] tr.mc-row > td.acct-cell,
  table.tbl[data-mcard] tr.mc-row > td.goods-cell,
  table.tbl[data-mcard] tr.mc-row > td.ln-xs,
  table.tbl[data-mcard] tr.mc-row > td.ln-sm,
  table.tbl[data-mcard] tr.mc-row > td.ln-md,
  table.tbl[data-mcard] tr.mc-row > td.ln-lg,
  table.tbl[data-mcard] tr.mc-row > td.ln-xl {
    min-width: 0 !important; max-width: none !important;
  }
  /* 卡片标题行（单号）允许换行但别 break-all 拆数字 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head { word-break: normal !important; overflow-wrap: anywhere; }
  /* 时间格的「日期 + 时刻」两个 div：横排放一行，不再互相压字 */
  table.tbl[data-mcard] tr.mc-row > td.time-cell { flex-wrap: wrap; align-items: baseline; gap: 2px 8px; }
  table.tbl[data-mcard] tr.mc-row > td.time-cell > div { white-space: nowrap; }
  table.tbl[data-mcard] tr.mc-row > td.time-cell .t-time { color: #8A94A6; }
  /* 状态徽章一行排开、靠右 */
  table.tbl[data-mcard] tr.mc-row > td .stage-cell { align-items: flex-end; }
  table.tbl[data-mcard] tr.mc-row > td .stage-cell .badge-row { justify-content: flex-end; }
  /* 商品格里的多行（主商品/规格/赠品）左对齐更易读 */
  table.tbl[data-mcard] tr.mc-row > td.goods-cell { text-align: right; }
}

/* ---------- 卡片里的「商品」副标题（紧贴标题、整行左对齐） ---------- */
@media (max-width: 640px) {
  table.tbl[data-mcard] tr.mc-row > td.mc-sub {
    display: block !important; width: 100% !important; max-width: none !important;
    text-align: left !important; padding: 0 0 9px !important; margin: -2px 0 7px;
    font-size: 13px; line-height: 1.55; color: var(--ink-2);
    border-bottom: 1px solid var(--line-soft) !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub::before { display: none; }
  /* 有副标题时，标题那条虚线就不需要了（连着看更顺） */
  table.tbl[data-mcard] tr.mc-row > td.mc-head:has(+ td.mc-sub) { border-bottom: 0 !important; padding-bottom: 4px !important; }
}

/* ---------- 勾选框列（卡密库批量选择）：卡片右上角，标"选" ---------- */
@media (max-width: 640px) {
  table.tbl[data-mcard] tr.mc-row > td.mc-check {
    position: absolute; top: 10px; right: 12px; left: auto;
    display: block !important; padding: 0 !important; min-height: 0 !important;
    width: auto !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-check::before { display: none; }
  table.tbl[data-mcard] tr.mc-row > td.mc-check input { width: 20px; height: 20px; }
  /* 有勾选框的卡片，标题行右侧再让一点位 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head { padding-right: 44px !important; }
}

/* ============================================================================
   ⭐ 2026-10-21 用户拍板：「卡片**全显示**，但排版紧凑一点（空白太多）」
   → 短字段两列并排（标签在上、值在下），长文本/链接/时间占整行；
     行高、内边距、卡片间距一起收 ✓
   ============================================================================ */
@media (max-width: 640px) {
  /* 卡片本身收一收 */
  table.tbl[data-mcard] tr.mc-row {
    padding: 9px 11px 9px !important; margin: 0 0 8px !important; border-radius: 10px !important;
  }
  /* 字段区 = 两列网格（短字段）+ 整行（长字段） */
  table.tbl[data-mcard] tr.mc-row {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 10px; row-gap: 6px;
  }
  /* 整行的格子：网格跨两列 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head,
  table.tbl[data-mcard] tr.mc-row > td.mc-sub,
  table.tbl[data-mcard] tr.mc-row > td.mc-wide,
  table.tbl[data-mcard] tr.mc-row > td.mc-ops,
  table.tbl[data-mcard] tr.mc-row > td.mc-empty,
  table.tbl[data-mcard] tr.mc-row > td.mc-check {
    grid-column: 1 / -1;
  }
  /* 短字段：标签一行、值一行，靠左（占半宽） */
  table.tbl[data-mcard] tr.mc-row > td.mc-half {
    display: block !important; text-align: left !important;
    padding: 0 !important; min-height: 0 !important; gap: 0 !important;
    border: 0 !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-half::before {
    display: block; content: attr(data-label);
    flex: none; max-width: none; font-size: 11.5px; line-height: 1.5; color: #97A1B2;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-half { font-size: 13.5px; line-height: 1.5; color: var(--ink); }

  /* 整行字段：标签左、值右，行高紧凑 */
  table.tbl[data-mcard] tr.mc-row > td.mc-wide {
    display: flex !important; align-items: baseline; justify-content: space-between; gap: 10px;
    padding: 0 !important; min-height: 0 !important; border: 0 !important;
    font-size: 13.5px; line-height: 1.5;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-wide::before {
    font-size: 12px; line-height: 1.5; color: #97A1B2; flex: 0 0 auto; max-width: 40%;
  }

  /* 标题行 / 商品副标题：紧凑 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head {
    font-size: 14.5px !important; padding: 0 0 2px !important; margin: 0 !important;
    border-bottom: 0 !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub {
    font-size: 13px !important; color: var(--ink-2); padding: 0 !important; margin: 0 !important;
    border-bottom: 0 !important; line-height: 1.5;
  }
  /* 标题 + 副标题下面来一条淡淡的横线，区分"头"和"字段" */
  table.tbl[data-mcard] tr.mc-row > td.mc-sub { padding-bottom: 6px !important; border-bottom: 1px solid var(--line-soft) !important; }
  table.tbl[data-mcard] tr.mc-row > td.mc-head:not(:has(+ td.mc-sub)) { padding-bottom: 5px !important; border-bottom: 1px solid var(--line-soft) !important; }

  /* 操作按钮：一行两列、36px 高 */
  /* ⚠️ 不加 display/grid 的 !important：行列数由 admin-mobile.js 写在行内
     （有 .row-actions 包裹层 → td block + 包裹层 grid；否则 td 自己 grid） */
  table.tbl[data-mcard] tr.mc-row > td.mc-ops {
    gap: 7px;
    padding: 8px 0 0 !important; margin-top: 2px;
    border-top: 1px solid var(--line-soft) !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-ops .btn,
  table.tbl[data-mcard] tr.mc-row > td.mc-ops button { min-height: 36px; padding: 8px 10px; margin: 0 !important; font-size: 13px; }

  /* 合计行也紧凑 */
  table.tbl[data-mcard] tr.mc-sum { padding: 8px 11px !important; }
  table.tbl[data-mcard] tr.mc-sum > td.mc-half::before { font-size: 11px; }

  /* 标题 / 副标题强制左对齐（桌面端 .goods-cell{text-align:right} 会盖过来 ✗） */
  table.tbl[data-mcard] tr.mc-row > td.mc-head,
  table.tbl[data-mcard] tr.mc-row > td.mc-sub { text-align: left !important; }

}

/* ============================================================================
   ⭐⭐ 2026-10-21 第二轮：用户圈出「卡片里有空洞、不够紧凑」
   三个根因（都在这里修）：
     ① 网格没有填满 → **空洞**：有的格子占两列（mc-wide），它前面如果剩一个半格，
        那个位置就空着 ✗ → `grid-auto-flow: row dense` 让后面的半格先回填 ✓
     ② 半格的「标签在上、值在下」两行太占高 ✗ → 改成**标签和值同一行**（省一半高度 ✓）
     ③ 「序号」排序控件（输入框+↑↓）放最前面，左边整片空 ✗ → JS 已把它挪到卡片末尾 ✓ 这里再压扁 ✓
   ============================================================================ */
@media (max-width: 640px) {
  /* ① 密集填充：空洞由后面的半格回填 */
  table.tbl[data-mcard] tr.mc-row {
    grid-auto-flow: row dense;
    align-items: start;
    column-gap: 12px; row-gap: 3px;
  }

  /* ② 半格：标签 + 值同一行（省一半高度） */
  table.tbl[data-mcard] tr.mc-row > td.mc-half {
    display: flex !important; align-items: baseline; justify-content: space-between; gap: 5px;
    text-align: left !important; padding: 1px 0 !important; min-height: 0 !important;
    font-size: 13.5px; line-height: 1.45;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-half::before {
    display: block; flex: 0 0 auto; max-width: none;
    font-size: 12px; line-height: 1.45; color: #97A1B2;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-half > * { min-width: 0; }

  /* 整行字段也压一行高 */
  table.tbl[data-mcard] tr.mc-row > td.mc-wide { padding: 1px 0 !important; line-height: 1.5; }
  table.tbl[data-mcard] tr.mc-row > td.mc-wide::before { font-size: 12px; }

  /* ③ 排序控件格：整行、压扁、放末尾 */
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl {
    grid-column: 1 / -1;
    display: flex !important; align-items: center; gap: 8px;
    padding: 6px 0 0 !important; margin-top: 4px;
    border-top: 1px dashed var(--line-soft) !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl::before { flex: 0 0 auto; font-size: 12px; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl input { width: 64px; min-height: 32px; padding: 4px 8px; font-size: 14px !important; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl button { min-height: 32px; min-width: 34px; padding: 4px 8px; }
  /* 排序控件的内部包裹层也压成一行（否则输入框和 ↑↓ 会上下错开 ✗） */
  /* 排序控件（输入框 + ↑↓）在手机上太挤、容易溢出 → **隐藏** ✓ 排序请在电脑端操作（真机实测：强制横排会溢出 96 个元素 ✗） */
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl { display: none !important; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl > div { display: flex !important; flex-direction: row !important; align-items: center; gap: 6px; flex-wrap: nowrap; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl > * { flex: 0 0 auto; }

  /* 字段区整体再收一点：标题/副标题与字段之间不留大缝 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head { padding-bottom: 1px !important; }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub { padding-bottom: 4px !important; }
}

/* ============================================================================
   ⭐⭐ 2026-10-21 第三轮（用户第 2 轮圈的 5 处）：
     ① 标题/副标题**提前**（order），这样 ID、数量这些短字段能并排，不再被标题打断成多行
     ② 操作按钮**按数量排一行**（--ops-n，由 admin-mobile.js 按按钮个数算）
     ③ 状态徽章**排一行**（不换行、字号缩一点）
     ④ 商品副标题（名称 + 规格）**并到一行**，利用右边那块空白（用户图1：'往右边空白处移动'）
   ============================================================================ */
@media (max-width: 640px) {
  /* ① 顺序：标题 → 副标题 → 字段 → 排序控件 → 操作按钮 */
  table.tbl[data-mcard] tr.mc-row > td.mc-head { order: -2; }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub { order: -1; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ctrl { order: 98; }
  table.tbl[data-mcard] tr.mc-row > td.mc-ops { order: 99; }

  /* ② 操作按钮：按个数一行平分 */
  /* ⚠️ 这里**不能加 display/grid 的 !important**：JS 会在行内决定「有包裹层就 display:block + 包裹层 grid」
     否则行内值被压掉 → 按钮只占一半宽（真机：订单 3 个按钮只有 52px 宽 ✗） */
  table.tbl[data-mcard] tr.mc-row > td.mc-ops {
    gap: 7px;
    padding: 8px 0 0 !important; margin-top: 4px;
    border-top: 1px solid var(--line-soft) !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-ops .btn,
  table.tbl[data-mcard] tr.mc-row > td.mc-ops button {
    min-height: 42px; padding: 8px 6px; margin: 0 !important;
    font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    width: 100%;
  }
  /* 按钮包裹层（.row-actions）在手机上占满整行、按个数分列（grid 由 JS 写在行内 ✓）
     必须压掉桌面的 .op-cell .row-actions { min-width:158px; max-width:190px }
     否则包裹层最宽只有 190px → 按钮被挤成 59px 宽（用户问「为什么这么小」就是这个，真机测到 kidW=190） */
  table.tbl[data-mcard] tr.mc-row > td.mc-ops > div {
    width: 100% !important; min-width: 0 !important; max-width: none !important;
  }

  /* ③ 状态徽章一行排开、靠右 */
  table.tbl[data-mcard] tr.mc-row > td .stage-cell { flex-direction: row !important; align-items: center; gap: 4px; flex-wrap: nowrap; }
  table.tbl[data-mcard] tr.mc-row > td .stage-cell .badge-row { flex-wrap: nowrap !important; justify-content: flex-end; gap: 4px; }
  table.tbl[data-mcard] tr.mc-row > td .stage-cell .badge,
  table.tbl[data-mcard] tr.mc-row > td .stage-cell .badge-row .badge { font-size: 11px !important; padding: 1px 5px !important; line-height: 16px !important; white-space: nowrap; }

  /* ④ 商品副标题：名称 + 规格并成一行（右半边不再空着） */
  table.tbl[data-mcard] tr.mc-row > td.mc-sub {
    display: flex !important; flex-wrap: nowrap; align-items: baseline; gap: 10px;
    overflow: hidden; padding-bottom: 5px !important;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub > div,
  table.tbl[data-mcard] tr.mc-row > td.mc-sub > span {
    display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub > div:last-child { color: #8A94A6; flex: 0 0 auto; }
  table.tbl[data-mcard] tr.mc-row > td.mc-sub > div:first-child { min-width: 0; }
}

/* ============================================================================
   ⭐ 2026-10-21 手机端 · 表单/上传页美化（用户要求「把表单页也过一遍」）
   目标：手指好点、不用缩放、不横向滚动
     · 上传位：「拍照 / 选图上传」变成**整行大按钮**（48px 高），说明文字在下
     · 字段：一律单列、输入框 16px（iOS 不会自动放大）
     · 主按钮：整行；次要按钮两列
     · 弹窗底部按钮：粘底、整行
   ============================================================================ */
@media (max-width: 640px) {
  /* ---- 实名/开店向导里的上传位：图片 + 大按钮 + 说明 ---- */
  .panel-body .field > div { flex-wrap: wrap !important; }
  .panel-body .field > div > div { flex: 1 1 100%; width: 100%; }
  .panel-body .field > div > img { max-width: 100% !important; max-height: 180px !important; height: auto !important; border-radius: 8px; }
  [data-rnpick] {
    width: 100%; min-height: 48px; margin-top: 8px;
    font-size: 14.5px !important; border-radius: 10px;
    border: 1px dashed var(--brand) !important; background: var(--brand-soft) !important; color: var(--brand) !important;
  }
  .panel-body .field > div > div > .hint { max-width: none !important; margin-top: 6px !important; }

  /* ---- 表单整体：字段单列、说明不挤 ---- */
  .panel-body { padding: 12px 12px 14px; }
  .panel-body .field { margin-bottom: 12px; }
  .panel-body .field > label { margin-bottom: 6px; display: block; }
  .panel-body .field input, .panel-body .field select, .panel-body .field textarea { width: 100%; }

  /* ---- 主按钮整行；连续两个按钮时两列平分 ---- */
  .panel-body > .btn, .panel-body > div > .btn { min-height: 44px; }
  .panel-body .btn-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }

  /* ---- 弹窗：底部按钮粘底、整行 ---- */
  .modal .btn-block, .modal .modal-foot .btn { min-height: 46px; width: 100%; }
  .modal .modal-foot { position: sticky; bottom: 0; background: #fff; padding-top: 10px; }

  /* ---- 表格外的操作条（如「+ 新增分类」「导出 CSV」）：整行大按钮 ---- */
  .page > .btn, .page > .panel > .panel-body > .btn { min-height: 44px; }
}
