/* ==========================================================================
   售电业务管理系统 · 样式
   恒源 VI 体系（色值/字体取自官方 VI 手册，不得自行更改）

   品牌色：恒蓝 #0A4CA6｜源绿 #00A651｜辉黄 #FFD100｜旭橙 #F26522
          墨蓝 #14253F｜雾灰 #C9CED6｜云白 #FFFFFF
   功能色：成功 源绿 #00A651｜警示 辉黄 #FFD100｜错误 #C62828｜信息 #2A6EBB
   文字  ：主要 #14253F 100%｜次要 #14253F 72%｜弱化 #14253F 45%
           （72%/45% 已按云白底合成：#566275 / #959DA9）
   分割线：#E3E6EB　背景：#F4F5F7
   字体  ：标题 思源宋体 / 正文 思源黑体（本地回退，不加载外部字体）
   ========================================================================== */

:root {
  /* ---- 品牌色 ---- */
  --hy-blue:   #0A4CA6;   /* 恒蓝 */
  --hy-green:  #00A651;   /* 源绿 */
  --hy-yellow: #FFD100;   /* 辉黄 */
  --hy-orange: #F26522;   /* 旭橙 */
  --hy-ink:    #14253F;   /* 墨蓝 */

  /* ---- 功能色 ---- */
  --c-danger:  #C62828;
  --c-info:    #2A6EBB;

  /* 预警色（可读化派生：底色为 6%~12% 淡底，文字加深保证对比度） */
  --lv-green-bg:  rgba(0, 166, 81, .07);
  --lv-green-fg:  #00794A;
  --lv-green-bar: #00A651;
  --lv-yellow-bg: rgba(255, 209, 0, .14);
  --lv-yellow-fg: #806000;
  --lv-yellow-bar:#FFD100;
  --lv-red-bg:    rgba(198, 40, 40, .07);
  --lv-red-fg:    #C62828;
  --lv-red-bar:   #C62828;

  /* ---- 文字 ---- */
  --t1: #14253F;   /* 主要 100% */
  --t2: #566275;   /* 次要 72%  */
  --t3: var(--n650);   /* 弱化 45%（旧色板遗留，已被 tokens/glass 同值覆盖，保持同源） */

  /* ---- 中性 ---- */
  --line:  #E3E6EB;
  --line2: #C9CED6;
  --bg:    #F4F5F7;
  --panel: #FFFFFF;
  --hover: rgba(10, 76, 166, .06);
  --active:rgba(10, 76, 166, .09);

  /* ---- 尺寸 ---- */
  --topbar-h: 60px;
  --side-w:   216px;
  --radius:   8px;

  /* ---- 字体 ---- */
  --font-serif: 'Source Han Serif SC', 'Noto Serif SC', 'Songti SC', 'STSong', 思源宋体, 宋体, SimSun, serif;
  --font-sans: 'Source Han Sans SC', 'Noto Sans SC', 'PingFang SC', 'Hiragino Sans GB',
               'Microsoft YaHei', 思源黑体, Arial, Helvetica, sans-serif;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0; padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--t1);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3, h4 { font-family: var(--font-serif); font-weight: 700; margin: 0; color: var(--t1); letter-spacing: .01em; }
p { margin: 0; }
a { color: var(--hy-blue); text-decoration: none; }
button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--line2); border-radius: 4px; }
::-webkit-scrollbar-track { background: transparent; }

/* ==========================================================================
   登录页
   ========================================================================== */
.login-wrap {
  min-height: 100%;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 32px 20px;
  background: var(--bg);
  /* 极淡的品牌底纹：恒蓝 3% 斜向渐变，不使用强渐变/投影 */
  background-image: linear-gradient(160deg, rgba(10,76,166,.05) 0%, rgba(255,255,255,0) 55%);
}

.login-card {
  width: 100%; max-width: 420px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 36px 34px 28px;
}

.login-brand { display: flex; align-items: center; gap: 14px; margin-bottom: 28px; }
.login-brand img { height: 40px; width: auto; display: block; }
.login-brand h1 { font-size: 19px; line-height: 1.3; }
.login-brand p {
  font-size: 11px; color: var(--t3);
  letter-spacing: .12em; text-transform: uppercase; margin-top: 3px;
}

.field { display: block; margin-bottom: 16px; min-width: 0; }
.field > span { display: block; font-size: 13px; font-weight: 500; color: var(--t1); margin-bottom: 6px; line-height: 1.4; }
.field input {
  width: 100%; height: 42px; padding: 0 12px;
  border: 1px solid var(--line2); border-radius: 6px;
  background: var(--panel); outline: none;
  transition: border-color .15s;
}
.field input:focus { border-color: var(--hy-blue); }

.login-err {
  background: rgba(198,40,40,.07); color: var(--c-danger);
  border-left: 3px solid var(--c-danger);
  padding: 8px 12px; border-radius: 4px;
  font-size: 13px; margin-bottom: 14px;
}

.login-demo { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--line); }
.demo-title { font-size: 12px; color: var(--t3); margin-bottom: 10px; }
.demo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.demo-chip {
  text-align: left; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--panel); cursor: pointer;
  transition: border-color .15s, background .15s;
}
.demo-chip:hover { border-color: var(--hy-blue); background: var(--hover); }
.demo-chip b { display: block; font-size: 12px; font-weight: 600; color: var(--t1); }
.demo-chip span { display: block; font-size: 11px; color: var(--t3); font-family: ui-monospace, Consolas, monospace; }

/* 登录页底部品牌宣传语 */
.login-slogan { margin-top: 16px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.login-slogan-text { font-size: 14px; font-weight: 500; color: #0A4CA6; letter-spacing: .5px; }
.login-slogan-line { width: 30px; height: 2px; background: #FFB300; opacity: .6; }

/* 登录页底部「申请注册」入口 */
.login-extra { margin-top: 18px; text-align: center; }
.login-link { font-size: 13px; color: var(--hy-blue); cursor: pointer; }
.login-link:hover { text-decoration: underline; }

/* 注册卡片（复用 .login-card 外观） */
.register-card { max-width: 460px; }
.reg-head { margin-bottom: 18px; }
.reg-title { font-size: 19px; }
.reg-sub { font-size: 12.5px; color: var(--t3); margin-top: 6px; line-height: 1.6; }
.radio-row { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.radio { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--t2); cursor: pointer; }
.radio input { width: 15px; height: 15px; accent-color: var(--hy-blue); flex: none; }
.reg-actions { margin-top: 18px; }
.reg-back { display: block; text-align: center; margin-top: 12px; font-size: 13px; color: var(--t2); }
.reg-back:hover { color: var(--hy-blue); }

/* 通知铃铛 + 面板 */
.notif-wrap { position: relative; }
.notif-btn { position: relative; color: #fff; }
.notif-badge {
  position: absolute; top: 1px; right: 1px;
  min-width: 16px; height: 16px; padding: 0 4px;
  background: var(--c-danger); color: #fff; border-radius: 9px;
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
  border: 1.5px solid var(--hy-ink);
}
.notif-panel {
  position: absolute; top: 42px; right: 0; width: 326px; max-width: 90vw;
  max-height: 70vh; background: var(--panel);
  border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 8px 28px rgba(20,37,63,.16);
  z-index: 80; display: flex; flex-direction: column; overflow: hidden;
}
.notif-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
  font-weight: 600; font-size: 13px; flex: none;
}
.link-btn { background: none; border: none; color: var(--hy-blue); cursor: pointer; font-size: 12px; }
.link-btn:hover { text-decoration: underline; }
.notif-list { overflow-y: auto; flex: 1; }
.notif-item { padding: 10px 14px; border-bottom: 1px solid var(--line); cursor: pointer; }
.notif-item:hover { background: var(--hover); }
.notif-item.read { opacity: .58; }
.notif-top { display: flex; align-items: center; gap: 8px; }
.notif-title { font-size: 13px; font-weight: 600; color: var(--t1); }
.notif-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--hy-blue); flex: none; }
.notif-body { font-size: 12px; color: var(--t2); margin-top: 3px; line-height: 1.55; }
.notif-time { font-size: 11px; color: var(--t3); margin-top: 4px; }
.notif-empty { padding: 24px; text-align: center; color: var(--t3); font-size: 13px; }
/* 通知深链定位高亮（2026-10-13）：从通知跳进冲突单列表时定位到目标行 */
@keyframes rowFocusPulse {
  0%, 100% { background: transparent; }
  18%, 62% { background: rgba(10, 76, 166, .13); }
}
tr.row-focus { animation: rowFocusPulse 1.1s ease-in-out 2; }
tr[data-cid-focus="1"] > td:first-child { box-shadow: inset 3px 0 0 var(--hy-blue); }

/* 用户状态徽标（注册审核流程） */
.badge-pending { background: rgba(10,76,166,.10); color: var(--hy-blue); }
.badge-rejected { background: rgba(198,40,40,.10); color: var(--c-danger); }

/* ==========================================================================
   顶栏
   ========================================================================== */
.topbar {
  height: var(--topbar-h);
  background: var(--hy-ink);
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px;
  position: sticky; top: 0; z-index: 60;
}

.topbar .brand { display: flex; align-items: center; gap: 10px; flex: none; }
.topbar .brand img { height: 26px; width: auto; display: block; }
.brand-text {
  color: #fff; font-family: var(--font-serif); font-size: 16px; font-weight: 700;
  letter-spacing: .02em; white-space: nowrap;
}

.globalsearch { position: relative; flex: 1; max-width: 460px; margin: 0 8px; }
.globalsearch .gs-icon {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  color: rgba(255,255,255,.55); pointer-events: none;
}
.globalsearch input {
  width: 100%; height: 34px; padding: 0 12px 0 33px;
  border: 1px solid rgba(255,255,255,.18); border-radius: 6px;
  background: rgba(255,255,255,.10); color: #fff; outline: none;
  transition: background .15s, border-color .15s;
}
.globalsearch input::placeholder { color: rgba(255,255,255,.5); }
.globalsearch input:focus { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.4); }

.gs-panel {
  position: absolute; top: 40px; left: 0; right: 0;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
  max-height: 60vh; overflow-y: auto;
  box-shadow: 0 6px 20px rgba(20,37,63,.10);
}
.gs-group-t { padding: 8px 14px 4px; font-size: 11px; color: var(--t3); letter-spacing: .06em; }
.gs-item {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 8px 14px; cursor: pointer; border: none; background: none; width: 100%; text-align: left;
}
.gs-item:hover { background: var(--hover); }
.gs-item b { font-weight: 600; color: var(--t1); }
.gs-item span { font-size: 12px; color: var(--t3); white-space: nowrap; }
.gs-empty { padding: 14px; text-align: center; color: var(--t3); font-size: 13px; }

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

.role-chip {
  font-size: 12px; color: rgba(255,255,255,.9);
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.18);
  padding: 4px 10px; border-radius: 20px; white-space: nowrap;
}

.user-menu { position: relative; }
.user-btn {
  display: flex; align-items: center; gap: 8px;
  background: none; border: none; color: #fff; cursor: pointer;
  padding: 5px 8px; border-radius: 6px; font-size: 13px;
}
.user-btn:hover { background: rgba(255,255,255,.12); }
.avatar {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--hy-blue); color: #fff;
  display: grid; place-items: center; font-size: 12px; font-weight: 700; flex: none;
}
.user-drop {
  position: absolute; right: 0; top: 42px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 6px;
  min-width: 130px; overflow: hidden; z-index: 70;
  box-shadow: 0 6px 20px rgba(20,37,63,.10);
}
.user-drop button {
  display: block; width: 100%; text-align: left;
  padding: 9px 14px; border: none; background: none; cursor: pointer; color: var(--t1);
}
.user-drop button:hover { background: var(--hover); }

.icon-btn {
  background: none; border: none; cursor: pointer; padding: 6px;
  border-radius: 6px; color: inherit; display: grid; place-items: center;
}
.icon-btn:hover { background: rgba(255,255,255,.12); }
.nav-toggle { display: none; color: #fff; }

/* ==========================================================================
   布局
   ========================================================================== */
.layout { display: flex; min-height: calc(100vh - var(--topbar-h)); }

.sidebar {
  width: var(--side-w); flex: none;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 14px 10px 24px;
  position: sticky; top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
}

.nav-group { margin-bottom: 16px; }
.nav-group-t {
  padding: 0 10px 6px; font-size: 11px; color: var(--t3);
  letter-spacing: .1em; font-weight: 600;
}
.nav-item {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  padding: 8px 10px; margin-bottom: 2px;
  border: none; background: none; border-radius: 6px;
  cursor: pointer; color: var(--t2); font-size: 13.5px;
  border-left: 3px solid transparent;
  transition: background .12s, color .12s;
}
.nav-item:hover { background: var(--hover); color: var(--t1); }
.nav-item.active {
  background: var(--active); color: var(--hy-blue);
  font-weight: 600; border-left-color: var(--hy-blue);
}
.nav-item .ico { width: 16px; height: 16px; flex: none; opacity: .85; }
.nav-item .badge-n {
  margin-left: auto; font-size: 11px; font-weight: 700;
  background: var(--c-danger); color: #fff;
  border-radius: 10px; padding: 1px 6px; min-width: 18px; text-align: center;
}
.nav-item .badge-n.soft { background: var(--line2); color: var(--t2); }

.scrim {
  position: fixed; inset: 0; background: rgba(20,37,63,.35); z-index: 45;
}

.main { flex: 1; min-width: 0; padding: 20px 24px 40px; }

.crumb {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--t3); margin-bottom: 14px;
}
.crumb b { color: var(--t2); font-weight: 600; }

.view { animation: fadeIn .18s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }

/* ==========================================================================
   通用块
   ========================================================================== */
.page-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 16px;
}
.page-title { font-size: 21px; }
.page-sub { font-size: 12.5px; color: var(--t3); margin-top: 3px; }
.page-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  margin-bottom: 16px;
}
.card-t {
  font-family: var(--font-serif); font-size: 15px; font-weight: 700;
  margin-bottom: 14px; display: flex; align-items: center; gap: 8px;
}
.card-t .hint { font-family: var(--font-sans); font-size: 12px; font-weight: 400; color: var(--t3); }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }

/* KPI 卡（legacy 全局定义。#app 内以 glass.css 的 #app .kpi 为准，
   两处保持同一套装饰语言：卡片本体细边 + 内高光，色彩语义在标签左侧短色条）。
   P2-13（2026-10-13）：与原 glass.css 同步去掉 border-top 3px 色条。 */
.kpi {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px;
  flex: 1 1 165px; min-width: 165px;
}
.kpi .kpi-label { border-left: 3px solid var(--hy-blue); padding-left: 8px; }
.kpi.green  .kpi-label { border-left-color: var(--hy-green); }
.kpi.orange .kpi-label { border-left-color: var(--hy-orange); }
.kpi.red    .kpi-label { border-left-color: var(--c-danger); }
.kpi.ink    .kpi-label { border-left-color: var(--hy-ink); }
.kpi-label { font-size: 12px; color: var(--t2); margin-bottom: 6px; }
.kpi-value {
  font-family: var(--font-serif); font-size: 24px; font-weight: 700;
  line-height: 1.2; color: var(--t1);
  font-variant-numeric: tabular-nums;
}
.kpi-value small { font-size: 13px; font-weight: 400; color: var(--t2); margin-left: 3px; }
.kpi-foot { font-size: 12px; color: var(--t3); margin-top: 6px; }

