/* ==========================================================================
   商家经营管理系统 · 管理后台样式
   无构建、无 npm：直接由 Go 提供静态文件。改完刷新即见（开发模式）。
   ========================================================================== */

/* 主题令牌。
   ──────────────────────────────────────────────────────────────────────
   这里的每个值都会被「技术管理 → 主题样式」注入的 :root 覆盖（见
   internal/web/theme_presets.go）。样式表里**不允许再出现写死的主色** ——
   唯一的例外是下面这个出厂默认（玫瑰丝绒），它同时是「没有配置时」的样子。

   三组令牌分别控制三件事：
     颜色  --accent / --accent-soft / --accent-line ……
     形状  --radius / --btn-radius / --input-radius
     底子  --bg / --card / --ink ……（中性色，决定冷暖）
*/
:root {
  --accent: #a83355;
  --accent-dark: #8a2b46;
  --accent-soft: #fbf3f5;
  --accent-line: #e9c9d3;
  --accent-2: #bc5a75;
  --nav-bg: #22171a;

  --ink: #1e1a16;
  --ink-2: #4a423a;
  --muted: #7b7064;
  --faint: #a89c8e;
  --line: #ebe3d7;
  --line-soft: #f5efe5;
  --bg: #faf7f2;
  --card: #fffdfa;

  --ok: #2f855a;
  --ok-soft: #eaf4ee;
  --warn: #b7791f;
  --warn-soft: #fdf8ef;
  --danger: #c53030;
  --danger-soft: #fdeaea;
  --info: #2b6cb0;
  --info-soft: #eaf1fa;

  --radius: 18px;
  /* 内部小件（标签块、缩略图、提示条、小卡片）的圆角。
     它跟随圆角轴变化，否则「硬边」主题里会散着一堆 10px 的圆角小块，
     看起来像没改完 —— 而这类不一致正是「丑」的主要来源。 */
  --radius-sm: 12px;
  --btn-radius: 999px;
  --input-radius: 12px;
  --card-shadow: 0 1px 2px rgba(20, 22, 26, .04), 0 8px 24px -14px rgba(20, 22, 26, .12);
  /* 旧名保留：样式表里几十处 var(--shadow)，逐处改是没必要的风险 */
  --shadow: var(--card-shadow);

  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

/* hidden 属性必须真的隐藏。
   类选择器上的 display（.flash / .svc / .pick / .picked 都是 flex）会盖掉
   UA 样式表里的 [hidden] { display: none } —— 于是「藏起来」的元素照样上屏。
   这不是样式问题而是功能问题：开单页用它做项目过滤、隐藏错误条与已选会员框，
   盖掉之后表现为「点分类没反应」「空红条一直挂在那」。 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--ink-2);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB",
               "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
h1, h2, h3 { margin: 0; color: var(--ink); font-weight: 700; letter-spacing: -.01em; }
.mono { font-family: var(--mono); font-size: 12.5px; }
.muted { color: var(--muted); }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.strong { font-weight: 650; color: var(--ink); }
.empty { color: var(--faint); font-size: 13px; margin: 8px 0 0; }

/* ── 主布局 ───────────────────────────────────────────────────────────── */

.shell {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  min-height: 100vh;
}

/* ── 侧栏 ─────────────────────────────────────────────────────────────── */

.side {
  background: var(--card);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 20px 20px 18px;
  border-bottom: 1px solid var(--line-soft);
}

.brand-logo {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
  color: #fff;
  font-weight: 700;
  font-size: 17px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 36px;
}

.brand-logo.big { width: 52px; height: 52px; flex: 0 0 52px; border-radius: 15px; font-size: 24px; }

/* Logo 图片：撑满那块圆角方块，但用 contain 而不是 cover ——
   招牌/文字型 Logo 被裁掉一条边比小一圈难看得多。
   背景仍保留渐变色：透明 PNG 放上去不会变成一块白板。 */
.brand-logo img { width: 100%; height: 100%; object-fit: contain; border-radius: inherit; display: block; }

/* 登录页页脚：联系方式 / 备案号各占一行，别挤成一条 */
.login-foot-sep { display: block; margin-top: 3px; font-size: 11.5px; color: var(--faint); }

.brand-text { display: flex; flex-direction: column; line-height: 1.3; }
.brand-text b { font-size: 15px; color: var(--ink); font-weight: 700; }
.brand-text span { font-size: 11.5px; color: var(--faint); letter-spacing: .04em; }

.menu { padding: 14px 12px 20px; flex: 1; }

.menu-group {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--faint);
  padding: 14px 10px 6px;
  text-transform: uppercase;
}

.menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: 13.5px;
  color: var(--ink-2);
  margin-bottom: 2px;
}

.menu-item .mi {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--line-soft);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 22px;
}

.menu-item .mt { flex: 1; }

.menu-item em {
  font-style: normal;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--faint);
  background: var(--line-soft);
  border-radius: 4px;
  padding: 1px 5px;
}

.menu-item.on {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 650;
}

.menu-item.on .mi { background: var(--accent); color: #fff; }
.menu-item.soon { color: var(--faint); cursor: default; }
.menu-item.soon .mi { background: var(--line-soft); color: var(--faint); }

.side-foot {
  border-top: 1px solid var(--line-soft);
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.me { display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0; }

.avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 700;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 32px;
}

.me-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.35; }
.me-text b { font-size: 13px; color: var(--ink); }
.me-text span {
  font-size: 11px;
  color: var(--faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.logout {
  font-size: 12px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 4px 10px;
  flex: 0 0 auto;
}
.logout:hover { color: var(--accent); border-color: var(--accent); }

/* ── 主区域 ───────────────────────────────────────────────────────────── */

.main { padding: 26px 30px 40px; min-width: 0; }

.topbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 22px;
}

.topbar h1 { font-size: 22px; }
.topbar .sub { margin: 4px 0 0; font-size: 12.5px; color: var(--faint); }
.topbar-right { display: flex; gap: 8px; flex-wrap: wrap; }

.pill {
  font-size: 12.5px;
  padding: 5px 12px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--accent-dark);
  font-weight: 600;
  white-space: nowrap;
}

.pill.soft {
  background: var(--bg);
  border-color: var(--line);
  color: var(--muted);
  font-weight: 500;
}

/* ── 指标卡 ───────────────────────────────────────────────────────────── */

.kpis {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
  margin-bottom: 20px;
}

.kpi {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 15px 16px 13px;
  box-shadow: var(--shadow);
}

.kpi.hi {
  background: linear-gradient(180deg, var(--card) 0%, var(--accent-soft) 100%);
  border-color: var(--accent-line);
}

.kpi-k { font-size: 12.5px; color: var(--muted); }
.kpi-v {
  font-size: 25px;
  font-weight: 750;
  color: var(--ink);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
  margin: 3px 0 1px;
}
.kpi.hi .kpi-v { color: var(--accent-dark); }
.kpi-v small { font-size: 12.5px; font-weight: 600; color: var(--muted); margin-left: 3px; }
.kpi-note { font-size: 11.5px; color: var(--faint); }

/* 需要有人处理的指标（待审批退款积压、库存预警）。
   只染数字与底色，不整块变红：它不等于出错，只是「有一件事等着你」。
   之前总部总览用了 .kpi.warn 却没有对应样式，结果和普通卡片长得一模一样，
   积压的数字根本没人会注意 —— 没样式的类名不会报错，只会安静地失效。 */
.kpi.warn {
  background: var(--warn-soft);
  border-color: #f0e2c4;
}
.kpi.warn .kpi-v { color: var(--warn); }

/* ── 卡片与栅格 ───────────────────────────────────────────────────────── */

.grid-main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 18px;
  align-items: start;
}

.col-right { display: flex; flex-direction: column; gap: 18px; }

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: var(--shadow);
  margin-bottom: 18px;
}

.grid-main > .card { margin-bottom: 0; }

.card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.card-head h2 { font-size: 15px; }
.card-note { font-size: 11.5px; color: var(--faint); }
.card-actions { margin-top: 14px; }

/* ── 表格 ─────────────────────────────────────────────────────────────── */

.tw { overflow-x: auto; }

/* 表格里的内联链接：表格本身没有链接感（a 是继承色、无下划线），
   所以「所属活动」「项目名」这类需要点进去的列要自己给个可点击的样子。 */
.lnk {
  color: var(--ink);
  border-bottom: 1px dashed var(--faint);
  transition: color .12s;
}
.lnk:hover { color: var(--accent-dark); border-bottom-color: var(--accent-dark); }

table { width: 100%; border-collapse: collapse; font-size: 13px; }

th {
  text-align: left;
  font-size: 11.5px;
  font-weight: 650;
  color: var(--muted);
  letter-spacing: .04em;
  padding: 0 10px 9px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

td {
  padding: 11px 10px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--card); }

.chips {
  display: inline-block;
  font-size: 12px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 2px 8px;
  color: var(--ink-2);
  white-space: nowrap;
}

.tag {
  display: inline-block;
  font-size: 11.5px;
  font-weight: 650;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
}

.st-pending { background: var(--warn-soft); color: #96601a; }
.st-paid { background: var(--info-soft); color: #23578f; }
.st-serving { background: var(--accent-soft); color: var(--accent-dark); }
.st-completed { background: var(--ok-soft); color: var(--ok); }
.st-cancelled { background: #f1f2f5; color: #5b6270; }

/* ── 统计列表 ─────────────────────────────────────────────────────────── */

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

.stat-list li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 9px 0;
  border-bottom: 1px dashed var(--line-soft);
}
.stat-list li:last-child { border-bottom: none; }

.sl-k { font-size: 13px; color: var(--muted); }
.sl-v { font-size: 16px; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.sl-v small { font-size: 11.5px; font-weight: 600; color: var(--muted); margin-left: 2px; }
.stat-list li.warn .sl-v { color: var(--warn); }

/* ── 键值列表 ─────────────────────────────────────────────────────────── */

.kv {
  display: grid;
  /* 标签列用 max-content：固定 62px 会把「余次 / 总次」这类长标签折断成两行，
     标签一旦折行，右侧的值也会跟着错位。 */
  grid-template-columns: minmax(62px, max-content) minmax(0, 1fr);
  gap: 5px 12px;
  margin: 8px 0 0;
  font-size: 13px;
}

.kv dt { color: var(--faint); white-space: nowrap; }
/* 用 overflow-wrap 而不是 word-break:break-all —— 后者会把订单号、手机号
   这类不该断的串也从中间劈开。 */
.kv dd { margin: 0; color: var(--ink-2); overflow-wrap: anywhere; }

.shop-block + .shop-block { margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--line-soft); }
.shop-name { font-size: 13.5px; font-weight: 650; color: var(--ink); }

.main-foot {
  margin-top: 26px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--faint);
  text-align: center;
}

/* ── 按钮 ─────────────────────────────────────────────────────────────── */

.btn-primary {
  display: inline-block;
  border: 0;
  border-radius: var(--btn-radius);
  background: var(--accent);
  color: #fff;
  font-size: 14px;
  font-weight: 650;
  padding: 11px 18px;
  cursor: pointer;
  font-family: inherit;
  transition: background .15s;
}
.btn-primary:hover { background: var(--accent-dark); }
/* 登录页与错误页的主按钮要占满整行；后台工具栏里的只按内容宽度撑开。 */
.login-form .btn-primary { width: 100%; }

.btn-ghost {
  display: inline-block;
  font-size: 12.5px;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: var(--btn-radius);
  padding: 6px 13px;
}
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }

/* ── 登录页 ───────────────────────────────────────────────────────────── */

.login-wrap {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
  background:
    radial-gradient(900px 420px at 50% -10%, var(--accent-soft) 0%, rgba(255, 255, 255, 0) 70%),
    var(--bg);
}

.login-card {
  width: 100%;
  max-width: 384px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  padding: 34px 32px 26px;
  box-shadow: 0 1px 2px rgba(20, 22, 26, .04), 0 22px 50px -28px rgba(20, 22, 26, .22);
}

.login-brand { text-align: center; margin-bottom: 24px; }
.login-brand .brand-logo { margin: 0 auto 14px; }
.login-brand h1 { font-size: 21px; letter-spacing: .01em; }
.login-brand p { margin: 5px 0 0; font-size: 12.5px; color: var(--faint); }

.login-form { display: flex; flex-direction: column; gap: 14px; }

.login-form label { display: flex; flex-direction: column; gap: 6px; }
.login-form label span { font-size: 12.5px; color: var(--muted); }

.login-form input {
  border: 1px solid var(--line);
  border-radius: var(--input-radius);
  padding: 10px 13px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
  background: var(--bg);
  transition: border-color .15s, background .15s;
}
.login-form input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--card);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.alert {
  background: var(--danger-soft);
  border: 1px solid #f5c6c6;
  color: #9b2c2c;
  border-radius: var(--radius-sm);
  padding: 10px 13px;
  font-size: 13px;
  margin-bottom: 16px;
}

.login-hint {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px dashed var(--line);
  font-size: 12px;
  color: var(--muted);
}
.login-hint b { display: block; color: var(--faint); font-size: 11.5px; letter-spacing: .06em; margin-bottom: 7px; }

.hint-row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.hint-row code {
  font-family: var(--mono);
  font-size: 11.5px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 6px;
  color: var(--accent-dark);
}
.hint-row span { color: var(--faint); }

.login-foot { margin-top: 22px; font-size: 12px; color: var(--faint); text-align: center; }

/* ── 错误页 ───────────────────────────────────────────────────────────── */

.err-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
}

.err-card {
  max-width: 620px;
  width: 100%;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 32px;
  box-shadow: var(--shadow);
}

.err-code {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--danger);
  margin-bottom: 8px;
}

.err-card h1 { font-size: 19px; margin-bottom: 14px; }

.err-detail {
  font-family: var(--mono);
  font-size: 12.5px;
  background: #fafafb;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px;
  overflow-x: auto;
  color: var(--danger);
  white-space: pre-wrap;
  word-break: break-all;
  margin: 0 0 18px;
}

.err-actions { display: flex; gap: 10px; }
.err-actions .btn-primary { width: auto; }

/* ==========================================================================
   后台管理页（P1）
   订单 / 项目 / 退款 / 门店 / 员工 这些「列表 + 表单」页面共用的一套件。
   约定：列表页 = 顶栏 + 筛选栏(.toolbar) + 卡片(.card) 包表格(.dt) + 分页(.pager)
   ========================================================================== */

/* ── 一次性提示条 ─────────────────────────────────────────────────────── */

.flash {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 11px 14px;
  font-size: 13.5px;
  margin-bottom: 18px;
}

.flash-ok { background: var(--ok-soft); border-color: #c6e3d3; color: #22694a; }
.flash-err { background: var(--danger-soft); border-color: #f5c6c6; color: #9b2c2c; }

.flash-mark {
  width: 20px; height: 20px; flex: 0 0 20px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; color: #fff;
}
.flash-ok .flash-mark { background: var(--ok); }
.flash-err .flash-mark { background: var(--danger); }

/* ── 菜单徽标 ─────────────────────────────────────────────────────────── */

.menu-badge {
  font-size: 10.5px;
  font-weight: 700;
  line-height: 17px;
  min-width: 18px;
  padding: 0 6px;
  text-align: center;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
}

/* ── 通用按钮（后台里的一整排操作） ──────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink-2);
  border-radius: var(--btn-radius);
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 600;
  font-family: inherit;
  line-height: 1.4;
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }

.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); color: #fff; }

.btn.danger { color: var(--danger); border-color: #f0c9c9; }
.btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }

.btn.sm { padding: 5px 11px; font-size: 12.5px; border-radius: var(--btn-radius); }
/* ── 「不可用」统一表达 ────────────────────────────────────────────────
   禁用/不可点一律用「低饱和底 + 中灰字」，不用半透明：
   半透明是把前景和背景一起压低 —— .45 合成后白字落在 rgb(228,171,190)
   这样的浅粉上，按实测色值换算对比度只剩约 1.9:1（WCAG AA 正文要 4.5:1），
   字基本被擦掉。改为 rgb(154,161,174) on rgb(240,241,244) 约 2.3:1 ——
   仍不达 AA，但禁用控件本就豁免该条，可读性已恢复。

   三类「不可点」共用这一条，避免各写各的又慢慢跑偏：
     · <button disabled> 按钮禁用
     · .btn.off          服务端渲染的不可用按钮
     · .pager .pg.off    分页「上一页 / 下一页」到头 */
.btn[disabled], .btn.off,
.pager .pg.off {
  background: var(--line-soft);
  border-color: var(--line);
  color: var(--faint);
  opacity: 1;
  cursor: not-allowed;
  pointer-events: none;
}
.btn[disabled].primary, .btn.off.primary { color: #b98aa0; }

/* 表格里的行内按钮再小一号，免得把行高撑开 */
.btn.xs { padding: 3px 9px; font-size: 12px; border-radius: 6px; font-weight: 600; }
.btn + .btn { margin-left: 6px; }

/* 行内表单：让 form 不占整行，按钮能跟在同一格里 */
.inline-form { display: inline-block; margin: 0; }

/* 折叠式操作（如「驳回」要点开才填原因）。
   用 <details> 而不是弹窗或 JS：不用写一行脚本，也不会被表格的
   横向滚动裁掉 —— 展开的内容顺着单元格往下排。 */
.pop { display: inline-block; vertical-align: middle; }
.pop > summary { list-style: none; cursor: pointer; }
.pop > summary::-webkit-details-marker { display: none; }
.pop[open] > summary { margin-bottom: 6px; }
.pop-body {
  display: flex;
  gap: 6px;
  align-items: center;
  margin: 6px 0 0;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fafafb;
}
.pop-body .inp { padding: 5px 9px; font-size: 12.5px; min-width: 140px; }

/* 展开后竖排的折叠面板：列里不止一个输入框时用（如员工列表的「重置密码」）。
   沿用 .pop-body 的横排会把这一个单元格撑到 350px 上下，整张表的列宽都被带偏；
   竖排 + 定宽则与「编辑 / 停用」那几个按钮same宽，展开前后列宽几乎不动。 */
.pop-body.stack { flex-direction: column; align-items: stretch; width: 178px; }
.pop-body.stack .inp { min-width: 0; width: 100%; }
.pop-body.stack .btn { width: 100%; }

/* ── 筛选栏 ───────────────────────────────────────────────────────────── */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  /* 列间距 10px：12px 时「7 个筛选条件 + 两个按钮」在 1440 宽下刚好越界 4px。
     但**不要**指望靠调 gap 来根治换行 —— 换行阈值取决于门店下拉这类
     「宽度由数据撑开」的控件，换一家店名更长的客户就又变回去了。
     真正的兜底是 .tb-acts（见下）把两个按钮捆成一个 flex item。 */
  gap: 10px;
  margin-bottom: 16px;
}

/* 「筛选 / 重置」整组一个 flex item，装不下就整组换行，永不出现单个按钮孤行。
   节点由 nav.js 的 groupToolbarActs() 在 DOM 就绪和 pjax 换页后搬进来，
   模板里不用写这一层。
   margin-left:auto：同排时 .grow 已经把富余空间吃干，等于无影响；
   一旦整组换到新行，靠右对齐读起来是「表单操作区」，而不是掉出来的按钮。 */
.toolbar .tb-acts {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  margin-left: auto;
  flex: 0 0 auto;
}

/* 搜索框是这一排里唯一「可以窄一点也照样能用」的控件：
   宁可把它从 160px 压到 120px，也不要把「筛选/重置」挤到第二行 ——
   按钮掉行之后和条件离得远，点之前要先找一下它在哪。
   注意别给按钮加 margin-left:auto 来「靠右」：那样两个按钮会分别换行，
   筛选留在上一行、重置掉到下一行，比原来更乱。 */
.toolbar .grow { flex: 1 1 160px; min-width: 120px; }
/* 光给 .fld 定 min-width 没用：里面的 input 有「默认 20 字符」的固有宽度，
   在 flex 里它自己有 min-width:auto，会把父级重新撑回 220px 左右。
   必须让 input 跟着父级宽度走（width:100%）并放弃固有下限（min-width:0）。 */
.toolbar .grow .inp { width: 100%; min-width: 0; }

.fld { display: flex; flex-direction: column; gap: 5px; }
.fld > span { font-size: 11.5px; color: var(--muted); font-weight: 600; }

/* ── 表单控件 ─────────────────────────────────────────────────────────── */

.inp, .sel, .ta {
  border: 1px solid var(--line);
  border-radius: var(--input-radius);
  padding: 8px 11px;
  font-size: 13.5px;
  font-family: inherit;
  color: var(--ink);
  background: var(--card);
  min-width: 0;
  transition: border-color .15s, box-shadow .15s;
}
.inp:focus, .sel:focus, .ta:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.inp::placeholder { color: var(--faint); }
.ta { resize: vertical; min-height: 76px; line-height: 1.6; }
.sel { appearance: none; padding-right: 28px;
       background-image: linear-gradient(45deg, transparent 50%, var(--faint) 50%),
                         linear-gradient(135deg, var(--faint) 50%, transparent 50%);
       background-position: calc(100% - 15px) 51%, calc(100% - 10px) 51%;
       background-size: 5px 5px, 5px 5px;
       background-repeat: no-repeat; }
.inp.money { text-align: right; font-variant-numeric: tabular-nums; }
.inp.date { font-variant-numeric: tabular-nums; }
.inp.num-sm { width: 96px; }

/* 只读的「输入框」：用来替代不该给点的下拉（如共用会员的归属门店）。
   长得像输入框是为了保持栅格对齐，但底色与光标都表明它不可改。 */
.inp-static {
  background: var(--bg);
  color: var(--muted);
  border-style: dashed;
  cursor: default;
}

/* 字段下方的补充说明：一句话解释为什么这一格是只读的。
   没有它，用户只会看到「改不了」而不知道为什么，然后来问是不是权限没开。 */
.field-hint {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
  margin-top: 2px;
}

/* ── 表单栅格 ─────────────────────────────────────────────────────────── */

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 15px 20px;
}

.form-grid .wide { grid-column: 1 / -1; }

.form-field { display: flex; flex-direction: column; gap: 6px; }
.form-field > label { font-size: 12.5px; color: var(--muted); font-weight: 600; }
.form-field > label i { font-style: normal; color: var(--danger); margin-left: 2px; }
.form-field .hint { font-size: 11.5px; color: var(--faint); line-height: 1.5; }

.form-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px dashed var(--line);
}

/* 复选框一行 */
.check { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
.check input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; margin: 0; }

/* 分段说明（表单右侧的「这个字段是干什么的」） */
.form-side { display: flex; flex-direction: column; gap: 16px; }
.form-side .card { margin-bottom: 0; }

