/*
  设计令牌**与小程序端 app.wxss 一一对应**，两边必须同时改——
  管理端是独立的一套 CSS（网页，不读小程序的变量），
  这里漏改一个值，同一个东西在网页和小程序上就会是两个颜色。

  核心是品牌色和主动作色分开：品牌色是识别，按钮和强调文字用近黑
  （浅色底压白字对比度不到 1.5:1，等于看不见），金额单独给一个红。

  ⚠️ **2026-08-26 换珊瑚：只换色相，结构一个字没动**（这一天先换玫红、
  再换珊瑚，两轮都是这么换的）。
  这套本来就是「浅色品牌面 + 近黑文字」，亮黄同样兼容——
  近黑 #1A1A1A 压在 #FFD84D 上是 **12.58:1**（暖杏 10.64、珊瑚 10.66、
  玫红 10.83、薄荷绿 10.67）。
  ⚠️ 换黄这轮**没有沿用「对齐亮度」那条**：亮黄比前几轮亮了一档
  （相对亮度 .592 → .709）。方向是变亮，浅底深字的对比只会更高，
  不产生新欠账，所以照样不用回测——但下次别拿这一轮当「亮度可以随便挪」的先例。

  ⚠️ **--price 这轮跟着小程序端变黑了**（#C44634 → #1A1A1A）。
  行动色变黑之后，金额留在红上会成为整屏唯一的彩色文字，反而比黑更吵。
  ⚠️ 但 --s-run / --s-err 留在红上不动：「进行中」和「出错」是状态不是金额，
  黑色说不出「出错了」。三个令牌当年没被合并，这一轮的分叉就是它们的用处。
  （⚠️ 那一轮它们的值是 #C44634，2026-09-17 跟小程序端一起挪到 #E7335C，
  见下面那条 ✅。留红的结论没变，变的是哪个红。）
  ⚠️ ========== 2026-09-17：上面这条反过来了，看完再动手 ==========

  原文是：「别把 --brand 改成深色再把 --on-brand 翻成白：那会推翻整套结构，
  --on-brand 在这份文件里引用了几十处，收益是零」。

  **两句都不成立，这一轮实测过：**

    · --on-brand **只有 1 处引用**（`.bt.pri`），不是几十处。那句是估的。
    · 「推翻整套结构」也没发生，因为**这份文件早就已经拆好了**：
      大面积的面一直写的是 `var(--yellow)`（5 处：登录页图标、顶栏 mark、
      侧栏选中条…），按钮/选中/聚焦一直写的是 `var(--brand)`（9 处）。
      两个令牌**同值只是巧合**——和小程序那边「早期 --brand 是主动作色、
      --yellow 是品牌黄，换紫那轮同值才被合并掉」是同一笔账。

  所以这一轮**没有改结构，只是把巧合拆开**：
    · `--brand` 族 → 行动色红（跟小程序端同值 #E7335C），`--on-brand` 翻白
    · `--yellow` 族 → 保持亮黄，继续做面
    · `--brand-tint` 当时留在浅黄 #FFF9E0。⚠️ **2026-09-17「全面去黄」那轮改成了
      中性 #F2F2F2**，不是浅红——理由不是色相好看，是有案底：2026-08-29 用过
      粉白（#FFF7F6）铺状态卡，评价是「这个配色是不是很诡异」。
      **面已经是红了，浅面再铺一层粉就是那次的翻版**，所以走中性。
      ⚠️ 这是个判断，不是算出来的；要翻成浅红就改这一个令牌。

  ⚠️ **对应关系跟着变了，这条要重记**：
      admin --yellow  ↔ 小程序 --sun     （品牌面，黄）
      admin --brand   ↔ 小程序 --brand   （行动色，红）
    以前是 admin --brand ↔ 小程序 --sun，**那是两边都还没拆开时的写法**。

  ⚠️ logo 不跟 --brand 走：`admin/logo.png` 由 tools/build-logo.sh 从小程序的
  --sun 生成，是**黄底**，归 tools/mirrorcheck.py 管。别看到 --brand 变红
  就去重出 logo。
  ⚠️ 2026-08-27 换黄黑之后，小程序那边的面是**黄 #FFD84D**、动作是**黑 #1A1A1A**。
  这边跟的是**面**，所以 --brand = 黄。
  ⚠️ 别看到小程序的 --brand 是黑就把这边也刷黑：两边的 --brand 指的不是一回事，
  对应关系一直是 admin 的 --brand ↔ 小程序的 --sun。

  ✅ **--s-run / --s-err 跟着小程序端走**：2026-08-26 一起对到 #C44634，
  2026-09-17 一起对到 **#E7335C**（小程序那轮把行动色换成了红，
  红这一族整体挪了位，见 miniprogram/app.wxss 的配色总纲）。
  最早推动对齐的是 tools/contrast.py：老值 #E63329 压白字只有 **4.31:1**，
  两头都差一点点过不了 AA。
  ⚠️ **--price 早就不在这条队里了**：换黄黑那轮它跟着变黑（#1A1A1A）并留到现在，
  小程序端却是红的。这是两边**已知的分叉**，不是漏改——这边的金额坐在
  密集表格里，红字一多整页就花。看到上面那句「已对齐」别顺手把它也刷红。
  ⚠️ 只动了红这一族。--s-done / --gold / 灰阶都还是这份文件自己的一套，
  和小程序端仍然不同值——那是另一笔账，别看到这条就以为两边全齐了。
  ⚠️ **admin 的 --brand 仍然是黄**（对应小程序的 --sun 品牌面）。小程序那边
  2026-09-17 把「行动」那一档从黄换成了红，admin 这边**没有对应的档**，
  按钮照旧是黄的。两端按钮不同色是这个映射关系的直接结果，不是漏改。

  ⚠️ 令牌名 --yellow 保留没改名（值已经不是黄了），理由同小程序那边的 --sun：
  改名的收益是"名字更贴切"，代价是一次全局替换加一轮回归，不值。

  ✅ **2026-08-26 文字色两端对齐了。** 小程序端原来是深靛蓝那一套
  （--ink #1E2A63 / --t2 #6B7099 / --t3 #8E94B8），这边一直是中性黑灰，
  两边差了一整套。那轮小程序按「简洁」换成中性近黑之后，
  --ink #1A1A1A / --ink-2 #333333 / --t1 / --t2 #666666 现在**逐个相同**。
  --t3 这边从 #999999 抬到 #969696 跟上小程序（2.85 → 2.96，两边同值）。
  ⚠️ 以后改文字色**两边一起改**，别再让它们分叉。
*/
:root {
  --brand: #E7335C; --brand-d: #A41335; --brand-tint: #F2F2F2;
  --on-brand: #FFFFFF; --accent: #B7153B;
  --yellow: #E7335C; --yellow-d: #A41335; --yellow-soft: #FDEDF0;
  --price: #1A1A1A;
  --ink: #1A1A1A; --ink-2: #333333;
  --gold: #E6A100; --gold-soft: #FFF6E0; --gold-ink: #8A6300;
  --s-wait: #999999; --s-taken: #2F6FBF; --s-run: #E7335C; --s-done: #14A05A; --s-err: #E7335C;
  --bg: #F4F4F4; --surface: #FFFFFF; --surface-2: #F5F5F5;
  --line: #EDEDED; --line-2: #DCDCDC;
  --t1: #1A1A1A; --t2: #666666; --t3: #969696;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  /* 三层暖色渐变去掉：新配色是中性灰阶，暖底会让整页发黄 */
  background: var(--bg);
  font-family: 'Noto Sans SC', sans-serif;
  color: var(--t1);
  min-height: 100vh;
}
.mono, .num { font-family: 'Barlow', sans-serif; font-variant-numeric: tabular-nums; }
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; }