/* 按钮 */
.btn {
  height: 34px; padding: 0 14px;
  border: 1px solid var(--line2); border-radius: 6px;
  background: var(--panel); color: var(--t1);
  cursor: pointer; font-size: 13px; white-space: nowrap;
  transition: background .13s, border-color .13s, color .13s;
  display: inline-flex; align-items: center; gap: 6px;
}
.btn:hover { border-color: var(--hy-blue); color: var(--hy-blue); }
.btn-primary { background: var(--hy-blue); border-color: var(--hy-blue); color: #fff; }
.btn-primary:hover { background: #083C85; border-color: #083C85; color: #fff; }
.btn-danger { background: var(--c-danger); border-color: var(--c-danger); color: #fff; }
.btn-danger:hover { background: #A52020; border-color: #A52020; color: #fff; }
.btn-sm { height: 28px; padding: 0 10px; font-size: 12px; }
.btn-block { width: 100%; justify-content: center; height: 42px; font-size: 15px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn:disabled:hover { border-color: var(--line2); color: var(--t1); }

/* 表单 */
.form-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.form-row .field { margin-bottom: 0; min-width: 150px; }
.form-row .field.grow { flex: 1; }
select, textarea {
  width: 100%; border: 1px solid var(--line2); border-radius: 6px;
  padding: 8px 10px; background: var(--panel); outline: none;
}
/* 下拉框垂直内边距必须为 0（2026-10-10 修复「选项文字被裁掉上半截」）
   原因：本条规则上方 `select, textarea { padding: 8px 10px }` 会给 select 加上 8px 上下内边距，
   与下面的固定 `height: 34px` 叠加后，在 border-box 下内容盒只剩 34 - 2(边框) - 16(内边距) = 16px；
   而文字行盒需要 14px(font-size) × 1.6(body line-height) = 22.4px。装不下 → Windows Chrome/Edge
   把选项文字垂直裁切（表现为「全部」只剩下半截）。
   本项目其他固定高度控件（.field input 42px、input[type=date] 34px、.globalsearch input 34px）
   一律使用 `padding: 0`，select 必须遵循同一约定。水平内边距保持 10px 不变，视觉零差异。 */
select { height: 34px; min-width: 140px; padding: 0 10px; white-space: nowrap; }
select option { white-space: nowrap; }
textarea { min-height: 76px; resize: vertical; }
select:focus, textarea:focus, input:focus { border-color: var(--hy-blue); }

/* 表格内 select 撑满列宽，避免列窄时被截断 */
.table-wrap tbody td select { width: 100%; min-width: 130px; }
/* 用户管理表格列宽策略：避免列被压缩到内容截断 */
.users-table { table-layout:  fixed; min-width: 1100px; }
.users-table th, .users-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 操作列里是交互控件而不是纯文本，不能用 ellipsis 截断按钮 */
.users-table td.col-actions { overflow: visible; white-space: nowrap; }
.users-table th.col-actions, .users-table td.col-actions { width: 260px; }
.users-table th.col-account, .users-table td.col-account { width: 110px; }
.users-table th.col-name,    .users-table td.col-name    { width: 110px; }
.users-table th.col-role,    .users-table td.col-role    { width: 150px; }
.users-table th.col-prov,    .users-table td.col-prov    { width: 140px; }
.users-table th.col-status,  .users-table td.col-status  { width: 80px;  }
.users-table th.col-audit,   .users-table td.col-audit   { width: 200px; }
.users-table th.col-login,   .users-table td.col-login   { width: 140px; }
/* 操作列按钮排版：横向紧凑、不换行 */
.users-table td.col-actions .btn-sm { padding: 4px 8px; font-size: 12px; margin-right: 4px; }
/* 原生月份/日期选择器：统一样式 + 恒蓝聚焦描边 */
input[type="month"], input[type="date"], input[type="time"], input[type="datetime-local"] {
  width: 100%; height: 34px; padding: 0 10px;
  border: 1px solid var(--line2); border-radius: 6px;
  background: var(--panel); outline: none; font-family: inherit; font-size: 13px; color: var(--t1);
  transition: border-color .15s;
}
input[type="month"]:focus, input[type="date"]:focus, input[type="time"]:focus, input[type="datetime-local"]:focus {
  border-color: var(--hy-blue); box-shadow: 0 0 0 2px rgba(10,76,166,.12);
}
.checkline { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--t2); cursor: pointer; }
.checkline input { width: 15px; height: 15px; accent-color: var(--hy-blue); }

/* 客户选择器 combobox（零依赖，浮层挂 body 避免被 modal overflow 裁剪） */
.cust-select { position: relative; display: flex; align-items: center; gap: 6px; width: 100%; }
.cust-select .cs-input { flex: 1; min-width: 0; }
.cust-select .cs-clear {
  flex: 0 0 auto; width: 26px; height: 26px; line-height: 1; font-size: 15px;
  border: 1px solid var(--line2); background: var(--panel); border-radius: 6px;
  color: var(--t2); cursor: pointer;
}
.cust-select .cs-clear:hover { color: #fff; background: var(--hy-orange, #F26522); border-color: var(--hy-orange, #F26522); }
.cs-panel-float {
  position: fixed; z-index: 3000; background: #fff; padding: 4px;
  border: 1px solid var(--line2); border-radius: 8px;
  box-shadow: 0 10px 30px rgba(20,37,63,.18); max-height: 300px; overflow-y: auto;
}
.cs-opt { padding: 8px 10px; border-radius: 6px; cursor: pointer; font-size: 13px; white-space: nowrap; }
.cs-opt + .cs-opt { margin-top: 2px; }
.cs-opt:hover, .cs-opt.active { background: var(--hy-blue-50, #EEF2F6); }
.cs-opt .cs-id { font-family: Consolas, 'SFMono-Regular', monospace; color: var(--t2); margin-right: 4px; }
.cs-opt .cs-name { font-weight: 600; }
.cs-opt .cs-prov { color: var(--t2); margin-left: 4px; }
.cs-empty { padding: 10px; color: var(--t2); font-size: 13px; }

/* 徽标 */
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 4px;
  font-size: 11.5px; font-weight: 600; line-height: 1.6; white-space: nowrap;
}
.badge-blue   { background: rgba(10,76,166,.10);  color: var(--hy-blue); }
.badge-green  { background: rgba(0,166,81,.12);   color: var(--lv-green-fg); }
.badge-yellow { background: rgba(255,209,0,.20);  color: var(--lv-yellow-fg); }
.badge-red    { background: rgba(198,40,40,.10);  color: var(--c-danger); }
.badge-gray   { background: rgba(201,206,214,.35); color: var(--t2); }
.badge-orange { background: rgba(242,101,34,.12); color: #C24E12; }

/* 状态点 */
.dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; }
.dot.green  { background: var(--hy-green); }
.dot.yellow { background: var(--hy-yellow); }
.dot.red    { background: var(--c-danger); }
.dot.gray   { background: var(--line2); }

/* ==========================================================================
   表格（三线表：顶线 + 表头线 + 底线，无竖线无行线）
   ========================================================================== */
.table-wrap { width: 100%; overflow-x: auto; }
table {
  width: 100%; border-collapse: collapse;
  border-top: 1.5px solid var(--hy-ink);
  border-bottom: 1.5px solid var(--hy-ink);
  font-size: 13px;
}
thead th {
  text-align: left; font-weight: 600; color: var(--t2);
  padding: 10px 12px; border-bottom: 1px solid var(--line2);
  white-space: nowrap; font-size: 12px; letter-spacing: .02em;
  background: var(--panel);
}
tbody td {
  padding: 9px 12px; border: none; color: var(--t1);
  vertical-align: middle;
}
tbody tr { border: none; }
tbody tr:hover { background: rgba(10,76,166,.035); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.mono { font-family: ui-monospace, Consolas, monospace; font-size: 12px; color: var(--t2); }
tfoot td {
  padding: 10px 12px; border-top: 1px solid var(--line2);
  font-weight: 700; font-variant-numeric: tabular-nums;
}

/* 预警行 */
tr.lv-green  { background: var(--lv-green-bg); }
tr.lv-green  td:first-child { box-shadow: inset 3px 0 0 var(--lv-green-bar); }
tr.lv-yellow { background: var(--lv-yellow-bg); }
tr.lv-yellow td:first-child { box-shadow: inset 3px 0 0 var(--lv-yellow-bar); }
tr.lv-red    { background: var(--lv-red-bg); }
tr.lv-red    td:first-child { box-shadow: inset 3px 0 0 var(--lv-red-bar); }
tr.lv-red td { font-weight: 600; }
.lv-text-red    { color: var(--lv-red-fg); font-weight: 700; }
.lv-text-yellow { color: var(--lv-yellow-fg); font-weight: 600; }
.lv-text-green  { color: var(--lv-green-fg); }

.empty { padding: 40px 20px; text-align: center; color: var(--t3); font-size: 13px; }

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

/* ==========================================================================
   其它组件
   ========================================================================== */
.note {
  background: rgba(255,209,0,.10);
  border-left: 3px solid var(--hy-yellow);
  padding: 10px 14px; border-radius: 4px;
  font-size: 12.5px; color: #6B5300; margin-bottom: 14px;
}
.note.info { background: rgba(42,110,187,.07); border-left-color: var(--c-info); color: #1D5A96; }

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin-bottom: 16px; flex-wrap: wrap; }
.tab {
  padding: 8px 14px; border: none; background: none; cursor: pointer;
  color: var(--t2); font-size: 13.5px; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tab:hover { color: var(--t1); }
.tab.active { color: var(--hy-blue); font-weight: 600; border-bottom-color: var(--hy-blue); }

.todo-item {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--line);
  border-radius: 6px; margin-bottom: 8px; cursor: pointer;
  background: var(--panel); transition: border-color .13s, background .13s;
}
.todo-item:hover { border-color: var(--hy-blue); background: var(--hover); }
.todo-n {
  font-family: var(--font-serif); font-size: 20px; font-weight: 700;
  color: var(--hy-blue); min-width: 42px; text-align: center;
}
.todo-n.zero { color: var(--t3); }
.todo-label { flex: 1; }
.todo-label b { display: block; font-size: 13.5px; font-weight: 600; }
.todo-label span { font-size: 12px; color: var(--t3); }

.tag-list { display: flex; flex-wrap: wrap; gap: 5px; }
.tag-pick {
  padding: 3px 9px; border: 1px solid var(--line2); border-radius: 14px;
  font-size: 12px; cursor: pointer; background: var(--panel); color: var(--t2);
}
.tag-pick.on { background: var(--hy-blue); border-color: var(--hy-blue); color: #fff; }

.progress { height: 6px; background: var(--line); border-radius: 3px; overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--hy-blue); }
.progress.green > i { background: var(--hy-green); }
.progress.orange > i { background: var(--hy-orange); }
.progress.red > i { background: var(--c-danger); }

.kv { display: grid; grid-template-columns: 96px 1fr; gap: 6px 12px; font-size: 13px; }
.kv dt { color: var(--t3); }
.kv dd { margin: 0; color: var(--t1); }

/* 弹层 */
.modal { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: 20px; }
.modal-scrim { position: absolute; inset: 0; background: rgba(20,37,63,.45); }
.modal-box {
  position: relative; background: var(--panel); border-radius: var(--radius);
  width: 100%; max-width: 720px; max-height: 86vh;
  display: flex; flex-direction: column; resize: both; overflow: hidden;
  min-width: 400px; min-height: 240px;
  box-shadow: 0 12px 40px rgba(20,37,63,.22);
}
.modal-box.wide { max-width: min(1180px, 96vw); }
.modal-box.xwide { max-width: min(1320px, 97vw); }
.modal-box.maximized {
  max-width: 100vw; width: 100vw; max-height: 100vh; height: 100vh;
  border-radius: 0; resize: none;
}
.modal-head {
  padding: 14px 18px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; flex: none;
}
.modal-head h3 { font-size: 16px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.modal-head .head-tools { display: flex; gap: 2px; flex: none; }
.modal-head .icon-btn { color: var(--t2); }
.modal-head .icon-btn:hover { background: var(--hover); }
.modal-body { padding: 18px; overflow: auto; flex: 1; min-height: 0; }
.modal-foot {
  padding: 12px 18px; border-top: 1px solid var(--line);
  display: flex; justify-content: flex-end; gap: 8px; flex: none;
}
.modal-foot:empty { display: none; }
/* P2-17（2026-10-13）：删除「最大化时默认光标 / 非最大化时整盒 se-resize」
   两条规则——原生 resize:both 的手柄自带光标且只在右下角出现，
   整盒 se-resize 是错误的功能预告（拖中间并不会缩放）。 */

/* 提示条 */
.toast-wrap { position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 200; display: grid; gap: 8px; }
.toast {
  padding: 10px 18px; border-radius: 6px; font-size: 13px; color: #fff;
  background: var(--hy-ink); box-shadow: 0 4px 16px rgba(20,37,63,.2);
  animation: toastIn .2s ease;
}
.toast.ok  { background: var(--hy-green); }
.toast.err { background: var(--c-danger); }
.toast.warn{ background: var(--hy-orange); }
@keyframes toastIn { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

.dup-hit {
  border: 1px solid var(--line); border-left: 3px solid var(--hy-orange);
  border-radius: 4px; padding: 10px 12px; margin-bottom: 8px; background: rgba(242,101,34,.04);
}
.dup-hit b { font-size: 13px; }
.dup-hit p { font-size: 12px; color: var(--t2); margin-top: 2px; }

/* 报告封面 */
.report-cover {
  text-align: center; padding: 32px 24px 26px;
  border-top: 3px solid var(--hy-blue);
}
.rc-brand img { height: 44px; width: auto; display: block; margin: 0 auto 14px; }
.report-cover h3 {
  font-size: 22px; letter-spacing: .04em; color: var(--t1); margin-bottom: 8px;
}
.rc-slogan { font-size: 13px; color: var(--hy-blue); letter-spacing: .06em; margin-bottom: 4px; }
.rc-period { font-size: 13px; color: var(--t3); }

.legend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--t2); align-items: center; }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }

/* ==========================================================================
   响应式（手机可操作）
   ========================================================================== */
@media (max-width: 1080px) {
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .grid-3 { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 860px) {
  .nav-toggle { display: grid; }
  .topbar .brand-text { display: none; }
  .topbar { gap: 8px; padding: 0 10px; }
  .globalsearch { max-width: none; margin: 0; }
  .role-chip { display: none; }

  .sidebar {
    position: fixed; left: 0; top: var(--topbar-h); z-index: 50;
    transform: translateX(-100%); transition: transform .2s ease;
    box-shadow: 4px 0 20px rgba(20,37,63,.12);
  }
  .sidebar.open { transform: none; }

  .main { padding: 14px 12px 40px; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .page-head { flex-direction: column; align-items: stretch; }
  .page-actions .btn { flex: 1; justify-content: center; }

  /* 表格转卡片 */
  table, thead, tbody, tr, td { display: block; width: 100%; }
  thead { display: none; }
  table { border-top: none; border-bottom: none; }
  tbody tr {
    border: 1px solid var(--line); border-radius: 6px;
    margin-bottom: 10px; padding: 10px 12px; background: var(--panel);
  }
  tbody td {
    display: flex; justify-content: space-between; gap: 12px;
    padding: 5px 0; border: none; text-align: right;
  }
  tbody td::before {
    content: attr(data-label);
    color: var(--t3); font-size: 12px; text-align: left; flex: none;
  }
  tbody td.num { text-align: right; }
  tr.lv-red td:first-child { box-shadow: none; }
  tr.lv-red { border-left: 3px solid var(--lv-red-bar); }
  tr.lv-yellow { border-left: 3px solid var(--lv-yellow-bar); }
  tr.lv-green { border-left: 3px solid var(--lv-green-bar); }
  tfoot td { border-top: 1px solid var(--line2); }
  .modal-box { max-width: 100%; }
  .form-row .field { min-width: 100%; }
}

@media print {
  .topbar, .sidebar, .page-actions, .pager, .toast-wrap, .tabs { display: none !important; }
  .main { padding: 0; }
  .card { break-inside: avoid; border-color: #999; box-shadow: none; }
  table { break-inside: auto; }
  tr { break-inside: avoid; }
  thead { display: table-header-group; }
  .report-cover { border-top: 2px solid #14253F; }
  svg { max-width: 100%; }
  body { background: #fff; }
}

/* ===== 客户详情 / 编辑 ===== */
.cust-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.cust-top-main { min-width: 0; }
.cust-name { font-size: 22px; font-weight: 700; color: var(--t1); line-height: 1.2; }
.cust-sub { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
.cust-sub .mono { font-size: 13px; }
.cust-top-side { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cust-unpaid { text-align: right; padding: 4px 14px; background: var(--panel-2, #F5F7FA); border-radius: 8px; }
.cust-unpaid .cu-label { font-size: 12px; color: var(--t3); }
.cust-unpaid .cu-value { font-size: 18px; font-weight: 700; color: var(--lv-red-fg, #C62828); }

.warn-banner { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: 13px; font-weight: 500; }
.warn-banner.warn { background: rgba(255,209,0,.18); color: #8a6d00; border: 1px solid rgba(255,209,0,.5); }
.warn-banner.danger { background: rgba(198,40,40,.10); color: var(--c-danger); border: 1px solid rgba(198,40,40,.3); }

.cust-sec { margin-bottom: 14px; }
.sec-head { font-size: 15px; font-weight: 600; color: var(--t1); padding: 12px 16px; border-bottom: 1px solid var(--line); }
.sec-body { padding: 14px 16px; }

.info-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px 24px; }
.info-row { display: flex; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--line); }
.info-label { flex: none; width: 110px; color: var(--t3); font-size: 13px; }
.info-val { color: var(--t1); font-size: 13px; word-break: break-all; }
.muted { color: var(--t3); }

.two-col-tables { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
.sub-t { font-size: 13px; font-weight: 600; color: var(--t2); margin-bottom: 6px; }

.audit-list { display: flex; flex-direction: column; gap: 8px; }
.audit-item { padding: 8px 12px; background: var(--panel-2, #F5F7FA); border-radius: 6px; }
.audit-meta { font-size: 13px; color: var(--t1); }
.audit-detail { font-size: 12px; color: var(--t2); margin-top: 3px; word-break: break-all; }

/* 编辑页 Tabs */
.edit-tabs { display: flex; gap: 4px; overflow-x: auto; border-bottom: 1px solid var(--line); margin-bottom: 14px; -webkit-overflow-scrolling: touch; }
.edit-tab { flex: none; padding: 10px 16px; border: none; background: none; font-size: 14px; color: var(--t2); cursor: pointer; border-bottom: 2px solid transparent; white-space: nowrap; }
.edit-tab.active { color: var(--hy-blue); border-bottom-color: var(--hy-blue); font-weight: 600; }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px 16px; }
.form-grid .field { margin-bottom: 0; }
.mgrid { grid-template-columns: repeat(3, minmax(0,1fr)); gap: 10px 12px; }
.chk-group { display: flex; flex-wrap: wrap; gap: 12px; padding-top: 4px; }
.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--t1); }
.att-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.att-item { display: flex; justify-content: space-between; gap: 10px; font-size: 13px; padding: 6px 10px; background: var(--panel-2, #F5F7FA); border-radius: 6px; }
.att-item a { color: var(--hy-blue); }
.form-err { color: var(--c-danger); background: rgba(198,40,40,.08); border: 1px solid rgba(198,40,40,.25); padding: 10px 12px; border-radius: 6px; margin-top: 12px; font-size: 13px; }

/* 列表行悬停高亮 */
.table-wrap tbody tr { transition: background .15s ease; }
.table-wrap tbody tr:hover { background: rgba(10,76,166,.06); }

/* ==========================================================================
   P1 保密：全站水印（姓名, 时间）—— 防截图/拍照外泄，可溯源到人
   固定定位铺满视口，pointer-events:none 不影响操作，低透明度不干扰阅读
   ========================================================================== */
#hy-watermark {
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  overflow: hidden;
  user-select: none;
}
#hy-watermark .wm-tile {
  position: absolute;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(20, 37, 63, 0.07);
  white-space: nowrap;
  transform: rotate(-22deg);
  font-weight: 400;
}
/* 打印时同样保留水印 */
@media print {
  #hy-watermark { position: fixed; }
  #hy-watermark .wm-tile { color: rgba(20, 37, 63, 0.12); }
}

@media (max-width: 768px) {
  .info-grid { grid-template-columns: 1fr; }
  .two-col-tables { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .mgrid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .cust-unpaid { text-align: left; }
}
/* 驾驶舱双栏：窄屏折叠为单列（P1-C 移动适配联动） */
@media (max-width: 1100px) {
  .ov-two-col { grid-template-columns: 1fr !important; }
}
@media (max-width: 640px) {
  .ov-two-col { display: block !important; }
  .ov-two-col .card { margin-bottom: 12px; }
}

/* 注册页：固定身份展示（2026-10-21 身份固定为「结算专员」，不再提供单选） */
.reg-fixed-role {
  display: flex; align-items: center; gap: 10px; margin-top: 6px;
  padding: 10px 12px; border: 1px solid var(--line2, #BFBFBF); border-radius: 8px;
  background: rgba(20, 37, 63, .03);
}
.reg-role-tip { font-size: 12px; color: var(--t3, #66707E); }
/* ==========================================================================
   售电业务管理系统 · Design Tokens
   --------------------------------------------------------------------------
   全站唯一的色彩 / 间距 / 圆角 / 字体 / 动效来源。
   加载顺序：style.css → **tokens.css** → login.css → glass.css
   （tokens 必须早于 login/glass，两者都引用这里的变量）

   设计原则
     色彩  ：主色 1 个 + 中性灰 9 级 + 语义色 3 个。除此之外全站不再出现新色值。
     间距  ：4px 基数（4/8/12/16/24/32/48）。
     圆角  ：统一 12px（胶囊另用 pill）。不出现第三种圆角。
     字体  ：中文系统栈，数字强制等宽（tabular-nums）。
     动效  ：150–250ms，缓动统一 cubic-bezier(.2,0,0,1)。宁可少不可多。

   对比度：所有色值均按「白底 / 深底 #0B1220」复核，
           正文 ≥4.5:1、UI 边界 ≥3:1。
   ========================================================================== */

:root {
  /* ==================================================================
     1. 色彩
     ================================================================== */

  /* ---- 主色（1 个）----
     电光蓝 #00A0E9 白字对比度仅 2.91:1，不达标。
     取同色相加深版 #0071A8：白字 5.35:1 / 白底文字 5.35:1 / 深底 5.4:1，三处均达标。 */
  --brand:        #0071A8;
  --brand-soft:   rgba(0, 113, 168, .08);   /* 选中底 */
  --brand-lit:    #4DC3F7;   /* 深底上的高光主色（on #0B1220 = 8.03:1） */

  /* ---- 中性灰 9 级 ----
     编号即层级：50 最浅 → 900 最深。
     括号内为白底 #FFFFFF 上的对比度。
     ⚠️ 2026-10-13 插入 --n650 并同日落深 --n700：原 --t3 指向 --n600（3.13:1），
        实测在玻璃底上仅约 3.0:1，承载面包屑/信息标签/通知时间/placeholder
        等近 10 处信息性文字，不满足 WCAG 1.4.3 的 4.5:1。
        若只加 --n650 不动 --n700，会出现“弱化文字比次要文字更深”的倒挂
        （#66707E 5.02:1 > #6E7580 4.65:1），且 n700 在玻璃最暗合成带
        （约 #F9FAFD）上仅 4.45:1。故两档同步定为：
        --n650 #66707E（白底 5.02 / 玻璃最暗带 4.81）、
        --n700 #636C79（白底 5.31 / 玻璃最暗带 5.09），ramp 恢复单调。
        --n600 退回归位，只做控件描边（非文本 UI，≥3:1 达标）。 */
  --n50:  #F7F8FA;   /* 1.06  页面次级底 */
  --n100: #EFF1F4;   /* 1.13  表头底 / 悬停底 */
  --n200: #E3E6EA;   /* 1.25  分隔线 */
  --n300: #D4D8DE;   /* 1.43  表格内部分隔 */
  --n600: #8B929E;   /* 3.13  UI 边界（控件描边，≥3:1） */
  --n650: #66707E;   /* 5.02  弱化信息文字（≥4.5:1） */
  --n700: #636C79;   /* 5.31  次要文字（≥4.5:1，留玻璃底余量） */
  --n900: #252830;   /* 14.74 标题 / 强调 */

  /* ---- 语义色（各 1 个）---- */
  --success: #00794A;   /* 5.48:1 */
  --warning: #8A6100;   /* 5.54:1（辉黄 #FFD100 仅 1.46:1，不可用） */
  --danger:  #C62828;   /* 5.62:1 */

  /* 语义淡底（用于徽标/行底色，文字仍用上面三个实色） */
  --success-bg: rgba(0, 121, 74, .09);
  --warning-bg: rgba(138, 97, 0, .10);
  --danger-bg:  rgba(198, 40, 40, .09);

  /* ---- 深色面（登录页左侧品牌区）---- */
  --ink-900: #0B1220;   /* 深蓝黑底 */

  /* ---- 语义别名（组件只许用别名，不许直接写色值）---- */
  --text-1: var(--n900);
  --text-2: var(--n700);
  --text-3: var(--n650);   /* 弱化信息文字：5.02:1（玻璃底约 4.85:1），2026-10-13 由 n600 加深 */
  --divider:    var(--n300);

  /* ==================================================================
     2. 间距（4px 基数）
     ================================================================== */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-6: 24px;
  --sp-8: 32px;
  --sp-12: 48px;

  /* ==================================================================
     3. 圆角（统一 12px）
     ================================================================== */
  --r:     12px;
  --r-pill: 999px;

  /* ==================================================================
     4. 字体
     ================================================================== */
  --font-sans:
    -apple-system, BlinkMacSystemFont, 'SF Pro SC', 'SF Pro Text',
    'PingFang SC', 'Helvetica Neue', 'Hiragino Sans GB',
    'Microsoft YaHei', 'Source Han Sans SC', 'Noto Sans SC', sans-serif;
  /* Apple 风格全站**不衬线**：标题也不再用宋体。
     原 --font-serif 指向思源宋体，会让 h1/标题/大数字变成宋体观感，与 Apple 语言冲突。
     保留该变量名（style.css 有引用），但映射到无衬线栈。 */
  --font-serif:
    -apple-system, BlinkMacSystemFont, 'SF Pro SC', 'SF Pro Display',
    'PingFang SC', 'Helvetica Neue', 'Microsoft YaHei', sans-serif;
  --font-mono:
    ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas,
    'JetBrains Mono', 'Liberation Mono', monospace;

  /* 数字等宽：电价 / 电量 / 金额必须列对齐 */
  --num: tabular-nums;

  /* ==================================================================
     5. 动效
     ================================================================== */
  --ease: cubic-bezier(.2, 0, 0, 1);        /* Apple 常用减速曲线 */
  --dur-1: 150ms;   /* 颜色 / 边框 */
  --dur-2: 200ms;   /* 悬停 / 阴影 */
  --dur-3: 250ms;   /* 浮层入场 */


  /* ==================================================================
     7. 尺寸
     ================================================================== */
  --topbar-h: 56px;   /* Apple 风格顶栏更矮 */
  --side-w:   224px;
  --row-h:    52px;   /* 表格行高 48–56px */

  /* ==================================================================
     8. 兼容旧变量名
     glass.css / style.css 仍在引用这些，映射到新 token，
     避免两个色板并存。
     ================================================================== */
  --hy-blue:   var(--brand);
  --hy-green:  #00A651;
  --hy-yellow: #FFD100;
  --hy-orange: #F26522;
  --hy-ink:    var(--ink-900);

  --t1: var(--n900);
  --t2: var(--n700);
  --t3: var(--n600);
  --line:  var(--n200);
  --line2: var(--n600);
  --bg:    var(--n50);
  --panel: #FFFFFF;
  --hover: var(--brand-soft);
  --active: rgba(0, 113, 168, .12);

  --c-danger:  var(--danger);
  --c-info:    var(--brand);

  --lv-green-bg:  var(--success-bg);
  --lv-green-fg:  var(--success);
  --lv-green-bar: #00A651;
  --lv-yellow-bg: var(--warning-bg);
  --lv-yellow-fg: var(--warning);
  --lv-yellow-bar:#FFD100;
  --lv-red-bg:    var(--danger-bg);
  --lv-red-fg:    var(--danger);
  --lv-red-bar:   var(--danger);

  --radius: var(--r);
}

/* ==========================================================================
   全局排版基线
   ========================================================================== */
html {
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* 标题族统一：Apple 风格全部无衬线，且字重不超过 600（超粗显廉价） */
h1, h2, h3, h4,
.page-title, .card-t, .lgz-title, .lgp-title,
.kpi-value, .cust-name, .report-cover h3, .todo-n {
  font-family: var(--font-sans) !important;
  font-weight: 600;
  letter-spacing: -.01em;
}

/* 数字 / 金额 / 编号一律等宽，保证表格列对齐 */
td.num, th.num,
.mono,
.kpi-value,
.tnum,
[class*="money"],
[class*="amount"] {
  font-variant-numeric: var(--num);
  font-feature-settings: "tnum" 1;
}

/* 尊重系统「减少动态效果」：全局关闭动画与过渡 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
/* ==========================================================================
   登录页 · Apple 风格 + 参考稿动效体系
   --------------------------------------------------------------------------
   动效参数对齐桌面参考稿 login-redesign.html：
     入场      fadeUp .8s cubic-bezier(.2,0,0,1)，延迟 0 / .12 / .24 / .36s
     按钮流光  shine 3.2s 循环（left -80px → 130%）
     状态呼吸  pulse 2.2s 无限
     底部滴落  drip 1.8s 无限
     loading   spin .7s linear infinite
     粒子      鼠标 180px 内吸附连线（JS 侧实现）

   与参考稿的差异（有意为之）：
     - 色板沿用本项目 tokens.css（主色 #0071A8，恒源 VI 内），未照搬参考稿的 #0A4CA6
     - 输入框保留 label 在上方（无障碍更佳，参考稿同样如此）
     - 未引入「手机验证码」Tab（后端无此能力）

   CSP：外部样式表，无 <style> 块、无内联事件。
   ========================================================================== */

/* ==========================================================================
   1. 舞台：55 / 45 分栏
   ========================================================================== */
#login {
  position: relative;
  min-height: 100vh;
  display: grid;
  grid-template-columns: 55% 45%;
  padding: 0;
  margin: 0;
  background: #FFFFFF;
  background-image: none;          /* 清掉 style.css .login-wrap 的斜向渐变底纹 */
  overflow: hidden;
}
/* style.css 的 .login-wrap 带 flex 列居中 + 32/20 内边距，
   在 grid 下会让两个分栏不拉伸、并整体内缩 —— 必须显式归零 */
#login.login-wrap {
  display: grid;
  flex-direction: row;
  align-items: stretch;
  justify-content: stretch;
  padding: 0;
  min-height: 100vh;
}

/* ==========================================================================
   2. 左：深色品牌视觉区（55%）
   ========================================================================== */
.lgp {
  position: relative;
  min-width: 0;
  /* 配色（对比度实测，白 logo 14.2:1 / 高光 8.2:1 / 正文 11.6:1）：
     原 #071E42 一路黑到底，显沉闷呆板；
     改为「电光深蓝 #082B52 → 品牌深蓝 #0A3A6B → 墨蓝 #071E42」三段，
     左上带一团电光蓝辉光，整体通透、有层次。 */
  background:
    radial-gradient(110% 110% at 18% 12%, rgba(0, 160, 233, .22) 0%, transparent 55%),
    linear-gradient(155deg, #082B52 0%, #0A3A6B 45%, #071E42 100%);
  display: grid;
  /* 顶栏 / 主视觉 / 指标带 / 落款 */
  grid-template-rows: auto 1fr auto auto;
  align-content: center;
  gap: var(--sp-6);
  padding: 44px 56px;
  overflow: hidden;
  isolation: isolate;
}
.lgp-inner { align-self: center; max-width: 520px; }
.lgp-metrics { align-self: end; }
.lgp-foot { align-self: end; }

/* canvas 电流粒子 */
.lgp-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
}

/* 底部渐深（参考稿只在底部压一层，不做四周暗角） */
.lgp::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(120% 80% at 12% 12%, rgba(0, 200, 255, .14) 0%, transparent 58%),
    linear-gradient(180deg, transparent 52%, rgba(5, 24, 54, .55) 100%);
}

/* 内容层 */
.lgp-top,
.lgp-inner,
.lgp-foot { position: relative; z-index: 2; }

/* ---- 顶栏：logo + 时钟 ---- */
.lgp-top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* ⚠️ flex 子项默认 flex-shrink:1，图片会被压扁（曾导致 logo 横向挤压变形）。
   必须显式 flex:none（= flex:0 0 auto），且 max-width 兜底。 */
.lgp-top img {
  height: 28px;
  width: auto;
  flex: none;
  max-width: none;
  display: block;
}
.lgp-clock {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  letter-spacing: .04em;
  color: #93AECC;
  font-variant-numeric: var(--num);
}

/* ---- 入场：fadeUp，对齐参考稿 ---- */
.lgp-top     { animation: lgFadeUp .8s cubic-bezier(.2, 0, 0, 1) both; }
.lgp-tag     { animation: lgFadeUp .8s cubic-bezier(.2, 0, 0, 1) both; }
.lgp-title   { animation: lgFadeUp .8s .12s cubic-bezier(.2, 0, 0, 1) both; }
.lgp-slogan  { animation: lgFadeUp .8s .24s cubic-bezier(.2, 0, 0, 1) both; }
.lgp-metrics { animation: lgFadeUp .8s .36s cubic-bezier(.2, 0, 0, 1) both; }
.lgp-foot    { animation: lgFadeUp .8s .48s cubic-bezier(.2, 0, 0, 1) both; }

@keyframes lgFadeUp {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ---- 状态胶囊（绿点呼吸）---- */
.lgp-tag {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: #A9C3E8;
  border: 1px solid rgba(169, 195, 232, .28);
  background: rgba(255, 255, 255, .04);
  padding: 7px 14px;
  border-radius: var(--r-pill);
  letter-spacing: .06em;
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.lgp-tag i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--hy-green);
  box-shadow: 0 0 8px var(--hy-green);
  animation: lgPulse 2.2s infinite;
}
@keyframes lgPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}

/* ---- 标题：关键词渐变 ---- */
.lgp-title {
  margin: 26px 0 0;
  font-size: clamp(34px, 3.6vw, 44px);
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: .02em;
  color: #FFFFFF;
}
.lgp-title .em {
  background: linear-gradient(90deg, #A5E0FC, var(--brand-lit));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ---- 副文案 ---- */
.lgp-slogan {
  margin: 18px 0 0;
  font-size: 14.5px;
  font-weight: 400;
  line-height: 1.9;
  letter-spacing: .02em;
  color: #B8CCEA;
}

/* ---- 指标带 ---- */
.lgp-metrics {
  display: flex;
  flex-wrap: wrap;
  gap: 44px;
  padding-top: var(--sp-6);
}
/* P1-8（2026-10-13）：[hidden] 必须显式赢过上面的 display:flex。
   作者样式优先级高于 UA 默认的 [hidden]{display:none}——不写这一条，
   JS 里 band.hidden = true 会被无视，未注入真实指标时满屏 “—” 的占位带照样占位。 */
.lgp-metrics[hidden] { display: none; }
/* 指标标签：在 #082B52 底上 #7E9CC8 只有 4.3:1，偏低 → 提到 #A9C3E8（8.5:1） */
.lgp-metric .k {
  display: block;
  font-size: 12px;
  letter-spacing: .06em;
  color: #A9C3E8;
}
.lgp-metric .v {
  display: block;
  margin-top: 6px;
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  color: #FFFFFF;
  font-variant-numeric: var(--num);
  min-height: 32px;
}
.lgp-metric .v b { font-weight: 700; }
/* 强调数字用能量金，深底上 9.7:1，是全页最亮的一处焦点 */
.lgp-metric .v b { color: var(--hy-yellow); }
.lgp-metric .u {
  font-size: 13px;
  font-weight: 400;
  color: #A9C3E8;
  margin-left: 3px;
}
/* 占位符：未接真实数据时是低调细横线 */
.lgp-metric .v b[data-empty] {
  display: inline-block;
  width: 36px;
  height: 3px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, .30);
  font-size: 0;
  vertical-align: middle;
}
.lgp-metric .v:has(b[data-empty]) .u { display: none; }

/* ---- 底部落款 + 滴落提示 ---- */
.lgp-foot {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--sp-4);
  font-size: 11.5px;
  letter-spacing: .05em;
  color: #93AECC;          /* 对比度实测（2026-10-13 复算修正）：三段渐变的任一一色标上均 ≥6.2:1；
                               落款实际停在最深的 #071E42 段、其上还压着 55% 暗色蒙版，实测约 7:1。
                               旧注释「原 #93AECC 仅 3.2:1，几乎看不见」是拿错底色算的——该色值无需更换。 */
}
.lgp-drip { display: flex; align-items: center; gap: 8px; }
.lgp-drip i {
  width: 1px;
  height: 26px;
  background: linear-gradient(180deg, var(--brand-lit), transparent);
  animation: lgDrip 1.8s infinite;
}
@keyframes lgDrip {
  0%        { transform: scaleY(0); transform-origin: top; }
  45%       { transform: scaleY(1); transform-origin: top; }
  55%       { transform: scaleY(1); transform-origin: bottom; }
  100%      { transform: scaleY(0); transform-origin: bottom; }
}

/* ==========================================================================
   3. 右：纯白表单区（45%）
   ========================================================================== */
.login-panelzone {
  position: relative;
  min-width: 0;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--sp-12) var(--sp-8);
}
/* 表单组整体真正垂直居中（参考稿是 .panel{justify-content:center} 同思路） */
.login-panelzone > * { flex-shrink: 0; }
.login-panelzone .login-card {
  width: 100%;
  max-width: 380px;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  animation: lgFadeUp .7s cubic-bezier(.2, 0, 0, 1) both;
}

.lgz-head { margin-bottom: var(--sp-6); }
.lgz-title {
  margin: 0;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-1);
  animation: lgFadeUp .7s .15s cubic-bezier(.2, 0, 0, 1) both;
}
.lgz-sub {
  margin: 10px 0 0;
  font-size: 13.5px;
  color: var(--text-2);
  animation: lgFadeUp .7s .25s cubic-bezier(.2, 0, 0, 1) both;
}

/* ---- 表单：整组错帧 ---- */
.login-panelzone form { animation: lgFadeUp .7s .45s cubic-bezier(.2, 0, 0, 1) both; }

.login-panelzone .field { display: block; margin-bottom: var(--sp-4); min-width: 0; }
.login-panelzone .field > span:first-child {
  display: block;
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: .04em;
  color: var(--text-2);
  margin-bottom: 8px;
}

/* ---- 输入框：前置图标 ---- */
.input-wrap { position: relative; display: block; }
.input-wrap > svg {
  position: absolute;
  left: 15px; top: 50%;
  transform: translateY(-50%);
  width: 17px; height: 17px;
  stroke: #A9B6C6;
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
  transition: stroke var(--dur-1) var(--ease);
}
.input-wrap input {
  width: 100%;
  height: 48px;
  padding: 0 44px;
  border: 1.5px solid var(--line2);
  border-radius: 11px;
  background: #FFFFFF;
  color: var(--text-1);
  font-size: 14px;
  outline: none;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.input-wrap input::placeholder {
  /* P0-4（2026-10-13）：原 #B3C2D9 白底约 1.8:1。
     改 --text-3（#66707E，白底 5.02:1，P0-2 加深后达标）。 */
  color: var(--text-3);
}
.input-wrap input:hover { border-color: #6E7A8A; }
/* 聚焦：边框转主色 + 4px 柔光圈，同时图标变主色 */
.input-wrap input:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 4px rgba(0, 113, 168, .12);
}
.input-wrap input:focus ~ svg,
.input-wrap:has(input:focus) > svg { stroke: var(--brand); }

/* ---- 密码明文切换 ---- */
.pwd-eye {
  position: absolute;
  right: 12px; top: 50%;
  transform: translateY(-50%);
  width: 26px; height: 26px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: grid; place-items: center;
  border-radius: var(--r-pill);
  transition: background var(--dur-1) var(--ease);
}
.pwd-eye:hover { background: rgba(0, 113, 168, .08); }
.pwd-eye svg {
  width: 18px; height: 18px;
  /* P0-4（2026-10-13）：密码显隐是真实控件（带 aria-label），
     图标按非文本 UI ≥3:1 从严：原 #A9B6C6 约 2.4:1 → --n600 3.13:1。 */
  stroke: var(--n600);
  fill: none;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke var(--dur-1) var(--ease);
}
.pwd-eye:hover svg { stroke: var(--brand); }
.pwd-eye[aria-pressed="true"] svg { stroke: var(--brand); }

/* ---- 密码明文切换 · 复用场景（注册卡 / 改密弹窗）--------------------------
   登录卡片已按新设计规格走（.input-wrap input 是 48px）。
   注册卡与改密弹窗里的输入框是 42px（style.css 的 .field input），
   若直接套用 .input-wrap 会让同一张表单里出现「密码框 48px、其它框 42px」两种高度。
   所以这里只做一件事：把右侧让给眼睛按钮，高度/圆角/描边各归各的主表单。 */
.register-card .input-wrap input,
.modal .input-wrap input {
  height: 42px;
  padding: 0 36px 0 12px;
  border-width: 1px;
  border-radius: 6px;
}
.register-card .pwd-eye,
.modal .pwd-eye { right: 6px; }

/* ---- 错误提示 ---- */
.login-panelzone .login-err {
  margin: 0 0 var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r);
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 13px;
  line-height: 1.5;
}

/* ---- 登录按钮：流光扫过 ---- */
.login-panelzone .btn-primary.btn-block {
  position: relative;
  height: 50px;
  margin-top: 8px;
  border: 0;
  border-radius: 12px;
  /* 按钮渐变只走品牌蓝的深浅两档（白字 ≥4.5:1）。
     不能延伸到 --brand-lit/#4DC3F7 —— 浅蓝底配白字只有 2.0:1，会读不清。 */
  background: linear-gradient(135deg, var(--brand) 0%, #0A63A8 100%);
  color: #FFFFFF;
  font-size: 15.5px;
  font-weight: 600;
  letter-spacing: 4px;
  text-indent: 4px;
  cursor: pointer;
  overflow: hidden;              /* 流光必须被裁在按钮内 */
  transition:
    transform var(--dur-2) var(--ease),
    box-shadow var(--dur-2) var(--ease);
  box-shadow: 0 10px 24px rgba(0, 113, 168, .30);
}
.login-panelzone .btn-primary.btn-block:hover {
  transform: translateY(-2px);
  box-shadow: 0 14px 30px rgba(0, 113, 168, .38);
}
.login-panelzone .btn-primary.btn-block:active {
  transform: translateY(0);
  box-shadow: 0 6px 14px rgba(0, 113, 168, .26);
}
.login-panelzone .btn-primary.btn-block:focus-visible {
  box-shadow: 0 0 0 4px rgba(0, 113, 168, .16);
}

/* 流光：一条 90px 宽的斜向高光带，从按钮左侧扫到右侧，3.2s 循环。
   --------------------------------------------------------------------------
   ⚠️ 必须让 .shine 自身**铺满按钮宽度**（width:100%），再用 background-size 画出窄光带。
      若把 .shine 本身设为 60px 宽，则 translateX(100%) 里的 100% 指「自身 60px」，
      流光只走 60px 就停在按钮中间 —— 这正是此前「动效卡住不通」的原因。
      现在 translateX(100%) = 按钮宽度，才能完整扫过。
      位移用 transform 而非 left：transform 不参与布局，不会撑大祖先 scrollWidth。 */
.login-panelzone .btn-primary.btn-block .shine {
  position: absolute;
  top: 0; bottom: 0;
  left: 0;
  width: 100%;
  background-image: linear-gradient(105deg,
    transparent 0%, rgba(255, 255, 255, .38) 50%, transparent 100%);
  background-size: 90px 100%;     /* 光带宽 90px，元素本身仍是整宽 */
  background-repeat: no-repeat;
  pointer-events: none;
  transform: translateX(-100px);
  /* P1-7（2026-10-13）：删 will-change: transform——infinite 动画期间引擎自动提层，
     常驻声明会让合成层在动画结束后仍不释放；登录页常驻内存，更不该留这种开销。 */
  animation: lgShine 3.4s cubic-bezier(.4, 0, .3, 1) infinite;
}
/* 节奏：0→52% 从左扫到右（ease-out 收尾），52%→100% 停在右侧等待下一轮。
   若写成 `55%,100%` 同终点，会在终点静止很久；这里把静止段压到末尾 ~48%。 */
@keyframes lgShine {
  0%        { transform: translateX(-100px); }
  46%       { transform: translateX(100%); }
  54%, 100% { transform: translateX(100%); }
}
/* loading 期间不再扫，避免动效叠加显得杂乱 */
.login-panelzone .btn-primary.btn-block.is-loading .shine { animation: none; opacity: 0; }

/* loading：文字淡出 + 按钮内转圈 */
.login-panelzone .btn-primary.btn-block.is-loading {
  pointer-events: none;
  letter-spacing: 2px;
}
.login-panelzone .btn-primary.btn-block.is-loading .btn-label { opacity: 0; }
.login-panelzone .btn-primary.btn-block .spinner {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 18px; height: 18px;
  border: 2.5px solid rgba(255, 255, 255, .35);
  border-top-color: #FFFFFF;
  border-radius: 50%;
  opacity: 0;
  animation: lgSpin .7s linear infinite;
}
.login-panelzone .btn-primary.btn-block.is-loading .spinner { opacity: 1; }
@keyframes lgSpin { to { transform: rotate(360deg); } }

/* ---- 底部入口 ---- */
.login-panelzone .login-extra {
  margin-top: var(--sp-6);
  text-align: center;
  animation: lgFadeUp .7s .55s cubic-bezier(.2, 0, 0, 1) both;
}
.login-panelzone .login-link {
  font-size: 13px;
  color: var(--text-2);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}
.login-panelzone .login-link:hover { color: var(--brand); }

/* ---- 落款 ---- */
.login-panelzone .login-slogan {
  margin-top: var(--sp-6);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  animation: lgFadeUp .7s .55s cubic-bezier(.2, 0, 0, 1) both;
}
.login-panelzone .login-slogan-text {
  font-size: 11.5px;
  /* P0-4（2026-10-13）：原 #A5B6CE 白底实测仅 2.06:1，品牌口号基本看不见。
     改 --text-2（#6E7580，白底 4.65:1），WCAG 1.4.3 达标。 */
  color: var(--text-2);
  letter-spacing: .05em;
}
.login-panelzone .login-slogan-line {
  width: 24px; height: 2px;
  background: var(--n300);
  border-radius: var(--r-pill);
}

/* ==========================================================================
   4. 响应式
   ========================================================================== */
@media (max-width: 1080px) {
  #login { grid-template-columns: 1fr; min-height: 100%; }
  .lgp {
    padding: var(--sp-8) var(--sp-6);
    min-height: 300px;
    grid-template-rows: auto auto auto auto;
    align-content: start;
  }
  .lgp-inner { align-self: auto; max-width: none; }
  .lgp-title { font-size: 32px; margin-top: 20px; }
  .lgp-metrics { gap: var(--sp-6); }
  .lgp-foot { display: none; }
  .login-panelzone { padding: var(--sp-8) var(--sp-6) var(--sp-12); }
}

@media (max-width: 560px) {
  .lgp { padding: var(--sp-6) var(--sp-4); min-height: 250px; }
  .lgp-title { font-size: 27px; }
  .lgp-slogan { font-size: 13px; }
  .lgp-metrics { gap: var(--sp-4); }
  .lgp-metric .v { font-size: 21px; }
  .login-panelzone { padding: var(--sp-6) var(--sp-4) var(--sp-8); }
  .lgz-title { font-size: 23px; }
  .login-panelzone .btn-primary.btn-block { letter-spacing: 2px; text-indent: 2px; }
}

/* ==========================================================================
   5. 降级
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .lgp-canvas { display: none; }
  .lgp-top, .lgp-tag, .lgp-title, .lgp-slogan, .lgp-metrics, .lgp-foot,
  .login-panelzone .login-card,
  .lgz-title, .lgz-sub,
  .login-panelzone form,
  .login-panelzone .login-extra,
  .login-panelzone .login-slogan { animation: none; }
  .lgp-tag i, .lgp-drip i,
  .login-panelzone .btn-primary.btn-block .shine { animation: none; }
  .login-panelzone .btn-primary.btn-block:hover { transform: none; }
}

.lgp { background-color: #071E42; }
/* ==========================================================================
   主应用表单内的密码框与同级输入框等高（2026-10-23 注册页对齐修复）
   --------------------------------------------------------------------------
   问题：`.input-wrap input` 是**登录卡规格**（48px / 内距 44px / 圆角 11px），
        而 `.field input` 是 42px。两者选择器同权重、bundle 中 login.css 在
        style.css 之后 → 登录卡规格**泄漏**到主应用表单（注册页）：
        密码框比同行的账号名称框高 6px → 两框底部错位。
   修法：仅在主应用作用域（#app）把 .input-wrap 内的 input 拉回与 .field input
        同一高度与内距；外观（玻璃凹面/描边/圆角）继续由 glass.css 统一提供。
        登录卡（.login-panelzone，不在 #app 内）保持 48px 原规格不变。
   ========================================================================== */
#app .field .input-wrap input {
  height: 42px;
  padding: 0 36px 0 12px;
}

/* 同上 · 同行两个输入框的垂直对齐（2026-10-23 注册页对齐修复 · 第二处根因）
   --------------------------------------------------------------------------
   普通 input 是 inline-block，其所在行盒会带**基线余量**；而 .input-wrap 是块级容器
   → 同一个 .form-row 里两种结构的 field 高度相差约 6px，flex 居中后
   「账号名称」框与「密码」框顶部错位（底边同样不齐）。
   修法：注册表单内的输入控件一律走块级流，去掉基线余量，两框结构一致即自然对齐。 */
#regForm .field > input,
#regForm .field > .input-wrap,
#regForm .field > .input-wrap > input {
  display: block;
  width: 100%;
}
/* .input-wrap 内的换行文本节点会生成一个空行盒，使该 field 比普通 field 高约 6px；
   .form-row 是 align-items: flex-end（底部对齐）→ 高度差 6px 表现为顶部错位。
   line-height: 0 消除空行盒，两个 field 结构完全一致 → 顶/底同时对齐。 */
#regForm .field > .input-wrap { line-height: 0; }
#regForm .field > .input-wrap > input { line-height: normal; }
/* 无「显示密码」按钮的包裹层：右侧不留按钮位 */
#regForm .field > .input-wrap:not(:has(.pwd-eye)) > input { padding-right: 12px; }
/* ==========================================================================
   售电业务管理系统 · 液态玻璃设计语言（Liquid Glass）
   --------------------------------------------------------------------------
   与登录封面（login.css）同一套材质规范：统一透明度、模糊强度、圆角、
   内高光与投影。

   加载顺序：必须在 style.css 与 login.css 之后，靠层叠覆盖旧样式，
             不改动 style.css（零侵入，易回滚）。

   作用域：仅 #app（登录后主界面）、.modal、.toast-wrap。
             **绝不覆盖 #login**（登录页由 login.css 独占）。

   CSP 合规：外部样式表（style-src-elem 'self'），无 <style> 块、无内联事件。

   --------------------------------------------------------------------------
   三层玻璃（层级越高 = 越通透、模糊越强、投影越深）
   --------------------------------------------------------------------------
     L0 背景层  app-bg      页面底：浅色渐变 + 电网细线，不加模糊
     L1 内容层  surface     卡片 / 侧边栏 / 表格容器 / 表单控件
     L2 导航层  chrome      顶栏（下方有内容滚动，故模糊最强）
     L3 浮层    overlay     弹窗 / 下拉 / 气泡 / Toast

   材质公式（统一，全站不再出现第二套）：
     背景 = rgba(255,255,255,α层)
     模糊 = blur(B层) saturate(S层)
     描边 = 外侧 1px rgba(20,37,63,.07) + 内侧 1px rgba(255,255,255,.72) 内高光
     投影 = 双层（贴地浅影 + 悬浮深影）
   ========================================================================== */

/* ==========================================================================
   1. 玻璃令牌
   ========================================================================== */
:root {
  /* ---- 层级透明度 ---- */
  --g-a-bg:   .72;   /* L1 内容层 */
  --g-a-chrome: .82;  /* L2 导航层 */
  --g-a-float: .88;   /* L3 浮层   */
  --g-a-deep:  .94;   /* L3 弹窗（最实，防止背后内容干扰阅读） */

  /* ---- 模糊强度 ---- */
  --g-blur-1: 10px;   /* 内容层 */
  --g-blur-2: 18px;   /* 表格容器 / 侧边栏 */
  --g-blur-3: 26px;   /* 顶栏 */
  --g-blur-4: 32px;   /* 弹窗 / 下拉 */
  --g-sat:    128%;   /* 统一饱和补偿，玻璃不发灰 */

  /* ---- 描边 ---- */
  --g-edge:      1px solid rgba(20, 37, 63, .07);  /* 装饰性，卡片/面板用（靠投影+底色差区分） */
  --g-edge-ctl:  1px solid #828C9B;                 /* 控件边界：承载 UI 语义，实测 3.29:1 */
  --g-edge-lit:  inset 0 1px 0 rgba(255, 255, 255, .78);
  --g-edge-lit-s:inset 0 1px 0 rgba(255, 255, 255, .55);
  --g-edge-lit-float: inset 0 1px 0 rgba(255, 255, 255, .8);

  /* ---- 投影（双层：贴地浅影 + 悬浮深影）---- */
  --g-sh-1: 0 1px 2px rgba(20,37,63,.04), 0 4px 14px -6px rgba(20,37,63,.12);
  --g-sh-2: 0 2px 4px rgba(20,37,63,.05), 0 12px 32px -10px rgba(20,37,63,.18);
  --g-sh-3: 0 4px 8px rgba(20,37,63,.06), 0 24px 56px -16px rgba(20,37,63,.26);

  /* ---- 圆角 ----
     Apple 规范要求「统一二选一」。本项目统一取 12px（tokens.css 的 --r）。
     原本的 6/8/12/16/20 五档全部收敛到同一值，只有胶囊形保留 pill。 ---- */
  --g-r-xs: var(--r);
  --g-r-sm: var(--r);
  --g-r-md: var(--r);
  --g-r-lg: var(--r);
  --g-r-full: var(--r-pill);

  /* ---- 交互态 ---- */
  --g-hover:   rgba(0, 113, 168, .07);
  --g-press:   rgba(0, 113, 168, .11);
  --g-sel-bg:  rgba(0, 113, 168, .12);
  --g-focus-ring: 0 0 0 3px rgba(0, 113, 168, .16);

  /* ---- 背景层 ---- */
  --g-base-b: #EDF1F7;

  /* ---- 文字 ----
     ⚠️ 这里必须**引用 tokens.css 的变量**，不能写死色值。
     之前写死了 #14253F / #4E5A6B / #66717F，把 tokens.css 的新色板整个盖掉了 ——
     结果页面文字仍是旧墨蓝，与新设计系统脱节。
     对比度（玻璃最暗合成带 #F9FAFD 上实测，2026-10-13 复测修正）：
       --t1 → --n900 #252830  14.1:1  PASS
       --t2 → --n700 #636C79   5.09:1 PASS（白底 5.31:1；2026-10-13 由 #6E7580
             落深——原值在最暗带上仅 4.45:1，且会低于新的 --t3 造成倒挂）
       --t3 → --n650 #66707E   4.81:1 PASS（弱化信息文字，≥4.5 阈值；
             2026-10-13 前指向 --n600 仅约 3.0:1，不达标，已加深修正） */
  --t1: var(--n900);
  --t2: var(--n700);
  --t3: var(--n650);
  /* P2-16（2026-10-13）：--t3-dim 已删除——全站 0 处引用（死 token），
     且 3.6:1 的「仅装饰可用」口径本身就是合规负担，不留隐患。 */

  /* ---- 中性 ---- */
  --line:  var(--n200);
  --line2: var(--n600);   /* 控件描边，3.13:1 */
  --bg:    var(--g-base-b);
  --panel: #FFFFFF;
  --hover: var(--g-hover);
  --active:var(--g-sel-bg);

  --radius: var(--g-r-md);
}

/* ==========================================================================
   2. L0 背景层
   ========================================================================== */
html, body {
  background: var(--g-base-b);
}

body {
  /* 页面底：斜向浅色渐变 + 3px 网点，与登录封面同源 */
  background-image:
    radial-gradient(rgba(20, 37, 63, .05) .5px, transparent .5px),
    linear-gradient(158deg, #FFFFFF 0%, #F4F7FB 46%, #E9EEF6 100%);
  background-size: 3px 3px, cover;
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

/* 主内容区：透明，让 L0 背景透上来 */
.layout, .main { background: none; }

/* 滚动条：与玻璃语言一致的细窄样式 */
::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb {
  background: rgba(20, 37, 63, .16);
  border-radius: var(--g-r-full);
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(20, 37, 63, .28); background-clip: padding-box; }
::-webkit-scrollbar-track { background: transparent; }

/* ==========================================================================
   3. L2 导航层 · 顶栏
   ========================================================================== */
#app .topbar {
  height: var(--topbar-h);
  background: rgba(255, 255, 255, var(--g-a-chrome));
  -webkit-backdrop-filter: blur(var(--g-blur-3)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-3)) saturate(var(--g-sat));
  border-bottom: var(--g-edge);
  box-shadow: var(--g-edge-lit), 0 1px 0 rgba(255,255,255,.4), 0 6px 20px -10px rgba(20,37,63,.16);
  padding: 0 18px;
  gap: 14px;
  color: var(--t1);
}

/* 顶栏文字由白转墨（原先深底白字） */
#app .topbar .brand-text {
  color: var(--t1);
  font-size: 16px;
  letter-spacing: .02em;
}
/* ⚠️ flex 子项默认可收缩，logo 图片会被压扁变形。
   必须 flex:none + max-width:none，与 login.css 同一处理。 */
#app .topbar .brand img {
  height: 26px;
  width: auto;
  flex: none;
  max-width: none;
  display: block;
}

/* 全局搜索：玻璃输入框 */
#app .globalsearch .gs-icon { color: var(--t3); }
#app .globalsearch input {
  height: 34px;
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-full);
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(var(--g-blur-1));
  backdrop-filter: blur(var(--g-blur-1));
  color: var(--t1);
  box-shadow: var(--g-edge-lit-s);
  transition: background .16s, border-color .16s, box-shadow .16s;
}
#app .globalsearch input::placeholder { color: var(--t3); }
#app .globalsearch input:hover { background: rgba(255, 255, 255, .74); }
#app .globalsearch input:focus {
  background: rgba(255, 255, 255, .92);
  border-color: var(--hy-blue);
  box-shadow: var(--g-focus-ring);
  outline: none;
}

/* 角色徽标 */
#app .role-chip {
  color: var(--hy-blue);
  background: rgba(0, 113, 168, .08);
  border: 1px solid rgba(0, 113, 168, .18);
  border-radius: var(--g-r-full);
  padding: 4px 11px;
  font-size: 12px;
  box-shadow: var(--g-edge-lit-s);
}

/* ---------------------------------------------------------------------------
   省份 / 业务片区切换器（2026-11-10）：右上角常驻「当前区域」标识 + 下拉。
   与通知面板同一层级；窄屏规则集中在 mobile.css。
   --------------------------------------------------------------------------- */
#app .region-wrap { position: relative; }
#app .region-chip {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--hy-blue); background: rgba(0, 113, 168, .08);
  border: 1px solid rgba(0, 113, 168, .18);
  border-radius: var(--g-r-full); padding: 4px 11px;
  font-size: 12px; cursor: pointer; white-space: nowrap;
  box-shadow: var(--g-edge-lit-s);
}
#app .region-chip:hover { background: rgba(0, 113, 168, .14); }
#app .region-chip[aria-expanded="true"] { background: rgba(0, 113, 168, .16); }
#app .region-role { color: var(--t2); }
#app .region-role:empty { display: none; }
#app .region-cur { font-weight: 600; color: var(--hy-blue); }
#app .region-cur::before { content: "·"; margin-right: 6px; color: var(--t3); }
#app .region-role:empty + .region-cur::before { content: ""; margin-right: 0; }
#app .region-caret { opacity: .7; }
#app .region-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
  width: 272px; max-width: calc(100vw - 24px);
  background: var(--g-surface, #fff);
  border: 1px solid #828C9B; border-radius: 12px;
  box-shadow: 0 12px 32px rgba(20, 37, 63, .16);
  padding: 8px; max-height: 70dvh; overflow-y: auto;
}
#app .region-head {
  padding: 6px 8px 10px; font-size: 12px; font-weight: 600; color: var(--t1);
  border-bottom: 1px solid var(--line); margin-bottom: 6px;
}
#app .region-hint { display: block; font-weight: 400; font-size: 11px; color: var(--t3); margin-top: 3px; }
#app .region-list { display: flex; flex-direction: column; gap: 2px; }
#app .region-opt {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: none; border-radius: 8px;
  padding: 9px 10px; font-size: 13px; color: var(--t1); cursor: pointer;
}
#app .region-opt:hover { background: rgba(0, 113, 168, .08); }
#app .region-opt.active { background: rgba(0, 113, 168, .12); font-weight: 600; }
#app .region-opt .tag { margin-left: auto; font-size: 11px; color: var(--t3); }
#app .region-empty { padding: 14px 10px; font-size: 12px; color: var(--t3); text-align: center; }
#app .region-busy { opacity: .6; pointer-events: none; }

/* 通知铃铛 / 图标按钮 */
#app .notif-btn, #app .nav-toggle, #app .icon-btn { color: var(--t2); }
#app .icon-btn {
  border-radius: var(--g-r-sm);
  transition: background .14s, color .14s;
}
#app .icon-btn:hover { background: var(--g-hover); color: var(--hy-blue); }
#app .icon-btn:active { background: var(--g-press); }
#app .nav-toggle { color: var(--t2); }
#app .notif-btn { color: var(--t2); }
#app .notif-badge {
  border: 1.5px solid rgba(255, 255, 255, .92);
  box-shadow: 0 1px 4px rgba(20,37,63,.2);
  line-height: 15px;
}

/* 用户按钮 + 头像 */
#app .user-btn {
  color: var(--t1);
  border-radius: var(--g-r-full);
  padding: 4px 10px 4px 5px;
  transition: background .14s;
}
#app .user-btn:hover { background: var(--g-hover); }
#app .user-btn:active { background: var(--g-press); }
#app .avatar {
  background: linear-gradient(140deg, #0071A8, #0E8CC4);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35), 0 2px 6px -2px rgba(0, 113, 168,.5);
}

/* ==========================================================================
   4. L1 内容层 · 侧边栏
   ========================================================================== */
#app .sidebar {
  background: rgba(255, 255, 255, var(--g-a-bg));
  -webkit-backdrop-filter: blur(var(--g-blur-2)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-2)) saturate(var(--g-sat));
  border-right: var(--g-edge);
  box-shadow: var(--g-edge-lit-s), 1px 0 0 rgba(255,255,255,.5);
  padding: 16px 10px 24px;
  width: var(--side-w);
}