/* ── 数据表格 ─────────────────────────────────────────────────────────── */

.dt { font-size: 13px; }

.dt th {
  padding: 0 12px 10px;
  position: sticky;
  top: 0;
  background: var(--card);
  z-index: 1;
}

.dt td { padding: 10px 12px; vertical-align: top; }
/* 内容一律不折行，宽度不够就交给外层 .tw 横向滚动。
   用于「一行一条记录」的窄表：折行会把一行拆成五六行，行高炸开、列也看着错位。 */
.dt.nw th, .dt.nw td { white-space: nowrap; }
.dt tbody tr:hover { background: #fbfbfd; }

.dt .act { text-align: right; white-space: nowrap; }
/* 操作格里的按钮与折叠面板一律顶对齐。
   默认的 baseline/middle 会让「编辑 / 停用」浮在展开面板的垂直中央 ——
   展开之后那三个东西看着像一伙的，点击目标也飘到面板旁边去了。 */
.dt .act > .btn,
.dt .act > .inline-form,
.dt .act > .pop { vertical-align: top; }
.dt .nowrap { white-space: nowrap; }
.dt tr.out td { background: var(--card); color: var(--muted); }
.dt tr.out .strong { color: var(--muted); }
.dt tr.warn td { background: var(--danger-soft); }
.dt tr.warn:hover td { background: #fae3e3; }
.dt tr.warn .strong { color: #9b2c2c; }
.dt .num.ok { color: var(--ok); font-weight: 600; }
.dt .num.bad { color: #9b2c2c; font-weight: 600; }

/* 表格底部的合计行 */
.dt tfoot td {
  border-top: 1px solid var(--line);
  border-bottom: none;
  background: #fafafb;
  font-weight: 650;
  color: var(--ink);
}
.dt tfoot .k { font-weight: 500; color: var(--muted); }

/* ── 排班网格（员工 × 一周）───────────────────────────────────────────────
 *
 * 七列固定宽度 + 首列员工名固定：排班是「按天扫一遍」的动作，
 * 列宽随内容浮动会让同一个员工的周一到周日对不齐，
 * 一行一行看下来就数不清哪格是哪天。窄屏交给外层 .tw 横向滚动。
 *
 * ⚠️ 列宽必须容得下**最长的那一句**「早班 09:00-18:00」：
 * 原生 select 里的文字既不会换行也不会缩字号，宽度不够就直接截断 ——
 * 截断点常常落在「-」后面，看起来就像时间被折成了两行。
 * 2026-10-21 实测 132px 不够（文字区只剩 96px），提到 150px 并把
 * select 右内边距压到 18px（给下拉箭头留位置）。 */
.sch-grid th.sch-day, .sch-grid td.sch-cell { width: 130px; min-width: 130px; padding: 6px 4px; }
.sch-grid th.sch-day { font-size: 12px; color: var(--muted); white-space: nowrap; }
/* 日期下面一行「周三」：排班时人记的是星期，日期只是给审计看的 */
.sch-grid th.sch-day i { display: block; font-style: normal; font-size: 11.5px; color: var(--faint); }
.sch-grid td.sch-who, .sch-grid th.sch-who { min-width: 96px; white-space: nowrap; }
.sch-job { font-style: normal; font-size: 11.5px; color: var(--faint); margin-left: 6px; }
/* 今天那一列标出来：排班时最容易错的就是「把今天排成下周」。
   用背景而不是边框，避免和表头的 sticky 描边打架。 */
.sch-grid td.sch-today { background: var(--accent-soft); }
.sch-sel {
  width: 100%;
  font-size: 12px;
  padding: 4px 18px 4px 6px;
  white-space: nowrap;   /* 兜一层：任何情况下都不把「09:00-18:00」折行 */
}
.sch-actions { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
/* 周选择用原生 date 控件。它的宽度是浏览器按区域格式自己撑的，
   给死 150px 时中文区域下「2026/10/19」会被裁掉尾部。 */
.sch-actions .inp[type="date"] { width: 168px; flex: none; }

/* ── 分页 ─────────────────────────────────────────────────────────────── */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line-soft);
}

.pager .pg {
  min-width: 30px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: #fff;
  color: var(--ink-2);
  font-size: 12.5px;
  font-family: inherit;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* :not(.off) 是必要的：不可用页也带 .pg，若不禁掉 hover，
   它会在鼠标悬停时透出品牌色，看起来像「可点」，与禁用语义自相矛盾。
   （原本靠 .off 的 pointer-events:none 兜着，但那是副作用、不是本意。） */
.pager .pg:not(.off):hover { border-color: var(--accent); color: var(--accent); }
.pager .pg.on { background: var(--accent); border-color: var(--accent); color: #fff; }
/* .pg.off 的外观统一在文件上方「不可用」那一组里定义，这里不再重复写 opacity。 */
.pager .gap { color: var(--faint); padding: 0 2px; user-select: none; }
.pager .pinfo { margin-left: auto; font-size: 12.5px; color: var(--faint); }

/* ── 状态标签补充 ─────────────────────────────────────────────────────── */

.tg-warn  { background: var(--warn-soft); color: #96601a; }
.tg-info  { background: var(--info-soft); color: #23578f; }
.tg-ok    { background: var(--ok-soft); color: var(--ok); }
.tg-bad   { background: var(--danger-soft); color: #9b2c2c; }
.tg-mute  { background: #f1f2f5; color: #5b6270; }
.tg-brand { background: var(--accent-soft); color: var(--accent-dark); }

/* 核销状态（明细行内的小标） */
.tag.xs { font-size: 11px; padding: 1px 7px; font-weight: 600; }

/* ── 金额 ─────────────────────────────────────────────────────────────── */

.amt { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 650; color: var(--ink); }
.amt.pos { color: var(--accent-dark); }   /* 收进来的钱 */
.amt.neg { color: var(--danger); }        /* 退出去的钱 */
.amt.zero { color: var(--faint); font-weight: 500; }

/* ── 详情页两栏 ───────────────────────────────────────────────────────── */

.detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 336px;
  gap: 18px;
  align-items: start;
}
.detail-grid > .card { margin-bottom: 0; }
/* 反过来的一版：左侧窄（放卡面摘要），右侧宽（放多列记录表）。
   次卡详情这类「一员一卡 + 一长串核销流水」适合这一版 —— 把 6 列表格
   塞进 336px 侧栏会逼着每列逐字换行。 */
.detail-grid.wide-right { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); }
.detail-side { display: flex; flex-direction: column; gap: 18px; }

/* ── 键值块（详情页头部的金额分解） ───────────────────────────────────── */

.money-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 2px 0;
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin: 4px 0 0;
}
.money-strip div { background: var(--card); padding: 10px 13px; }
.money-strip .ms-k { font-size: 11.5px; color: var(--muted); }
.money-strip .ms-v {
  font-size: 16px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.money-strip .ms-v.dim { color: var(--muted); font-weight: 600; }
.money-strip .ms-v.pos { color: var(--accent-dark); }
.money-strip .ms-v.neg { color: var(--danger); }

/* ── 空状态 ───────────────────────────────────────────────────────────── */

.blank {
  text-align: center;
  padding: 40px 20px;
  color: var(--faint);
  font-size: 13px;
}
.blank b { display: block; color: var(--muted); font-size: 14px; margin-bottom: 4px; }

/* ── 响应式补充 ───────────────────────────────────────────────────────── */

@media (max-width: 1180px) {
  .kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-main { grid-template-columns: minmax(0, 1fr); }
  .detail-grid { grid-template-columns: minmax(0, 1fr); }
  .form-side { order: -1; }
}

/* 默认（桌面）：手机专用的两个控件不出现 */
.side-toggle { display: none; }
.nav-close { display: none; }

@media (max-width: 820px) {
  .shell { grid-template-columns: 1fr; }

  /* 侧栏在手机上变成一条顶栏：品牌 + 「菜单」按钮。
     原来是 .menu { display:none } —— 干净，但也意味着手机上**没有任何导航**，
     只能靠浏览器后退键在页面之间跳。这是个死胡同，不是「简化」。 */
  .side {
    position: sticky; top: 0; z-index: 60;
    flex-direction: row; align-items: center; gap: 10px;
    height: auto; padding: 8px 12px;
    border-right: 0; border-bottom: 1px solid var(--line);
    overflow: visible;
  }
  .brand { margin: 0; padding: 0; flex: 1 1 auto; min-width: 0; }
  .brand-text b { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .brand-text span { display: none; }     /* 副标题在 390px 上只剩省略号，不如不占位 */

  .side-toggle {
    display: inline-flex; align-items: center; gap: 7px; flex: none;
    border: 1px solid var(--line); background: #fff; color: var(--ink-2);
    border-radius: var(--radius-sm); padding: 7px 11px; font: inherit; font-size: 13px;
    font-weight: 600; cursor: pointer;
  }
  .side-toggle[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
  .st-bars { display: inline-flex; flex-direction: column; gap: 3px; }
  .st-bars i { display: block; width: 13px; height: 1.5px; background: currentColor; border-radius: 2px; }

  /* 菜单变成全屏抽屉：30 多项菜单在小屏上是纵向列表，抽屉比下拉更好点。
     用 body.nav-open 控制，JS 见 nav.js（点任意菜单项 / 点关闭 / Esc 都会收起）。 */
  .menu {
    display: none;
    position: fixed; inset: 0; z-index: 80;
    background: #fff; padding: 58px 12px 28px; overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  body.nav-open .menu { display: block; }
  body.nav-open { overflow: hidden; }     /* 抽屉开着时背景不跟着滚 */
  .nav-close {
    display: flex; align-items: center; justify-content: center;
    position: fixed; top: 10px; right: 12px; z-index: 81;
    width: 36px; height: 36px; border-radius: var(--radius-sm);
    border: 1px solid var(--line); background: #fff; color: var(--ink-2);
    font-size: 20px; line-height: 1; cursor: pointer;
  }
  .side-foot { display: none; }           /* 底部账号信息挪到顶栏右上，见 .topbar */

  .main { padding: 14px 12px 30px; }
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .topbar { flex-direction: column; align-items: stretch; gap: 10px; }
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  /* 筛选条：桌面上 6 个控件挤一行，手机上改成两列 ——
     每个控件独占一行会把「筛选」按钮推到首屏之外，得往下滚才点得到。 */
  .toolbar { flex-wrap: wrap; gap: 8px; align-items: flex-end; }
  .toolbar .fld { flex: 1 1 calc(50% - 4px); min-width: 0; }
  .toolbar .fld.grow { flex: 1 1 100%; }
  .toolbar .inp, .toolbar .sel, .toolbar select { width: 100%; min-width: 0; }
  .toolbar .btn { flex: 0 0 auto; }
  /* 手机上「筛选/重置」独占一行、两枚等宽：右对齐会在左边留半行空白，
     改成对半分满，拇指够得着，也不再出现「按钮只剩半个格子」的空洞。 */
  .toolbar .tb-acts { flex: 1 1 100%; margin-left: 0; }
  .toolbar .tb-acts .btn { flex: 1 1 0; }
  .pager .pinfo { margin-left: 0; width: 100%; }

  /* 表格：手机上不做「一列一个字」的强行压缩，改成卡片内横向滚动。
     640px 是 6 列的最小可读宽度，低于它每格只剩一个汉字宽。 */
  .main { overflow-x: hidden; }
  .card { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .main table { min-width: 640px; }
  .main table td, .main table th { white-space: nowrap; }
  /* 例外：详情页里那种「字段名 + 值」的两列表不横滚，让它自然换行 */
  .main table.kv { min-width: 0; }
  .main table.kv td, .main table.kv th { white-space: normal; }
}


/* ── 角色权限配置：权限点分组勾选 ─────────────────────────────── */
.perm-groups { display: flex; flex-direction: column; gap: 14px; margin-top: 6px; }
.perm-group { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.perm-group-title {
  background: var(--bg-soft); padding: 8px 14px; font-size: 13px; font-weight: 600; color: var(--ink);
  border-bottom: 1px solid var(--line);
}
.perm-items {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; padding: 12px 14px;
}
.perm-item { align-items: flex-start; gap: 8px; padding: 6px 2px; }
.perm-item b { font-weight: 600; color: var(--ink); }

/* ── 开单页（/orders/new）───────────────────────────────────────────────
   页面结构：左边是「挑」（顾客 / 项目 / 自定义收费项目），右边是「算与收」
   （清单 / 金额 / 开单）。右侧沿用 .col-right 的 320px 窄栏，
   所以清单每一行竖着排两三层，而不是挤成一长条控件带。 */

/* 项目过滤条：输入框 + 分类 chip */
.svc-bar { display: flex; flex-direction: column; gap: 10px; }
.svc-bar .inp { width: 100%; }

.chip {
  border: 1px solid var(--line);
  background: #fff;
  color: var(--ink-2);
  border-radius: 999px;
  padding: 4px 11px;
  font-size: 12.5px;
  font-family: inherit;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.chip b { font-weight: 600; color: var(--faint); margin-left: 3px; }
.chip:hover { border-color: var(--accent); color: var(--accent-dark); }
/* 选中态与禁用态用同一套「实底 + 白字」写法，对比度足够 —— 
   不用半透明，见文件上半部分禁用态的约定。 */
.chip.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.chip.on b { color: rgba(255, 255, 255, .8); }
#svc-cats { display: flex; flex-wrap: wrap; gap: 7px; }

/* 项目卡：整块可点，点一次加进清单 */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(154px, 1fr));
  gap: 10px;
  margin-top: 14px;
}
.svc {
  appearance: none;
  text-align: left;
  font-family: inherit;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  cursor: pointer;
  min-height: 64px;
  transition: border-color .15s, box-shadow .15s, transform .06s;
}
.svc:hover { border-color: var(--accent); box-shadow: 0 6px 16px -12px var(--accent); }
.svc:active { transform: translateY(1px); }
.svc-n { font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.4; }
.svc-m {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 12.5px; color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.svc-m s { color: var(--faint); font-size: 11.5px; }
.svc-mem { color: var(--accent-dark); }
.svc-m i { font-style: normal; color: var(--faint); font-size: 11.5px; }

/* 实物商品：与服务项目同在一个目录里，只靠一个小标记和库存数字区分。
   不做成两种颜色的大卡片 —— 收银台要的是「一眼扫过去都是能卖的东西」，
   分成两栏反而会让人在一栏里找不到刚卖出去的那瓶精华。 */
.svc--prod { border-style: dashed; }
.svc-kind {
  font-style: normal; margin-left: 6px; padding: 0 5px;
  border-radius: 4px; background: var(--warn-soft); color: #96601a;
  font-size: 10.5px; font-weight: 600; vertical-align: 1px;
}
.svc-stock { margin-left: auto !important; color: var(--ink-2) !important; }

/* 自定义收费项目的录入行 */
.cform { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-top: 12px; }
.cform .fld.grow { flex: 1 1 200px; min-width: 160px; }

/* 会员搜索结果 */
.picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; }
.pick {
  appearance: none;
  text-align: left;
  font-family: inherit;
  border: 1px solid var(--line);
  background: #fff;
  border-radius: var(--radius-sm);
  padding: 8px 11px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  cursor: pointer;
  font-size: 13px;
}
.pick:hover { border-color: var(--accent); background: var(--accent-soft); }
.pick b { color: var(--ink); }
.pick .mono { color: var(--muted); }

/* 会员搜索结果的「档案卡」版 —— 开单页用。
 *
 * 为什么要把一条结果做这么高：收银台选人这一刻同时要判断三件事 ——
 * 是不是本人（手机号）、该按什么价说（级别）、手上还有没有次卡（余额）。
 * 原来一行「姓名 手机号 级别」三样都答不上，店员只能再开一个标签页去查，
 * 忙起来就干脆不查，于是「顾客说我有卡」时现场翻记录。
 *
 * 190px 放不下六项事实，所以这一组单独放宽到 300px；放不下时自动单列。 */
.picks--mem { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.pick-mem {
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  padding: 9px 12px;
}
.pm-top { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 13.5px; }
.pm-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2px 10px;
  font-size: 11.5px;
  color: var(--ink-2, #4a4a52);
}
.pm-facts span { display: flex; gap: 4px; min-width: 0; }
.pm-facts i { font-style: normal; color: var(--faint); flex: none; }
.pm-cards { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; font-size: 11.5px; }
.pm-cards > i { font-style: normal; color: var(--faint); }
.pm-card {
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 6px;
  color: var(--ink-2, #4a4a52);
  white-space: nowrap;
}
.pm-card b { color: var(--accent-dark); font-variant-numeric: tabular-nums; }
.pm-card em { font-style: normal; color: var(--faint); margin-left: 4px; }
.pm-card.is-off { color: var(--faint); border-style: dashed; }
.pm-card.is-off b { color: var(--faint); }
.pm-sum { color: var(--accent-dark); }
.pm-sum.is-off { color: var(--faint); }

/* 已选会员：标签 + 第二行摘要竖排 */
.picked-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.picked-sub { font-size: 11.5px; color: var(--muted); }

/* 清单行上的小字提示（如「无适用次卡」） */
.cl-note { font-size: 11.5px; color: var(--faint); }
.cl-amt--card { color: var(--accent-dark); font-size: 12px; }

/* 已选会员 */
.picked {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--accent); background: var(--accent-soft);
  border-radius: var(--radius-sm); padding: 8px 12px;
}

/* 清单行：竖着三层（标题 / 单价×数量 / 操作），320px 窄栏放得下 */
.cline { border-top: 1px solid var(--line-soft); padding: 10px 0; }
.cline:first-child { border-top: 0; padding-top: 0; }
.cl-top { display: flex; align-items: baseline; gap: 6px; }
.cl-name { flex: 1; font-size: 13px; font-weight: 600; color: var(--ink); }
.cl-amt { font-size: 13px; font-weight: 650; color: var(--accent-dark); font-variant-numeric: tabular-nums; }
.cl-mid { font-size: 12px; color: var(--muted); margin-top: 2px; font-variant-numeric: tabular-nums; }
.cl-ops { display: flex; align-items: center; gap: 6px; margin-top: 7px; flex-wrap: wrap; }
.cl-qty { font-size: 13px; min-width: 22px; text-align: center; font-variant-numeric: tabular-nums; }
.sel.sel-xs { padding: 3px 24px 3px 8px; font-size: 12px; border-radius: 7px; }
/* 次卡下拉的选项文案较长（卡名 + 剩 N 次 + 有效期），限宽避免把清单撑破 */
.sel.sel-card { max-width: 100%; }
.cl-ops .btn.xs { padding: 3px 9px; }

/* 告警条：项目合计里出现「自定义项目不参与优惠」这类提示时用 */
.flash-warn { background: var(--warn-soft); border-color: #f0dcb4; color: #96601a; }
.flash-warn .flash-mark { background: var(--warn); }
/* 卡片内的告警条不要撑开卡片间距（正常的整页提示条才需要下边距） */
.card .flash { margin-bottom: 0; }

/* ── 说明性段落 ─────────────────────────────────────────────────────────
   刻意不复用 .hint-row：那个是 display:flex 的「一行小标记」（<code> + <span>），
   把整句话塞进去会让每个文本节点变成独立的 flex item，
   句子被拆成几列错位排布 —— 看一眼就知道坏了，但很容易被当成字体问题。 */
.hint {
  font-size: 12.5px;
  color: var(--faint);
  line-height: 1.7;
  margin: 10px 0 0;
}
.hint b { color: var(--ink-2); font-weight: 650; }
.hint code {
  font-family: var(--mono);
  font-size: 11.5px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 6px;
  color: var(--accent-dark);
}

/* ── 评价管理（/reviews、/reviews/new）─────────────────────────────────────
   星级用文字符号 ★☆ 而不是图标字体：本项目无 npm、无图标字体，
   引一套字体就等于多一个必须随二进制分发的外部资源。★/☆ 在
   utf8mb4 下是普通字符，任何设备都能渲染，且可以直接被搜索到。 */

.stars {
  font-size: 15px;
  letter-spacing: 1px;
  color: var(--accent);
  line-height: 1.3;
}
/* 低分用暗金色而不是红色：红色在表里会盖过真正的告警色，
   而 1-2 星本身就是一个正常的业务事实，不是系统故障。 */
.stars.low { color: #a8741a; }

.rv-stars { width: 92px; }
.rv-stars .muted { font-size: 11.5px; }

/* 列表里的长文本截断。
   用 CSS 而不是在 Go 里截字符串：截字符串会丢掉后半段，
   而鼠标选中复制走的是**截断后**的内容 —— 店员复制差评去汇报时会少一半。 */
.clamp2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.6;
  word-break: break-word;
}
.rv-cell { max-width: 260px; }

/* 已隐藏的行整行降调，但**不用 opacity**：透明度会把前景和背景一起压低，
   正文会掉到看不清（与「禁用态」那条踩过的坑同一个原因）。
   改用背景色 + 文字色，保持对比度。 */
.dt tr.rv-hidden > td { background: #fafafb; color: var(--muted); }
.dt tr.rv-hidden > td .strong { color: var(--muted); font-weight: 600; }

/* 星级分布 */
.dist { padding: 4px 16px 16px; }
.dist-row {
  display: grid;
  grid-template-columns: 46px 1fr 130px;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  font-size: 12.5px;
}
.dist-k { color: var(--ink-2); font-weight: 600; }
.dist-bar {
  height: 8px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
}
.dist-bar i {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 999px;
}
.dist-n { color: var(--faint); text-align: right; white-space: nowrap; }

/* 评价详情 */
.rv-body { padding: 4px 16px 16px; }
.rv-text {
  margin: 0 0 10px;
  font-size: 14px;
  line-height: 1.85;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-word;
}
.rv-imgs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.rv-img {
  display: block;
  width: 88px;
  height: 88px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #fff;
}
.rv-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rv-img:hover { border-color: var(--accent); }

/* 评分选择：原生 radio + 可见文字，不用 JS 造控件。
   无 JS 时也必须能选中一个星级 —— 代录页的提交按钮不能依赖脚本。 */
.rate-pick { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.rate-pick label {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  color: var(--ink-2);
}
.rate-pick label:hover { border-color: var(--accent); background: var(--accent-soft); }
.rate-pick input { width: 15px; height: 15px; accent-color: var(--accent); margin: 0; cursor: pointer; }
.rate-pick b {
  font-family: var(--mono);
  color: var(--accent);
  letter-spacing: 1px;
  margin-right: 7px;
  font-weight: 600;
}
.rate-pick label:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-weight: 600;
}

/* 图片上传 */
.rv-upload { margin-top: 6px; }
.rv-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.rv-thumb {
  position: relative;
  width: 78px;
  height: 78px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #fff;
}
.rv-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rv-del {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 20px;
  height: 20px;
  border: none;
  border-radius: 999px;
  background: rgba(20, 22, 26, .62);
  color: #fff;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.rv-del:hover { background: var(--danger); }
.hint.err { color: var(--danger); }

/* 可点击的标签（复选框 + 芯片外观） */
.chip-x { display: inline-flex; cursor: pointer; }
.chip-x input { position: absolute; opacity: 0; width: 0; height: 0; }
.chip-x input:focus-visible + .chip { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip-x input:checked + .chip {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* 折叠区：把「隐藏评价」这类危险动作收起来，避免误点。
   用原生 <details>，不需要 JS，键盘可达。 */
.fold { margin-top: 12px; border-top: 1px solid var(--line-soft); padding-top: 12px; }
.fold > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  list-style: none;
  user-select: none;
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "▸ "; color: var(--faint); }
.fold[open] > summary::before { content: "▾ "; }
.fold > summary:hover { color: var(--accent-dark); }

/* 表单纵向排布 */
.stack { display: flex; flex-direction: column; gap: 12px; }
.stack > .btn { align-self: flex-start; }
.btn.block { display: block; width: 100%; text-align: center; }
.btn + .stack { margin-top: 10px; }

/* 须知列表 */
.hint-list {
  margin: 0;
  padding: 0 0 0 18px;
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.8;
}
.hint-list li { margin-bottom: 6px; }
.hint-list b { color: var(--ink-2); font-weight: 650; }

.danger-text { color: var(--danger); font-size: 13px; line-height: 1.75; margin: 0; }

/* 详情页右栏里的表格与卡片间距 */
.detail-side .card .stack { padding: 0 16px 16px; }
.detail-side .card > .rv-text { padding: 0 16px 4px; }

/* ── 次卡详情：收款与退卡 ────────────────────────────────────────────────
   退卡和「核销退款」是两条路，页面上的视觉也要能区分：
   核销退款只是把次数还回来，退卡是整张卡作废。
   所以退卡区用一条上边框和上面的表格隔开，读起来像「另一件事」。 */

.refund-state {
  margin-top: 14px;
  padding: 12px 14px;
  background: var(--line-soft);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 12.5px;
  line-height: 1.6;
}
.rs-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
}
.rs-act { display: flex; align-items: center; gap: 8px; margin-top: 4px; }

.refund-form {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px dashed var(--line);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.refund-pick {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--card);
}
.rp-head {
  font-size: 11.5px;
  color: var(--faint);
  font-weight: 600;
  padding-bottom: 6px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--line-soft);
}

/* 整行可点：点在哪都能勾上，不用非得瞄准那个小方框。
   hover 只改底色，不加位移 —— 这是表单不是卡片列表，动起来更乱。 */
.ck-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 6px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 13px;
  line-height: 1.5;
}
.ck-row:hover { background: var(--accent-soft); }
.ck-row input[type="checkbox"] { margin: 0; flex: none; width: 15px; height: 15px; accent-color: var(--accent); }
.ck-row .num { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }

.refund-meta {
  font-size: 12.5px;
  color: var(--ink-2);
  line-height: 1.85;
  background: var(--warn-soft);
  border: 1px solid #f2e4c9;
  border-radius: var(--radius-sm);
  padding: 10px 14px;
}
.refund-meta b { color: var(--ink); font-weight: 650; }
.refund-meta .muted { margin-top: 2px; }

/* ── 业务日志（/logs） ─────────────────────────────────────────────────────
   五列里「经过」是唯一需要折行的：一句话里塞着原因、金额、状态变化，
   强行折成一列会把表撑到几千像素。所以前三列定宽不折，
   最后一列吃掉剩余宽度并按正常语速换行。

   列宽用固定 px 而不是 %：操作人和时间的长短是可预期的（4 字姓名、5 字时间），
   按百分比分配会让「经过」在窄屏下被压成一条竖线。 */
.dt.log-dt { table-layout: fixed; width: 100%; min-width: 720px; }
.dt.log-dt .log-at   { width: 92px; white-space: nowrap; }
.dt.log-dt .log-who  { width: 96px; }
.dt.log-dt .log-what { width: 128px; }
.dt.log-dt .log-obj  { width: 160px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 动作标签用的是全局 .tag（nowrap），一旦动作名比列宽长——
   比如没登记过的新动作会渲染成「模块 / action」——它会直接压到「对象」列上，
   看着像两列粘在一起。这里只放开日志表的这一个标签：允许折行、不超列宽。 */
.dt.log-dt .log-what .tag {
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
  line-height: 1.45;
  text-align: left;
}
.dt.log-dt .log-detail {
  white-space: normal;
  line-height: 1.75;
  color: var(--ink-2);
  /* 长单号、长文件名不给它撑破表格的机会：允许在任意字符处断开。 */
  overflow-wrap: anywhere;
}
.dt.log-dt .log-mod { font-size: 11px; margin-top: 3px; }
.dt.log-dt .log-obj a { color: var(--accent-dark); }

/* ── 订单详情：用券记录 ──────────────────────────────────────────────── */

/* 放在 money-strip 下面，而不是当成第 8 个格子：
   券名长短差很多，塞进等宽格子必然折行；而这一行本来就该读成一句话
   （「优惠券 满500减80 CP2026... −¥80」）。 */
.coupon-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
  padding: 10px 14px;
  border-radius: var(--radius);
  background: var(--accent-soft);
  font-size: 13px;
}
.coupon-line-k { color: var(--muted); flex: 0 0 auto; }
.coupon-line-v { color: var(--ink); font-weight: 600; }
/* 券码用等宽字：顾客报券码时店员要照着念，等宽更好认。 */
.coupon-line-code {
  margin-left: 8px;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
}
.coupon-line-amt {
  margin-left: auto;
  color: var(--accent-dark);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

/* ── 站内导航：换页时的轻量反馈 ────────────────────────────────────── */

/* 只把主区域的对比度压一点，不做遮罩、不做转圈：
   这个动作通常 100~300ms 就结束了，弹一个大 loading 出来反而闪。
   侧栏不动 —— 它本来就是「没被换掉的那一半」，压暗它会让人以为整个后台在重载。 */
body.is-nav .main {
  opacity: .55;
  transition: opacity .12s ease;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  body.is-nav .main { transition: none; }
}

/* ── 选项卡（会员资料等）───────────────────────────────────────────────
   用 <a href="?tab=x"> 而不是 JS 切的 div：每个 tab 的内容都是各自查库得来的，
   地址里带 tab 才能「刷新后还在同一页」「发给同事能直接打开同一页」。
   换页由 nav.js 接管，不会整页刷新。 */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  border-radius: var(--radius-sm);
  background: var(--line-soft);
}
.tab-item {
  padding: 6px 13px;
  border-radius: 7px;
  font-size: 13px;
  color: var(--muted);
  white-space: nowrap;
}
.tab-item:hover { color: var(--ink); background: rgba(255, 255, 255, .7); }
.tab-item.on {
  background: var(--card);
  color: var(--accent-dark);
  font-weight: 650;
  box-shadow: 0 1px 2px rgba(20, 22, 26, .06);
}
.tab-count {
  margin-left: 5px;
  font-size: 11px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.tab-item.on .tab-count { color: var(--accent); }

/* ── 业绩汇总条的小字 ─────────────────────────────────────────────────
   .money-strip 原本只用于订单详情（一律「键 + 大数字」两行）。
   员工业绩要在一个格子里说两件事（数字 + 口径），所以补一行小字，
   而不是新造一套卡片样式 —— 同一页里两种「数字块」长相差太多会显得散。 */
.money-strip .ms-s {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}

/* ── 工作轨迹（员工业绩页右栏）────────────────────────────────────────
   一条竖线 + 圆点的时间轴。不用表格：轨迹是「按时间读下来的故事」，
   表格的列会把人往横向比较上引，而这里要回答的是「他这一天怎么过的」。 */
.timeline {
  list-style: none;
  margin: 4px 0 0;
  padding: 0 0 0 14px;
  position: relative;
  max-height: 520px;
  overflow-y: auto;
}
/* 竖线画在容器的左侧 5px 处，从第一颗圆点连到最后一颗 */
.timeline::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--line);
}
.tl-item {
  position: relative;
  padding: 0 0 14px 12px;
}
.tl-dot {
  position: absolute;
  left: -13px;
  top: 5px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--faint);
  box-shadow: 0 0 0 2px var(--card);
}
/* 四种动作各一个颜色：收款（钱进来）用品牌色、核销（做完服务）用绿色、
   开单用中性灰、被预约用橙色。颜色只是索引，文字才是信息。 */
.tl-pay .tl-dot { background: var(--accent); }
.tl-verify .tl-dot { background: var(--ok); }
.tl-order .tl-dot { background: var(--info); }
.tl-appt .tl-dot { background: var(--warn); }
.tl-body { min-width: 0; }
.tl-top {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 13px;
  color: var(--ink);
}
.tl-top b { font-weight: 600; }
.tl-amt {
  margin-left: auto;
  font-size: 12.5px;
  font-weight: 650;
  color: var(--accent-dark);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tl-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--faint);
}
.tl-meta a { color: var(--muted); }
.tl-meta a:hover { color: var(--accent-dark); }

/* ── 可点击的整行 ─────────────────────────────────────────────────────
   业绩表里「点一行看这个人的轨迹」是页面的主操作。只让姓名可点时，
   手指会落在右边的数字上 —— 那里才是他想看的东西。 */
.row-link { cursor: pointer; }
.row-link:hover { background: var(--accent-soft); }
.row-link.is-on { background: var(--accent-soft); }

/* ── 总部总览 · 平台动作 ───────────────────────────────────────────────
   这一块放的是「只有超管会做的动作」（新增门店、审批全店退款、备份），
   不是单店经营操作。做成竖排的三段式条目而不是一排按钮：
   每个动作都需要一句解释（为什么这件事该在这里做），
   按钮放不下那句话，而放不下就会有人点错。 */
.quick { display: flex; flex-direction: column; }

.quick-a {
  display: block;
  padding: 11px 0;
  border-bottom: 1px dashed var(--line-soft);
  text-decoration: none;
}
.quick-a:last-child { border-bottom: none; }

.quick-t {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
.quick-a:hover .quick-t { color: var(--accent-dark); }

.quick-d {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--faint);
}

/* ── 「进店」状态胶囊 ──────────────────────────────────────────────────
   全部门店的账号把数据范围临时收窄到一家店时，这个小胶囊是**全站唯一**
   告诉人「你现在只看这一家」的地方。所以它必须和平时那个「全部门店」
   长得不一样：实心品牌色，而不是浅底描边。
   看不到这个差别，老板会以为报表真的掉了一半 —— 那比没有这个功能更糟。 */
.pill--shop {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* 退出链接：与胶囊同色、只加下划线。做成按钮会抢走主操作的注意力，
   而退出是个「看错了才用」的动作。 */
.pill-x {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
  opacity: .9;
}
.pill-x:hover { opacity: 1; }

/* ══ 技术管理端（/admin）═══════════════════════════════════════════════
   这一段的样式只服务平台级配置页。刻意不复用门店页面的卡片密度：
   配置页是「一个月改一次」的页面，宁可一屏放得下、也不要挤。 */

/* 首页：就绪度总结 */
.admin-sum {
  display: flex;
  gap: 24px;
  align-items: center;
  margin-bottom: 18px;
}
.admin-sum-l { flex: none; text-align: center; min-width: 120px; }
.admin-sum-n {
  font-size: 34px;
  font-weight: 750;
  color: var(--accent-dark);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.admin-sum-n small { font-size: 14px; font-weight: 600; color: var(--muted); margin-left: 4px; }
.admin-sum-k { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.admin-sum-r { flex: 1; font-size: 12.5px; line-height: 1.8; color: var(--ink-2); }

/* 首页：模块卡 */
.admin-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}
.admin-mod {
  display: block;
  padding: 16px 16px 14px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color .12s, box-shadow .12s, transform .12s;
}
.admin-mod:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}
.admin-mod-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.admin-mod-icon {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-size: 14px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* 「待配置」用橙色而不是红色：它是待办，不是错误 */
.admin-mod.is-todo .admin-mod-flag { color: var(--warn); background: var(--warn-soft); }
.admin-mod.is-ok .admin-mod-flag { color: var(--ok); background: var(--ok-soft); }
.admin-mod-flag {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
}
.admin-mod-title { font-size: 14px; font-weight: 650; color: var(--ink); }
.admin-mod-note { margin-top: 4px; font-size: 12.5px; color: var(--muted); }
.admin-mod-detail {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--line-soft);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--faint);
}

/* 配置表单 */
.afield { margin-bottom: 18px; }
.aflabel {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 6px;
}
.afreq, .afsec {
  font-size: 10.5px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 999px;
}
.afreq { color: var(--danger); background: var(--danger-soft); }
.afsec { color: var(--info); background: var(--info-soft); }
.afhint { margin-top: 5px; font-size: 11.5px; line-height: 1.65; color: var(--faint); }
.afhint--ok { color: var(--ok); }
.inp--secret { font-family: var(--mono); letter-spacing: .04em; }
.aactions {
  display: flex;
  gap: 10px;
  padding-top: 6px;
  border-top: 1px solid var(--line-soft);
  margin-top: 4px;
  padding-top: 16px;
}
.afield--inline { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }

/* 复选框行（启用开关） */
.acheck {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--ink-2);
  white-space: nowrap;
}

/* 两列紧凑表单（映射、登记） */
.stack--tight > * + * { margin-top: 8px; }
.frow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.frow > .inp { flex: 1 1 200px; min-width: 160px; }
.frow > .btn { flex: none; }

/* 提示卡（平台页「当前无官方接口」） */
/* 说明块（.note）：采购 / 导出 / 技能 / 定时任务 四页共用的前置说明。
   它一直在模板里、却一直没有样式 —— 表现是「一段灰字贴在卡片外面」，
   看着像样式坏了，而实际上只是漏了一个类。 */
.note {
  margin-bottom: 16px;
  padding: 13px 18px;
  border-radius: var(--radius);
  font-size: 12.5px;
  line-height: 1.85;
  background: var(--card);
  border: 1px solid var(--line);
  color: var(--ink-2);
}
.note b { color: var(--ink); }
.note code {
  font-family: var(--mono);
  font-size: 12px;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--line-soft);
}
/* info：讲口径（为什么这么算），不是警告 */
.note.info { background: var(--info-soft); border-color: #d6e4f5; color: #1c4a80; }
.note.info b { color: #143b66; }
.note.info code { background: rgba(255, 255, 255, .7); }
/* warn：讲风险（这一步做了就改不回来） */
.note.warn { background: var(--warn-soft); border-color: #f0e0c2; color: #7a5312; }
.note.warn b { color: #5f3f08; }
.note.warn code { background: rgba(255, 255, 255, .7); }

.note-card {
  margin-bottom: 16px;
  padding: 14px 18px;
  border-radius: var(--radius);
  font-size: 12.5px;
  line-height: 1.8;
  border: 1px solid var(--info-soft);
  background: var(--info-soft);
  color: #234a75;
}
.note-card--info a { color: #1c4a80; text-decoration: underline; }

/* 三格指标：.kpis 默认 6 列，核销页只要 3 个 */
.kpis--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* 筛选条（配置页与核销页共用） */
.filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.filters .inp { flex: 0 1 150px; min-width: 120px; }

/* 主题预览 */
.theme-demo {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px;
  border-radius: var(--radius);
  background: var(--bg);
  border: 1px dashed var(--line);
}
.theme-demo-money {
  font-size: 22px;
  font-weight: 750;
  color: var(--accent-dark);
  font-variant-numeric: tabular-nums;
}
.theme-demo-note { font-size: 11.5px; color: var(--faint); }

/* 配置页的输入框要占满整行。
   .stack 是纵向 flex，子项默认按内容宽度收缩 —— 于是这些输入框
   只有 190px 左右，右边空出一大片，而长密钥根本看不全。 */
.afield > .inp,
.afield > textarea.inp,
.afield > select.inp { width: 100%; }

/* ── 图片字段（服务项目 / 商品 / 套餐的封面） ─────────────────────
   预览固定 16:9：封面图最终出现在小程序卡片上就是 16:9，
   在这里用别的比例预览，会让人挑到一张「在后台好看、上小程序被裁掉半张脸」的图。
   建议尺寸 800×450，模板里写清楚，别只写「建议 16:9」。 */
.media-field { display: flex; flex-direction: column; gap: 8px; }
.media-frame {
  position: relative;
  width: 100%;
  max-width: 360px;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #f4f4f6;
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
}
.media-preview {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: none;
}
.media-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  color: var(--faint);
  font-size: 12px;
  text-align: center;
  padding: 10px;
}
.media-empty small { font-size: 11px; }
.media-acts { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.media-tip { font-size: 11.5px; color: var(--muted); display: none; }
.media-tip--err { color: var(--danger); }
.media-field--drag .media-frame { border-color: var(--accent); border-style: dashed; }
.media-field .btn.off { opacity: .55; pointer-events: none; }

/* 列表里的封面缩略图：同样 16:9，尺寸小，但比例一致。 */
.thumb {
  width: 56px;
  aspect-ratio: 16 / 9;
  border-radius: 6px;
  object-fit: cover;
  background: #f4f4f6;
  border: 1px solid var(--line);
  display: block;
}
.thumb-empty {
  width: 56px;
  aspect-ratio: 16 / 9;
  border-radius: 6px;
  background-color: #f4f4f6;
  /* 放一个极淡的图片图标：纯灰框在表格里会被当成复选框，
     有图标才读得出「这里本该有一张图」。 */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b9bcc6' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2.5'/%3E%3Ccircle cx='9' cy='9.5' r='1.6'/%3E%3Cpath d='M4 17l4.5-4.5 3 3L15 12l5 5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 15px 15px;
  border: 1px solid var(--line);
  display: block;
}

/* 列表里的「缩略图 + 名称」一行：图的宽高比与表单预览、小程序卡片一致。 */
.row-media { display: flex; align-items: center; gap: 10px; }
.row-media > div { min-width: 0; }

/* 环境徽标。开发/测试/UAT 用中性色，**生产用红色描边**：
   改配置前先看清自己在哪个环境，比任何提示文案都管用。 */
.env-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: #f7f7f8;
  color: var(--muted);
  font-size: 11.5px;
}
.env-pill b { color: var(--ink); }
.env-pill em { font-style: normal; color: var(--danger); font-weight: 600; }
.env-pill--prod { border-color: var(--danger); background: #fdf2f2; }

/* ── 主题选择器 ────────────────────────────────────────────────────
   每张卡上半部分是「按这套主题的真实参数画出来的缩略图」，
   下半部分是名字、属性标签和一句说明。

   为什么不是三颗色点：圆点只能表达颜色，而这批主题的差别更多在版式上 ——
   「顶部导航」「图标窄栏」「硬边」「深色侧栏」在点开之前完全看不出来。
   为什么不是整张卡涂主色：三十套涂满就是三十块色斑，眼睛没法比较。 */
.tp-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
  overflow-x: auto;
  padding-bottom: 8px;
  margin-bottom: 6px;
  scrollbar-width: none;
}
.tp-tabs::-webkit-scrollbar { display: none; }
.tp-tab {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--muted);
  border-radius: 999px;
  padding: 5px 12px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.tp-tab em {
  font-style: normal;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--faint);
}
.tp-tab:hover { border-color: var(--accent-line); color: var(--accent); }
.tp-tab.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.tp-tab.on em { color: rgba(255, 255, 255, .78); }

.theme-grid {
  display: grid;
  /* 上限写死 268px：卡片宽度不该随「这个行业有几套」而变 ——
     切页签时卡片忽大忽小，几张图就没法横向比较了。 */
  grid-template-columns: repeat(auto-fill, minmax(212px, 268px));
  justify-content: start;
  gap: 12px;
}
.theme-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 9px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--card);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s, transform .12s;
}
.theme-card input { position: absolute; opacity: 0; pointer-events: none; }
.theme-card:hover { border-color: var(--accent-line); transform: translateY(-1px); }
.theme-card.on {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft), 0 10px 22px -16px var(--accent);
}
/* 选中角标：一屏十几张卡时，「当前用的是哪张」必须一眼看到，
   只靠描边的话在浅色主题上几乎看不出来。 */
.theme-card.on::after {
  content: "✓";
  position: absolute;
  top: -7px;
  right: -7px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 12px;
  line-height: 20px;
  font-weight: 700;
  text-align: center;
  box-shadow: 0 0 0 2px var(--card);
}

/* 缩略图：按主题真实参数绘制（侧栏宽度 / 圆角 / 深色底 / 顶部导航） */
.tc-shot {
  display: flex;
  height: 86px;
  border-radius: calc(var(--pr, 12px) * .5);
  overflow: hidden;
  background: var(--pbg, #f6f7f9);
  box-shadow: inset 0 0 0 1px rgba(20, 22, 26, .06);
}
.tc-side {
  width: 26%;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 7px;
  background: var(--pcard, #fff);
  box-shadow: inset -1px 0 0 rgba(20, 22, 26, .05);
}
.tc-side i {
  display: block;
  height: 4px;
  border-radius: 2px;
  background: rgba(20, 22, 26, .12);
}
.tc-side i.tc-brand {
  height: 8px;
  width: 62%;
  margin-bottom: 2px;
  border-radius: 3px;
  background: var(--p, #a83355);
}
.tc-side i.on {
  height: 9px;
  border-radius: 3px;
  background: var(--pl, #eaced6);
}
.tc-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px;
}
.tc-bar {
  display: block;
  height: 6px;
  width: 52%;
  border-radius: 3px;
  background: var(--pcard, #fff);
}
.tc-kpis { display: flex; gap: 5px; }
.tc-kpis i {
  flex: 1;
  height: 22px;
  border-radius: calc(var(--pr, 12px) * .28);
  background: var(--pcard, #fff);
  box-shadow: inset 0 0 0 1px rgba(20, 22, 26, .05);
}
.tc-kpis i.hi { background: var(--ps, #fbf3f5); }
.tc-lines { display: flex; flex-direction: column; gap: 4px; }
.tc-lines i {
  display: block;
  height: 4px;
  border-radius: 2px;
  background: rgba(20, 22, 26, .1);
}
.tc-lines i:nth-child(2) { width: 82%; }
.tc-lines i:nth-child(3) { width: 58%; }
.tc-btn {
  display: block;
  height: 9px;
  width: 34%;
  margin-top: auto;
  border-radius: 999px;
  background: var(--p, #a83355);
}

/* 版式变体：顶部导航 —— 侧栏变成一条顶栏，内容区自然变高 */
.tc-shot--top { flex-direction: column; }
.tc-shot--top .tc-side {
  width: 100%;
  height: 17px;
  flex: none;
  flex-direction: row;
  align-items: center;
  gap: 4px;
  padding: 0 7px;
  box-shadow: inset 0 -1px 0 rgba(20, 22, 26, .05);
}
.tc-shot--top .tc-side i { height: 3px; width: 10%; }
.tc-shot--top .tc-side i.tc-brand {
  height: 6px;
  width: 16%;
  margin: 0 5px 0 0;
}
.tc-shot--top .tc-side i.on {
  height: 7px;
  width: 13%;
  background: var(--pl, #eaced6);
  box-shadow: none;
}

/* 版式变体：图标窄栏 */
.tc-shot--rail .tc-side {
  width: 16%;
  align-items: center;
  padding: 8px 4px;
}
.tc-shot--rail .tc-side i { width: 100%; }
.tc-shot--rail .tc-side i.tc-brand { width: 82%; }
.tc-shot--rail .tc-side i.on { box-shadow: inset 0 0 0 1px var(--p, #a83355); }

/* 深色侧栏：底变成 nav-bg，里面的线要反过来 */
.tc-shot--dark .tc-side {
  background: var(--pd, #22171a);
  box-shadow: none;
}
.tc-shot--dark .tc-side i { background: rgba(255, 255, 255, .22); }
.tc-shot--dark .tc-side i.tc-brand { background: var(--p); }
.tc-shot--dark .tc-side i.on {
  background: rgba(255, 255, 255, .14);
  box-shadow: inset 2px 0 0 var(--p);
}

/* 自定义：没有预设色，用斜纹占位，明摆着「这不是一套方案」 */
.tc-shot--custom {
  background: repeating-linear-gradient(45deg, #f5f6f8, #f5f6f8 6px, #eceef2 6px, #eceef2 12px);
}
.tc-shot--custom .tc-side { background: #fff; }
.tc-shot--custom .tc-side i.tc-brand,
.tc-shot--custom .tc-btn { background: #b9c0ca; }
.tc-shot--custom .tc-kpis i { background: #fff; }
.tc-shot--custom .tc-kpis i.hi { background: #f2f3f5; }

.tc-meta {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 0 3px 3px;
}
.tc-meta b { font-size: 13.5px; color: var(--ink); }
.tc-meta small { font-size: 11px; color: var(--faint); line-height: 1.45; }
.tc-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.tc-chips em {
  font-style: normal;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  background: var(--line-soft);
  border-radius: 4px;
  padding: 1px 5px;
}

/* 试穿状态条：告诉人「现在看到的是试穿效果，还没保存」 */
.tp-previewbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 9px 12px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  color: var(--muted);
}
.tp-previewbar b { color: var(--accent-dark); }
.tp-hint { color: var(--faint); }
.tp-hint b { color: var(--accent-dark); }
.tp-revert {
  margin-left: auto;
  border: 1px solid var(--accent-line);
  background: var(--card);
  color: var(--accent-dark);
  border-radius: 999px;
  padding: 4px 12px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}
.tp-revert:hover { border-color: var(--accent); }

/* ── 短信发送点位（技术端 · 短信配置页）─────────────────────────────
   每个点位一张小卡、一个独立表单。左侧 3px 竖条表示「这条会发」——
   开关与状态在同一屏里有 5 个，纯靠文字判断太慢。 */
.sms-group {
  margin: 18px 0 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--faint);
  text-transform: uppercase;
}
.sms-group:first-of-type { margin-top: 6px; }

.sms-scene {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px 10px;
  margin-bottom: 10px;
  background: var(--card);
  border-left: 3px solid var(--line);
}
.sms-scene.on {
  border-left-color: var(--accent);
  background: #fff;
}
.sms-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.sms-switch {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13.5px;
  cursor: pointer;
}
.sms-switch b { font-weight: 600; color: var(--ink); }
.sms-state {
  font-size: 11px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--line-soft);
  color: var(--muted);
}
.sms-scene.on .sms-state { background: var(--accent-soft); color: var(--accent-dark); }
.sms-code {
  margin-left: auto;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
}
.sms-grid {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(260px, 1.4fr);
  gap: 8px 20px;
  margin-top: 8px;
}
.sms-grid .afield { margin-bottom: 6px; }
.sms-check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12.5px;
  color: var(--ink-2);
  margin-right: 14px;
  line-height: 1.9;
  cursor: pointer;
}
.sms-roles { display: flex; flex-wrap: wrap; align-items: center; }
.sms-fixed {
  font-size: 12.5px;
  color: var(--ink-2);
  padding: 6px 10px;
  border-radius: 7px;
  background: var(--line-soft);
  line-height: 1.9;
}
.sms-foot {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding-top: 9px;
  border-top: 1px dashed var(--line);
}
.sms-foot .btn { margin-left: auto; }
.sms-who { font-size: 11.5px; color: var(--muted); }
.sms-who b { color: var(--ink-2); font-weight: 600; }

@media (max-width: 900px) {
  .sms-grid { grid-template-columns: 1fr; }
}

/* KPI 数值后缀（ms / 次）—— 小一号，避免和数字争视线 */
.kpi-v .unit { font-size: 13px; font-weight: 500; margin-left: 3px; color: var(--muted); }

/* ── 待办中心 ─────────────────────────────────────────────────────
   一行一件事：左边大数字，中间名称+说明，右边"去处理"。
   几乎全是可点的（整行是 <a>），因为这里的目的就是点进去干活。 */
.todo-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.todo-row {
  display: flex; align-items: center; gap: 14px;
  padding: 13px 16px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--card); text-decoration: none; color: inherit;
  transition: border-color .12s, background .12s;
}
.todo-row:hover { border-color: var(--accent); background: #fff; }
.todo-row.urgent { border-left: 3px solid var(--danger); }
.todo-n {
  flex: none; min-width: 38px; height: 38px; border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-soft, #fdf2f6); color: var(--accent);
  font-size: 17px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.todo-row.urgent .todo-n { background: #fdeaea; color: var(--danger); }
.todo-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.todo-body b { font-size: 14px; font-weight: 600; }
.todo-body small { font-size: 12px; color: var(--faint); }
.todo-go { flex: none; font-size: 12.5px; color: var(--muted); }
.todo-row:hover .todo-go { color: var(--accent); }
.todo-empty { text-align: center; padding: 28px 16px; }
.todo-empty b { font-size: 15px; }
.todo-empty p { margin: 6px 0 0; font-size: 13px; color: var(--faint); }

/* 技能勾选网格：一眼看全，不用翻页 */
.skill-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; }
.skill-item {
  display: flex; align-items: center; gap: 8px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--card);
  font-size: 13px; cursor: pointer;
}
.skill-item.on { border-color: var(--accent); background: var(--accent-soft, #fdf2f6); }

/* ── 配置页里的图片字段（Logo）─────────────────────────────────────────
   左预览右操作：上传前先看见「现在是什么」，否则改 Logo 只能靠记忆。
   预览框固定 88px 的正方形，与侧边栏/登录页那个圆角方块同一形状 ——
   这里看到的比例就是页面上会呈现的比例。 */
.afimage { display: flex; gap: 16px; align-items: center; }
.afimage-pv {
  width: 88px; height: 88px; flex: 0 0 88px; border-radius: 14px;
  background: var(--line-soft); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  position: relative; overflow: hidden;
}
.afimage-pv img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.afimage-empty { font-size: 11.5px; color: var(--faint); }
.afimage-clear {
  position: absolute; top: 1px; right: 5px; cursor: pointer;
  font-size: 15px; line-height: 1; color: var(--faint); user-select: none;
}
.afimage-clear:hover { color: #d64545; }
.afimage-act { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
.afimage-note { font-size: 11.5px; color: var(--faint); }

/* ══════════════════════════════════════════════════════════════════════════
   版式轴 · 主题 v2
   ──────────────────────────────────────────────────────────────────────
   body 上的 data-* 由 theme_presets.go 的 StyleAttr() 输出，六个轴各自一段：

     data-nav     侧栏底色      浅色 / 淡彩 / 深色
     data-shape   圆角          柔和 / 大圆角 / 硬边（半径走变量，这里只补细节）
     data-density 松紧          常规 / 紧凑
     data-font    标题字族      系统黑体 / 衬线 / 圆润
     data-card    卡片阴影      常规 / 无阴影 / 轻投影
     data-layout  导航位置      左侧栏 / 图标窄栏 / 顶部横向

   为什么用属性而不是一堆 utility 类：六个轴任意组合，类名会变成
   .l-top.s-round.d-compact.f-serif 这种笛卡尔积，模板里拼字符串必然出错；
   属性只表达「当前是哪个值」，组合交给样式表，加一个轴只加一段 @规则。
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 侧栏：淡彩 ──────────────────────────────────────────────────────────
   浅底侧栏最容易做砸：底一提亮，选中块就必须从「主色块」退成「白块」，
   否则同色系撞在一起，选中项反而看不清。 */
body[data-nav="tint"] .side {
  background: var(--accent-soft);
  border-right-color: var(--accent-line);
}
body[data-nav="tint"] .brand { border-bottom-color: var(--accent-line); }
body[data-nav="tint"] .side-foot { border-top-color: var(--accent-line); }
body[data-nav="tint"] .menu-item .mi { background: var(--card); }
body[data-nav="tint"] .menu-item.on {
  background: var(--card);
  box-shadow: 0 1px 2px rgba(20, 22, 26, .06);
}
body[data-nav="tint"] .menu-item.on .mi { background: var(--accent); }
body[data-nav="tint"] .avatar { background: var(--card); }

/* ── 侧栏：深色 ──────────────────────────────────────────────────────────
   深色侧栏不用「主色块」做选中态，而是用一条主色竖标 + 半透明白底。
   整块涂主色在深底上非常刺眼，且一旦主色偏浅（金/橙）文字就糊了。 */
body[data-nav="dark"] .side {
  background: var(--nav-bg);
  border-right-color: rgba(255, 255, 255, .06);
}
body[data-nav="dark"] .brand { border-bottom-color: rgba(255, 255, 255, .08); }
body[data-nav="dark"] .brand-text b { color: #fff; }
body[data-nav="dark"] .brand-text span { color: rgba(255, 255, 255, .5); }
body[data-nav="dark"] .brand-logo { box-shadow: 0 0 0 1px rgba(255, 255, 255, .1); }
body[data-nav="dark"] .menu-group { color: rgba(255, 255, 255, .34); }
body[data-nav="dark"] .menu-item { color: rgba(255, 255, 255, .74); }
body[data-nav="dark"] .menu-item .mi {
  background: rgba(255, 255, 255, .08);
  color: rgba(255, 255, 255, .56);
}
body[data-nav="dark"] .menu-item em {
  background: rgba(255, 255, 255, .1);
  color: rgba(255, 255, 255, .5);
}
body[data-nav="dark"] .menu-item.on {
  background: rgba(255, 255, 255, .1);
  color: #fff;
  box-shadow: inset 2px 0 0 0 var(--accent);
}
body[data-nav="dark"] .menu-item.on .mi {
  background: rgba(255, 255, 255, .18);
  color: #fff;
}
body[data-nav="dark"] .menu-item.soon { color: rgba(255, 255, 255, .3); }
body[data-nav="dark"] .menu-item.soon .mi {
  background: rgba(255, 255, 255, .04);
  color: rgba(255, 255, 255, .28);
}
body[data-nav="dark"] .menu-badge { background: var(--accent); color: #fff; }
body[data-nav="dark"] .side-foot { border-top-color: rgba(255, 255, 255, .08); }
body[data-nav="dark"] .me-text b { color: #fff; }
body[data-nav="dark"] .me-text span { color: rgba(255, 255, 255, .45); }
body[data-nav="dark"] .avatar {
  background: rgba(255, 255, 255, .12);
  color: #fff;
}
body[data-nav="dark"] .logout {
  color: rgba(255, 255, 255, .72);
  border-color: rgba(255, 255, 255, .2);
}
body[data-nav="dark"] .logout:hover { color: #fff; border-color: #fff; }
body[data-nav="dark"] .side-toggle {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .18);
  color: #fff;
}

/* ── 圆角：硬边 ──────────────────────────────────────────────────────────
   硬边不只是「小圆角」：胶囊标签、圆形徽标、圆头像统统要跟着方起来，
   否则一张卡片里同时出现 5px 和 999px，看起来像没改完。 */
body[data-shape="sharp"] .tag,
body[data-shape="sharp"] .menu-badge { border-radius: 4px; }
body[data-shape="sharp"] .avatar,
body[data-shape="sharp"] .brand-logo { border-radius: 6px; }
body[data-shape="sharp"] .menu-item .mi { border-radius: 4px; }
body[data-shape="sharp"] .card { border-color: var(--line); }
body[data-shape="sharp"] { --radius-sm: 5px; }

/* ── 圆角：大圆角 ── 补足「胶囊感」的其余零件 */
body[data-shape="round"] .avatar { border-radius: 50%; }
body[data-shape="round"] .brand-logo { border-radius: 16px; }
body[data-shape="round"] .kpi { border-radius: var(--radius); }
body[data-shape="round"] { --radius-sm: 14px; }

/* ── 松紧：紧凑 ──────────────────────────────────────────────────────────
   门店前台只有一台 1366×768 的旧机器时，紧凑模式能多露出 3 行表格。
   只压纵向间距，横向留白不动 —— 横向也压会变成一堵字墙。 */
body[data-density="compact"] { font-size: 13.5px; }
body[data-density="compact"] .main { padding: 18px 22px 32px; }
body[data-density="compact"] .topbar { padding-bottom: 13px; margin-bottom: 16px; }
body[data-density="compact"] .topbar h1 { font-size: 20px; }
body[data-density="compact"] .card { padding: 14px 16px; margin-bottom: 14px; }
body[data-density="compact"] .kpi { padding: 12px 14px 10px; }
body[data-density="compact"] .kpi-v { font-size: 22px; }
body[data-density="compact"] .kpis { gap: 10px; margin-bottom: 14px; }
body[data-density="compact"] table { font-size: 12.5px; }
body[data-density="compact"] .menu-item { padding: 6px 10px; }
body[data-density="compact"] .grid-main { gap: 14px; }
body[data-density="compact"] .col-right { gap: 14px; }

/* ── 字体：标题字族 ──────────────────────────────────────────────────────
   只换标题与品牌名，正文一律保持无衬线：中文衬线正文在 13px 上会糊，
   而「标题用宋体」这一件事就足够把整套界面的气质拉开。
   字族按可用性排序（macOS / Windows / Linux 各有一套）。 */
body[data-font="serif"] h1,
body[data-font="serif"] h2,
body[data-font="serif"] .brand-text b,
body[data-font="serif"] .login-brand h1,
body[data-font="serif"] .kpi-v {
  font-family: "Songti SC", "Noto Serif SC", "Source Han Serif SC", "STSong", SimSun, Georgia, serif;
  font-weight: 600;
  letter-spacing: .01em;
}
body[data-font="serif"] .kpi-v { font-weight: 600; }
body[data-font="round"] h1,
body[data-font="round"] h2,
body[data-font="round"] .brand-text b,
body[data-font="round"] .login-brand h1 {
  font-family: "Yuanti SC", "Hiragino Maru Gothic ProN", "PingFang SC", "Microsoft YaHei", sans-serif;
  font-weight: 700;
  letter-spacing: 0;
}

/* ── 卡片：无阴影 / 轻投影 ───────────────────────────────────────────────
   无阴影时描边要稍深一点，否则白卡压在近白的底上会「飘」起来。 */
body[data-card="flat"] .card,
body[data-card="flat"] .kpi { box-shadow: none; }
body[data-card="flat"] .card { border-color: var(--line); }
body[data-card="flat"] .kpi { border-color: var(--line); }
body[data-card="deco"] .card,
body[data-card="deco"] .kpi { box-shadow: var(--card-shadow); }

/* ── 布局：图标窄栏 ──────────────────────────────────────────────────────
   84px 只放图标与两三个字，把 150px 还给内容区。
   菜单分组标题降级成一条分隔线（字号归零，保留边框）——
   直接 display:none 会让十几项菜单连成一片，找不到分界。 */
@media (min-width: 821px) {
  body[data-layout="rail"] .shell { grid-template-columns: 84px minmax(0, 1fr); }
  body[data-layout="rail"] .brand {
    padding: 16px 8px 12px;
    justify-content: center;
    gap: 6px;
  }
  body[data-layout="rail"] .brand-text { display: none; }
  body[data-layout="rail"] .menu { padding: 6px 8px 14px; }
  /* 窄栏里放不下一条滚动条：它会把本就只有 84px 的宽度再削掉 8px，
     菜单文字被挤成省略号。滚轮与触控板照样能滚，藏掉更划算。 */
  body[data-layout="rail"] .side { scrollbar-width: none; }
  body[data-layout="rail"] .side::-webkit-scrollbar { width: 0; }
  body[data-layout="rail"] .menu-group {
    font-size: 0;
    padding: 0;
    margin: 8px 12px;
    border-top: 1px solid var(--line);
    line-height: 0;
  }
  body[data-nav="dark"][data-layout="rail"] .menu-group { border-top-color: rgba(255, 255, 255, .1); }
  body[data-layout="rail"] .menu-item {
    position: relative;
    flex-direction: column;
    gap: 3px;
    padding: 8px 2px;
    margin-bottom: 3px;
    font-size: 10.5px;
  }
  body[data-layout="rail"] .menu-item .mi {
    width: 30px;
    height: 30px;
    flex: 0 0 30px;
    font-size: 12.5px;
  }
  body[data-layout="rail"] .menu-item .mt {
    flex: none;
    max-width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  body[data-layout="rail"] .menu-item em { display: none; }
  body[data-layout="rail"] .menu-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 15px;
    line-height: 15px;
    font-size: 9.5px;
    padding: 0 4px;
  }
  body[data-layout="rail"] .side-foot { padding: 10px 6px; justify-content: center; }
  body[data-layout="rail"] .me { justify-content: center; }
  body[data-layout="rail"] .me-text,
  body[data-layout="rail"] .logout { display: none; }
  body[data-layout="rail"] .main { padding: 24px 26px 40px; }

  /* ── 布局：顶部横向导航 ────────────────────────────────────────────────
     侧栏变成一条常驻顶栏，内容区居中限宽。
     限宽是必要的：1920 的屏上表格铺满全宽，视线每行要横跳 60cm。 */
  /* 顶部导航用纵向弹性布局，而不是「把两列网格改成一列」：
     网格里的自动行会被 min-height:100vh 拉去平分剩余高度（顶栏被撑到 212px），
     而 sticky 顶栏又会被限制在那一行的高度里，滚两下就跟着跑掉。
     改成 flex 之后，.side 高度就是内容高度，粘性也以整页为参照。 */
  body[data-layout="top"] .shell { display: flex; flex-direction: column; }
  body[data-layout="top"] .side {
    position: sticky;
    top: 0;
    z-index: 60;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 4px;
    padding: 0 18px;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    overflow: visible;
    backdrop-filter: saturate(1.4) blur(6px);
  }
  body[data-layout="top"] .brand {
    padding: 12px 16px 12px 0;
    border-bottom: 0;
    flex: 0 0 auto;
  }
  body[data-layout="top"] .menu {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 1 1 auto;
    /* min-width:0 是这行的关键：flex 子项默认 min-width:auto，
       会撑到内容宽度而不肯收缩，于是菜单把右侧的账号块挤出屏幕。 */
    min-width: 0;
    padding: 8px 4px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
  }
  body[data-layout="top"] .menu::-webkit-scrollbar { display: none; }
  body[data-layout="top"] .menu-group { display: none; }
  body[data-layout="top"] .menu-item {
    flex: 0 0 auto;
    margin-bottom: 0;
    padding: 7px 12px;
    border-radius: var(--btn-radius);
    white-space: nowrap;
  }
  body[data-layout="top"] .menu-item .mi { display: none; }
  body[data-layout="top"] .menu-item.on { background: var(--accent-soft); }
  body[data-layout="top"] .side-foot {
    border-top: 0;
    padding: 0;
    flex: 0 0 auto;
    gap: 8px;
    margin-left: 8px;
  }
  body[data-layout="top"] .me { gap: 8px; }
  body[data-layout="top"] .me-text { display: none; }
  body[data-layout="top"] .main {
    width: 100%;
    max-width: 1440px;
    margin: 0 auto;
    padding: 24px 28px 40px;
  }
  body[data-layout="top"] .topbar { padding-top: 2px; }
  body[data-nav="dark"][data-layout="top"] .side {
    background: var(--nav-bg);
    border-bottom-color: rgba(255, 255, 255, .08);
  }
  body[data-nav="tint"][data-layout="top"] .side {
    background: var(--accent-soft);
    border-bottom-color: var(--accent-line);
  }
}

/* 手机上「顶部导航」本来就是这个形态（见 820px 那一节），
   这里只补两处：深色/淡彩底色的横条，以及侧栏里多余的底部账号块。 */
@media (max-width: 820px) {
  body[data-nav="dark"] .side { background: var(--nav-bg); border-bottom-color: rgba(255, 255, 255, .08); }
  body[data-nav="tint"] .side { background: var(--accent-soft); border-bottom-color: var(--accent-line); }
  body[data-nav="dark"] .menu { background: var(--nav-bg); }
  body[data-nav="dark"] .nav-close { background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .18); color: #fff; }
  body[data-nav="tint"] .menu { background: var(--accent-soft); }
  body[data-layout="rail"] .side { gap: 8px; }

  /* 抽屉里的账号块：手机上原本 .side-foot 被 display:none 藏掉了，
     于是「退出登录」在手机端完全没有入口 —— 只能清缓存。
     这里把它钉在抽屉底部（只在抽屉打开时出现），顺手给出一个能退出、
     能看到「我是谁」的位置。 */
  body.nav-open .side-foot {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 81;
    padding: 12px 18px calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--card);
    border-top: 1px solid var(--line);
  }
  body[data-nav="dark"].nav-open .side-foot {
    background: var(--nav-bg);
    border-top-color: rgba(255, 255, 255, .1);
  }
  body[data-nav="tint"].nav-open .side-foot {
    background: var(--accent-soft);
    border-top-color: var(--accent-line);
  }
  body.nav-open .menu { padding-bottom: 88px; }
}