/* ============ 登录屏 ============ */
.login-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.login-card {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 40px 32px;
  box-shadow: 0 4px 12px rgba(0,0,0,.05), 0 16px 40px -12px rgba(0,0,0,.1);
  text-align: center;
}
.login-mk {
  width: 52px; height: 52px; border-radius: 14px;
  /* ⚠️ 2026-09-17 全面去黄：--yellow 从亮黄变成红，字必须跟着翻白
     （红底近黑字 4.17，黄底近黑字 12.58 是白捡的，换色之后就没了）*/
  background: var(--yellow); color: var(--on-brand);
  display: flex; align-items: center; justify-content: center;
  font-size: 24px; font-weight: 900; margin: 0 auto 16px;
  overflow: hidden; /* logo.png 是满幅 --brand 底（2026-09-17 起，原来是 --sun 黄），靠这里切出圆角 */
}
.login-mk img { width: 100%; height: 100%; display: block; }
.login-title { font-size: 22px; font-weight: 900; }
.login-sub { font-size: 12.5px; color: var(--t2); margin-top: 4px; margin-bottom: 28px; }
.login-input {
  width: 100%; height: 44px; border: 1.5px solid var(--line);
  border-radius: 10px; padding: 0 14px; font-size: 14px;
  background: var(--surface-2); margin-bottom: 12px;
}
.login-input:focus { outline: none; border-color: var(--brand); }
.login-error { font-size: 12.5px; color: var(--s-err); min-height: 18px; margin-bottom: 8px; }
.login-btn {
  width: 100%; height: 44px; border: none; border-radius: 10px;
  /* 同上：红底白字 */
  background: var(--yellow); color: var(--on-brand); font-size: 14px; font-weight: 800;
}
.login-btn:disabled { opacity: .6; }
.login-warning {
  background: var(--yellow-soft); color: var(--gold-ink); border-radius: 8px;
  padding: 10px 12px; font-size: 12px; line-height: 1.6; margin-bottom: 16px; text-align: left;
}