#app .nav-group { margin-bottom: 18px; }
#app .nav-group-t {
  color: var(--t3);
  font-size: 11px;
  letter-spacing: .1em;
  padding: 0 12px 8px;
}
#app .nav-item {
  color: var(--t2);
  border-radius: var(--g-r-sm);
  border-left: 3px solid transparent;
  padding: 9px 10px;
  margin-bottom: 2px;
  transition: background .14s, color .14s, border-color .14s;
}
#app .nav-item:hover {
  background: var(--g-hover);
  color: var(--t1);
}
#app .nav-item:active { background: var(--g-press); }
#app .nav-item.active {
  background: var(--g-sel-bg);
  color: var(--hy-blue);
  border-left-color: var(--hy-blue);
  box-shadow: var(--g-edge-lit-s);
}
#app .nav-item .ico { opacity: .9; }
#app .nav-item .badge-n {
  box-shadow: 0 1px 3px rgba(198,40,40,.3);
  border-radius: var(--g-r-full);
  padding: 1px 6px;
}

/* ==========================================================================
   5. 卡片 / 面板 / 网格
   ========================================================================== */
/* 网格自适应列数：原 style.css 固定列数，在 1440px 下每列过窄导致内容裁切。
   ⚠️ minmax 下限的两个约束（都要满足，否则不是换行就是溢出）：
     ① 上限：容器宽 / 期望列数 ≥ 下限，否则列数掉下来
     ② 下限：单块最小可用宽度（放得下内容）
     grid-4（KPI，约 1130px 宽放 4 列）：1130/4 ≈ 282 → 下限取 230，留出余量
     grid-3（预警小卡，装在右半栏约 410px 放 3 列）：410/3 ≈ 136 → 下限取 120
   另外给容器加 min-width:0，防止 grid 子项的固有宽度把网格撑破。 */
#app .grid-2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--sp-4); align-items: start; }
#app .grid-3 { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--sp-2); align-items: start; }
#app .grid-4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--sp-3); align-items: start; }
/* grid 子项默认 min-width:auto 会被内容撑破，必须归零 */
#app .grid-2 > *, #app .grid-3 > *, #app .grid-4 > * { min-width: 0; }
#app .card {
  background: rgba(255, 255, 255, var(--g-a-bg));
  -webkit-backdrop-filter: blur(var(--g-blur-1)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-1)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit), var(--g-sh-1);
  padding: 18px;
  margin-bottom: 16px;
}
#app .card-t {
  color: var(--t1);
  font-size: 15px;
  margin-bottom: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
#app .card-t .hint { color: var(--t3); }

/* KPI 卡：4 列并排时每列约 280px；金额数字必须完整显示，
   因此给足最小宽度并允许卡片独占一行（flex-wrap 自然换行）。
   P2-13（2026-10-13）：装饰语言统一——原「顶部 3px 品牌色条」与卡片的
   内高光玻璃边是两种装饰语言混搭（且旧注释声称已改、实际未改），
   现去掉色条，色彩语义迁移到标签左侧 3px 短色条（见下方 .kpi-label 规则），
   卡片本体只保留细边 + 内高光一种语言。 */
#app .kpi {
  background: rgba(255, 255, 255, var(--g-a-bg));
  -webkit-backdrop-filter: blur(var(--g-blur-1)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-1)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit), var(--g-sh-1);
  padding: var(--sp-4) var(--sp-6);
  flex: 1 1 auto;
  min-width: 0;              /* 不设下限：一律跟随所在网格的列宽，
                                否则 min-width 会与 grid 列宽冲突并撑破容器 */
  overflow: hidden;
}
/* grid 里的 KPI 是 grid 子项，flex/min-width 都不适用，显式归零 */
#app .grid-2 > .kpi, #app .grid-3 > .kpi, #app .grid-4 > .kpi {
  flex: none;
  min-width: 0;
}
/* P2-13：色彩语义由标签左侧短色条承担——默认品牌蓝，
   各修饰类只改色条颜色，不动卡片本体。
   注意作用域是 .kpi 内的 label：详情页等处的裸 .kpi-label 是字段标签，
   不该带色条（那些场景用 #app .kpi-label 的基础样式）。 */