/* ============ 应用外壳 ============ */
.app-shell { display: flex; min-height: 100vh; }
/*
  ⚠️ **侧栏定住，用 sticky 不用 fixed。**
  fixed 会脱离 flex 流，得再给 .main 补一个 200px 的左边距——
  那就有两个「200」要同步，改宽度时必漏一个。sticky 留在流里，宽度只写一处。

  height: 100vh + overflow-y 是给导航项多到一屏放不下时留的（现在 9 项还差得远）。
  overflow-x 必须保持 hidden：右上角那个装饰圆是 ::after 定位到框外的，靠它裁掉。
*/
.side {
  width: 200px; background: var(--ink); flex-shrink: 0;
  padding: 18px 0; position: sticky; top: 0;
  height: 100vh; overflow: hidden auto;
}
.side::after {
  content: ''; position: absolute; right: -46px; top: -46px;
  width: 132px; height: 132px; border-radius: 50%; background: rgba(231,51,92,.22);
}
.side > * { position: relative; z-index: 1; }
.logo { padding: 0 18px 20px; display: flex; align-items: center; gap: 9px; }
.logo .mk {
  /*
    ⚠️ 原注释：「深色侧边栏上不能用 --brand（近黑，会糊在背景里），强调一律用黄」。
    **前半句 2026-09-17 失效了**——--brand 早就不是近黑，是红 #E7335C，
    在深色侧栏上一点也不糊。后半句跟着没了意义：这里现在写 --yellow，
    而 --yellow 和 --brand 同值，两个写法渲染一样。
    ⚠️ **仍然写 --yellow**：它是「面」，这颗 mark 是个色块不是动作。
    令牌选对，下次两者再分家时这儿不用回来重判。
  */
  width: 28px; height: 28px; border-radius: 8px; background: var(--yellow);
  display: flex; align-items: center; justify-content: center; color: var(--on-brand);
  font-size: 14px; font-weight: 900; overflow: hidden;
}
.logo .mk img { width: 100%; height: 100%; display: block; }
.logo .tx { color: #fff; font-size: 14px; font-weight: 700; }
.navi {
  display: flex; align-items: center; gap: 9px; padding: 11px 18px;
  color: #9A9A9A; font-size: 13px; cursor: pointer; user-select: none;
}
.navi:hover { color: #E0E0E0; }
.navi .g { width: 16px; text-align: center; font-size: 13px; }
.navi.on {
  color: #fff; font-weight: 700; background: rgba(231,51,92,.16);
  box-shadow: inset 3px 0 0 var(--yellow);
}
.navi .bdg {
  margin-left: auto; background: var(--s-err); color: #fff;
  font-family: 'Barlow', sans-serif; font-size: 10.5px; font-weight: 700;
  padding: 1px 6px; border-radius: 20px;
}
.sidefoot {
  position: absolute; bottom: 16px; left: 18px; right: 18px;
  color: #9A877E; font-size: 11px; line-height: 1.7;
}
.sidefoot .lk { color: #C8C8C8; text-decoration: underline; cursor: pointer; }

.main { flex: 1; background: var(--surface-2); padding: 20px 26px 40px; min-width: 0; }

/* ============ 实时地图 ============ */
/*
  高度用 vh 而不是固定 px：县城一屏装得下全城，图给得越满越好用。
  减掉的是页头 + 面板头 + 图例那几块的大概高度。
*/
.mapbox {
  height: calc(100vh - 260px);
  min-height: 380px;
  background: var(--bg);
  display: flex; align-items: center; justify-content: center;
}
.maplegend {
  display: flex; flex-wrap: wrap; gap: 14px;
  padding: 10px 16px; border-top: 1px solid var(--line);
  font-size: 12px; color: var(--t2);
}
.maplegend span { display: flex; align-items: center; gap: 5px; }
.maplegend i {
  width: 10px; height: 10px; border-radius: 50%;
  box-shadow: 0 0 0 2px #fff, 0 0 0 3px var(--line-2);
}
.maplegend .dim { margin-left: auto; color: var(--t3); }
/* 自动刷新那个勾。⚠️ 默认关着是有意的，见 app.js 里那段注释 */
.mapauto { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--t2); cursor: pointer; }
.loading-full { padding: 80px; text-align: center; color: var(--t3); font-size: 13px; }

.mhead { display: flex; align-items: baseline; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.mhead h2 { font-size: 21px; font-weight: 900; letter-spacing: -.01em; }
.mhead .sub { font-size: 12.5px; color: var(--t2); }
.mhead .rt { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.bt {
  background: var(--surface); border: 1.5px solid var(--line); border-radius: 8px;
  padding: 7px 14px; font-size: 12.5px; font-weight: 500; color: var(--t1);
}
.bt:hover { border-color: var(--line-2); }
/* 主按钮黄底黑字，和小程序端 .cta 同口径 */
.bt.pri { background: var(--brand); border-color: var(--brand); color: var(--on-brand); font-weight: 800; }
.bt.dark { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 700; }
.bt:disabled { opacity: .5; cursor: default; }

/* KPI */
.kpis { display: flex; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.kpi {
  flex: 1; min-width: 130px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 14px 16px;
  box-shadow: 0 1px 2px rgba(0,0,0,.03), 0 2px 8px rgba(0,0,0,.04);
}
.kn { font-family: 'Barlow', sans-serif; font-variant-numeric: tabular-nums; font-size: 28px; font-weight: 700; line-height: 1.1; letter-spacing: -.02em; }
.kn.w { color: var(--price); } .kn.e { color: var(--s-err); } .kn.g { color: var(--s-done); }
.kl { font-size: 11.5px; color: var(--t2); margin-top: 4px; }
.kd { font-family: 'Barlow', sans-serif; font-size: 11px; color: var(--t3); margin-top: 2px; }

/* 面板/表格 */
.panel {
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  overflow: hidden; margin-bottom: 16px;
  box-shadow: 0 2px 6px rgba(0,0,0,.04), 0 8px 24px -6px rgba(0,0,0,.07);
}
.ph { padding: 13px 16px; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ph .pt { font-size: 14px; font-weight: 700; }
.ph .ps { font-size: 11.5px; color: var(--t3); }
.ph .pr { margin-left: auto; display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }

.tablewrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th {
  background: var(--surface-2); font-size: 11.5px; color: var(--t2); text-align: left;
  padding: 9px 15px; font-weight: 700; white-space: nowrap;
}
td { font-size: 12.5px; padding: 10px 15px; border-bottom: 1px solid var(--line); vertical-align: middle; }
tr:last-child td { border-bottom: none; }
tr.alert td { background: #FDF0EE; }
tr.warn td { background: #FEF8EE; }
.sname { font-weight: 700; }
.tbl-empty { padding: 40px; text-align: center; color: var(--t3); font-size: 13px; }

.tg { font-size: 11px; font-weight: 700; padding: 3px 8px; border-radius: 6px; white-space: nowrap; display: inline-block; }
/*
  ⚠️ **和小程序端同一个坑，2026-08-26 一起修的。**
  这两条原来把状态色原值当字色压在手写浅底上：
    .tg.wait   #F0E9E3 底 + --s-wait  #999999 = 2.37:1
    .tg.taken  #E7EFF6 底 + --s-taken #2F6FBF = 4.36:1
  状态色是给圆点/进度条那种**色块**用的（3:1 就够），当文字色是用错了档。
  改法跟着小程序那边走：底和字成对，字色单独压深到过 4.5。
*/
.tg.wait { background: #F0E9E3; color: #666666; }
.tg.taken { background: #E7EFF6; color: #2A639F; }
.tg.run { background: var(--brand-tint); color: var(--brand-d); }
.tg.done { background: #E6F1EA; color: #256540; }
.tg.err { background: var(--s-err); color: #fff; }
.tg.sun { background: var(--gold-soft); color: #8A6207; }

.chips { display: flex; gap: 7px; flex-wrap: wrap; }
.chips b {
  font-size: 12px; font-weight: 500; padding: 5px 12px; border-radius: 20px;
  background: var(--surface); border: 1.5px solid var(--line); color: var(--t2); cursor: pointer;
}
.chips b.on { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 700; }

.filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; align-items: center; }
.filters select, .filters input {
  height: 34px; border: 1.5px solid var(--line); border-radius: 8px;
  padding: 0 10px; font-size: 12.5px; background: var(--surface); color: var(--t1);
}

/* 双栏（纠纷处理）*/
.two { display: flex; gap: 16px; align-items: flex-start; }
.two .lft { width: 280px; flex-shrink: 0; }
.two .rgt { flex: 1; min-width: 0; }
.caseitem { padding: 13px 15px; border-bottom: 1px solid var(--line); cursor: pointer; }
.caseitem:last-child { border-bottom: none; }
.caseitem.on { background: #FFF7F6; box-shadow: inset 3px 0 0 var(--s-err); }
.caseitem .cr { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.caseitem .cn { font-size: 13px; font-weight: 700; }
.caseitem .cm { font-size: 11.5px; color: var(--t2); margin-top: 5px; line-height: 1.55; }

.tl { padding: 4px 0; }
.tli { display: flex; gap: 11px; position: relative; padding-bottom: 15px; }
.tli:last-child { padding-bottom: 0; }
.tli::before { content: ''; position: absolute; left: 5px; top: 15px; bottom: 0; width: 1.5px; background: var(--line); }
.tli:last-child::before { display: none; }
.tld { width: 12px; height: 12px; border-radius: 50%; background: var(--line-2); flex-shrink: 0; margin-top: 3px; z-index: 1; }
.tld.ok { background: var(--s-done); }
.tld.err { background: var(--s-err); }
.tld.now { background: var(--ink); box-shadow: 0 0 0 4px rgba(231,51,92,.45); }
.tlt { font-size: 13px; font-weight: 700; }
.tls { font-family: 'Barlow', sans-serif; font-size: 11.5px; color: var(--t3); margin-top: 2px; }

.kv { font-size: 13px; line-height: 2.1; display: flex; gap: 8px; }
.kv b { color: var(--t2); font-weight: 500; min-width: 80px; flex-shrink: 0; }

.pick {
  border: 1.5px solid var(--line); border-radius: 9px; padding: 12px 14px;
  margin-bottom: 8px; display: flex; align-items: flex-start; gap: 10px;
  background: var(--surface); cursor: pointer;
}
/* ⚠️ 底色用 var(--brand-tint)，别写死。这里曾经是珊瑚轮留下的 #FEF6F3，
   换黄黑时描边跟着 --brand 变了、底色没跟，成了黄描边配粉底 */
.pick.on { border-color: var(--brand); background: var(--brand-tint); }
.pick .rd {
  width: 17px; height: 17px; border-radius: 50%; border: 2px solid var(--line-2);
  flex-shrink: 0; margin-top: 2px; position: relative;
}
.pick.on .rd { border-color: var(--brand); }
.pick.on .rd::after { content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--brand); }
.pick .pt { font-size: 13.5px; font-weight: 700; }
.pick .pm { font-size: 11.5px; color: var(--t2); margin-top: 4px; line-height: 1.55; }
.ta {
  width: 100%; border: 1.5px solid var(--line); border-radius: 9px; padding: 11px 13px;
  min-height: 72px; font-size: 13px; color: var(--t1); background: var(--surface); resize: vertical;
}

.avatar {
  width: 30px; height: 30px; border-radius: 9px; background: var(--ink); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; margin-right: 9px; vertical-align: middle; flex-shrink: 0;
}
.avatar.sun { background: var(--gold); color: #5C3B00; }

.bar { height: 7px; border-radius: 4px; background: var(--surface-2); overflow: hidden; width: 88px; display: inline-block; vertical-align: middle; }
.bar i { display: block; height: 100%; background: var(--s-done); border-radius: 4px; }
.bar i.mid { background: var(--gold); } .bar i.low { background: var(--s-err); }

.note { background: var(--surface-2); border: 1px dashed var(--line-2); border-radius: 10px; padding: 12px 14px; font-size: 12.5px; color: var(--t2); line-height: 1.8; }
.note b { color: var(--t1); font-weight: 700; }

/* 图片证件 */
.certgrid { display: flex; gap: 12px; flex-wrap: wrap; }
.certimg {
  width: 200px; height: 130px; border-radius: 9px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line); cursor: zoom-in;
  display: flex; align-items: center; justify-content: center; color: var(--t3); font-size: 12px;
}
.certimg img { width: 100%; height: 100%; object-fit: cover; }
/* 换不到临时链接 / 图片加载不出来：写明原因，别留一块空白让人以为没上传 */
.certimg.certfail { cursor: default; color: var(--accent); background: var(--brand-tint); text-align: center; padding: 0 10px; line-height: 1.5; }

/* 表单 */
.formgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 12px; color: var(--t2); font-weight: 600; }
.field input, .field select {
  height: 36px; border: 1.5px solid var(--line); border-radius: 8px;
  padding: 0 12px; font-size: 13px; background: var(--surface);
}
.field .desc { font-size: 11px; color: var(--t3); }

.rangetable { width: 100%; border-collapse: collapse; margin-top: 10px; }
.rangetable th, .rangetable td { padding: 8px 10px; border-bottom: 1px solid var(--line); font-size: 12.5px; }
.rangetable input { width: 100%; height: 32px; border: 1.5px solid var(--line); border-radius: 6px; padding: 0 8px; font-size: 12.5px; }

/* 弹层（改密码 / 图片预览） */
.modal-mask {
  position: fixed; inset: 0; background: rgba(0,0,0,.5);
  display: flex; align-items: center; justify-content: center; z-index: 200; padding: 20px;
}
.modal-box {
  background: var(--surface); border-radius: 14px; padding: 26px;
  max-width: 420px; width: 100%; box-shadow: 0 16px 48px -12px rgba(0,0,0,.28);
}
.modal-box h3 { font-size: 17px; font-weight: 800; margin-bottom: 16px; }
.modal-box .row { display: flex; gap: 10px; margin-top: 18px; justify-content: flex-end; }
.modal-img { max-width: 90vw; max-height: 90vh; }
.modal-mask.img-preview { background: rgba(20,16,14,.86); }

.pagerow { display: flex; align-items: center; gap: 12px; padding: 12px 16px; font-size: 12.5px; color: var(--t2); }
.pagerow .grow { flex: 1; }

@keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
.panel, .kpis { animation: fadeUp .35s cubic-bezier(.22,.68,.32,1) both; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; } }

@media (max-width: 860px) {
  .app-shell { flex-direction: column; }
  .side { width: 100%; padding: 12px 0; }
  .sidefoot { display: none; }
  .two { flex-direction: column; }
  .two .lft { width: 100%; }
  /* 客服台三列在窄屏排不下，改竖排；高度也不再锁死，否则每块都只剩一条缝 */
  .supportwrap { flex-direction: column; height: auto; }
  .supportlist, .supportinfo { width: 100%; max-height: 320px; }
  .supportchat { height: 60vh; }
}

/* ============ 客服消息（A8） ============ */
/*
  左列表 + 右会话。**整块自己撑满视口剩余高度**，两列各自内部滚动——
  让整页滚的话，右边的输入框会被推到屏幕外面，回消息得先往下滚一段。
*/
.supportwrap {
  display: flex;
  gap: 16px;
  height: calc(100vh - 150px);
  min-height: 420px;
}
.supportlist {
  width: 300px;
  flex-shrink: 0;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.supportchat {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
}
.sp-empty { padding: 60px 20px; text-align: center; color: var(--t3); font-size: 13px; }

/* ---- 会话列表 ---- */
.sprow {
  position: relative;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
}
.sprow:hover { background: var(--surface-2); }
.sprow.on { background: var(--brand-tint); }
.sptop { display: flex; align-items: baseline; gap: 8px; }
.spname { font-size: 13.5px; font-weight: 700; flex: 1; min-width: 0; }
.sptime { font-size: 11px; color: var(--t3); flex-shrink: 0; }
.spmeta { display: flex; align-items: center; gap: 8px; margin-top: 5px; }
.spphone { font-size: 11px; color: var(--t3); }
.splast {
  margin-top: 5px;
  font-size: 12px;
  color: var(--t2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 未读红点。绝对定位，不占位——占位的话有无红点两种行宽不一致 */
.spdot {
  position: absolute;
  top: 12px;
  right: 12px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--price);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

/* ---- 右侧会话 ---- */
.sphead {
  flex-shrink: 0;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
}
.spht { font-size: 14px; font-weight: 700; }
.sphs { font-size: 11.5px; color: var(--t3); margin-top: 3px; }

.spmsgs { flex: 1; min-height: 0; overflow-y: auto; padding: 14px 16px; background: var(--bg); }
.spsys {
  margin: 10px auto;
  max-width: 70%;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .05);
  color: var(--t3);
  font-size: 11px;
  text-align: center;
  width: fit-content;
}
/* 一条消息。默认左（用户发的），.me 翻到右（管理员自己发的） */
.sprow2 { margin-bottom: 12px; max-width: 72%; }
.sprow2.me { margin-left: auto; text-align: right; }
.spwho { font-size: 11px; color: var(--t3); margin-bottom: 3px; }
.spbub {
  display: inline-block;
  padding: 9px 12px;
  border-radius: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.55;
  text-align: left;
  word-break: break-word;
  white-space: pre-wrap;
}
/*
  ⚠️ 2026-09-17 补了 color。底是 var(--brand)，这一轮从亮黄变成红之后，
  .spbub 继承下来的近黑字压在红上只有 4.17:1——**黄底黑字 12.58 是白捡的，
  红底黑字就不行了**。凡是拿 --brand 当底的地方都得显式写 --on-brand。
*/
.sprow2.me .spbub { background: var(--brand); border-color: var(--brand-d); color: var(--on-brand); }
.spts { font-size: 10.5px; color: var(--t3); margin-top: 3px; }

.spinput {
  flex-shrink: 0;
  display: flex;
  gap: 10px;
  padding: 12px 14px;
  border-top: 1px solid var(--line);
}
.spinput input {
  flex: 1;
  min-width: 0;
  height: 34px;
  padding: 0 12px;
  border: 1px solid var(--line-2);
  border-radius: 8px;
  font-size: 13px;
}

/* ---- 第三列：对方是谁 ---- */
/*
  客服会话的第一句话往往是「我的单怎么还没到」，不带单号也说不清是哪家店。
  这一列就是为了不让人为了回这一句去翻订单台账（见 app.js 的 loadSupportProfile）。
  和左右两列一样**自己内部滚动**，输入框不能被它顶出屏幕。
*/
.supportinfo {
  width: 300px;
  flex-shrink: 0;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.spihead { padding: 13px 15px; border-bottom: 1px solid var(--line); }
.spit { font-size: 14px; font-weight: 700; }
.spis { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
.spibody { padding: 12px 15px; }
.spisec { font-size: 12px; font-weight: 700; color: var(--t2); margin: 16px 0 8px; }
/* 一张在途/近期单的小卡，点开是订单全景 */
.spord {
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 9px 11px;
  margin-bottom: 8px;
  cursor: pointer;
}
.spord:hover { border-color: var(--line-2); background: var(--surface-2); }
.spordt { display: flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 700; }
.spords { font-size: 11px; color: var(--t3); margin-top: 4px; line-height: 1.5; word-break: break-all; }

/* ============ 客服监控台 ============ */
/*
  订单全景 / 用户档案两个弹层。信息量比其它弹层大一档，420px 装不下，
  所以 .modal-box 上加一个 .wide——**不是另开一套弹层实现**。
  高度封在视口内并自己滚动：内容长度差别很大（一单可能带三条售后，也可能什么都没有）。
*/
.modal-box.wide { max-width: 780px; max-height: 86vh; overflow-y: auto; }
.modal-box.wide h3 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* 分节小标题。比 h3 轻一档，用来把弹层切成「全链路 / 三方 / 钱 / 异常」几块 */
.monh {
  font-size: 12.5px; font-weight: 700; color: var(--t2);
  margin: 20px 0 10px; padding-bottom: 6px; border-bottom: 1px solid var(--line);
}
.monh:first-of-type { margin-top: 4px; }

/* 卡片按宽度自己排，一行两到三张 */
.mongrid { display: flex; gap: 12px; flex-wrap: wrap; }
.moncard {
  flex: 1 1 220px; min-width: 0;
  border: 1px solid var(--line); border-radius: 10px;
  padding: 12px 14px; background: var(--surface-2);
}
.mct {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13px; font-weight: 700; margin-bottom: 9px;
}
.mct > span:first-child { flex: 1; min-width: 0; }
/* 一行「标签 值」。标签固定宽，多行时值不会跟标签错位 */
.mcr { display: flex; gap: 10px; font-size: 12.5px; line-height: 1.9; }
.mcr > b { color: var(--t2); font-weight: 500; flex-shrink: 0; min-width: 72px; }
.mcr > span { min-width: 0; word-break: break-all; }
.moncard .bt { margin-top: 10px; width: 100%; }

/* 可点开全景的订单行。**光标 + 悬停底色两样都要**：
   只有 cursor 的话，表格看上去和别处那些不可点的表一模一样 */
tr.mon-open { cursor: pointer; }
tr.mon-open:hover > td { background: var(--surface-2); }


/* ============ 售后判定 ============ */
/*
  ⚠️ 这一页和「纠纷处理」共用 .two / .caseitem / .pick / .certgrid，
  只有下面这几条是它自己的。别为了它再复制一套双栏。
*/

/* 对方（商户/骑手）的申辩。左边一条竖线表示"这是别人说的话" */
.asresp {
  border-left: 3px solid var(--line-2);
  padding: 8px 0 8px 12px;
  margin-bottom: 10px;
}
.asresp .pt { font-size: 12.5px; font-weight: 700; }
.asresp .pm { font-size: 12px; color: var(--t2); margin-top: 4px; line-height: 1.6; }

/*
  对方提出的私了，还等着顾客确认。

  ⚠️ **和申辩（.asresp）故意长得不一样**：那是「他说了什么」，这是
  「这单大概不用判了」。用琥珀底把它从一串灰竖线里挑出来——管理员一眼
  看到这块，就该先想能不能判「双方已协商解决」，而不是往下读申辩去分对错。
  ⚠️ 底色取 `.asstat.warn` 那一对（--gold-soft / --gold-ink），不另挑一组：
  这个文件里已经因为"差几个色阶的近似色"栽过一次（见下面 .asdone 那段）。
*/
.asoffer {
  margin-top: 4px;
  background: var(--gold-soft);
  border-radius: 9px;
  padding: 12px 14px;
}
.asoffer .pt { font-size: 12.5px; font-weight: 700; color: var(--gold-ink); }
.asoffer .pm { font-size: 12px; color: var(--t2); margin-top: 4px; line-height: 1.6; }

/* 一句灰色补充说明（还没人回应 / 正在拉历史） */
.asnote { font-size: 12px; color: var(--t3); margin-top: 10px; line-height: 1.6; }

/*
  这个顾客报过几次。
  ⚠️ **默认是中性灰，只有可疑时才转琥珀。** 它是给人看的参考，不是判定结论；
  每条都染色等于在暗示"这人有问题"，那就成了自动判定。
*/
.asstat {
  font-size: 12.5px;
  color: var(--t2);
  background: var(--surface-2);
  border-radius: 8px;
  padding: 10px 12px;
  line-height: 1.6;
}
.asstat b { color: var(--t1); }
.asstat.warn { background: var(--gold-soft); color: var(--gold-ink); }
.asstat.warn b { color: var(--gold-ink); }

/*
  判完之后的结论块。绿底表示"这条处理完了"，和全局「绿=已完成」一致。

  ⚠️ 底色和字色**直接取 `.tg.done` 那一对**（#E6F1EA / #256540），
  没有另挑一组。小程序端 app.wxss 里专门骂过这件事：
  「页面里还冒出了第三个几乎同色的 #E8F5EE」——差几个色阶的近似色
  是配色里最难查的一类债，改配色时永远会漏掉其中一个。
  这两个值将来该收成 --s-done-soft / --s-done-ink 两个令牌，那是另一笔账。
*/
.asdone {
  margin-top: 18px;
  background: #E6F1EA;
  border-radius: 9px;
  padding: 12px 14px;
}
.asdone .pt { font-size: 13.5px; font-weight: 700; color: #256540; }
.asdone .pm { font-size: 12px; color: var(--t2); margin-top: 4px; line-height: 1.55; }

/*
  ---- 售后：商品返还（「退款要退货」，见 cloudfunctions/api/modules/aftersales.js）----

  三个状态各一个小标。⚠️ **没有一个是红的**：
    wait   还没收到      —— 待办
    done   已确认收到    —— 「已完成」那档绿
    cannot 顾客声明无法返还 —— 警示色，因为它**需要多看一眼**，
                             不是因为顾客可疑（洒了/变质/吃了才发现都是真事）
  总原则：面向用户的文案和配色都不给任何一方定性。
*/
.asret {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 5px;
  font-size: 11.5px;
  font-weight: 700;
  margin-right: 6px;
}
.asret.wait { background: var(--surface-2); color: var(--t1); }
.asret.done { background: var(--s-done-soft); color: var(--s-done-ink); }
.asret.cannot { background: var(--gold-soft); color: var(--gold-ink); }

/*
  货没回来还要判商户责任时才出现的放行框。
  ⚠️ 底色和 .asstat.warn 同一对，理由同那条：这一块是「停一下，先看清楚」，
  不是错误。⚠️ 它**不是一个无声的开关**——理由必填才放行，而且会存进工单。
*/
.aswaive {
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--gold-soft);
  color: var(--gold-ink);
}
.aswaive .pt { font-size: 12.5px; font-weight: 700; }
.aswaive .pm { font-size: 12px; margin-top: 6px; line-height: 1.6; }
/* 管理端没有通用的 input 样式（只有 .ta），这颗就地写一份，跟 .ta 一个模子 */
.aswaive .ipt {
  width: 100%;
  box-sizing: border-box;
  margin-top: 10px;
  height: 36px;
  border: 1.5px solid var(--line);
  border-radius: 9px;
  padding: 0 13px;
  font-size: 13px;
  color: var(--t1);
  background: var(--surface);
}

/*
  队列里那条「⚑ 反复声明商品无法返还」。
  ⚠️ 用警示色不是错误红，理由和 .asret.cannot 一样：**这是"多看一眼"，
  不是"这个人有问题"**。它只是个排序用的提示，判定依据仍旧是照片和说明。
*/
.cflag {
  margin-top: 5px;
  font-size: 11px;
  font-weight: 700;
  color: var(--gold-ink);
}