#app .kpi .kpi-label { border-left: 3px solid var(--hy-blue); padding-left: 8px; }
#app .kpi.green  .kpi-label { border-left-color: var(--hy-green); }
#app .kpi.orange .kpi-label { border-left-color: var(--hy-orange); }
#app .kpi.red    .kpi-label { border-left-color: var(--c-danger); }
#app .kpi.ink    .kpi-label { border-left-color: var(--hy-ink); }
/* 窄格预警卡（grid-3 内）：标签与数字都要能压缩，不换行不裁切 */
#app .grid-3 .kpi,
#app .grid-2 .kpi { padding: var(--sp-3); }
#app .grid-3 .kpi-label { white-space: normal; line-height: 1.35; margin-bottom: 6px; }
#app .grid-3 .kpi-value { font-size: clamp(20px, 1.6vw, 26px); }
#app .kpi-label {
  color: var(--n700);
  font-size: 12px;
  margin-bottom: 10px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 大数字：40px+，等宽数字，是卡片的绝对重心。
   ⚠️ 金额绝不能截断 —— 这是核心业务数据。
   做法：不加 text-overflow，靠 clamp() 按视口缩字号 + 允许换行；
   数字再长也完整可读，代价只是变小。 */
#app .kpi-value {
  font-size: clamp(24px, 2.1vw, 40px);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--n900);
  font-variant-numeric: var(--num);
  font-feature-settings: "tnum" 1;
  min-width: 0;
  white-space: nowrap;
  overflow: visible;          /* 不裁切、不加省略号 */
}
/* 单位/说明性后缀（小）可单独换行，不影响主数字 */
#app .kpi-value small {
  font-size: 13px;
  font-weight: 400;
  color: var(--n700);
  margin-left: var(--sp-1);
}
#app .kpi-foot { color: var(--n600); font-size: 12px; margin-top: 10px; }

/* ==========================================================================
   6. 按钮（交互态：默认 / 悬停 / 按下 / 禁用）
   ========================================================================== */
#app .btn {
  height: 34px;
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-sm);
  /* P1-7（2026-10-13）：按钮去掉 backdrop-filter——用户管理页实测 51 个可见
     模糊层中按钮约占 3/4（每行 2~3 个操作按钮 × 十几行）。而按钮 99% 的时间
     停在卡片/白底上，模糊前后视觉差几乎为零，是最贵却最用不到的模糊。
     rgba 白底保留；玻璃质感由卡片、侧栏、浮层这些「真正压在内容之上」的表面承担。 */
  background: rgba(255, 255, 255, .62);
  color: var(--t1);
  box-shadow: var(--g-edge-lit-s);
  transition: background .14s, border-color .14s, color .14s, box-shadow .14s, transform .1s;
}
#app .btn:hover {
  background: rgba(255, 255, 255, .88);
  border-color: var(--hy-blue);
  color: var(--hy-blue);
  box-shadow: var(--g-edge-lit), 0 2px 10px -4px rgba(0, 113, 168,.28);
}
#app .btn:active {
  background: rgba(0, 113, 168, .10);
  transform: translateY(.5px);
  box-shadow: inset 0 1px 3px rgba(20,37,63,.10);
}
#app .btn:focus-visible { outline: none; box-shadow: var(--g-focus-ring); }

#app .btn-primary {
  position: relative;
  isolation: isolate;
  overflow: hidden;              /* 主按钮加流光，必须能裁切 */
  background: linear-gradient(135deg, #0071A8 0%, #0E8CC4 100%);
  border-color: rgba(0, 113, 168, .5);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), 0 10px 24px -6px rgba(0, 113, 168,.45);
}
/* 流光扫过：与登录页同一套参数（3.2s / left -80px → 130%），学自参考稿。
   ⚠️ 用独立 <i class="btn-shine"> 元素，**不要用 ::after**：
      伪元素会让按钮的 scrollWidth 变大，触发文本裁切误判/实际挤压。
      脚本见 scripts/btn-shine.js（构建时注入，或运行时批量补）。 */
#app .btn-shine {
  position: absolute;
  top: 0; bottom: 0;
  /* ⚠️ 必须让 .btn-shine **铺满按钮宽度**，光带靠 background-size 控制。
     若元素本身只有 56px 宽，keyframes 里的 translateX(100%) 指的是「自身 56px」，
     流光只走 56px 就卡在按钮中间 —— 这是「动效卡住不通」的根因。
     现在 translateX(100%) = 按钮宽度，可完整扫过。 */
  left: 0;
  width: 100%;
  background-image: linear-gradient(105deg,
    transparent 0%, rgba(255, 255, 255, .34) 50%, transparent 100%);
  background-size: 84px 100%;
  background-repeat: no-repeat;
  pointer-events: none;
  z-index: 0;
  transform: translateX(-100px);
  /* P1-7（2026-10-13）：删 will-change: transform——infinite 动画运行期间浏览器
     本就自动提层，常驻 will-change 反而让合成层永不回收（页面切换后仍占显存）。
     动画结束即释放，交给引擎自己判断。 */
  animation: gShine 3.4s cubic-bezier(.4, 0, .3, 1) infinite;
}
/* 按钮文字抬到流光之上 */
#app .btn-primary > *:not(.btn-shine) { position: relative; z-index: 1; }
/* 节奏与登录页一致：0→46% 扫过，54%~100% 停在右侧等待下一轮 */
@keyframes gShine {
  0%        { transform: translateX(-100px); }
  46%       { transform: translateX(100%); }
  54%, 100% { transform: translateX(100%); }
}
#app .btn-primary:hover {
  background: linear-gradient(135deg, #005C8A 0%, #0071A8 100%);
  border-color: rgba(0, 92, 138, .6);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.24), 0 14px 30px -8px rgba(0, 113, 168,.5);
}
#app .btn-primary:active {
  background: linear-gradient(135deg, #004A70 0%, #005C8A 100%);
  box-shadow: inset 0 2px 5px rgba(0,0,0,.2);
}
/* 禁用时停止流光，避免「禁用按钮还在闪」的错觉 */
#app .btn-primary:disabled .btn-shine { animation: none; opacity: 0; }
#app .btn-danger {
  background: linear-gradient(140deg, #C62828 0%, #D84343 100%);
  border-color: rgba(198, 40, 40, .5);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.24), 0 4px 14px -6px rgba(198,40,40,.45);
}
#app .btn-danger:hover {
  background: linear-gradient(140deg, #A52020 0%, #C62828 100%);
  border-color: rgba(165, 32, 32, .6);
  color: #fff;
}
#app .btn-sm { height: 28px; border-radius: var(--g-r-xs); padding: 0 10px; font-size: 12px; }
#app .btn-block { height: 42px; font-size: 15px; border-radius: var(--g-r-md); }

/* 禁用态：降饱和 + 去阴影，保持可读（不靠 opacity 压到不可读） */
#app .btn:disabled,
#app .btn[disabled] {
  background: rgba(20, 37, 63, .05);
  border-color: rgba(20, 37, 63, .10);
  color: var(--t3);
  box-shadow: none;
  cursor: not-allowed;
  opacity: 1;
}
#app .btn:disabled:hover {
  background: rgba(20, 37, 63, .05);
  border-color: rgba(20, 37, 63, .10);
  color: var(--t3);
  box-shadow: none;
}

/* ==========================================================================
   7. 表单控件
   ========================================================================== */
#app .field > span { color: var(--t1); }

/* 文本类输入：玻璃凹面 */
#app .field input,
#app input[type="text"],
#app input[type="password"],
#app input[type="search"],
#app input[type="number"],
#app input[type="month"],
#app input[type="date"],
#app input[type="time"],
#app input[type="datetime-local"],
#app textarea {
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-sm);
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(var(--g-blur-1));
  backdrop-filter: blur(var(--g-blur-1));
  color: var(--t1);
  box-shadow: var(--g-edge-lit-s), inset 0 1px 2px rgba(20,37,63,.03);
  transition: background .15s, border-color .15s, box-shadow .15s;
}
#app .field input:hover,
#app input[type="text"]:hover,
#app input[type="password"]:hover,
#app input[type="search"]:hover,
#app input[type="number"]:hover,
#app input[type="month"]:hover,
#app input[type="date"]:hover,
#app input[type="time"]:hover,
#app input[type="datetime-local"]:hover,
#app textarea:hover {
  background: rgba(255, 255, 255, .78);
  border-color: rgba(0, 113, 168, .22);
}
#app .field input:focus,
#app input[type="text"]:focus,
#app input[type="password"]:focus,
#app input[type="search"]:focus,
#app input[type="number"]:focus,
#app input[type="month"]:focus,
#app input[type="date"]:focus,
#app input[type="time"]:focus,
#app input[type="datetime-local"]:focus,
#app textarea:focus,
#app select:focus {
  background: rgba(255, 255, 255, .94);
  border-color: var(--hy-blue);
  box-shadow: var(--g-focus-ring);
  outline: none;
}
#app input::placeholder, #app textarea::placeholder { color: var(--t3); opacity: 1; }
#app input:disabled, #app select:disabled, #app textarea:disabled {
  background: rgba(20, 37, 63, .045);
  color: var(--t3);
  border-color: rgba(20, 37, 63, .10);
  cursor: not-allowed;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* 下拉：保留 height:34px + padding:0 10px（盒模型铁律，勿改） */
#app select {
  height: 34px;
  min-width: 140px;
  padding: 0 10px;
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-sm);
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(var(--g-blur-1));
  backdrop-filter: blur(var(--g-blur-1));
  color: var(--t1);
  box-shadow: var(--g-edge-lit-s);
  white-space: nowrap;
}
#app select:hover { background: rgba(255, 255, 255, .80); }
#app select option { background: #FFFFFF; color: var(--t1); }
/* P1-7（2026-10-13）：表格行内编辑控件（每行的角色/状态下拉、单元格输入框）
   不参与模糊。用户管理页一屏 18 个行内 select，占全页模糊层总数的 4/5——
   而它们压在表格白底上，模糊有无肉眼难辨，是最典型的「成本全付、收益为零」。
   筛选栏等表格外的控件保留模糊（数量有限，仍是玻璃语言的一部分）。 */
#app tbody select, #app tbody input, #app tbody textarea {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

#app textarea { min-height: 76px; padding: 8px 10px; resize: vertical; }

/* 勾选 / 单选：品牌色 */
#app .checkline input, #app .radio input, #app .chk input {
  accent-color: var(--hy-blue);
  width: 15px; height: 15px;
}

/* 客户选择器 */
#app .cust-select .cs-clear {
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-sm);
  background: rgba(255, 255, 255, .58);
  color: var(--t2);
  box-shadow: var(--g-edge-lit-s);
  transition: background .14s, color .14s, border-color .14s;
}
#app .cust-select .cs-clear:hover {
  background: var(--hy-orange);
  border-color: var(--hy-orange);
  color: #fff;
}

/* ==========================================================================
   8. 表格
   ========================================================================== */
/* 表格横向滚动容器
   --------------------------------------------------------------------------
   ⚠️ 历史踩坑记录（2026-10 上旬，保留备查）：
      当时给 `.table-wrap { overflow-x: auto }` 配 thead sticky 失败：
      CSS 规范把 overflow-y 计算成 auto，sticky 相对 .table-wrap 定位，
      而容器高度=内容高不滚动 → 表头停在容器顶部，页面滚动时整行消失
      （截图里只剩一条线）。且 overflow-y 无法只设一个 visible。
   ✅ 2026-10-14 已解决（P1-9）：让容器自身成为纵向滚动容器
      （overflow-y: auto + max-height 65vh），sticky 相对容器可视区生效。
      生效定义见下方第二个 `#app .table-wrap`（含完整注释）。
      此处原为重复定义（同特异性、被下方完整版层叠覆盖），已移除。 */

/* ==========================================================================
   筛选栏（.form-row）—— 全站 20+ 个页面共用
   --------------------------------------------------------------------------
   ⚠️ 真实 bug：style.css 里 `.form-row .field { min-width:150px }` 与
      `.field.grow { flex:1 }` 组合时，`flex:1` = `flex:1 1 0%`，
      但 **flex item 的 min-width 优先于 flex-basis，会阻止收缩** →
      装不下的字段既不压缩也不换行，把整行撑出卡片，
      行尾的「查询」按钮被切掉一半（用户报「重叠错码」）。
   → grow 字段必须 `min-width:0` 才能真正伸缩；
     非 grow 字段保留 150px 下限；
     行内按钮 `flex:none`，宁可换行也不被压扁。 */
#app .form-row { min-width: 0; }
#app .form-row > * { min-width: 0; }
#app .form-row .field.grow { flex: 1 1 0; min-width: 0; }
#app .form-row > .field:not(.grow) { flex: 0 1 170px; min-width: 150px; }
/* select 例外：选项文字可能很长（如审计日志「全部（12 人）」、「系统管理员（3）」），
   固定 170px 会把文字裁掉。给一个更大上限，并允许文字完整显示。 */
#app .form-row > .field:not(.grow):has(select) { flex: 0 1 auto; min-width: 168px; }
#app .form-row select { width: auto; min-width: 148px; max-width: 260px; }
/* ⚠️ 包着按钮的 .field（审计日志「查询/重置」就是这么写的）：
   它会继承上面的 `flex:0 1 170px` 而被压缩 → 「查询」两字被裁成 54px。
   → 用 :has(> .btn) 排除掉，按钮宽度由内容决定。 */
#app .form-row > .field:has(> .btn) {
  flex: none;
  min-width: 0;
  width: auto;
  align-self: flex-end;
}
/* 按钮本身也不能收缩：`.btn` 的 flex 基础值是 `0 1 auto`，
   在窄 field 里会被压到 56px，「查询」两字被裁。 */
#app .form-row .field > .btn { flex: none; width: auto; }
#app .form-row > .btn,
#app .form-row > select { flex: none; align-self: flex-end; }
/* 表格
   ⚠️ 用 table-layout: fixed + min-width 强制「容器窄时横向滚动」，
   而不是让浏览器压缩列宽 → 压缩会让各列基线错位，视觉上像文字重叠。

   min-width 的取值权衡（实测）：
     880px → 1080px 视口下客户名称只剩 ~108px，「察哈尔右翼前旗清源水务投资有限公司」
             被截成「察哈尔右翼...」，业务上等于看不清客户是哪家 —— 不可接受。
     1180px → 名称列约 200px，可显示 12~14 字，足够识别；
             容器不够宽时出现横向滚动条，这是正确取舍（信息完整 > 一屏放下）。
   → 定为 1180px。 */
#app table {
  border-top: 1.5px solid rgba(20, 37, 63, .82);
  border-bottom: 1.5px solid rgba(20, 37, 63, .82);
  font-size: 13px;
  /* ⚠️ 不要再用 `table-layout:fixed` + 大 `min-width`。
     那个组合在窄容器（<1400px）+ 空数据时会出现：
       表格被撑到 1460~2092px，容器只有 750px，
       表头文字全部落在容器右侧之外 → 用户看到「一片空白、表头不见了、
       左边只剩一条竖线、右边浮着一个复选框」，完全无法阅读。
     → 改为 auto 布局 + width:100%：
         列宽由内容自然决定（配合 core.js 的 c-* 语义档位给下限），
         容器够宽就铺满，不够就横向滚动；
         空表时表头与容器同宽，永远可见。 */
  table-layout: auto;
  width: 100%;
  min-width: 0;
}
/* 横向滚动提示：右侧渐隐，提示「还有内容可右滑」
   --------------------------------------------------------------------------
   2026-10-14 重新启用表头吸顶（P1-9），正确姿势是「容器自身成为纵向滚动容器」：
     旧方案（已废弃）：overflow-x:auto + thead sticky —— CSS 规范把 overflow-y
       计算成 auto，sticky 相对 .table-wrap 定位，而容器高度=内容高不滚动，
       表头停在容器顶部随页面一起消失（实测截图只剩一条线）。
     新方案：overflow-y: auto + max-height 限高 → 容器自己滚，
       thead 的 sticky top:0 相对容器可视区生效，表头始终吸附容器顶部。
     短表格（内容 < 65vh）不触发滚动，行为与原来一致，零影响。
   弹窗内的表格排除限高（.modal-box .table-wrap 见下方）：
     弹窗本身由 .modal-body 滚动，内部表格再限高会出现双滚动条。 */
#app .table-wrap {
  width: 100%;
  overflow-x: auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* 给横向滚动条预留高度，避免遮挡最后一行 */
  padding-bottom: 10px;
  /* 65vh：1080p 视口约 700px，容纳 12~13 行后开始表格内滚 + 表头吸顶。
     留 var() 入口供将来单页定制（如 --tbl-max-h: 80vh），不预设定义。 */
  max-height: var(--tbl-max-h, 65vh);
  background:
    linear-gradient(to left, rgba(255,255,255,.5), rgba(255,255,255,0) 24px) right 12px center / 24px calc(100% - 10px) no-repeat,
    linear-gradient(to left, rgba(20,37,63,.10), rgba(20,37,63,0) 24px) right 12px center / 10px calc(100% - 10px) no-repeat,
    linear-gradient(to left, rgba(20,37,63,.12), rgba(20,37,63,0) 6px) right center / 6px calc(100% - 10px) no-repeat;
  background-attachment: local, scroll, scroll;
}
/* 弹窗内表格：不吸顶、不限高，随 .modal-body 一起滚（避免双滚动条）。
   views-a.js 有内联 max-height:60vh 的详情表格不受影响（内联优先级更高）。 */
.modal-box .table-wrap { max-height: none; }
#app thead th {
  color: var(--t2);
  /* ⚠️ sticky 表头背景必须不透明：滚动时下方行会透过半透明背景显影。
     #F7F7F8 = 原 rgba(20,37,63,.035) 叠白底的实色近似，视觉零差异。 */
  background: #F7F7F8;
  border-bottom: 1px solid var(--line2);
  padding: 10px var(--sp-3);
  /* 表头吸顶（2026-10-14 P1-9）：.table-wrap 已成为纵向滚动容器（见上方注释），
     sticky top:0 相对容器可视区生效 —— 长表格滚动时表头始终可见。 */
  position: sticky;
  top: 0;
  z-index: 2;
}
/* 表格单元格
   ⚠️ 行高 52px 必须配合「单行显示」：
   若允许文字换行，长文本会占 2~3 行，而相邻列仍是单行，
   各列基线错位，视觉上就像文字互相重叠（实际是行高不一致 + 垂直居中）。
   → 强制 nowrap，超长用省略号，完整内容交给 title 属性/详情页查看。 */
/* 列宽系统（2026-11 重做）
   --------------------------------------------------------------------------
   ⚠️ 上一版用 nth-child 猜列宽是错的：各视图表格的列序完全不同。
      「第 2 列是名称」在用户管理页成立（账号/姓名），
      在「未回款 TOP10」也成立（客户/责任人），但换个表就错位了 ——
      结果账号列只有 92px（189471187… 被截），姓名列却空了一半。
      用户原话：「姓名很短却空了很长，前面的账号很长却空了很短」。

   → 改为「基准档位 + 实测加宽」两层（core.js 的 tagTableColumns）：
      ① 这里给出**每类列的基准宽度**（内容通常多长）；
      ② JS 渲染后量真实内容，超出基准的列自动加宽（th/td 同步）。
      这样长内容不被截、短内容不浪费空间，不会再出现「一边空一半一边被切」。

   语义档位：
     c-id    编号 / 单号          短、格式定长
     c-name  名称 / 账号 / 客户   长（最易溢出，故基准最宽且允许被 JS 加宽）
     c-mid   角色 / 省份 / 类型   中
     c-num   金额 / 数量          中（等宽数字、右对齐）
     c-date  日期 / 时间          中定长
     c-tag   状态 / 徽标          短（2~4 字）
     c-act   操作                按按钮数定（JS 会实测）
   → 改为「语义下限 + 内容自适应」两层：
      ① 这里给每类列一个 **min-width**（不是 width —— auto 布局下 width 只是建议，
         内容更长时浏览器会自动撑开；min-width 才是真正的下限）；
      ② 浏览器按内容自动分配剩余空间，短列不会空一半，长列自动够宽。
         JS 侧的 fitColumnWidths() 只在内容确实超限时再补一刀。

   ⚠️ 严禁改回 table-layout:fixed + 大 min-width：那个组合在窄容器 + 空数据时
      会把表格撑到 1400~2000px 而容器仅 750px，表头全部落到容器外不可见。

   语义档位：
     c-pick  复选框列             极窄
     c-id    编号 / 单号          短、格式定长
     c-name  名称 / 账号 / 客户   长（最易溢出）
     c-mid   角色 / 省份 / 类型   中
     c-num   金额 / 数量          中（等宽数字、右对齐）
     c-date  日期 / 时间          中定长
     c-tag   状态 / 徽标          短（2~4 字）
     c-act   操作                按按钮数定（JS 实测） */
#app th.c-pick, #app td.c-pick { min-width: 46px; padding: 0; text-align: center; }
#app td.c-pick input, #app th.c-pick input { margin: 0; }
#app th.c-id,   #app td.c-id   { min-width: 96px; }
#app th.c-name, #app td.c-name { min-width: 180px; }
#app th.c-mid,  #app td.c-mid  { min-width: 110px; }
#app th.c-num,  #app td.c-num  { min-width: 118px; }
#app th.c-date, #app td.c-date { min-width: 136px; }
#app th.c-tag,  #app td.c-tag  { min-width: 76px; }
#app th.c-act,  #app td.c-act  { min-width: 128px; }

/* 表格内的 select 必须铺满所在列。
   auto 布局下列宽由内容决定，但 select 的自然宽度只反映「当前选中项」；
   切成「售电事业部负责人」这类长选项就会显示成「售电事业部…」。
   → width:100% 让它跟随列宽，列宽下限由下面 col-role/col-prov 的 min-width 保证。 */
#app tbody td select {
  width: 100%;
  text-overflow: ellipsis;
}

/* 含交互控件的列（select / input / 多按钮）必须完整显示，
   否则控件被裁切、下拉框压扁、按钮只露一半。 */
#app td:has(select), #app th:has(select),
#app td:has(input),  #app th:has(input) {
  overflow: visible;
  text-overflow: clip;
  white-space: nowrap;
}

/* 未识别语义的列：给一个不会挤爆的最小宽度 */
#app td:where(:not([class*="c-"]):not([class*="col-"])),
#app th:where(:not([class*="c-"]):not([class*="col-"])) {
  min-width: 96px;
}

/* 语义列不做截断（内容必须完整可读） */
#app td.c-id, #app td.c-tag, #app td.c-num, #app td.c-date, #app td.c-act {
  overflow: visible;
  text-overflow: clip;
}

/* 多徽标单元格（如合同「标签」列的「市场化联动」+「≥311/40%分成」）：
   允许换行到多行，而不是把第二个徽标裁掉。 */
#app td:has(> .badge ~ .badge),
#app td:has(.badge + .badge) {
  white-space: normal;
  overflow: visible;
  line-height: 1.7;
}
#app td .badge { white-space: nowrap; }

/* views-c.js 的 users-table 自带语义列类。
   ⚠️ 每列只允许出现一次 —— 之前同一列在文件里有两条定义（196px 与 120px），
      后者覆盖前者，导致「售电事业部负责人」被显示成「售电事业部…」。
   下限按用户管理页实际最长内容给定：
     role「售电事业部负责人」≈196px、prov（省份列，兼容历史长写法）≈152px、
     account「189471187…@qq.com」≈170px、audit「已拒绝 2026-10-10…」≈170px。 */
#app th.col-account, #app td.col-account { min-width: 170px; }
#app th.col-name,    #app td.col-name    { min-width: 88px; max-width: 150px; }
#app th.col-role,    #app td.col-role    { min-width: 196px; }
#app th.col-prov,    #app td.col-prov    { min-width: 152px; }
#app th.col-status,  #app td.col-status  { min-width: 76px; }
#app th.col-audit,   #app td.col-audit   { min-width: 170px; }
#app th.col-login,   #app td.col-login   { min-width: 136px; }
#app th.col-actions, #app td.col-actions { min-width: 160px; }
#app tbody td.col-actions .btn-sm {
  margin-right: 4px;
  margin-bottom: 2px;
}
/* 按钮多的操作列：允许换行，避免被裁 */
#app td.col-actions {
  white-space: normal;
  line-height: 1.6;
}

/* 窄屏：解除 JS 注入的 min-width，让表格按内容自适应并横向滚动。
   （全局已是 table-layout:auto，这里只需清掉 JS 写的内联 min-width） */
@media (max-width: 860px) {
  #app th, #app td { min-width: 0 !important; }
}

/* 驾驶舱双栏（.ov-two-col）
   ⚠️ 视图里的内联写法是 `grid-template-columns: 1fr 3fr`，
   而 `1fr` 等价于 `minmax(auto, 1fr)`，auto 下限 = 内容最小宽度
   → 左栏塞进 1098px 的宽表后整栏被撑开，整页出现 15px 横向溢出。
   → 强制改为 minmax(0, …)，允许列收缩到 0，宽内容由内部 .table-wrap 横向滚动承接。 */
#app .ov-two-col { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important; }
#app .ov-two-col > * { min-width: 0; }
/* 第二个变体（views-h.js 用 1fr 3fr） */
#app .ov-two-col:has(> .card:nth-child(2) .table-wrap) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 3fr) !important;
}

#app tbody td {
  color: var(--t1);
  border-bottom: 1px solid var(--divider);
  padding: 0 var(--sp-3);
  height: var(--row-h);              /* 行高 52px（规范 48–56px） */
  vertical-align: middle;            /* 各列文字垂直居中对齐，基线一致 */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* 旧规则 `max-width: 0` 是 table-layout:fixed 用来强制列宽可约束的技巧；
     现在改用 auto 布局，max-width:0 会让所有列塌缩到 0，必须移除。 */
  max-width: none;
}
#app thead th {
  vertical-align: middle;
  white-space: nowrap;
}
/* 操作列与状态列不截断（内容短且必须可点、可读） */
#app tbody td.col-actions,
#app tbody td:has(.btn),
#app tbody td:has(.badge),
#app tbody td.num {
  overflow: visible;
  text-overflow: clip;
  white-space: nowrap;
  max-width: none;
}
/* P2-14（2026-10-13）：数字列字号 13 → 13.5px。
   电价/金额/百分比是核心数据，13px 在 1080p 以上高密度屏偏小；
   取 13.5px 这个半步进——比 14px 省列宽（宽表总宽敏感），
   又比 13px 明显易读。行高不动（由 --row-h 控制，与字号解耦）。 */
#app th.num, #app td.num { font-size: 13.5px; }
/* 含徽标的列必须有足够宽度，否则 max-width:0 的单元格会把徽标压成 0 宽。
   徽标文字最长约 6 字（45~83px），给 120px 稳妥。 */
#app td:has(> .badge), #app th:has(> .badge) { width: 120px; }
#app td:has(> .btn),   #app th:has(> .btn)   { width: auto; }
#app tbody tr { transition: background var(--dur-1) var(--ease); }
/* hover 整行高亮；不做斑马纹 */
#app tbody tr:hover { background: var(--brand-soft); }
#app tbody tr:hover td:first-child { border-radius: var(--r) 0 0 var(--r); }
#app tbody tr:hover td:last-child  { border-radius: 0 var(--r) var(--r) 0; }
#app tfoot td {
  border-top: 1px solid var(--line2);
  background: var(--n100);
  font-variant-numeric: var(--num);
}

/* 预警行：玻璃底上的淡彩，仍保持左侧色条语义 */
#app tr.lv-green  { background: rgba(0, 166, 81, .07); }
#app tr.lv-yellow { background: rgba(255, 209, 0, .12); }
#app tr.lv-red    { background: rgba(198, 40, 40, .07); }
#app tr.lv-red td { font-weight: 600; }
#app tr.lv-green td:first-child  { box-shadow: inset 3px 0 0 var(--lv-green-bar); }
#app tr.lv-yellow td:first-child { box-shadow: inset 3px 0 0 var(--lv-yellow-bar); }
#app tr.lv-red td:first-child    { box-shadow: inset 3px 0 0 var(--lv-red-bar); }
#app tr.lv-green:hover  { background: rgba(0, 166, 81, .11); }
#app tr.lv-yellow:hover { background: rgba(255, 209, 0, .17); }
#app tr.lv-red:hover    { background: rgba(198, 40, 40, .11); }

/* ==========================================================================
   9. 徽标 / 状态点 / 进度条 / 标签
   ========================================================================== */
#app .badge {
  border-radius: var(--g-r-xs);
  padding: 2px 8px;
  box-shadow: var(--g-edge-lit-s);
  border: 1px solid transparent;
}
#app .badge-blue   { background: rgba(0, 113, 168,.10);  color: #006294; border-color: rgba(0, 113, 168,.16); }
#app .badge-green  { background: rgba(0,166,81,.12);   color: var(--lv-green-fg); border-color: rgba(0,166,81,.20); }
#app .badge-yellow { background: rgba(255,209,0,.22);  color: var(--lv-yellow-fg); border-color: rgba(198,157,0,.28); }
#app .badge-red    { background: rgba(198,40,40,.10);  color: #A81F1F; border-color: rgba(198,40,40,.18); }
#app .badge-gray   { background: rgba(20,37,63,.07);   color: var(--t2); border-color: rgba(20,37,63,.10); }
#app .badge-orange { background: rgba(242,101,34,.12); color: #B3440F; border-color: rgba(242,101,34,.20); }
#app .badge-pending  { background: rgba(0, 113, 168,.10); color: #006294; border-color: rgba(0, 113, 168,.16); }
#app .badge-rejected { background: rgba(198,40,40,.10); color: #A81F1F; border-color: rgba(198,40,40,.18); }

#app .dot { box-shadow: 0 0 0 2px rgba(255,255,255,.7); }

#app .progress {
  background: rgba(20, 37, 63, .08);
  border-radius: var(--g-r-full);
  height: 6px;
  box-shadow: inset 0 1px 2px rgba(20,37,63,.08);
}
#app .progress > i { border-radius: var(--g-r-full); }

#app .tag-pick {
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-full);
  background: rgba(255, 255, 255, .55);
  color: var(--t2);
  box-shadow: var(--g-edge-lit-s);
  transition: background .14s, color .14s, border-color .14s;
}
#app .tag-pick:hover { background: rgba(255,255,255,.85); border-color: rgba(0, 113, 168,.28); color: var(--hy-blue); }
#app .tag-pick.on {
  background: linear-gradient(140deg, #0071A8, #0E8CC4);
  border-color: rgba(0, 113, 168,.5);
  color: #fff;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.26), 0 2px 8px -3px rgba(0, 113, 168,.5);
}

/* ==========================================================================
   10. 提示块 / 空状态 / 加载态
   ========================================================================== */
#app .note {
  background: rgba(255, 209, 0, .13);
  border: 1px solid rgba(198, 157, 0, .22);
  border-left: 3px solid var(--hy-yellow);
  border-radius: var(--g-r-sm);
  color: #6B5300;
  box-shadow: var(--g-edge-lit-s);
}
#app .note.info {
  background: rgba(42, 110, 187, .08);
  border-color: rgba(42, 110, 187, .18);
  border-left-color: var(--c-info);
  color: #1D5A96;
}

#app .warn-banner {
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-s);
  border: 1px solid transparent;
}
#app .warn-banner.warn   { background: rgba(255,209,0,.18); color: #7A6000; border-color: rgba(198,157,0,.30); }
#app .warn-banner.danger { background: rgba(198,40,40,.10); color: #A81F1F; border-color: rgba(198,40,40,.26); }

#app .form-err {
  background: rgba(198, 40, 40, .09);
  border: 1px solid rgba(198, 40, 40, .26);
  border-radius: var(--g-r-sm);
  color: #A81F1F;
  box-shadow: var(--g-edge-lit-s);
}

#app .login-err {
  background: rgba(198, 40, 40, .09);
  border: 1px solid rgba(198, 40, 40, .26);
  border-left: 3px solid var(--c-danger);
  border-radius: var(--g-r-sm);
}

/* 空状态：玻璃内凹 + 图标位 */
#app .empty,
#app .notif-empty,
#app .gs-empty,
#app .cs-empty {
  color: var(--t3);
  background: rgba(255, 255, 255, .34);
  border: 1px dashed rgba(20, 37, 63, .14);
  border-radius: var(--g-r-md);
  box-shadow: inset 0 1px 3px rgba(20,37,63,.04);
}
#app .empty { padding: 40px 20px; }

/* 待办卡 */
#app .todo-item {
  background: rgba(255, 255, 255, .58);
  -webkit-backdrop-filter: blur(var(--g-blur-1));
  backdrop-filter: blur(var(--g-blur-1));
  border: var(--g-edge-ctl);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-s);
  transition: background .14s, border-color .14s, box-shadow .14s;
}
#app .todo-item:hover {
  background: rgba(255, 255, 255, .86);
  border-color: rgba(0, 113, 168, .30);
  box-shadow: var(--g-edge-lit), 0 3px 12px -5px rgba(0, 113, 168,.3);
}
#app .todo-n { color: var(--hy-blue); }

/* Tabs：分段控件质感 */
#app .tabs {
  border-bottom: 1px solid var(--line);
  gap: 4px;
}
#app .tab {
  border-radius: var(--g-r-sm) var(--g-r-sm) 0 0;
  padding: 8px 14px;
  color: var(--t2);
  transition: background .14s, color .14s;
}
#app .tab:hover { background: var(--g-hover); color: var(--t1); }
#app .tab.active {
  background: rgba(0, 113, 168, .08);
  color: var(--hy-blue);
  border-bottom-color: var(--hy-blue);
  box-shadow: var(--g-edge-lit-s);
}
#app .edit-tab {
  border-radius: var(--g-r-sm) var(--g-r-sm) 0 0;
  padding: 10px 16px;
  color: var(--t2);
  transition: background .14s, color .14s;
}
#app .edit-tab:hover { background: var(--g-hover); color: var(--t1); }
#app .edit-tab.active {
  background: rgba(0, 113, 168, .08);
  color: var(--hy-blue);
  border-bottom-color: var(--hy-blue);
}

/* ==========================================================================
   11. L3 浮层 · 下拉 / 气泡 / Toast
   ========================================================================== */
/* 全局搜索结果面板 */
#app .gs-panel {
  background: rgba(255, 255, 255, var(--g-a-float));
  -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-float), var(--g-sh-2);
  overflow: hidden;
}
#app .gs-group-t {
  color: var(--t3);
  background: rgba(20, 37, 63, .03);
  border-bottom: 1px solid var(--line);
  padding: 8px 14px 5px;
}
#app .gs-item { padding: 9px 14px; transition: background .12s; }
#app .gs-item:hover { background: var(--g-hover); }
#app .gs-item b { color: var(--t1); }
#app .gs-item span { color: var(--t3); }

/* 通知面板 */
#app .notif-panel {
  background: rgba(255, 255, 255, var(--g-a-float));
  -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-float), var(--g-sh-2);
  overflow: hidden;
}
#app .notif-head {
  background: rgba(20, 37, 63, .03);
  border-bottom: 1px solid var(--line);
  padding: 11px 14px;
}
#app .notif-item {
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  transition: background .12s;
}
#app .notif-item:hover { background: var(--g-hover); }
#app .notif-item.read { opacity: .62; }
#app .notif-title { color: var(--t1); }
#app .notif-body  { color: var(--t2); }
#app .notif-time  { color: var(--t3); }
#app .notif-empty { border: none; background: none; box-shadow: none; padding: 24px; }

/* 用户下拉 */
#app .user-drop {
  background: rgba(255, 255, 255, var(--g-a-float));
  -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-float), var(--g-sh-2);
  min-width: 140px;
  padding: 5px;
}
#app .user-drop button {
  border-radius: var(--g-r-xs);
  padding: 9px 12px;
  color: var(--t1);
  transition: background .12s, color .12s;
}
#app .user-drop button:hover { background: var(--g-hover); color: var(--hy-blue); }

/* 客户选择器浮层（挂在 body，不在 #app 内） */
.cs-panel-float {
  background: rgba(255, 255, 255, var(--g-a-float));
  -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-float), var(--g-sh-2);
  padding: 5px;
}
.cs-opt { border-radius: var(--g-r-xs); padding: 8px 10px; transition: background .12s; }
.cs-opt:hover, .cs-opt.active { background: rgba(0, 113, 168, .10); }
.cs-opt .cs-id { color: var(--t2); }
.cs-opt .cs-prov { color: var(--t2); }
.cs-empty { color: var(--t2); }

/* Toast */
.toast {
  background: rgba(20, 37, 63, .90);
  -webkit-backdrop-filter: blur(var(--g-blur-3)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-3)) saturate(var(--g-sat));
  border: 1px solid rgba(255, 255, 255, .16);
  border-radius: var(--g-r-md);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 12px 32px -10px rgba(20,37,63,.5);
  color: #fff;
  padding: 11px 20px;
}
.toast.ok   { background: rgba(0, 118, 62, .90);  border-color: rgba(255,255,255,.22); }
.toast.err  { background: rgba(168, 31, 31, .90); border-color: rgba(255,255,255,.22); }
.toast.warn { background: rgba(176, 82, 12, .90); border-color: rgba(255,255,255,.22); }

.link-btn { color: var(--hy-blue); border-radius: var(--g-r-xs); padding: 2px 6px; }
.link-btn:hover { background: var(--g-hover); text-decoration: none; }

/* ==========================================================================
   12. L3 浮层 · 弹窗
   ========================================================================== */
.modal-scrim {
  background: rgba(20, 37, 63, .38);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.modal-box {
  background: rgba(255, 255, 255, var(--g-a-deep));
  -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
  border: var(--g-edge);
  border-radius: var(--g-r-lg);
  box-shadow: var(--g-edge-lit-float), var(--g-sh-3);
}
.modal-box.maximized { border-radius: 0; }
.modal-head {
  background: rgba(255, 255, 255, .55);
  border-bottom: 1px solid var(--line);
  padding: 15px 20px;
  flex: none;
}
.modal-head h3 { color: var(--t1); font-size: 16px; }
.modal-head .icon-btn { color: var(--t2); border-radius: var(--g-r-sm); }
.modal-head .icon-btn:hover { background: var(--g-hover); color: var(--hy-blue); }
.modal-body { padding: 20px; }
.modal-foot {
  background: rgba(20, 37, 63, .028);
  border-top: 1px solid var(--line);
  padding: 13px 20px;
}
/* P2-17（2026-10-13）：删除整盒 se-resize 光标规则。
   弹窗可缩放由 style.css 的原生 resize:both 提供，浏览器只在右下角
   手柄上显示 se-resize 光标——这正是应有的行为预告粒度。
   原规则让整盒都显示 resize 光标，等于预告「拖哪儿都能缩放」，是错误预告。
   ⚠️ style.css 里同款规则必须同步删：两表同特异性，后加载者胜，
      只删这里的话先加载的 style.css 规则会重新生效。 */

/* ==========================================================================
   13. 客户详情 / 编辑页
   ========================================================================== */
#app .crumb { color: var(--t3); }
#app .crumb b { color: var(--t2); }
#app .page-title { color: var(--t1); }
#app .page-sub { color: var(--t3); }
#app .muted { color: var(--t3); }
#app .cust-name { color: var(--t1); }

#app .cust-unpaid {
  background: rgba(255, 255, 255, .60);
  -webkit-backdrop-filter: blur(var(--g-blur-1));
  backdrop-filter: blur(var(--g-blur-1));
  border: var(--g-edge);
  border-radius: var(--g-r-md);
  box-shadow: var(--g-edge-lit-s);
  padding: 8px 16px;
}
#app .cust-unpaid .cu-label { color: var(--t3); }
#app .cust-unpaid .cu-value { color: #A81F1F; }

#app .sec-head {
  color: var(--t1);
  border-bottom: 1px solid var(--line);
  background: rgba(20, 37, 63, .028);
  padding: 12px 18px;
}
#app .info-row { border-bottom: 1px dashed var(--line); }
#app .info-label { color: var(--t3); }
#app .info-val { color: var(--t1); }
#app .sub-t { color: var(--t2); }
#app .kv dt { color: var(--t3); }
#app .kv dd { color: var(--t1); }

#app .audit-item,
#app .att-item {
  background: rgba(255, 255, 255, .55);
  border: 1px solid var(--line);
  border-radius: var(--g-r-sm);
  box-shadow: var(--g-edge-lit-s);
}
#app .audit-meta { color: var(--t1); }
#app .audit-detail { color: var(--t2); }

#app .dup-hit {
  background: rgba(242, 101, 34, .06);
  border: 1px solid rgba(242, 101, 34, .22);
  border-left: 3px solid var(--hy-orange);
  border-radius: var(--g-r-sm);
  box-shadow: var(--g-edge-lit-s);
}

/* 报告封面 */
#app .report-cover {
  background: rgba(255, 255, 255, .5);
  border-top: 3px solid var(--hy-blue);
  border-radius: var(--g-r-md) var(--g-r-md) 0 0;
}
#app .rc-slogan { color: var(--hy-blue); }
#app .rc-period { color: var(--t3); }
#app .legend { color: var(--t2); }

/* 分页 */
#app .pager { color: var(--t2); }
#app .pager .btn { min-width: 30px; }

/* ==========================================================================
   14. 动效系统（时序对齐登录页/参考稿体系）
   ========================================================================== */
/* 页面切换：淡入 200ms（比 150ms 略宽，给「呼吸感」，但仍属快反馈区间） */
#app .view {
  animation: gViewIn var(--dur-2) var(--ease);
}
@keyframes gViewIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
#app .nav-group { animation: gViewIn var(--dur-2) var(--ease); }

/* 卡片入场：280ms + 8px 上浮。
   P1-5（2026-10-13）由 420ms/12px 收紧：原节奏偏慢，筛选后要盯着半透明的
   卡片飘半天；280ms 仍在「有感知、不拖沓」区间，与 --dur-2 页面淡入同族。
   错帧由五级（0/80/160/240/320）收敛为三级 0/60/120ms：列表页十几张卡时，
   原最后一张要等 320+420=740ms 才现身；现在第 3 张起统一 120ms，齐步感更好。 */
#app .card, #app .kpi {
  animation: gSurfIn 280ms var(--ease) backwards;
}
@keyframes gSurfIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
#app .view > .card:nth-child(1), #app .view > .grid-2 > .card:nth-child(1),
#app .view > .grid-3 > .card:nth-child(1), #app .view > .grid-4 > .card:nth-child(1) { animation-delay: 0ms; }
#app .view > .card:nth-child(2), #app .view > .grid-2 > .card:nth-child(2),
#app .view > .grid-3 > .card:nth-child(2), #app .view > .grid-4 > .card:nth-child(2) { animation-delay: 60ms; }
#app .view > .card:nth-child(n+3), #app .view > .grid-2 > .card:nth-child(n+3),
#app .view > .grid-3 > .card:nth-child(n+3), #app .view > .grid-4 > .card:nth-child(n+3) { animation-delay: 120ms; }

/* P1-5：同视图重渲染（筛选/分页/搜索——core.js 的 render() 打 data-reenter）
   跳过卡片入场动画。首进新视图照常播放；重渲染只换数据，
   不该让整页卡片再翻一次跟头——这正是此前「筛选后画面闪一下」的成因。 */
#app .view[data-reenter] .card, #app .view[data-reenter] .kpi { animation: none; }

/* 骨架屏：代替转圈 loading */
.skeleton {
  display: block;
  border-radius: var(--g-r-sm);
  background: linear-gradient(90deg,
    rgba(37, 40, 48, .05) 25%,
    rgba(37, 40, 48, .09) 37%,
    rgba(37, 40, 48, .05) 63%);
  background-size: 400% 100%;
  animation: gShimmer 1.4s ease-in-out infinite;
}
.skeleton-text { height: 12px; margin-bottom: 8px; }
.skeleton-text:last-child { margin-bottom: 0; }
.skeleton-title { height: 20px; width: 40%; margin-bottom: 16px; }
@keyframes gShimmer {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}

/* 表格骨架容器 */
.skeleton-table { width: 100%; }

/* 弹窗：从触发点缩放浮现（scale .96→1），不是从天上掉下来 */
.modal-box {
  animation: gModalIn var(--dur-3) var(--ease);
  transform-origin: var(--modal-ox, center) var(--modal-oy, center);
}
@keyframes gModalIn {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: scale(1); }
}
.modal-scrim { animation: gFadeIn var(--dur-2) var(--ease); }
@keyframes gFadeIn { from { opacity: 0; } to { opacity: 1; } }

/* 下拉 / 浮层：同族缩放浮现 */
#app .gs-panel,
#app .notif-panel,
#app .user-drop,
.cs-panel-float {
  animation: gPopIn var(--dur-1) var(--ease);
  transform-origin: top center;
}
@keyframes gPopIn {
  from { opacity: 0; transform: scale(.97) translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* Toast：从上方轻落 */
.toast { animation: gToastIn var(--dur-2) var(--ease); }
@keyframes gToastIn {
  from { opacity: 0; transform: translateY(-8px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* 按钮：按下微沉，反馈即时 */
#app .btn:active { transform: translateY(.5px); }

/* ==========================================================================
   15. 响应式
   ========================================================================== */
@media (max-width: 860px) {
  /* 侧边栏抽屉：L3 浮层材质 */
  #app .sidebar {
    background: rgba(255, 255, 255, .96);
    -webkit-backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
    backdrop-filter: blur(var(--g-blur-4)) saturate(var(--g-sat));
    box-shadow: var(--g-edge-lit), 8px 0 32px -8px rgba(20,37,63,.28);
  }
  #app .scrim {
    background: rgba(20, 37, 63, .34);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
  }
  /* 表格转卡片：每行成为独立玻璃片 */
  #app tbody tr {
    background: rgba(255, 255, 255, .66);
    -webkit-backdrop-filter: blur(var(--g-blur-1));
    backdrop-filter: blur(var(--g-blur-1));
    border: var(--g-edge);
    border-radius: var(--g-r-md);
    box-shadow: var(--g-edge-lit-s), var(--g-sh-1);
    padding: 12px 14px;
    margin-bottom: 10px;
  }
  #app tbody td { border: none; }
  #app tfoot td { border-top: 1px solid var(--line2); }
  #app .main { padding: 14px 12px 40px; }

  /* 固定列宽表（用户管理）：窄屏转卡片后必须解除 min-width 与固定列宽，
     否则整表撑到 1100px，按钮溢出视口。原本只有 ≤860px 断点，这里把 1100px
     下限也一并放开，保证任何窄屏都不横向溢出。 */
  #app .users-table {
    min-width: 0;
    table-layout: auto;
    width: 100%;
  }
  #app .users-table th,
  #app .users-table td {
    width: auto !important;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
  #app .users-table td.col-actions {
    flex-wrap: wrap;
    gap: 4px;
  }
  #app .users-table td.col-actions .btn-sm { margin-right: 0; }
}

/* 移动端：关闭大面积模糊，保流畅（视觉仍保留半透明玻璃底） */
@media (max-width: 860px), (hover: none) and (max-width: 1180px) {
  #app .card, #app .kpi, #app .todo-item,
  #app .cust-unpaid, #app .audit-item, #app .att-item,
  #app tbody tr, #app .btn,
  #app .field input, #app select, #app textarea {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  #app .card, #app .kpi { background: rgba(255, 255, 255, .88); }
  #app .btn, #app .field input, #app select, #app textarea { background: rgba(255, 255, 255, .82); }
  #app tbody tr { background: rgba(255, 255, 255, .84); }
}

/* 打印：玻璃一律还原为纯白，避免打印机吃不到半透明 */
@media print {
  body { background: #fff !important; background-image: none !important; }
  #app .topbar, #app .sidebar, #app .page-actions, #app .pager,
  .toast-wrap, #app .tabs { display: none !important; }
  #app .card, #app .kpi, .modal-box, #app tbody tr, #app .btn {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    background: #fff !important;
    box-shadow: none !important;
    border-color: #C8CDD4 !important;
  }
  #app thead th { background: #F0F2F5 !important; position: static !important; }
}

/* ==========================================================================
   16. 无障碍降级
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  #app .view, #app .card, #app .kpi, #app .nav-group,
  .modal-box, .modal-scrim, .toast,
  #app .gs-panel, #app .notif-panel, #app .user-drop, .cs-panel-float,
  #app .btn-primary .btn-shine {
    animation: none;
  }
  .skeleton { animation: none; background: rgba(37, 40, 48, .06); }
}

/* 不支持 backdrop-filter 的浏览器：提高底色不透明度保住对比度 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  #app .topbar    { background: rgba(255, 255, 255, .98); }
  #app .sidebar   { background: rgba(255, 255, 255, .98); }
  #app .card,
  #app .kpi,
  #app .todo-item,
  #app .cust-unpaid,
  #app .audit-item,
  #app .att-item { background: rgba(255, 255, 255, .97); }
  #app .gs-panel,
  #app .notif-panel,
  #app .user-drop,
  .cs-panel-float { background: rgba(255, 255, 255, .99); }
  .modal-box { background: #fff; }
  #app .btn,
  #app .field input,
  #app select,
  #app textarea,
  #app .globalsearch input { background: rgba(255, 255, 255, .96); }
  .modal-scrim, #app .scrim { background: rgba(20, 37, 63, .55); }
}

/* 高对比度偏好：加粗描边、去除半透明 */
@media (prefers-contrast: more) {
  #app .card, #app .kpi, .modal-box, #app .sidebar, #app .topbar {
    border-color: var(--line2);
    background: #fff;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  #app .muted { color: var(--t2); }
}

/* ==========================================================================
   17. 水印（防截屏外泄）
   --------------------------------------------------------------------------
   定位原则：可溯源 ≠ 满屏显眼。
   之前步距 260×150 太密，且叠在玻璃卡片（半透明白）之上，
   整屏斜纹把表格数字糊掉，用户会当成"显示异常"。
   现在：步距放大 + 透明度压到刚好可辨 + 打印时才加重。
   ========================================================================== */
#hy-watermark { z-index: 9999; }
#hy-watermark .wm-tile {
  color: rgba(20, 37, 63, .032);
  font-size: 11.5px;
  line-height: 1.5;
  white-space: nowrap;
  transform: rotate(-22deg);
  font-weight: 400;
  letter-spacing: .02em;
}
@media print {
  #hy-watermark { position: fixed; }
  #hy-watermark .wm-tile { color: rgba(20, 37, 63, .14); }
}

/* ==========================================================================
   对比度复核（底 = 内容层玻璃 #FAFBFD / 浮层 #FCFDFE / 弹窗 #FEFEFF）
   --------------------------------------------------------------------------
   --t1  #252830  14.1 : 1.00   主要文字
   --t2  #636C79   5.09: 1.00   次要文字（白底 5.31:1，2026-10-13 落深保 4.5 余量）
   --t3  #66707E   4.81: 1.00   弱化文字（2026-10-13 由 n600 加深，≥4.5 达标）
   --line2 #8B929E 3.0 : 1.00   控件描边（非文本 UI ≥3:1 达标）
   ========================================================================== */
/* ==========================================================================
   结算页页签（二期申报工作台，2026-10-20）—— pill 分段控件
   ========================================================================== */
#app .seg-tabs {
  display: inline-flex; gap: 4px; padding: 3px;
  background: rgba(20, 37, 63, .055);
  border-radius: 9px; margin-bottom: 14px;
}
#app .seg-tabs button {
  border: 0; background: transparent; cursor: pointer;
  padding: 6px 16px; border-radius: 7px;
  font: inherit; font-size: 13px; color: var(--t2);
  transition: background var(--dur-2, .18s) var(--ease, ease), color var(--dur-2, .18s) var(--ease, ease);
}
#app .seg-tabs button:hover { color: var(--t1); }
#app .seg-tabs button.on {
  background: #FFF; color: var(--hy-blue, #0A4CA6);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(20, 37, 63, .12);
}

/* ==========================================================================
   结算申报台：操作列固定右侧 + 列宽自适应 + KPI 可点击（2026-10-21）
   --------------------------------------------------------------------------
   问题：申报清单列过多，操作列被挤到容器右侧，必须横向滚动到底才能看到。
   处理：① 隐藏「营业代码」列（税号改由客户名悬浮提示与档案弹窗查看）
        ② 操作列 sticky right —— 无论是否横向滚动始终可见
        ③ 长文本列（签约模式）限宽 + 省略号，避免把操作列推出容器
   ========================================================================== */
#app #stWbTable th.c-mid,
#app #stWbTable td.c-mid {
  max-width: 132px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#app #stWbTable th.c-act,
#app #stWbTable td.c-act {
  position: sticky;
  right: 0;
  z-index: 2;
  background: #FFF;
  box-shadow: -6px 0 10px -8px rgba(20, 37, 63, .45);
}
#app #stWbTable th.c-act {
  z-index: 3;                 /* 表头需盖住 body 的 sticky 列 */
  background: #F7F7F8;       /* 与 sticky 表头同色 */
}
#app #stWbTable tbody tr:hover td.c-act { background: var(--brand-soft); }
#app #stWbTable td.c-act { white-space: nowrap; }

/* KPI 卡片可点击：光标/悬停/选中态（点击后联动清单筛选或跳转批次页） */
#app .kpi.kpi-click { cursor: pointer; transition: border-color .18s var(--ease), box-shadow .18s var(--ease); }
#app .kpi.kpi-click:hover {
  border-color: rgba(10, 76, 166, .35);
  box-shadow: var(--g-edge-lit), var(--g-sh-1), 0 0 0 2px rgba(10, 76, 166, .10);
}
#app .kpi.kpi-click:focus-visible { outline: 2px solid var(--hy-blue, #0A4CA6); outline-offset: 2px; }
#app .kpi.kpi-click.on {
  border-color: rgba(10, 76, 166, .55);
  box-shadow: var(--g-edge-lit), var(--g-sh-1), inset 3px 0 0 var(--hy-blue, #0A4CA6);
}

/* 申报台：固定列宽布局（2026-10-21 补充）
   auto 布局下长客户名会把列撑开（实测溢出 614px），操作列被推出容器，
   必须横向滚动到底才能看到 → 改 table-layout:fixed + colgroup 百分比列宽，
   单元格内容省略号显示。窄屏（<900px）回退 auto，允许横向滚动。 */
#app #stWbTable { table-layout: fixed; width: 100%; }
#app #stWbTable th,
#app #stWbTable td { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app #stWbTable td.c-act,
#app #stWbTable th.c-act { overflow: visible; }
@media (max-width: 900px) {
  #app #stWbTable { table-layout: auto; }
  #app #stWbTable th, #app #stWbTable td { overflow: visible; }
}

/* KPI 卡紧凑化：8 张卡一行排齐（2026-10-21）
   默认 .kpi 内边距/字号偏大，8 张卡在 1130px 容器内会换行（7+1），
   观感上「第 8 张独占一行」。这里仅压缩可点击卡片的排版，不动数值语义。 */
#app .kpi.kpi-click { padding: var(--sp-3) var(--sp-4); }
#app .kpi.kpi-click .kpi-label { font-size: 11.5px; white-space: nowrap; }
#app .kpi.kpi-click .kpi-value { font-size: clamp(17px, 1.35vw, 22px); }

/* 申报台：实收 / 应收 / 服务费差额 三行合一列（2026-10-21）
   业务口径：实收服务费 = 客户实收电费账单里的服务费；
             应收服务费 = 按签约模式（套餐）算出的应得服务费；
             服务费差额 = 实收 − 应收，结账周期到期时按对应月份合计多退少补。 */
#app #stWbTable td.c-fee3 { line-height: 1.25; }
#app #stWbTable td.c-fee3 span { display: block; font-size: 12px; }
#app #stWbTable td.c-fee3 .f-real { color: var(--t2, #2B2B2B); font-weight: 600; }
#app #stWbTable td.c-fee3 .f-due { color: var(--t3, #5A5A5A); }
#app #stWbTable td.c-fee3 .f-diff.pos { color: #A61C26; }
#app #stWbTable td.c-fee3 .f-diff.neg { color: #0A4CA6; }
#app #stWbTable td.c-fee3 .f-inv { color: var(--hy-blue, #0A4CA6); font-weight: 600; }


/* 结算批次明细：17 列较宽，首列（客户）吸附，横向滚动时不丢上下文（2026-10-27） */
#app table.st-detail th:first-child,
#app table.st-detail td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface, #FFFFFF);
  box-shadow: 1px 0 0 rgba(0, 0, 0, 0.06);
}
#app table.st-detail thead th:first-child { z-index: 2; }

/* ==========================================================================
   宽表操作列保持可见（2026-11-10）
   --------------------------------------------------------------------------
   实测客户查索 / 结算明细 / 合同管理等表格列宽总和超过容器，
   操作按钮默认落在右侧视口之外（如「编辑」right=1415 > 容器 1397），
   用户看不到也点不到，只有横向滚动到底才能操作。
   让最后一列（含按钮的操作列）在横向滚动时吸附在容器右边缘。
   表头同步吸附，避免滚动后表头与操作列错位。
   ⚠️ mobile.css 在 ≤860px 表格转卡片模式下会解除 sticky。
   ========================================================================== */
#app .table-wrap table thead th:last-child,
#app .table-wrap table tbody td:last-child:has(button) {
  position: sticky;
  right: 0;
  z-index: 2;
}
#app .table-wrap table thead th:last-child { background: #F7F7F8; }
#app .table-wrap table tbody td:last-child:has(button) { background: #F9FBFD; }
#app .table-wrap table tbody tr.lv-red td:last-child:has(button) { background: #F9FBFD; }
/* ==========================================================================
   售电业务管理系统 · 移动端系统适配层 (mobile.css)
   --------------------------------------------------------------------------
   加载顺序：style.css → tokens.css → login.css → glass.css → **mobile.css**
   （服务端 BUNDLE_CSS 最后一位；同特异度时本文件优先，可覆盖 glass.css 的
     #app 作用域规则，无需到处写 !important）

   设计约束（本次需求）
     · 只调整【尺寸 / 间距 / 布局 / 触控】四类属性；
       配色、字体族、圆角语言、阴影材质、动效、业务内容一律不动 → 视觉风格不变。
     · 不改任何 JS / 接口 / 业务数据，不新增功能。
     · 所有布局规则限定 `@media screen`，不影响打印样式。
     · 桌面（>900px 且非粗指针）完全不受影响 → 零回归面。

   断点体系（按设备而非按像素拍脑袋）
     ≤1100px  窄桌面 / 横屏平板：双栏等宽化兜底
     ≤900px   手机与窄屏主区间：抽屉导航、表格转卡片、触控目标、安全区、动态视口
     ≤640px   主流手机：顶栏、卡片、表单、浮层紧凑化
     ≤400px   小屏 / 极窄屏（320/360）：进一步收紧
     横屏      orientation: landscape + max-height:500px：登录页双栏、顶栏压缩
     安全区    env(safe-area-inset-*) 独立变量，无安全区设备 fallback 0px
     动态视口  100dvh（带 100vh fallback），规避移动端地址栏收起导致的跳动
   ========================================================================== */

/* ==========================================================================
   0. 全局基线（所有尺寸；无安全区 / 非触控设备时完全等价于现状）
   ========================================================================== */
:root {
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --tap-min: 44px;   /* Apple HIG / WCAG 2.5.5 建议的最小触点 */
}

/* 阻止 iOS 横竖屏切换时自动放大字号（不改实际字号，只关掉系统自动缩放） */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* 媒体元素永远不撑破容器；不影响显式设置了 height 的 logo（只限宽） */
img, svg, canvas, video { max-width: 100%; }

/* 触控设备：去掉点击灰块、消除双击缩放 300ms 延迟、禁止横向橡皮筋 */
@media screen and (hover: none), screen and (pointer: coarse) {
  html, body { -webkit-tap-highlight-color: transparent; }
  button, a, input, select, textarea, label, [role="button"] {
    touch-action: manipulation;
  }
  html { overscroll-behavior-x: none; }
}

/* 动态视口：仅在浏览器支持时替换 100vh（旧浏览器继续用现有 100vh） */
@supports (height: 100dvh) {
  @media screen {
    #login { min-height: 100dvh; }
    .layout { min-height: calc(100dvh - var(--topbar-h)); }
    .sidebar { height: calc(100dvh - var(--topbar-h) - var(--safe-t)); }
    .modal-box.maximized { max-height: 100dvh; height: 100dvh; }
  }
}

/* ==========================================================================
   0.5 触控设备基线（**不限宽度**）
   --------------------------------------------------------------------------
   按「设备能力」而非「屏幕宽度」判断：横屏手机 932×430、平板 1024×768 都是
   触控设备，却都宽于 900px；若把触控规则只放在窄屏断点里，这两类设备会漏掉。
   固定 height 时垂直 padding 保持 0，遵守项目 CSS 盒模型约定。
   ========================================================================== */
@media screen and (hover: none), screen and (pointer: coarse) {
  #app .btn, .btn { height: var(--tap-min); min-height: var(--tap-min); padding: 0 14px; font-size: 14px; }
  #app .btn-sm, .btn-sm { height: 40px; min-height: 40px; padding: 0 10px; }
  #app .icon-btn, .icon-btn {
    width: var(--tap-min); height: var(--tap-min);
    min-width: var(--tap-min); min-height: var(--tap-min);
    /* 固定 height 时垂直 padding 必须为 0（项目盒模型约定），
       用 grid 居中图标即可，不需要靠上下内边距撑高度 */
    padding: 0;
    display: grid; place-items: center;
  }
  #app .nav-toggle, #app .notif-btn, #app .user-btn { min-width: var(--tap-min); }
  #app .user-btn { min-height: var(--tap-min); }
  #app .nav-item, .nav-item { min-height: var(--tap-min); padding: 10px 12px; }
  #app .tab, .tab { min-height: var(--tap-min); padding: 10px 14px; font-size: 14px; }
  #app .edit-tab, .edit-tab { min-height: var(--tap-min); padding: 10px 14px; }
  #app .field input, .field input,
  #app select, select,
  #app .input-wrap input, .input-wrap input {
    height: var(--tap-min); min-height: var(--tap-min);
    padding: 0 12px;                 /* 垂直 padding 必须为 0 */
    font-size: 16px;                 /* iOS Safari <16px 聚焦会自动放大整页 */
  }
  #app select, select { padding: 0 30px 0 10px; }
  #app textarea, textarea { font-size: 16px; line-height: 1.5; min-height: 88px; }
  #app .globalsearch input, .globalsearch input { font-size: 16px; }
  .pwd-eye { width: var(--tap-min); height: var(--tap-min); min-width: var(--tap-min); right: 2px; }
  .pwd-eye svg { width: 20px; height: 20px; }
  #app .radio input, .radio input, #app .chk input, .chk input {
    width: 20px; height: 20px; min-width: 20px;
  }
  #app .radio, .radio { min-height: var(--tap-min); padding: 4px 0; }
  .login-link, .reg-back, .link-btn, .pager button, .pager a {
    min-height: var(--tap-min); display: inline-flex; align-items: center;
  }
  #app .gs-item, .gs-item { min-height: var(--tap-min); }
  #app .notif-item, .notif-item { padding: 12px 14px; }
  #app .user-drop button, .user-drop button { min-height: var(--tap-min); padding: 10px 14px; }
  #app .modal-head .icon-btn, .modal-head .icon-btn { min-width: var(--tap-min); }
}

/* ==========================================================================
   1. ≤1100px：窄桌面 / 横屏平板兜底
   ========================================================================== */
@media screen and (max-width: 1100px) {
  /* 固定列宽表在 860~1100px 之间仍可能撑到 1100px；交给容器横向滚动承接，
     避免整页出现不可控的横向滚动条 */
  #app .users-table, .users-table { min-width: 0; table-layout: auto; }
  #app .users-table th, #app .users-table td { width: auto !important; }
}

/* ==========================================================================
   2. ≤900px：手机 / 窄屏主适配区
   ========================================================================== */
@media screen and (max-width: 900px) {

  /* ---- 2.1 安全区：刘海 / 灵动岛 / 底部 Home 条 ---- */
  #app .topbar {
    padding-left: calc(10px + var(--safe-l));
    padding-right: calc(10px + var(--safe-r));
  }
  #app .main {
    padding-bottom: calc(32px + var(--safe-b));
    padding-left: calc(12px + var(--safe-l));
    padding-right: calc(12px + var(--safe-r));
  }
  #app .sidebar {
    top: calc(var(--topbar-h) + var(--safe-t));
    height: calc(100dvh - var(--topbar-h) - var(--safe-t));
    padding-bottom: calc(24px + var(--safe-b));
  }
  #login .lgp { padding-top: calc(32px + var(--safe-t)); padding-bottom: calc(24px + var(--safe-b)); }
  #login .login-panelzone {
    padding-left: calc(24px + var(--safe-l));
    padding-right: calc(24px + var(--safe-r));
    padding-bottom: calc(32px + var(--safe-b));
  }
  .modal { padding: calc(20px + var(--safe-t)) calc(20px + var(--safe-r)) calc(20px + var(--safe-b)) calc(20px + var(--safe-l)); }
  .toast-wrap { top: calc(16px + var(--safe-t)); }

  /* ---- 2.2 触控目标已统一放在「0.5 触控设备基线」（按设备能力，不限宽度）----
     窄屏鼠标窗口也补一条眼睛按钮兜底，避免 26px 的小热点。 */
  .pwd-eye { width: var(--tap-min); height: var(--tap-min); min-width: var(--tap-min); }

  /* ---- 2.3 弹窗：min-width:400px 是 320/360/375 屏溢出的直接原因 ---- */
  .modal-box {
    min-width: 0;
    min-height: 0;
    width: 100%;
    max-width: calc(100vw - 40px);
    max-height: calc(100dvh - 40px);
    resize: none;                    /* 触屏无法拖拽 CSS resize 手柄，反而易误触 */
  }
  .modal-box.wide, .modal-box.xwide { max-width: calc(100vw - 40px); }
  .modal-body { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
  .modal-head { padding: 12px 14px; }
  .modal-head h3 { font-size: 15px; }
  .modal-foot { flex-wrap: wrap; }

  /* ---- 2.4 表格转卡片：解除 glass.css 施加的固定行高与横向截断 ----
     原 #app tbody td 有 height:var(--row-h)=52px + nowrap + ellipsis，
     在卡片模式下会让「每个字段独占 52px、长文本被省略号截断」，
     表现为卡片巨大、内容读不全 —— 这是移动端最明显的布局不协调。 */
  #app tbody td {
    height: auto;
    min-height: 0;
    padding: 7px 0;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
    overflow-wrap: anywhere;
    align-items: flex-start;
    line-height: 1.5;
  }
  #app tbody td::before { flex: 0 0 auto; max-width: 42%; line-height: 1.5; }
  #app tbody td.col-actions,
  #app tbody td:has(.btn),
  #app tbody td:has(.badge),
  #app tbody td.num {
    white-space: normal;
    flex-wrap: wrap;
    justify-content: flex-end;
  }
  #app tbody tr { padding: 12px 14px; margin-bottom: 12px; }
  /* ⚠️ 卡片模式必须解除「桌面列宽」。玻璃层给 .c-id/.c-name 设了 min-width，
     JS 的 fitColumnWidths() 还会给表/列加 inline min-width；卡片模式下这些列
     仍按桌面宽度撑开（实测 td 350px > 卡片内容区 324px），导致整表横向滚动、
     长客户名称右端被裁。这里统一归零，让每格严格贴合卡片宽度。
     !important 是为了压过 JS 写在元素上的 inline style。 */
  #app .table-wrap table,
  #app .table-wrap thead,
  #app .table-wrap tbody,
  #app .table-wrap tfoot,
  #app .table-wrap tbody tr {
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    box-sizing: border-box;
  }
  #app .table-wrap tbody td,
  #app .table-wrap tfoot td,
  #app .table-wrap td[class^="c-"] {
    width: auto !important;
    min-width: 0 !important;
    max-width: 100% !important;
    box-sizing: border-box;
  }
  /* 单元格内的链接 / 编号 / 徽标同样允许收缩与换行，长名称完整可读 */
  #app .table-wrap tbody td > * {
    min-width: 0;
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
    text-align: right;
  }
  #app .table-wrap tbody td::before { white-space: normal; }
  #app .users-table, .users-table { min-width: 0; table-layout: auto; width: 100%; }
  #app .users-table th, #app .users-table td { width: auto !important; }
  #app table.st-detail th:first-child,
  #app table.st-detail td:first-child { position: static; box-shadow: none; }

  /* ---- 2.5 浮层 / 卡片 / 栅格：不撑破视口 ---- */
  /* 面板 right:0 相对 .notif-wrap 定位，wrap 右侧距视口约 71px。
     宽度必须小于锚点右边界，否则左边界会跑到视口外（实测 320px 屏 x=-47、390px 屏 x=-7）。 */
  #app .notif-panel, .notif-panel {
    width: min(326px, calc(100vw - 80px));
    max-width: calc(100vw - 80px);
    max-height: 70dvh;
  }
  .user-drop, .gs-panel { max-width: calc(100vw - 24px); }
  .toast { max-width: calc(100vw - 32px); }
  #app .kpi, .kpi { min-width: 0; flex: 1 1 calc(50% - 8px); }
  #app .card, .card { max-width: 100%; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .info-grid, .two-col-tables, .form-grid { grid-template-columns: minmax(0, 1fr); }
  .mgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ov-two-col { grid-template-columns: minmax(0, 1fr) !important; display: grid !important; }
  .page-actions { width: 100%; }
  .page-actions .btn { min-width: 0; }

  /* ---- 2.6 横向滚动容器：惯性 + 不把橡皮筋传导到整页 ---- */
  .table-wrap, .tabs, .edit-tabs, .notif-list, .gs-panel, .modal-body {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }
  .table-wrap { max-width: 100%; }

  /* ---- 2.7 字号与间距：手机高密度屏可读性，不改字体族 ---- */
  #app .crumb, .crumb { font-size: 13px; }
  #app .page-sub, .page-sub { font-size: 13px; line-height: 1.6; }
  #app .page-title, .page-title { font-size: 20px; }
  #app .card-t, .card-t { font-size: 15px; }
  #app .card-t .hint, .card-t .hint { font-size: 12.5px; }
  #app table, table { font-size: 13.5px; }
  #app thead th, thead th { font-size: 13px; }
  #app .field > span, .field > span { font-size: 13px; }
  #app .card, .card { padding: 14px; margin-bottom: 12px; }
  #app .sec-body, .sec-body { padding: 12px 14px; }
  #app .sec-head, .sec-head { padding: 12px 14px; }

/* ---- 2.9 修复「白字白底」：玻璃层降级规则把 #app .btn 全设成近白底，
       连带覆盖了 .btn-primary 的品牌蓝渐变，白字在白底上完全不可读
       （截图里「新增客户 / 查询」两个按钮看起来是空框，就是这个原因）。
       这里把主/危险按钮的**原有**渐变原样还原 —— 不是改视觉风格，
       只是撤销降级规则的误伤；桌面（未触发降级）本就是这个样子。 ---- */
  #app .btn-primary, .btn-primary {
    background: linear-gradient(135deg, #0071A8 0%, #0E8CC4 100%);
    border-color: rgba(0, 113, 168, .5);
    color: #fff;
  }
  #app .btn-danger, .btn-danger {
    background: linear-gradient(140deg, #C62828 0%, #D84343 100%);
    border-color: rgba(198, 40, 40, .5);
    color: #fff;
  }

  /* ---- 2.10 长文本：编号、金额、备注不撑破卡片 ---- */
  .mono, .info-val, .audit-detail, .att-item, .notif-body, .toast {
    overflow-wrap: anywhere; word-break: break-word;
  }
}

/* 降级规则在「触控 + ≤1180px」也会触发，上面 ≤900px 覆盖不到 900~1180px
   的横屏手机 / 平板，这里补一份同规则，保证所有触控尺寸都不会白字白底。 */
@media screen and (hover: none) and (min-width: 901px) and (max-width: 1180px),
       screen and (pointer: coarse) and (min-width: 901px) and (max-width: 1180px) {
  #app .btn-primary {
    background: linear-gradient(135deg, #0071A8 0%, #0E8CC4 100%);
    border-color: rgba(0, 113, 168, .5);
    color: #fff;
  }
  #app .btn-danger {
    background: linear-gradient(140deg, #C62828 0%, #D84343 100%);
    border-color: rgba(198, 40, 40, .5);
    color: #fff;
  }
}

/* ==========================================================================
   3. ≤640px：主流手机紧凑化
   ========================================================================== */
@media screen and (max-width: 640px) {
  #app .topbar { gap: 6px; padding-left: calc(8px + var(--safe-l)); padding-right: calc(8px + var(--safe-r)); }
  #app .brand { gap: 6px; }
  #app .brand img { height: 26px; }
  #app .globalsearch { min-width: 0; flex: 1 1 auto; }
  #app .globalsearch input { padding-left: 30px; }
  #app .topbar-right { gap: 2px; flex: none; }
  #app .user-btn span:not(.avatar) { display: none; }   /* 只留头像，避免顶栏挤爆 */
  #app .main { padding-top: 12px; }
  #app .crumb { margin-bottom: 10px; }
  #app .page-head { gap: 10px; margin-bottom: 12px; }
  #app .page-actions .btn { flex: 1 1 auto; }

  /* 登录页 */
  #login .lgp { padding: 20px var(--sp-4) 16px; min-height: 0; }
  #login .lgp-title { font-size: 26px; }
  #login .lgp-slogan { font-size: 12.5px; }
  #login .lgp-metrics { gap: 16px; padding-top: 8px; }
  #login .lgp-metric .v { font-size: 19px; }
  #login .login-panelzone { padding: 20px var(--sp-4) calc(24px + var(--safe-b)); }
  .login-card { padding: 22px 18px 18px; }
  .lgz-title { font-size: 22px; }
  .login-slogan { margin-top: 12px; }

  /* 卡片 / 表格 */
  #app tbody tr { padding: 10px 12px; }
  #app .card, .card { padding: 12px; }
  #app .kpi, .kpi { padding: 12px; }
  #app .kpi .kpi-value, .kpi .kpi-value { font-size: clamp(18px, 5.5vw, 22px); }
  .modal { padding: calc(12px + var(--safe-t)) calc(12px + var(--safe-r)) calc(12px + var(--safe-b)) calc(12px + var(--safe-l)); }
  .modal-box { max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px); }
  .modal-head, .modal-body, .modal-foot { padding-left: 14px; padding-right: 14px; }
}

/* ==========================================================================
   4. ≤400px：小屏 / 极窄屏（iPhone SE 320、Android 360、375/390）
   --------------------------------------------------------------------------
   顶栏在 320px 宽度下要同时容纳 汉堡 / logo / 搜索 / 通知 / 头像，
   搜索框会被挤到不足 60px 而不可用。改为「两行顶栏」：第一行放身份与操作，
   第二行给搜索框整行宽度；同步修正所有依赖 --topbar-h 的容器高度。
   ========================================================================== */
@media screen and (max-width: 400px) {
  :root { --topbar-h: 96px; }
  #app .topbar {
    height: 96px;
    flex-wrap: wrap;
    align-content: center;
    row-gap: 0;
    padding-top: 4px;
    padding-bottom: 4px;
  }
  #app .globalsearch {
    order: 10;
    flex: 1 1 100%;
    min-width: 0;
    margin: 0;
  }
  #app .globalsearch input,
  #app .gs-panel { height: 38px; min-height: 38px; }
  #app .layout { min-height: calc(100dvh - 96px); }
  #app .sidebar {
    top: calc(96px + var(--safe-t));
    height: calc(100dvh - 96px - var(--safe-t));
  }
  #app .main { padding-left: calc(10px + var(--safe-l)); padding-right: calc(10px + var(--safe-r)); }
  #app .brand img { height: 24px; }
  #app .globalsearch input::placeholder { color: var(--t3); }   /* 两行后占位文字可正常显示 */
  #app .globalsearch input { font-size: 16px; }
  #app .kpi, .kpi { flex: 1 1 100%; }
  #app .page-title, .page-title { font-size: 19px; }
  .login-card { padding: 18px 14px 14px; }
  .lgz-title { font-size: 20px; }
  .login-panelzone { padding-left: calc(16px + var(--safe-l)); padding-right: calc(16px + var(--safe-r)); }
  .lgp { padding-left: 16px; padding-right: 16px; }
  #app tbody td::before { max-width: 46%; font-size: 12px; }
  #app .card, .card { padding: 10px; }
  .btn { padding: 0 10px; }
}

/* 真正的小屏（≤370px：iPhone SE 320 / Android 360）：多列表单折叠为单列。
   320px 时两列各仅 ~140px，「关键词（名称 / ID / 供电局 / 责任人）」会被挤成
   三行、下拉框被压窄；单列后标签不折行、控件全宽、触控面积充足。
   ⚠️ 断点刻意只覆盖 ≤370px：375/390/430 这些主流手机两列仍有足够空间，
      改成单列反而会把筛选区拉到 600px+，首屏完全看不到数据列表（实测过）。
   横屏手机宽度 >480px，保持两列以利用横向空间。 */
@media screen and (max-width: 370px) and (orientation: portrait) {
  #app .form-row, .form-row, .modal .form-row {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  #app .form-row .field, .form-row .field,
  #app .form-row .field.grow, .form-row .field.grow,
  .modal .form-row .field {
    width: 100%;
    min-width: 0;
    flex: none;
    margin-bottom: 0;
  }
  #app .form-grid, .form-grid,
  #app .mgrid, .mgrid,
  #app .info-grid, .info-grid { grid-template-columns: minmax(0, 1fr); }
  /* 桌面两列表单里 select 被刻意收窄（width:auto / min-width:148 / max-width:260），
     单列后必须放开，否则会出现「关键词框全宽、三个下拉框只有半宽」的不协调。 */
  #app .form-row select, .form-row select, .modal .form-row select {
    width: 100%;
    min-width: 0;
    max-width: 100%;
  }
  #app .form-row > .field:not(.grow):has(select),
  .form-row > .field:not(.grow):has(select) { min-width: 0; }
}

/* ==========================================================================
   5. 横屏矮高度（手机横屏 320~430px 高）
   --------------------------------------------------------------------------
   问题：≤1080px 时登录页被改成单列，品牌区 min-height:300px 把整个登录卡片
   推到 y≈415px，而横屏视口只有 320~430px 高 → 用户必须滚动才能看到登录框。
   对策：横屏恢复左右双栏并整体压缩垂直节奏，**保留品牌区全部内容**（不隐藏指标、
   不删标语），只缩小字号与间距，让两栏都落在首屏内。
   ========================================================================== */
@media screen and (orientation: landscape) and (max-height: 500px) {

  /* 顶栏在矮屏占比过大 → 压到 48px，并同步所有依赖该变量的容器。
     ⚠️ 显式复位为单行：若设备同时满足「≤400px 两行顶栏」与「横屏矮屏」，
        必须让横屏规则赢，否则 48px 高度装不下两行内容会溢出。 */
  :root { --topbar-h: 48px; }
  #app .topbar {
    height: 48px;
    flex-wrap: nowrap;
    padding-top: 0;
    padding-bottom: 0;
    gap: 6px;
  }
  #app .globalsearch { order: 0; flex: 0 1 auto; min-width: 0; margin: 0; }
  #app .globalsearch input { height: 36px; min-height: 36px; }
  #app .layout { min-height: calc(100dvh - 48px); }
  #app .sidebar {
    top: calc(48px + var(--safe-t));
    height: calc(100dvh - 48px - var(--safe-t));
  }
  #app .main { padding-top: 10px; padding-bottom: calc(20px + var(--safe-b)); }

  /* 登录页：双栏 */
  #login {
    grid-template-columns: minmax(0, 42%) minmax(0, 58%);
    grid-template-rows: minmax(0, 1fr);
    min-height: 100dvh;
    height: 100dvh;
    overflow-y: auto;
  }
  #login .lgp {
    min-height: 0;
    height: 100%;
    padding: calc(10px + var(--safe-t)) 18px calc(10px + var(--safe-b));
    gap: 6px;
    align-content: stretch;
    overflow: hidden;
  }
  #login .lgp-top { gap: 8px; }
  #login .lgp-top img { height: 22px; }
  #login .lgp-clock { font-size: 11px; }
  #login .lgp-inner { max-width: none; align-self: center; }
  #login .lgp-tag { font-size: 11px; padding: 3px 9px; }
  #login .lgp-title { font-size: clamp(19px, 2.9vw, 26px); margin-top: 6px; line-height: 1.25; }
  #login .lgp-slogan { font-size: 11px; line-height: 1.45; margin-top: 5px; }
  #login .lgp-metrics { gap: 14px; padding-top: 6px; }
  #login .lgp-metric .k { font-size: 10px; }
  #login .lgp-metric .v { font-size: 16px; }
  #login .lgp-metric .u { font-size: 10px; }
  #login .login-panelzone {
    padding: calc(10px + var(--safe-t)) calc(18px + var(--safe-r)) calc(10px + var(--safe-b)) 18px;
    justify-content: center;
    overflow-y: auto;
  }
  #login .login-card { padding: 12px 16px; max-width: 420px; }
  #login .lgz-head { margin-bottom: 8px; }
  #login .lgz-title { font-size: 19px; }
  #login .lgz-sub { font-size: 11.5px; }
  #login .field { margin-bottom: 7px; }
  #login .field > span { font-size: 12px; margin-bottom: 3px; }
  #login .input-wrap input { height: 40px; min-height: 40px; font-size: 16px; }
  #login .login-extra { margin-top: 8px; }
  #login .login-slogan { margin-top: 8px; }

  /* 弹窗在矮屏要留出上下余量 */
  .modal-box { max-height: calc(100dvh - 20px); }
  .modal-head { padding: 8px 14px; }
  .modal-foot { padding-top: 8px; padding-bottom: 8px; }
  .toast-wrap { top: calc(8px + var(--safe-t)); }
}

/* ==========================================================================
   6. 高 DPR 视网膜屏：仅做「不破坏视觉」的清晰度处理
      不改边框颜色/粗细，避免改变整体视觉风格；只保证 1px 边界不被二次栅格化糊掉
   ========================================================================== */
@media screen and (min-resolution: 2dppx) and (max-width: 900px) {
  #app .btn, #app .card, #app .field input, #app select, #app textarea {
    -webkit-font-smoothing: antialiased;
  }
}

/* ==========================================================================
   7. 无障碍偏好：减少动效时，移动端也不做任何位移动画（沿用系统既有降级）
   ========================================================================== */
@media screen and (prefers-reduced-motion: reduce) and (max-width: 900px) {
  #app .sidebar, #app .view, #app .card, .toast, .modal-box { transition: none; }
}


/* ≤860px 表格已转为卡片，sticky 操作列不再适用（且会与 display:block 冲突） */
@media screen and (max-width: 860px) {
  #app .table-wrap table thead th:last-child,
  #app .table-wrap table tbody td:last-child:has(button) { position: static; background: transparent; }
}

/* ==========================================================================
   8. 省份 / 业务片区切换器（2026-11-10）
   窄屏下隐藏角色文字、只留当前区域；触控目标 ≥44px；面板不溢出视口。
   注意：style.css 在 ≤860px 会隐藏旧的 .role-chip，这里用的是新的 .region-chip，
   必须显式保留可见，否则切换器在手机上会消失。
   ========================================================================== */
@media screen and (max-width: 860px) {
  #app .region-chip {
    display: inline-flex !important;
    min-height: var(--tap-min, 44px); padding: 0 12px;
    box-sizing: border-box;
  }
  #app .region-role { display: none; }
  #app .region-cur::before { content: ""; margin-right: 0; }
  #app .region-panel { width: min(272px, calc(100vw - 24px)); right: 0; }
  #app .region-opt { min-height: var(--tap-min, 44px); }
}
@media screen and (max-width: 400px) {
  #app .region-panel { right: -8px; width: min(272px, calc(100vw - 32px)); }
}
