/* ============================================================
   医涵客户项目管理系统 · 原型样式
   设计方向：工业实用主义（instrument panel）—— 信息密度优先，
   等宽数字对齐，暖白底 + 深青绿主色，语义色仅用于状态。
   字体走系统栈：本系统将部署在内网/自建环境，不依赖外网 CDN 字体。
   ============================================================ */

:root{
  /* 表面 */
  --bg:#f4f3ef;
  --surface:#ffffff;
  --surface-2:#faf9f6;
  --surface-3:#f0efea;

  /* 文字
     ⚠ --text-3 第四批评审 C1 改：原 #93918a 在白底只有 3.15:1、在 --bg 底只有 2.84:1，
     低于 WCAG AA 要求的 4.5:1。它承载全部 12~13px 次级信息（分组标题、脚注、空状态、
     字段说明、图例…），投屏时更吃亏。改为 #6f6d66 → 白底 4.62:1 / --bg 底 4.17:1。 */
  --text:#1c1b18;
  --text-2:#63625b;
  --text-3:#6f6d66;

  /* 线条 */
  --line:rgba(28,27,24,.10);
  --line-2:rgba(28,27,24,.18);
  --line-3:rgba(28,27,24,.28);

  /* 主色（深青绿） */
  --brand:#0f6e56;
  --brand-ink:#0b5142;
  --brand-soft:#e3f3ec;
  --brand-line:rgba(15,110,86,.28);

  /* 语义色 */
  --danger:#b3312a;      --danger-soft:#fbeceb;   --danger-line:rgba(179,49,42,.26);
  --warn:#93610f;        --warn-soft:#faf0d9;     --warn-line:rgba(147,97,15,.26);
  --ok:#3d6b1c;          --ok-soft:#e9f2dc;       --ok-line:rgba(61,107,28,.26);
  --info:#2b5f8f;        --info-soft:#e8f0f7;     --info-line:rgba(43,95,143,.24);

  /* 语义色（文字用，便于组件统一引用） */
  --danger-ink:var(--danger);
  --warn-ink:var(--warn);
  --ok-ink:var(--ok);
  --info-ink:var(--info);

  /* 圆角 / 间距 */
  --r-xs:3px; --r-sm:5px; --r-md:8px; --r-lg:12px;
  /* 第四批 C16 补：气泡/胶囊两档圆角。此前 AI 气泡、角色徽标等处直接写 8/10/14px 裸值，
     绕开了圆角阶梯；补进 token 后改圆角只需动一处。 */
  --r-pill:10px; --r-bubble:14px;
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px;
  /* 控件内边距（C15）：6px 10px 这组在表单/表格/下拉里出现十余次，过去各写各的 */
  --pad-control:6px 10px;
  --pad-control-sm:4px 8px;

  /* 地图热力（C17）：原先 style.css 与 app.js 图例**各写一遍**十六进制值，改色必漏一处 */
  --heat-2:#cfe8dd; --heat-3:#a8d6c5; --heat-4:#7cbfaa;
  /* 看板 hero 金色（同样两处重复） */
  --gold:#f2c870;

  /* 阴影（3 级） */
  --sh1:0 1px 2px rgba(28,27,24,.06);
  --sh2:0 4px 14px rgba(28,27,24,.08);
  --sh3:0 12px 40px rgba(28,27,24,.14);

  /* 动效 */
  --ease:cubic-bezier(.16,1,.3,1);
  --fast:140ms; --base:280ms;

/* ========== 字号阶梯（2026-10-12 第三批 P2-14）==========
     收敛前 25 个取值（含 10.5/11.5/12.5/13.5/14.5 五个半像素档）；
     合档规则：**只吞掉差值 ≤0.5px 的半像素档**，差值 ≥1px 的档位
     （9/9.5 地图标签、17/19/22/26 看板大数）一律保留 ——
     1px 以上会改变视觉重量，地图标签还可能引起重叠。
     要再调整任何一档，改下面一行即可。 */
  --fs-9:    9px;    /* 地图刻度（随 SVG 缩放，不参与合档） */
  --fs-95:   9.5px;  /* 地图标签（同上） */
  --fs-10:   10px;   /* 旧 10px */
  --fs-11:   11px;   /* 旧 10.5 / 11px（合档 +0.5） */
  --fs-12:   12px;   /* 旧 11.5 / 12px（合档 +0.5） */
  --fs-13:   13px;   /* 旧 12.5 / 13px（合档 +0.5） */
  --fs-14:   14px;   /* 旧 13.5 / 14px（合档 +0.5） */
  --fs-15:   15px;   /* 旧 14.5 / 15px（合档 +0.5） */
  --fs-16:   16px;   /* 表单控件 / 手机上输入框硬要求 */
  --fs-17:   17px;   /* 看板指标值（窄屏） */
  --fs-18:   18px;   /* AI 面板关闭按钮 */
  --fs-19:   19px;   /* 看板指标值 */
  --fs-20:   20px;   /* 登录页系统名 / KPI（窄屏） */
  --fs-22:   22px;   /* 看板内联指标值 */
  --fs-24:   24px;   /* KPI 数值 */
  --fs-26:   26px;   /* 看板顶部大数 */
  --fs-28:   28px;   /* 看板大数（窄屏）/ Hero 标题（窄屏） */
  --fs-34:   34px;   /* Hero 标题 / 看板大数 */
  --fs-40:   40px;   /* Hero 金额（窄屏） */
  --fs-56:   56px;   /* Hero 金额 */

  /* ========== 层级分层（2026-10-12 第三批 P2-15）==========
     11 个裸数字 → 11 个具名层级。**数值刻意保持不变、不合并**：
     z-index 的合并正是 P0-4（.tip 80 > 弹窗 60）那类 bug 的成因，
     这里的价值在于「顺序自解释 + 后续加层有据可依」。
     自下而上：base < board < float < backdrop < nav < tip < modal < panel < drawer < pop < toast */
  --z-base:     1;   /* 表格吸顶表头、看板层内元素 */
  --z-board:    3;   /* 看板 KPI 悬浮卡 */
  --z-float:    6;   /* 页内浮块：选中指标、添加账号按钮 */
  --z-backdrop: 15;  /* 抽屉导航遮罩 */
  --z-nav:      20;  /* 侧栏（抽屉态） */
  --z-tip:      50;  /* 悬浮明细卡（必须在弹窗之下） */
  --z-modal:    60;  /* 弹窗、AI 悬浮球 */
  --z-panel:    59;  /* AI 助手面板（C20：原为 61，高于 --z-modal 60 →
                        先开 AI 面板再开表单时，面板浮在遮罩之上不被压暗） */
  --z-drawer:   62;  /* 右侧抽屉 */
  --z-pop:      90;  /* 弹窗内浮层：人员选择、日期选择 */
  --z-toast:    99;  /* 全局提示（最高） */

  /* ========== 间距（2026-10-12 第三批 P2-16）==========
     原 --s1..--s6 = 4/8/12/16/24/32。写死的 5/7/9px 是旧代码里的
     「表单控件纵向内边距」(7/9)、「标签内衬」(5)，与阶梯差 1px，
     已归入 --s1/--s2；2px 是唯一的贴边量级，单列 --s0。 */
  --s0:2px;    /* 光学贴合（--s1 以下的唯一档位） */

  --font:"Microsoft YaHei UI","Microsoft YaHei","PingFang SC","Hiragino Sans GB","Source Han Sans SC","Noto Sans CJK SC",sans-serif;
  --mono:"Cascadia Mono","Consolas","SF Mono","Menlo","DejaVu Sans Mono",monospace;
  --cnt:#2b5f8f;          /* 数量/项目：冷蓝 */
  --cnt-soft:#eaf1f8;
  --cnt-line:rgba(43,95,143,.30);
  --amt:#8a5a12;          /* 金额：暖金 */
  --amt-soft:#fbf1dc;
  --amt-line:rgba(138,90,18,.30);
}

*{box-sizing:border-box;}
html,body{height:100%;}
body{
  margin:0;
  font-family:var(--font);
  font-size:var(--fs-14);
  line-height:1.65;
  color:var(--text);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0;font-weight:500;letter-spacing:-.01em;}
button{font-family:inherit;font-size:inherit;cursor:pointer;}
:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:var(--r-xs);}
[hidden]{display:none !important;}

/* 数字：统一等宽 + 表格数字对齐 */
.num,.mono{
  font-family:var(--mono);
  font-variant-numeric:tabular-nums;
  letter-spacing:-.01em;
}

/* ================= 登录 ================= */
.login{
  /* C7（2026-10-12）：矮视口（手机横屏、分屏）下登录面板比视口高，
     原来没有 overflow → 顶部被切掉且无法回滚。加 overflow:auto 让 grid 居中也能滚。 */
  position:fixed; inset:0; display:grid; place-items:center; overflow:auto;
  background:
    linear-gradient(180deg, rgba(15,110,86,.05), rgba(15,110,86,0) 42%),
    repeating-linear-gradient(90deg, transparent 0 79px, rgba(28,27,24,.035) 79px 80px),
    repeating-linear-gradient(0deg, transparent 0 79px, rgba(28,27,24,.035) 79px 80px),
    var(--bg);
  padding:var(--s5);
}
.login__panel{
  width:min(560px,100%);
  background:var(--surface);
  border:1px solid var(--line-2);
  border-radius:var(--r-lg);
  box-shadow:var(--sh3);
  padding:var(--s6);
  animation:rise var(--base) var(--ease) both;
}
@keyframes rise{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}
.login__brand{display:flex;flex-direction:column;align-items:center;gap:var(--s3);text-align:center;padding-bottom:var(--s5);border-bottom:1px solid var(--line);}
.login__logo{height:110px;width:auto;flex:none;}
.login__brand h1{font-size:var(--fs-20);font-weight:600;letter-spacing:.5px;}
.login__brand p{margin:var(--s0) 0 0;color:var(--text-2);font-size:var(--fs-13);}

/* —— 登录页副标题「AI赋能·智慧管理」：字号小于系统名，带入场动画 + 常驻扫光 —— */
/* 选择器带 p 标签抬高优先级，压过下面的 .login__brand p（否则字号/间距会被它吃掉） */
.login__brand p.login__slogan{
  margin:-4px 0 0;                 /* 抵消 .login__brand 的 12px 行间距，紧贴系统名 */
  font-size:var(--fs-13);                  /* 系统名 20px，这里明显小一号 */
  font-weight:500;
  letter-spacing:.22em;
  color:var(--brand);
}
.login__slogan > span{
  display:inline-block;
  padding:var(--s0) var(--s0) var(--s0) 0;
  /* ① 入场一次（淡入 + 上浮 + 字距收拢）  ② 之后光带持续扫过 */
  animation:
    slogan-in 720ms var(--ease) 240ms both,
    slogan-sheen 3.6s linear 1.2s infinite;
}
/* 渐变文字（扫光）只在浏览器支持 background-clip:text 时启用 ——
   否则 color:transparent 会让文字直接隐形，这是最保险的写法。 */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .login__slogan > span{
    background:linear-gradient(100deg,
      var(--brand-ink) 0%, var(--brand) 28%, #33a884 44%, var(--brand) 60%, var(--brand-ink) 100%);
    background-size:280% 100%;
    -webkit-background-clip:text;
    background-clip:text;
    color:transparent;
    -webkit-text-fill-color:transparent;
  }
}
@keyframes slogan-in{
  from{opacity:0;transform:translateY(7px);letter-spacing:.55em;filter:blur(3px);}
  to{opacity:1;transform:translateY(0);letter-spacing:.22em;filter:blur(0);}
}
@keyframes slogan-sheen{
  from{background-position:130% 0;}
  to{background-position:-130% 0;}
}
.brand__cn{margin:var(--s1) 0 0;color:var(--text-2);font-size:var(--fs-13);font-weight:500;letter-spacing:.3px;}
.brand__en{margin:3px 0 0;color:var(--text-3);font-size:var(--fs-11);font-weight:500;letter-spacing:.9px;text-transform:uppercase;}
.login__section{padding:var(--s5) 0 var(--s4);}
.login__label{display:block;font-size:var(--fs-12);color:var(--text-3);margin-bottom:var(--s3);}
.role-list{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s2);}
.role{
  text-align:left;padding:var(--s3) var(--s3);
  background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-md);transition:border-color var(--fast),background var(--fast),transform var(--fast);
}
.role:hover{border-color:var(--brand-line);background:var(--brand-soft);transform:translateY(-1px);}
.role strong{display:block;font-size:var(--fs-14);font-weight:500;}
.role span{display:block;font-size:var(--fs-12);color:var(--text-2);margin-top:1px;}
.login__foot{display:flex;flex-direction:column;align-items:center;gap:var(--s1);text-align:center;font-size:var(--fs-12);color:var(--text-3);padding-top:var(--s4);margin-top:var(--s1);border-top:1px solid var(--line);}
.foot__brand{display:flex;flex-direction:column;gap:var(--s0);}
.foot__brand .brand__cn{margin-top:0;}
.foot__meta{display:flex;align-items:center;gap:var(--s2);margin-top:var(--s0);}
.foot__meta #loginVer{color:var(--text-2);font-weight:500;white-space:nowrap;}

/* —— 账号密码登录表单 —— */
.login__panel--narrow{width:min(420px,100%);}
.login-form{margin-top:var(--s2);}
.login-form .field input{padding:var(--s2) var(--s3);font-size:var(--fs-14);}
.login-form__error{
  margin:var(--s2) 0 var(--s3);padding:var(--s2) 10px;font-size:var(--fs-13);
  color:var(--danger-ink);background:var(--danger-soft);
  border:1px solid var(--danger-line);border-radius:var(--r-sm);
}
.login__hint{margin-top:var(--s3);font-size:var(--fs-12);color:var(--text-3);line-height:1.6;}
.btn--block{display:block;width:100%;margin-top:var(--s2);}
/* C18：此处原来还有一条 .btn:disabled{opacity:.55}，与 345 行的 .45 同权重且被永久覆盖，已删除。 */

/* —— 用户管理小部件 —— */
.pill{
  display:inline-block;padding:1px var(--s2);font-size:var(--fs-12);border-radius:999px;
  border:1px solid var(--line-2);background:var(--surface-3);color:var(--text-2);
}
.pill--ok{color:var(--ok-ink);background:var(--ok-soft);border-color:var(--ok-line);}
.pill--danger{color:var(--danger-ink);background:var(--danger-soft);border-color:var(--danger-line);}
.form-body{display:grid;gap:var(--s3);padding:var(--s4) 0 0;}
.form-body .field{margin-bottom:0;}
.row-actions{display:flex;gap:6px;}
.col-name{min-width:104px;}
.hint-line{margin-top:var(--s3);font-size:var(--fs-12);color:var(--text-3);}
.cred{
  font-family:var(--mono);font-size:var(--fs-13);letter-spacing:.02em;
  background:var(--brand-soft);border:1px dashed var(--brand-line);
  border-radius:var(--r-sm);padding:var(--s0) var(--s2);color:var(--brand-ink);
}

/* ================= 应用壳 ================= */
.app{display:grid;grid-template-columns:216px 1fr;height:100vh;overflow:hidden;} /* 2026-10-12 206→216：给系统名留足一行宽（P0-3） */

.sidebar{
  background:var(--surface);
  border-right:1px solid var(--line-2);
  display:flex;flex-direction:column;min-height:0;
}
.sidebar__head{display:flex;gap:10px;align-items:center;padding:var(--s4) var(--s4) var(--s3);border-bottom:1px solid var(--line);}
.sidebar__logo{height:36px;width:auto;flex:none;}
.sidebar__title strong{display:block;font-size:var(--fs-14);font-weight:500;line-height:1.3;white-space:nowrap;} /* P0-3：系统名不许出现「台」字孤行 */
.sidebar__title span{display:block;font-family:var(--mono);font-size:var(--fs-10);color:var(--text-3);letter-spacing:.06em;}
.nav{flex:1;overflow-y:auto;padding:var(--s3) var(--s2);}
/* 左侧栏目分组：段标题加黑 + 分隔线，与下属菜单形成层级 */
.nav__group{
  font-size:var(--fs-12);font-weight:700;color:var(--text);
  padding:var(--s3) var(--s3) var(--s1);letter-spacing:.02em;
}
/* 分隔线用 border 不加 margin-top —— 分组标题间距收紧，避免侧栏高度溢出出现滚动条 */
.nav__group:not(:first-child){border-top:1px solid var(--line);}
.nav__item{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);
  width:100%;
  /* 菜单项相对段标题向右缩进约两个汉字宽，形成清晰的层级关系 */
  padding:var(--s2) var(--s3) var(--s2) calc(var(--s3) + var(--s5) + var(--s1));
  margin-bottom:1px;
  background:none;border:0;border-radius:var(--r-sm);
  color:var(--text-2);text-align:left;font-size:var(--fs-13);
  transition:background var(--fast),color var(--fast);
}
.nav__item:hover{background:var(--surface-3);color:var(--text);}
.nav__item.is-active{background:var(--brand-soft);color:var(--brand-ink);font-weight:500;}
.nav__badge{
  font-family:var(--mono);font-size:var(--fs-11);min-width:20px;text-align:center;
  padding:0 var(--s1);border-radius:20px;background:var(--danger-soft);color:var(--danger);
}
.sidebar__foot{padding:var(--s3);border-top:1px solid var(--line);display:flex;gap:var(--s2);align-items:center;justify-content:space-between;}
/* P0-2（2026-10-12 评审）：「改密/退出」曾被挤到竖排 —— 按钮不许换行，
   空间不够时缩的是用户名（省略号），不再是按钮 */
.sidebar__foot .btn{white-space:nowrap;flex:none;padding:var(--s1) var(--s2);}
.sidebar__user{min-width:0;flex:1 1 auto;}
.sidebar__user span{display:block;font-size:var(--fs-13);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.sidebar__user em{display:block;font-style:normal;font-size:var(--fs-11);color:var(--text-3);}

.main{display:flex;flex-direction:column;min-width:0;min-height:0;}
.topbar{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4);
  padding:var(--s3) var(--s5);background:var(--surface);
  border-bottom:1px solid var(--line-2);flex:none;
}
.topbar h2{font-size:var(--fs-16);}
.topbar__sub{font-size:var(--fs-12);color:var(--text-3);}
.topbar__left{display:flex;align-items:baseline;gap:var(--s3);min-width:0;}
.topbar__right{display:flex;align-items:center;gap:var(--s2);flex:none;}
.view{flex:1;overflow-y:auto;padding:var(--s5);min-height:0;position:relative;}

/* ================= 通用元件 ================= */
.tag{
  display:inline-flex;align-items:center;gap:var(--s1);
  font-size:var(--fs-12);line-height:1.7;padding:1px var(--s2);border-radius:20px;
  border:1px solid var(--line-2);color:var(--text-2);background:var(--surface-2);
  white-space:nowrap;
}
.tag::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;flex:none;}
.tag--plain::before{display:none;}
.tag--plain{font-family:var(--mono);}
.tag--danger{color:var(--danger);background:var(--danger-soft);border-color:var(--danger-line);}
.tag--warn{color:var(--warn);background:var(--warn-soft);border-color:var(--warn-line);}
.tag--ok{color:var(--ok);background:var(--ok-soft);border-color:var(--ok-line);}
.tag--info{color:var(--info);background:var(--info-soft);border-color:var(--info-line);}
.tag--brand{color:var(--brand-ink);background:var(--brand-soft);border-color:var(--brand-line);}
/* 系统自动生成任务的「系统」小标记（2026-10-16），比常规标签更紧凑 */
.tag--xs{font-size:var(--fs-11,11px);line-height:1.5;padding:0 var(--s2);border-radius:12px;font-weight:600;}

/* 时间标签转为可点击按钮 */
.topbar__right{position:relative;}
.tag--btn{cursor:pointer;font-family:inherit;transition:background .15s,border-color .15s;}
.tag--btn:hover{background:var(--surface-3);border-color:var(--line-3);}
.tag--btn[aria-expanded="true"]{background:var(--brand-soft);border-color:var(--brand-line);color:var(--brand-ink);}

/* —— 右上角时间面板：农历 / 节假日 / 生日 —— */
.time-panel{
  position:absolute;top:calc(100% + 8px);right:0;z-index:60;
  width:300px;max-width:calc(100vw - 16px);
  background:var(--surface);border:1px solid var(--line-3);border-radius:var(--r-md);
  box-shadow:var(--sh3);padding:var(--s3) var(--s4);
  display:flex;flex-direction:column;gap:var(--s2);
  font-size:var(--fs-13);color:var(--text);text-align:left;
  animation:tpIn .14s ease;
}
.time-panel[hidden]{display:none;}
@keyframes tpIn{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
.tp-solar{font-size:var(--fs-14);font-weight:600;}
.tp-lunar{color:var(--text-2);}
.tp-row{padding:var(--s2) var(--s3);border-radius:var(--r-sm);background:var(--surface-2);}
.tp-holiday{background:var(--ok-soft);color:var(--ok);}
.tp-work{background:var(--warn-soft);color:var(--warn);}
.tp-next{background:var(--info-soft);color:var(--info);}
.tp-sec-title{margin-top:var(--s1);font-weight:600;color:var(--text-2);}
.tp-bd-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s1);max-height:240px;overflow:auto;}
.tp-bd-item{display:grid;grid-template-columns:1fr auto auto;gap:var(--s2);align-items:center;padding:var(--s1) var(--s2);border-radius:var(--r-sm);background:var(--surface-2);}
.tp-bd-name{font-weight:600;}
.tp-bd-date{font-family:var(--mono);color:var(--text-2);font-size:var(--fs-12);}
.tp-bd-cnt{color:var(--text-2);font-size:var(--fs-12);white-space:nowrap;}
.tp-bd-cnt b{color:var(--brand-ink);}
.tp-bd-today{color:var(--danger);font-weight:700;}
.tp-empty{color:var(--text-3);font-size:var(--fs-12);padding:var(--s1) 0;}
@media (max-width:560px){
  .time-panel{left:8px;right:8px;width:auto;}
}

.btn{
  padding:6px 13px;border-radius:var(--r-sm);border:1px solid var(--line-3);
  background:var(--surface);color:var(--text);font-size:var(--fs-13);
  transition:background var(--fast),border-color var(--fast),color var(--fast);
}
.btn:hover{background:var(--surface-3);}
.btn--sm{padding:var(--s1) 10px;font-size:var(--fs-13);}
.btn--ghost{border-color:var(--line-2);background:transparent;color:var(--text-2);}
.btn--ghost:hover{background:var(--surface-3);color:var(--text);}
.btn--primary{background:var(--brand);border-color:var(--brand);color:#fff;}
.btn--primary:hover{background:var(--brand-ink);border-color:var(--brand-ink);}
.btn--danger{background:var(--danger);border-color:var(--danger);color:#fff;}
.btn--danger:hover{filter:brightness(.92);}
.btn:disabled{opacity:.45;cursor:not-allowed;}

.mask-notice{font-size:var(--fs-12);color:var(--warn);background:var(--warn-soft);border:1px solid var(--warn-line);padding:var(--s0) var(--s2);border-radius:20px;}

/* ================= 卡片 / KPI ================= */
.section{margin-bottom:var(--s5);}
.section__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3);margin-bottom:var(--s3);}
.section__head h3{font-size:var(--fs-14);}
.section__head span{font-size:var(--fs-12);color:var(--text-3);}

/* 客户详情：整段无数据时的轻量提示（替代空盒子，避免界面空旷） */
.cd-empty{
  margin:0;padding:var(--s4) var(--s3);
  text-align:center;color:var(--text-3);font-size:var(--fs-13);
  background:var(--surface-2);
  border:1px dashed var(--line-2);border-radius:var(--r-md);
}
/* 客户详情大段：段标题前加分类色点，钱=绿 / 事=品牌 / 人=蓝 */
.section--cd .section__head h3{display:flex;align-items:center;gap:8px;}
.section--cd .section__head h3::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--line-2);flex:0 0 auto;}
.section--cd.tone-ok .section__head h3::before{background:var(--ok);}
.section--cd.tone-brand .section__head h3::before{background:var(--brand);}
.section--cd.tone-info .section__head h3::before{background:var(--info);}

/* ============ 客户详情 · 上下文条（2026-10-16 李总提）============
   长表往下滚时，页首「哪个客户、有哪些项目、正看到哪张表」会全部滚出视口，
   回顶部找上下文很折腾。这条子把这些信息固在顶栏之下、滚动区之上：
   返回 | 客户名 | 合作状态 | 地区·联系人·电话 | 项目构成 | 当前项目表 | 未回款。

   **为什么挂在应用壳（.main）里而不是 #view 里做 sticky**：
   #view 有 24px 内边距，sticky 元素被父容器内容盒钳着，最多贴在内容盒顶
   （= 视口顶 +24px），顶上那条 24px 缝会有表格行透出来 —— 实测过，不行。
   放进壳里当常驻副标题栏：flex:none 不参与滚动分配，#view 该怎么滚怎么滚，
   条子永远不动，也不需要 z-index / 负边距 / 滚动监听那一套。

   「当前项目表」为什么不吸顶面板标题：.panel 自带 overflow:hidden，粘滞元素会
   以面板自己为滚动容器（面板不滚 → 吸顶成摆设，C3 同款坑）—— 改由 app.js 滚动时
   按「可见面积最大」算一张表名同步进条子，顶部/中部/底部都对。 */
.cd-bar{
  flex:none;
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2) var(--s3);
  padding:var(--s2) var(--s5);
  background:var(--surface);
  border-bottom:1px solid var(--line);
}
.cd-bar__back{flex:none;}
.cd-bar__name{
  font-size:var(--fs-15);font-weight:700;
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.cd-bar__meta{
  font-size:var(--fs-12);color:var(--text-3);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.cd-bar__right{margin-left:auto;display:flex;align-items:center;flex-wrap:wrap;gap:var(--s1) var(--s3);min-width:0;}
.cd-bar__chips{font-size:var(--fs-12);color:var(--text-2);white-space:nowrap;}
.cd-bar__chips b{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:600;color:var(--text);}
/* 「当前所在项目表」：品牌色胶囊，前缀「当前」二字走 ::before，JS 只换表名 */
.cd-bar__cur{
  display:inline-flex;align-items:center;gap:var(--s1);
  font-size:var(--fs-12);color:var(--brand-ink);background:var(--brand-soft);
  border:1px solid var(--brand-line);border-radius:999px;
  padding:1px var(--s2);white-space:nowrap;
}
.cd-bar__cur::before{content:"当前";color:var(--brand);}
.cd-bar__pay{font-size:var(--fs-12);color:var(--text-2);white-space:nowrap;}
.cd-bar__pay.is-danger{color:var(--danger);font-weight:600;}

.kpis{display:grid;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));gap:var(--s3);}
.kpi{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3) var(--s4);position:relative;overflow:hidden;
}
.kpi::after{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--line-2);}
.kpi--danger::after{background:var(--danger);}
.kpi--warn::after{background:var(--warn);}
.kpi--ok::after{background:var(--ok);}
.kpi--brand::after{background:var(--brand);}
.kpi__label{font-size:var(--fs-12);color:var(--text-2);display:block;}
.kpi__value{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-24);line-height:1.2;display:block;margin-top:var(--s0);letter-spacing:-.02em;}
.kpi--danger .kpi__value{color:var(--danger);}
.kpi--warn .kpi__value{color:var(--warn);}
.kpi--ok .kpi__value{color:var(--ok);}
.kpi__foot{font-size:var(--fs-12);color:var(--text-3);display:block;margin-top:1px;}

/* ================= 表格 ================= */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;}
.panel + .panel{margin-top:var(--s4);}
.panel__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--surface-2);
}
.panel__head h3{font-size:var(--fs-14);}
.panel__head .count{font-family:var(--mono);font-size:var(--fs-12);color:var(--text-3);min-width:0;max-width:58%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.table-wrap{overflow-x:auto;}
table{width:100%;border-collapse:collapse;font-size:var(--fs-13);}
thead th{
  text-align:left;font-weight:500;font-size:var(--fs-12);color:var(--text-2);
  padding:6px 10px;border-bottom:1px solid var(--line-2);
  background:var(--surface-2);white-space:nowrap;position:sticky;top:0;z-index:var(--z-base);
}
tbody td{padding:6px 10px;border-bottom:1px solid var(--line);vertical-align:middle;}
tbody tr:last-child td{border-bottom:0;}
/* 客户台账列表：按回款健康度整行标识（红=逾期 / 黄=未回款 / 绿=全回完），与回款界面颜色规则一致 */
.table-wrap tbody tr.is-danger{box-shadow:inset 3px 0 0 var(--danger);background:var(--danger-soft);}
.table-wrap tbody tr.is-warn{box-shadow:inset 3px 0 0 var(--warn);background:var(--warn-soft);}
.table-wrap tbody tr.is-ok{box-shadow:inset 3px 0 0 var(--ok);background:var(--ok-soft);}
.table-wrap tbody tr.is-danger:hover{background:#f7e2e0;}
.table-wrap tbody tr.is-warn:hover{background:#f7e8cf;}
.table-wrap tbody tr.is-ok:hover{background:#dcebc6;}
.table-wrap tbody tr.is-danger td:first-child{background:var(--danger-soft);}
.table-wrap tbody tr.is-warn td:first-child{background:var(--warn-soft);}
.table-wrap tbody tr.is-ok td:first-child{background:var(--ok-soft);}
.table-wrap tbody tr.is-danger:hover td:first-child{background:#f7e2e0;}
.table-wrap tbody tr.is-warn:hover td:first-child{background:#f7e8cf;}
.table-wrap tbody tr.is-ok:hover td:first-child{background:#dcebc6;}
tbody tr{transition:background var(--fast);}
tbody tr:hover{background:var(--surface-2);}
td.num,th.num{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap;}
td.nowrap,th.nowrap{white-space:nowrap;}
/* 列宽策略（2026-10-12 UI 评审 P0-1）：行高压回单行。
   短列 col-xs 上限 132px（容得下 8 字客户名）、长列 col-lg 上限 130px，
   一律单行 + 省略号，悬停 td 有全文；数字/日期列 nowrap。
   13 列表总宽约 1390px：≥1620px 视口整表放下（含操作列），
   更窄的屏横向滚动（Excel 习惯），换来行高 77~185px → 39px、一屏 20+ 行。 */
.col-xs{white-space:nowrap;max-width:132px;overflow:hidden;text-overflow:ellipsis;}
.col-lg{white-space:nowrap;max-width:130px;overflow:hidden;text-overflow:ellipsis;}
/* 第四批 C2（2026-10-12）：项目名 / 标题类**单独一档 180px**。
   原先它和「备注」共用 col-lg 的 130px（≈9 个汉字），
   「济南市人民医院 HIS 系统驻场服务项目」只剩一小截，而项目名恰恰是最该看清的一列。
   放宽到 180px 能显示 12~13 字，表格总宽只多约 50px。 */
.col-md{white-space:nowrap;max-width:180px;overflow:hidden;text-overflow:ellipsis;}

/* C4（2026-10-12）：横向滚动时首列固定。13 列的表一横滚，
   滚到右边就分不清这行到底是哪条记录了。首列必须有不透明背景，否则会透出下层内容。 */
.table-wrap th:first-child,
.table-wrap td:first-child{position:sticky;left:0;background:var(--surface);}
.table-wrap thead th:first-child{background:var(--surface-2);z-index:calc(var(--z-base) + 1);}
.table-wrap tbody tr:hover td:first-child{background:var(--surface-2);}

/* ---- 冻结列：横向滚动时钉住「身份列」（2026-10-16 李总提）----
   13 列的表一横滚，客户名称、项目名称就滚没了。首列原本只钉住了批量勾选框，
   身份列照样滚走；这里把勾选 / 客户 / 项目名称…整块钉在左侧（列由 app.js 的
   IDENTITY_KEYS 挑，left 由 freezeTableCols() 按实际列宽现算）。
   钉住的格子**必须有不透明底色**，否则横滚时下面的内容会透出来。 */
.table-wrap th.is-fz,
.table-wrap td.is-fz{position:sticky;background:var(--surface);}
.table-wrap thead th.is-fz{background:var(--surface-2);z-index:calc(var(--z-base) + 1);}
.table-wrap tbody tr:hover td.is-fz{background:var(--surface-2);}
/* 冻结块最右一列拉一条竖线：明确"这边是钉住的、那边才开始滚" */
.table-wrap .is-fz-edge{border-right:1px solid var(--line-2);}
/* 回款树按行上色（未回款 / 已回款 / 逾期）：td 不透明，行底色透不出来，
   所以把同样的底色补到冻结列上，别让钉住的那几格变成白补丁 */
.ptree__body tbody tr.is-paid td.is-fz{background:var(--ok-soft);}
.ptree__body tbody tr.is-unpaid.is-late td.is-fz{background:var(--danger-soft);}

/* C3（2026-10-12）：sticky 表头只在"能纵向滚动的容器"里才有意义。
   .table-wrap 写了 overflow-x:auto → 按规范 overflow-y 被算成 auto，
   但它高度是 auto、永远不纵向滚动，于是 thead 的 sticky 永远贴自己盒顶 = 摆设。
   非 fill-page 的场景（客户详情的多张表、导入结果）给一个最大高度让它真能滚。 */
.section .table-wrap,
.modal__body .table-wrap,
.drawer__body .table-wrap{max-height:min(60vh,520px);overflow:auto;}
/* 操作列在 13 列表里是最后被挤下屏的 —— 按钮和间距收紧一档 */
.table-wrap td.col-actions .btn{padding:var(--s1) var(--s2);}
.table-wrap .row-actions{gap:var(--s1);}
.cell-strong{font-weight:500;}
.cell-sub{display:block;font-size:var(--fs-12);color:var(--text-3);}
.empty{padding:var(--s6);text-align:center;color:var(--text-3);font-size:var(--fs-13);}
/* 空状态（2026-10-12 UI 评审 P1-10）：图标 + 一句话 + 行动按钮 */
.empty--rich{display:flex;flex-direction:column;align-items:center;gap:var(--s3);padding:var(--s6) var(--s4);}
.empty__icon{width:44px;height:44px;color:var(--line-3);flex:none;}
.empty__text{margin:0;color:var(--text-3);font-size:var(--fs-13);line-height:1.6;max-width:52ch;}
.empty__actions{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;justify-content:center;}
/* 列表页的空状态在面板里垂直居中，别把空白全堆在下面 */
.fill-page > .panel > .empty--rich{flex:1;justify-content:center;}

/* 弹窗双列栅格（2026-10-12 UI 评审 P1-11）：760px 的单列表单要滚好几屏 */
.form-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s3) var(--s4);align-items:start;}
.form-grid > .field--form{margin-bottom:0;}
.form-grid > .field--wide{grid-column:1 / -1;}

/* 第十五轮：表格首列左内边距加大，账号/姓名等内容不再贴边 */
.table-wrap th:first-child,.table-wrap td:first-child{padding-left:14px;}

/* ================= 异常项列表 ================= */
.alerts{display:flex;flex-direction:column;}
.alert{
  display:grid;grid-template-columns:88px 1fr auto;gap:var(--s3);align-items:center;
  padding:10px var(--s4);border-bottom:1px solid var(--line);
  border-left:3px solid transparent;background:none;width:100%;text-align:left;
  transition:background var(--fast);
}
.alert:last-child{border-bottom:0;}
.alert:hover{background:var(--surface-2);}
.alert--danger{border-left-color:var(--danger);}
.alert--warning{border-left-color:var(--warn);}
.alert__type{font-size:var(--fs-12);}
.alert--danger .alert__type{color:var(--danger);}
.alert--warning .alert__type{color:var(--warn);}
.alert__title{font-size:var(--fs-14);}
.alert__detail{display:block;font-size:var(--fs-12);color:var(--text-3);font-family:var(--mono);}
.alert.is-hidden{display:none;}

/* ---- 议程筛选页签 ---- */
.tabs{
  display:flex;gap:var(--s2);align-items:center;flex-wrap:wrap;
  padding:var(--s2) var(--s4);border-bottom:1px solid var(--line);background:var(--surface-2);
}
.tab{
  -webkit-appearance:none;appearance:none;font:inherit;color:var(--text-2);cursor:pointer;
  padding:var(--s1) var(--s3);border-radius:999px;border:1px solid var(--line);
  background:var(--surface);font-size:var(--fs-12);line-height:1.6;
  transition:background var(--fast),color var(--fast),border-color var(--fast);
}
.tab:hover{background:var(--surface-3);}
.tab.is-on{background:var(--brand);border-color:var(--brand);color:#fff;}
.tab--danger.is-on{background:var(--danger);border-color:var(--danger);}
.tab--warn.is-on{background:var(--warn);border-color:var(--warn);}

/* ---- 顶部卡片跳转落点：滚到位后闪一下 ---- */
#agenda{scroll-margin-top:var(--s4);}
@keyframes agendaFlash{
  0%{box-shadow:0 0 0 0 rgba(15,110,86,.35);}
  35%{box-shadow:0 0 0 6px rgba(15,110,86,.16);}
  100%{box-shadow:0 0 0 0 rgba(15,110,86,0);}
}
.is-flash{animation:agendaFlash 1.8s var(--ease);border-radius:var(--r-md);}

/* ================= 详情 ================= */
.facts{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:var(--s3) var(--s4);}
.fact__label{font-size:var(--fs-12);color:var(--text-3);display:block;}
.fact__value{font-size:var(--fs-14);display:block;}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4);align-items:start;}

/* ================= 表单 / 向导 ================= */
.field{margin-bottom:var(--s3);}
.field label{display:block;font-size:var(--fs-12);color:var(--text-2);margin-bottom:var(--s1);}
.field input,.field select,.field textarea{
  width:100%;padding:6px 10px;font-family:inherit;font-size:var(--fs-13);color:var(--text);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--brand);outline:none;}
.field input[readonly]{background:var(--surface-3);color:var(--text-2);}

/* 第五批（2026-10-13）：.steps / .step* 全量检索在 app.js 中零引用（早期版本的多步条已拆成
   独立 panel），删除 11 行死样式。留着只会让后来人以为有这套组件。 */

.dropzone{
  border:1.5px dashed var(--line-3);border-radius:var(--r-md);
  padding:var(--s6) var(--s4);text-align:center;background:var(--surface-2);
  cursor:pointer;
  transition:border-color var(--fast),background var(--fast),box-shadow var(--fast);
}
.dropzone:hover{border-color:var(--brand-line);background:var(--brand-soft);}
.dropzone strong{display:block;font-size:var(--fs-14);font-weight:500;}
.dropzone span{display:block;font-size:var(--fs-12);color:var(--text-3);margin-top:var(--s0);}
/* 真实拖放反馈（第五批 P0-2）：hover 态用虚线边框已经表达"可点"，
   拖拽悬停必须给一个更强、且一眼能分辨的态，否则用户不知道松手会不会生效。 */
.dropzone:focus-visible{outline:2px solid var(--brand);outline-offset:2px;}
.dropzone.is-over{
  border-color:var(--brand);border-style:solid;
  background:var(--brand-soft);
  box-shadow:0 0 0 3px var(--brand-soft);
}

/* 置信度标注：颜色 + 文字双重编码（不依赖颜色单独表达） */
.conf{display:inline-flex;align-items:center;gap:var(--s1);font-size:var(--fs-11);padding:0 6px;border-radius:var(--r-xs);border:1px solid;white-space:nowrap;}
.conf--high{color:var(--ok);border-color:var(--ok-line);background:var(--ok-soft);}
.conf--mid{color:var(--warn);border-color:var(--warn-line);background:var(--warn-soft);}
.conf--low{color:var(--danger);border-color:var(--danger-line);background:var(--danger-soft);}
/* C18（2026-10-12）：.row-low / .row-mid 经全量检索在 app.js 中**零引用**，已删除。 */
input.is-low{border-color:var(--danger-line);background:var(--danger-soft);}
input.is-mid{border-color:var(--warn-line);background:var(--warn-soft);}
/* 第五批（2026-10-13）：合同解析第 2 步里，日期字段走的是
   <div class="im-date is-low"> + 内部 .dsel__view，而上面两条只选中了 input，
   导致「低置信度日期」和普通日期长得一模一样——最该被提示的字段反而没提示。 */
.im-date{display:block;}
.im-date.is-low .dsel__view{border-color:var(--danger-line) !important;background:var(--danger-soft);}
.im-date.is-mid .dsel__view{border-color:var(--warn-line) !important;background:var(--warn-soft);}

.progress{height:5px;background:var(--surface-3);border-radius:20px;overflow:hidden;}
.progress span{display:block;height:100%;background:var(--brand);transition:width var(--base) var(--ease);}

.banner{
  display:flex;gap:var(--s3);align-items:flex-start;
  padding:var(--s3) var(--s4);border-radius:var(--r-sm);border:1px solid var(--line-2);
  background:var(--surface-2);font-size:var(--fs-13);margin-bottom:var(--s4);
}
.banner--warn{background:var(--warn-soft);border-color:var(--warn-line);color:var(--warn);}
.banner--danger{background:var(--danger-soft);border-color:var(--danger-line);color:var(--danger);}
.banner--ok{background:var(--ok-soft);border-color:var(--ok-line);color:var(--ok);}
.banner ul{margin:0;padding-left:1.2em;}
.banner li{margin:1px 0;}

/* ================= Toast ================= */
.toast{
  position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(8px);
  background:var(--text);color:#fff;padding:var(--s2) 18px;border-radius:var(--r-md);
  font-size:var(--fs-13);box-shadow:var(--sh3);z-index:var(--z-toast);opacity:0;
  transition:opacity var(--base) var(--ease),transform var(--base) var(--ease);
}
.toast.is-show{opacity:1;transform:translateX(-50%);}
/* C9（2026-10-12）：给提示分类型。原来成功和失败长得一模一样，
   连续操作时第二条还会直接顶掉第一条 —— 中间的失败提示被吃掉了
   （现在改为队列串行播放，失败停留 4 秒）。 */
.toast--ok{background:var(--ok-ink);}
.toast--warn{background:var(--warn-ink);}
.toast--danger{background:var(--danger-ink);font-weight:500;}

/* ===== 第四批新增组件（2026-10-12）===== */

/* B7：筛选态芯片 —— 让"正在按什么条件筛选"看得见、点得掉 */
.chip{
  display:inline-flex;align-items:center;gap:var(--s1);
  min-height:32px;padding:var(--s1) var(--s3);
  border:1px solid var(--brand-line);border-radius:999px;
  background:var(--brand-soft);color:var(--brand-ink);
  font-size:var(--fs-12);font-weight:500;cursor:pointer;
  max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.chip:hover{background:#d7ece2;}
.chip:focus-visible{outline:2px solid var(--brand);outline-offset:2px;}

/* B5：可排序表头 */
thead th .th-sort{
  display:inline-flex;align-items:center;gap:var(--s1);
  background:none;border:0;padding:0;margin:0;
  font:inherit;color:inherit;cursor:pointer;
  /* 表头单元格本身的内边距归 th，这里只补一个可点的热区 */
  min-height:24px;
}
thead th .th-sort:hover{color:var(--brand);}
thead th .th-sort:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:var(--r-xs);}
.th-sort__ind{font-size:var(--fs-11);opacity:.5;}
.th-sort.is-on{color:var(--brand);font-weight:600;}
.th-sort.is-on .th-sort__ind{opacity:1;}

/* C10：闲置倒计时横幅 */
.idle-warn{
  position:fixed;left:50%;bottom:96px;transform:translateX(-50%) translateY(8px);
  display:flex;align-items:center;gap:var(--s3);
  background:var(--surface);border:1px solid var(--warn-line);
  border-left:4px solid var(--warn);
  border-radius:var(--r-md);box-shadow:var(--sh3);
  padding:var(--s2) var(--s3);z-index:var(--z-toast);
  font-size:var(--fs-13);color:var(--text);
  opacity:0;transition:opacity var(--base) var(--ease),transform var(--base) var(--ease);
  max-width:calc(100vw - 32px);
}
.idle-warn.is-show{opacity:1;transform:translateX(-50%);}
.idle-warn b{font-family:var(--mono);color:var(--warn-ink);}

/* C11（2026-10-13）：批量操作条 + 复选列 */
.bulkbar{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2);
  padding:var(--s2) var(--s3);margin-bottom:var(--s3);
  background:var(--brand-soft);border:1px solid var(--brand-line);
  border-radius:var(--r-md);
}
.bulkbar__count{font-size:var(--fs-13);color:var(--brand-ink);font-weight:500;}
.bulkbar__count b{font-family:var(--mono);font-size:var(--fs-14);}
.bulkbar__hint{font-style:normal;color:var(--text-3);font-weight:400;margin-left:var(--s1);font-size:var(--fs-12);}
.bulkbar__sel{
  font:inherit;font-size:var(--fs-13);color:var(--text);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
  padding:var(--pad-control);min-height:32px;max-width:180px;
}
.bulkbar .btn--danger{background:var(--danger);border-color:var(--danger);color:#fff;}

/* 复选列：窄一点，勾选框垂直居中，别把行撑高（行高稳定是 P0-1 的老成果） */
th.col-check,td.col-check{width:38px;max-width:38px;padding-left:var(--s3);padding-right:0;}
th.col-check .check-all,td.col-check .row-check{
  width:16px;height:16px;margin:0;display:block;cursor:pointer;accent-color:var(--brand);
}
/* 桌面上表头复选框比正文略小一号，视觉上不抢戏 */
th.col-check .check-all{width:15px;height:15px;}
/* 手机上表头整行隐藏，看不到全选框 —— 操作条里的「全选/反选」按钮顶上 */
.bulkbar__all{display:none;}

/* B2：校验失败时把出错的控件标出来 —— 原来只有弹窗底部一行红字，用户根本找不到是哪一格 */
.is-error{border-color:var(--danger) !important;box-shadow:0 0 0 3px rgba(179,49,42,.14);}
.field.is-error > label, .field--form.is-error > label{color:var(--danger-ink);}

/* （原「投屏模式」已按 2026-10-12 李总要求移除：手机端改为正式支持录入，不再需要投屏放大字号那套） */

/* ================= v0.3 新增：工具栏 / 分页 / 弹窗 ================= */

/* B6：审计流水筛选用的下拉（与搜索框同一视觉重量） */
.toolbar__select{
  font:inherit;font-size:var(--fs-13);color:var(--text);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
  padding:var(--pad-control);min-height:32px;max-width:220px;
}
.muted{color:var(--text-3);}
.cell-danger{color:var(--danger-ink);font-weight:500;}
.cell-warn{color:var(--warn-ink);font-weight:500;}

/* 可点击的单元格（客户名称 → 客户详情） */
.link{
  background:none;border:0;padding:0;font:inherit;color:var(--brand-ink);
  text-align:left;text-decoration:underline;text-decoration-color:var(--brand-line);
  text-underline-offset:3px;
}
.link:hover{color:var(--brand);text-decoration-color:var(--brand);}

/* 表格页工具栏 */
.toolbar{
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  margin-bottom:var(--s3);
}
.toolbar__search{
  flex:1;min-width:180px;max-width:340px;padding:6px 11px;font-size:var(--fs-13);
  font-family:inherit;color:var(--text);background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.toolbar__search:focus{border-color:var(--brand);outline:none;}
.toolbar__count{font-family:var(--mono);font-size:var(--fs-12);color:var(--text-3);margin-left:auto;}
.toolbar .btn{text-decoration:none;display:inline-block;}

/* 客户台账「显示排序」下拉（2026-10-16） */
.sort-menu{
  display:none;position:fixed;z-index:1300;min-width:224px;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--sh3);padding:var(--s2);font-size:var(--fs-13);color:var(--text);
}
.sort-menu__title{font-size:var(--fs-12);color:var(--text-3);margin:0 var(--s1) var(--s2);}
.sort-menu__row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s2);
  padding:6px var(--s2);border-radius:var(--r-sm);
}
.sort-menu__row + .sort-menu__row{margin-top:2px;}
.sort-menu__row.is-active{background:var(--brand-soft);}
.sort-menu__name{font-weight:500;}
.sort-menu__row.is-active .sort-menu__name{color:var(--brand);font-weight:600;}
.sort-menu__arrows{display:flex;gap:4px;}
.sort-menu__arr{
  width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--surface);
  color:var(--text-2);cursor:pointer;font-size:var(--fs-14);line-height:1;
}
.sort-menu__arr:hover{border-color:var(--brand);color:var(--brand);}
.sort-menu__arr.is-on{background:var(--brand);border-color:var(--brand);color:#fff;}
.sort-menu__reset{margin-top:var(--s2);width:100%;}

.pager{display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;justify-content:center;margin-top:var(--s3);font-size:var(--fs-13);color:var(--text-2);}
.pager .btn.is-on{background:var(--brand);border-color:var(--brand);color:#fff;cursor:default;}
.pager__gap{color:var(--text-3);padding:0 var(--s0);}
.pager__info{margin-left:var(--s2);color:var(--text-3);font-family:var(--mono);}

/* 第十五轮：分页页铺满主区域（绝对定位填满 .view，避免百分比高度解析不稳定），表格区内部滚动，分页条常驻底部可见 */
.fill-page{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;min-height:0;}
.fill-page > .toolbar{flex:none;}
.fill-page > .banner{flex:none;}
.fill-page > .panel{flex:1;min-height:0;display:flex;flex-direction:column;}
.fill-page > .panel > .panel__head{flex:none;}
/* 回款节点树：.ptree 是面板的直接子级且是滚动区。原来只给 .table-wrap 设了 overflow:auto，
   但 .table-wrap 嵌在 .ptree__body 里、没有 flex 高度约束 → 面板 overflow:hidden 直接裁掉
   展开的客户明细，既无滚动条也看不到下面的内容。这里把 .ptree 设为 flex:1 + overflow:auto。 */
.fill-page > .panel > .ptree{flex:1;min-height:0;overflow:auto;}
.fill-page > .panel .table-wrap{flex:1;min-height:0;overflow:auto;}
.fill-page .grid-2{flex:1;min-height:0;align-items:stretch;grid-template-rows:minmax(0,1fr);}
.fill-page .grid-2 .panel{display:flex;flex-direction:column;min-height:0;}
.fill-page .grid-2 .panel > .panel__head{flex:none;}
.fill-page .grid-2 .panel .table-wrap{flex:1;min-height:0;overflow:auto;}
.fill-page .grid-2 .panel .form-body{flex:1;min-height:0;overflow:auto;}
.fill-page__foot{flex:none;margin-top:var(--s3);padding-bottom:var(--s3);} /* P1-12：底部契约说明不再贴着视口底边（fill-page 是绝对定位，.view 的 padding-bottom 兜不住） */

/* 表单字段（弹窗内） */
.field--form{margin-bottom:var(--s3);}
.field--form label{display:block;font-size:var(--fs-12);color:var(--text-2);margin-bottom:var(--s1);}
.field__note{display:block;font-size:var(--fs-11);color:var(--text-3);font-weight:400;margin-top:1px;line-height:1.5;}
/* 选填标记（如「下周计划」）—— 与 field__note 区分：note 是说明，opt 是可选徽标 */
.field__opt{display:inline-block;margin-left:6px;font-size:var(--fs-11);font-weight:400;color:var(--text-3);
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r-pill);padding:0 6px;line-height:1.5;}
.req{color:var(--danger);font-style:normal;font-weight:600;}
.modal .banner{margin-bottom:var(--s3);}

/* 弹窗 */
.modal{
  position:fixed;inset:0;z-index:var(--z-modal);
  background:rgba(28,27,24,.34);
  display:grid;place-items:center;padding:var(--s5);
  animation:rise 160ms var(--ease) both;
}
.modal__panel{
  width:min(760px,100%);max-height:86vh;display:flex;flex-direction:column;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-lg);
  box-shadow:var(--sh3);
}
.modal__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--surface-2);
  border-radius:var(--r-lg) var(--r-lg) 0 0;
}
.modal__head h3{font-size:var(--fs-14);}
.modal__body{padding:var(--s4);overflow-y:auto;}
.modal__body .field--form input,
.modal__body .field--form select,
.modal__body .field--form textarea{
  width:100%;padding:6px 10px;font-family:inherit;font-size:var(--fs-13);color:var(--text);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.modal__body .field--form input:focus,
.modal__body .field--form select:focus,
.modal__body .field--form textarea:focus{border-color:var(--brand);outline:none;}
/* 自动生成、不允许手填的字段（如周次） */
.modal__body .field--form input.is-auto{background:var(--surface-2);color:var(--text-2);cursor:default;}
.modal__foot{
  display:flex;align-items:center;gap:var(--s2);justify-content:flex-end;
  padding:var(--s3) var(--s4);border-top:1px solid var(--line);background:var(--surface-2);
  border-radius:0 0 var(--r-lg) var(--r-lg);
}
/* 说明文字靠左（它是说明不是操作），按钮靠右下 —— 见 app.js formFoot() 的注释 */
.modal__foot .hint-line{margin:0 auto 0 0;}

/* 右侧抽屉（创建/编辑等从右滑入的面板） */
.drawer{
  position:fixed;inset:0;z-index:var(--z-drawer);
  background:rgba(28,27,24,.34);
  display:flex;justify-content:flex-end;
  animation:rise 160ms var(--ease) both;
}
.drawer.is-closing{animation:none;}
.drawer__panel{
  width:min(440px,100%);height:100%;display:flex;flex-direction:column;
  background:var(--surface);box-shadow:var(--sh3);
  transform:translateX(100%);animation:drawer-in 220ms var(--ease) forwards;
}
.drawer.is-closing .drawer__panel{animation:drawer-out 180ms var(--ease) forwards;}
@keyframes drawer-in{to{transform:translateX(0);}}
@keyframes drawer-out{to{transform:translateX(100%);}}
.drawer__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
  padding:var(--s3) var(--s4);border-bottom:1px solid var(--line);background:var(--surface-2);
}
.drawer__head h3{font-size:var(--fs-14);}
.drawer__body{padding:var(--s4);overflow-y:auto;flex:1;min-height:0;}
.drawer__body .field--form input,
.drawer__body .field--form select{
  width:100%;padding:6px 10px;font-family:inherit;font-size:var(--fs-13);color:var(--text);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.drawer__body .field--form input:focus,
.drawer__body .field--form select:focus{border-color:var(--brand);outline:none;}
.drawer__foot{
  display:flex;align-items:center;gap:var(--s2);justify-content:flex-end;
  padding:var(--s3) var(--s4);
  border-top:1px solid var(--line);background:var(--surface-2);
}
.drawer__foot .hint-line{margin:0 auto 0 0;}

/* 用户管理：右下方「添加账号」浮动按钮 */
.user-add-fab{
  position:fixed;right:22px;bottom:140px;z-index:var(--z-float);
  display:inline-flex;align-items:center;gap:6px;
  padding:10px 18px;border:0;border-radius:999px;
  background:linear-gradient(135deg,var(--brand),var(--brand-ink));color:#fff;
  font-size:var(--fs-14);font-weight:600;cursor:pointer;
  box-shadow:0 8px 24px rgba(20,120,90,.35);
  transition:transform var(--fast) var(--ease),box-shadow var(--fast) var(--ease);
}
.user-add-fab:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(20,120,90,.45);}
/* 第五批（2026-10-13）：AI 面板 440×620，右 22px / 底 84px 起，正好盖住
   右 22px / 底 140px 的这个按钮（面板 z=59，按钮 z=6）。开着小智时点不到「添加账号」，
   用户会以为按钮消失了。面板打开时直接把它收起来，比抬 z-index 更干净
   ——抬上去会让按钮浮在会话窗口上面，反而挡内容。 */
body.ai-open .user-add-fab{opacity:0;transform:translateY(8px);pointer-events:none;}

/* ================= 移动端（v0.4）：抽屉式导航 + 紧凑布局 ================= */
.btn-menu{display:none;}
.nav-backdrop{display:none;}

/* ============================================================
   会议看板 v2：分区指标 + 山东省地图
   —— 数量类用冷色（蓝），金额类用暖色（金），两套配色互不混用。
   —— 配色变量 --cnt-* / --amt-* 已并入文件顶部 :root（第三批 P2-16 合并变量块）。
   ============================================================ */

.board{display:flex;flex-direction:column;gap:var(--s5);max-width:1440px;margin:0 auto;}

/* ---- 顶部总览 ---- */
/* 经典版顶部四卡：前三个（客户/严重/提示）收窄，第四个金额卡放宽，与 v2 Hero 金额卡呼应。
   移动端 1845 行另有 1fr 1fr 两列覆盖，此处只管桌面/平板。 */
.board-top{
  display:grid;grid-template-columns:1fr 1fr 1fr 1.6fr;gap:var(--s3);
}
/* 会议看板顶部登录地址条（2026-10-19 李总提：展示系统登录地址） */
.board-urlbar{
  display:flex;align-items:center;gap:var(--s2);
  padding:var(--s2) var(--s4);
  background:var(--brand-soft);border:1px solid var(--brand-line);border-radius:var(--r-md);
  font-size:var(--fs-12);color:var(--brand-ink);
}
.board-urlbar a{color:var(--brand);font-weight:600;text-decoration:none;font-family:var(--mono);}
.board-urlbar a:hover{text-decoration:underline;}

/* ===== 生日喜庆主题 + 庆祝弹窗（2026-10-19 李总提）===== */
html.is-birthday{
  --brand:#e11d48; --brand-ink:#9f1239; --brand-soft:#ffe4e6; --brand-line:#fecdd3;
  --gold:#d4a017;
}
html.is-birthday .topbar{box-shadow:inset 0 3px 0 var(--gold);}
html.is-birthday .sidebar__foot{background:linear-gradient(135deg,#fff3f5,#ffe8ec);}

/* 庆祝弹窗 */
.bd-overlay{position:fixed;inset:0;z-index:9500;display:flex;align-items:center;justify-content:center;
  background:rgba(28,22,30,.5);backdrop-filter:blur(2px);animation:bd-fade .3s ease both;}
.bd-card{position:relative;width:min(440px,92vw);background:#fff;border-radius:20px;padding:34px 30px 26px;text-align:center;
  box-shadow:0 30px 80px rgba(0,0,0,.32);border:2px solid #ffd9e0;animation:bd-pop .45s cubic-bezier(.2,.9,.3,1.3) both;overflow:hidden;}
.bd-card::before{content:"";position:absolute;inset:0;background:radial-gradient(circle at 50% 0%,#fff3f5,transparent 70%);pointer-events:none;}
.bd-cake{font-size:74px;line-height:1;animation:bd-bounce 1.1s ease-in-out infinite;filter:drop-shadow(0 8px 14px rgba(225,29,72,.25));}
.bd-title{margin-top:10px;font-size:26px;font-weight:800;color:#e11d48;letter-spacing:.04em;}
.bd-name{margin-top:2px;font-size:18px;color:#9f1239;font-weight:600;}
.bd-bless{margin:16px auto 6px;max-width:340px;font-size:15px;line-height:1.7;color:#4a3b3f;}
.bd-thanks{margin:10px auto 18px;max-width:340px;font-size:14px;font-weight:700;color:#b45309;
  background:#fff7ed;border:1px solid #fdba74;border-radius:12px;padding:10px 12px;}
.bd-btn{appearance:none;border:0;cursor:pointer;background:linear-gradient(135deg,#e11d48,#f43f5e);color:#fff;
  font-size:15px;font-weight:700;padding:11px 26px;border-radius:999px;box-shadow:0 8px 20px rgba(225,29,72,.35);transition:transform .15s;}
.bd-btn:hover{transform:translateY(-2px);}

/* 彩带 */
.bd-confetti{position:fixed;inset:0;z-index:9000;pointer-events:none;overflow:hidden;}
.bd-confetti i{position:absolute;top:-14px;border-radius:2px;opacity:.92;animation:bd-fall linear infinite;}
@keyframes bd-fade{from{opacity:0}to{opacity:1}}
@keyframes bd-pop{from{opacity:0;transform:scale(.8) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}
@keyframes bd-bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-14px)}}
@keyframes bd-fall{to{transform:translateY(106vh) rotate(540deg);opacity:.15}}
.bstat{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s3) var(--s4);
}
.bstat__label{display:block;font-size:var(--fs-12);color:var(--text-2);}
.bstat__value{
  display:block;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:var(--fs-26);line-height:1.15;margin-top:var(--s0);letter-spacing:-.02em;
}
/* C18：此处原来还有一条 .bstat__foot{display:block}，被 794 行的 display:flex 永久覆盖，已删除。 */
.bstat--brand .bstat__value{color:var(--brand);}
.bstat--danger .bstat__value{color:var(--danger);}
.bstat--warn .bstat__value{color:var(--warn);}
.bstat--money{background:var(--amt-soft);border-color:var(--amt-line);}
.bstat--money .bstat__value{color:var(--amt);}
.bstat--money.bstat--jump:hover{border-color:var(--amt);}

/* 系统设置（用户管理页内的「技术岗可见金额」开关）*/
.settings-body{padding:var(--s3) var(--s4);}
.setting-row{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s4);}
.setting-text{min-width:0;}
.setting-name{font-weight:500;font-size:var(--fs-14);}
.setting-desc{color:var(--text-2);font-size:var(--fs-12);margin-top:3px;max-width:680px;line-height:1.5;}

/* 用户管理页：账号列表 / 系统设置 两个页签（2026-10-20 李总要求做成 Tab，不再上下堆叠）*/
.fill-page > .panel > .tabs{flex:none;}
.utab-pane{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;}
.utab-pane[hidden]{display:none;}
.utab-pane > .panel__head{flex:none;}
.utab-pane .table-wrap{flex:1;min-height:0;overflow:auto;}
.utab-pane--scroll{display:block;overflow:auto;}

/* 可跳转的顶部卡片（需在会上处理 / 需留意） */
.bstat--jump{
  -webkit-appearance:none;appearance:none;font:inherit;color:inherit;text-align:left;
  cursor:pointer;width:100%;position:relative;
  transition:transform var(--fast),box-shadow var(--fast),border-color var(--fast);
}
.bstat--jump:hover{transform:translateY(-2px);box-shadow:var(--sh2);border-color:var(--line-2);}
.bstat--jump:active{transform:translateY(0);}
.bstat--jump.is-active{border-color:var(--brand);box-shadow:var(--sh2);}
.bstat--danger.bstat--jump:hover{border-color:var(--danger);}
.bstat--warn.bstat--jump:hover{border-color:var(--warn);}
.bstat--brand.bstat--jump:hover{border-color:var(--brand);}
.bstat__foot{display:flex;align-items:center;justify-content:space-between;gap:6px;}
.bstat__go{
  font-style:normal;font-size:var(--fs-11);color:var(--brand);opacity:0;
  transform:translateY(-2px);transition:opacity var(--fast),transform var(--fast);
}
.bstat--jump:hover .bstat__go,.bstat--jump.is-active .bstat__go{opacity:1;transform:none;}

/* ---- 指标分区 ---- */
.board-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s3);}
.mgrp{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;display:flex;flex-direction:column;
}
.mgrp__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s2);
  padding:var(--s2) var(--s4);background:var(--surface-2);border-bottom:1px solid var(--line);
}
.mgrp__head h4{font-size:var(--fs-13);letter-spacing:.01em;}
.mgrp__head .muted{font-size:var(--fs-12);}
.mgrp__body{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:var(--s2) var(--s3);padding:var(--s3) var(--s4) var(--s4);
}
.mgrp__col{display:flex;flex-direction:column;gap:var(--s2);min-width:0;}
.mgrp__col--money{border-left:1px dashed var(--line-2);padding-left:var(--s3);}
.mgrp__col--solo{grid-column:1 / -1;}

/* 第二行：预签约 / 任务待办 各占半宽，回款整行铺满（2026-10-12 P1-6：
   原来 预签约 2 + 任务 1 留一格空洞、回款 2 又留两格空洞 —— 现在排满无空洞） */
/* 4 列（宽屏）下的跨列规则 —— **每一行的跨度和必须正好等于 4**，否则自动排布会留洞：
     [实施][售后][接口][免费] = 4
     [合同变更][预签约 2][任务] = 4
     [回款] 整行
   「任务待办」在这里只能是 span 1：它若也跨 2，第二行就变成 1+2+2=5，
   任务被挤到第三行，第二行末尾和第三行各留一个洞（2026-10-13 实测）。 */
.mgrp[data-grp="ps"]{grid-column:span 2;}
.mgrp[data-grp="ps"] .mgrp__head{padding:var(--s2) var(--s3);}
.mgrp__body--inline{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:stretch;}
.mgrp__body--inline .mtile{flex:1 1 150px;min-width:0;}
.mgrp__body--inline .mtile__value{font-size:var(--fs-22);}
.mgrp__tag{
  font-size:var(--fs-11);letter-spacing:.06em;color:var(--text-3);
  display:flex;align-items:center;gap:var(--s1);
}
.mgrp__tag::before{content:"";width:8px;height:8px;border-radius:2px;background:var(--line-2);}
.mgrp__col--count .mgrp__tag::before{background:var(--cnt);}
.mgrp__col--money .mgrp__tag::before{background:var(--amt);}
.mgrp__none{font-size:var(--fs-12);color:var(--text-3);padding:var(--s0) 0;}

/* ---- 指标瓦片 ---- */
.mtile{
  -webkit-appearance:none;appearance:none;text-align:left;cursor:pointer;
  display:grid;grid-template-columns:1fr auto;grid-template-rows:auto auto;
  align-items:baseline;column-gap:var(--s2);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:var(--s2) var(--s3);font:inherit;color:inherit;
  transform-origin:center left;
  transition:transform var(--base) var(--ease),box-shadow var(--base) var(--ease),
             opacity var(--base) var(--ease),background var(--fast) linear;
  will-change:transform;
}
.mtile__label{grid-column:1;font-size:var(--fs-12);color:var(--text-2);line-height:1.35;}
.mtile__value{
  grid-column:2;grid-row:1;font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:var(--fs-19);line-height:1.25;letter-spacing:-.02em;white-space:nowrap;
}
.mtile--count{border-left:3px solid var(--cnt-line);}
.mtile--count .mtile__value{color:var(--cnt);}
/* 金额瓦片：**文字一行、金额一行**（2026-10-16 李总提，2026-10-19 推广到全部面板）。
   默认布局是 label 左 / 金额右同处一行，标签一长就把数字挤得没处站——
   实施项目·合同金额 在窄列里标签被压成一字一行、数字溢出瓦片（李总 2026-10-19 报）；
   上下两行后，标签占整行、金额独占一行，各面板样式也统一。 */
.mgrp .mtile--money{grid-template-columns:1fr;row-gap:2px;}
.mgrp .mtile--money .mtile__label{grid-row:1;grid-column:1;font-size:var(--fs-11);}
.mgrp .mtile--money .mtile__value{grid-row:2;grid-column:1;font-size:var(--fs-17);}
/* 面板内多个金额瓦片之间用虚线分隔（2026-10-16 李总提） */
.mgrp .mgrp__col--money .mtile--money + .mtile--money{
  border-top:1px dashed var(--line-2);margin-top:var(--s1);padding-top:var(--s3);
}
.mtile--money{border-left:3px solid var(--amt-line);background:var(--amt-soft);}
.mtile--money .mtile__value{color:var(--amt);}
.mtile--danger{border-left-color:var(--danger);}
.mtile--danger .mtile__value{color:var(--danger);}
.mtile--warn{border-left-color:var(--warn);}
.mtile--warn .mtile__value{color:var(--warn);}
.mtile--ok{border-left-color:var(--ok);}
.mtile--ok .mtile__value{color:var(--ok);}
.mtile--zero .mtile__value{color:var(--text-3);}
.mtile:focus-visible{outline:2px solid var(--brand);outline-offset:1px;}

/* 待回款金额：整体放大、字体加粗、金额换行显示 */
.mtile--big{
  grid-template-columns:1fr;row-gap:var(--s2);
  align-items:start;
  padding:var(--s3) var(--s4);
  background:var(--amt-soft);
  border-left:4px solid var(--amt);
}
.mtile--big .mtile__label{font-size:var(--fs-13);font-weight:600;color:var(--text);}
.mtile--big .mtile__value{
  grid-column:1;grid-row:auto;
  font-size:var(--fs-34);font-weight:800;line-height:1.18;letter-spacing:-.02em;
  color:var(--amt);
  white-space:normal;word-break:break-all;word-wrap:break-word;
}

/* 悬浮：组内其他瓦片轻微收缩，当前瓦片放大（弱化背景压暗，保证可读性） */
.mgrp__body:hover .mtile{transform:scale(.98);opacity:.82;}
.mgrp__body .mtile:hover,
.mgrp__body .mtile.is-active{
  transform:scale(1.03);opacity:1;z-index:var(--z-float);position:relative;
  box-shadow:var(--sh2);background:var(--surface);
}

/* ---- 悬浮明细 ---- */
.tip{
  /* P0-4（2026-10-12 评审）：原 80 > 弹窗的 60，看板悬浮明细会盖在表单上。
     现降到弹窗/抽屉之下、看板内容之上 */
  position:fixed;z-index:var(--z-tip);max-width:330px;min-width:200px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-md);
  box-shadow:var(--sh3);padding:var(--s3) var(--s4);pointer-events:none;
  opacity:0;transform:translateY(4px);transition:opacity var(--fast) linear,transform var(--fast) var(--ease);
}
.tip.is-on{opacity:1;transform:translateY(0);}
.tip__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s2);
  padding-bottom:var(--s2);margin-bottom:var(--s2);border-bottom:1px solid var(--line);
}
.tip__title{font-size:var(--fs-13);font-weight:600;}
.tip__num{font-family:var(--mono);font-size:var(--fs-12);color:var(--text-3);}
.tip__list{display:flex;flex-direction:column;gap:6px;max-height:260px;overflow:auto;}
.tip__row{display:flex;flex-direction:column;gap:1px;}
.tip__row b{font-size:var(--fs-12);font-weight:600;line-height:1.35;}
.tip__row span{font-size:var(--fs-11);color:var(--text-3);line-height:1.35;}
.tip__row em{
  font-style:normal;font-family:var(--mono);font-size:var(--fs-11);color:var(--amt);
}
.tip__more{font-size:var(--fs-11);color:var(--text-3);padding-top:var(--s2);border-top:1px solid var(--line);}
.tip__empty{font-size:var(--fs-12);color:var(--text-3);}
/* 第三层：状态解释（2026-10-17 李总提，从瓦片 foot 挪到浮层末尾） */
.tip__note{
  margin-top:var(--s2);padding-top:var(--s2);border-top:1px dashed var(--line-2);
  font-size:var(--fs-11);color:var(--text-3);line-height:1.5;
}
/* 分组小标题（如「已上线」拆成未回款 / 已回款两组）：比总标题弱一档，靠色带区分 */
.tip__grp{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s2);
  margin-top:var(--s2);padding:var(--s1) var(--s2);
  background:var(--surface-2);border-radius:var(--r-sm);
  font-size:var(--fs-11);font-weight:600;color:var(--text-2);
}
.tip__grp + .tip__list{margin-top:var(--s1);}
.tip__line{
  display:grid;grid-template-columns:auto auto 1fr;align-items:baseline;gap:var(--s2);
  padding:var(--s1) 0;border-top:1px solid var(--line);
}
.tip__line:first-of-type{border-top:0;}
.tip__k{font-size:var(--fs-11);color:var(--text-3);white-space:nowrap;}
.tip__line b{font-size:var(--fs-13);font-weight:700;color:var(--brand-ink);white-space:nowrap;}
.tip__v{font-size:var(--fs-11);color:var(--text-2);line-height:1.35;overflow:hidden;text-overflow:ellipsis;}
.tip__go{
  font-size:var(--fs-11);color:var(--brand);padding-top:var(--s2);margin-top:var(--s2);
  border-top:1px dashed var(--line-2);
}

/* ============================================================
   项目闭环 · 阶段条（2026-10-13 第二版）
   —— 第一版是看板顶部一张全局环形图，李总否掉了：位置不对（与分区指标重复），
      粒度也不对（暗示所有项目走同一条路）。现在下沉到**每条项目记录**自己的进度条。
   —— 用 CSS 胶囊而不是 SVG：要塞进表格的一格里，且窄屏/卡片模式下必须能换行，
      胶囊天然可换行，SVG 做不到。
   —— 颜色之外都带文字（步骤名 + 状态名 + 读屏「已卡住」），不靠颜色单独表达状态。
   ============================================================ */
.lc{display:flex;flex-wrap:wrap;align-items:center;gap:3px;}
.lc__s{
  display:inline-flex;align-items:center;
  font-size:var(--fs-11);line-height:1.5;white-space:nowrap;
  padding:1px 7px;border-radius:999px;border:1px solid transparent;
}
.lc__a{
  flex:none;width:6px;height:1px;background:var(--line-3);
}
/* 未到达 */
.lc__s.is-todo{color:var(--text-3);background:transparent;border-style:dashed;border-color:var(--line-2);}
/* 已走过 */
.lc__s.is-done{color:var(--text-2);background:var(--surface-2);border-color:var(--line);}
/* 当前 */
.lc__s.is-cur{color:#fff;background:var(--brand);border-color:var(--brand);font-weight:600;}
/* 卡住：实施延期 / 服务期已过仍挂服务中 / 商机超期 —— 会上必须交代 */
.lc__s.is-stuck{color:#fff;background:var(--danger);border-color:var(--danger);font-weight:600;}
/* 暂停：不是错，但要提醒 */
.lc__s.is-warn{color:var(--warn-ink,var(--warn));background:var(--warn-soft);border-color:var(--warn-line);font-weight:600;}
/* 已取消 */
.lc__s.is-dead{color:var(--text-3);background:var(--surface-3);border-color:var(--line);text-decoration:line-through;}

/* 表格里这一列：别让它把整行撑高，也别在手机卡片里被 nowrap 截断 */
.table-wrap td.col-lc,.table-wrap th.col-lc{white-space:normal;min-width:150px;max-width:260px;}

/* ============================================================
 * 回款节点 · 按客户成树（2026-10-13）
 * ============================================================ */

/* 已回款金额：绿字 + 略降饱和。与浅绿底、✓ 前缀一起构成"已回款"的三重标识，
   任一重单独看都能区分（色觉障碍 / 投影偏色 / 黑白打印都不丢信息） */
.paid-amt{color:var(--ok);font-weight:500;opacity:.92;}

/* 顶部三格汇总：未回款 / 已回款 / 客户数，一眼知道全局盘子。
   2026-10-13 加粗版：金额从 13px 提到 18px 并给整格上色 —— 原来三格都是灰底，
   主角（未回款）没被拎出来，李总说"不够醒目"，所以未回款格红边红字、已回款格绿边绿字。 */
.paysum{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s3);margin:0 0 var(--s3);
}
.paysum__cell{
  display:flex;flex-direction:column;gap:2px;
  padding:var(--s3) var(--s4);border-radius:var(--r-sm);
  background:var(--surface-2);border:1px solid var(--line);
}
.paysum__cell span{font-size:var(--fs-12);color:var(--text-3);}
.paysum__cell b{font-size:var(--fs-14);}
.paysum__cell em{
  font-style:normal;font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-13);
}
/* 主角格：颜色之外，字重与边框也一起变 —— 黑白打印时靠边框和字重仍能分辨哪格是欠的钱 */
.paysum__cell.is-unpaid{
  background:var(--danger-soft);border-color:var(--danger-line);
  border-left:4px solid var(--danger);
}
.paysum__cell.is-unpaid em{font-size:var(--fs-18);font-weight:700;color:var(--danger);}
.paysum__cell.is-paid{background:var(--ok-soft);border-color:var(--ok-line);border-left:4px solid var(--ok);}
.paysum__cell.is-paid em{font-size:var(--fs-18);font-weight:700;color:var(--ok);}
/* 合同总额格：主色（深青绿）与未回款（红）/已回款（绿）同构 —— 颜色之外边框+字重一起变，
   黑白打印仍能分辨哪格是总盘子。四格并排：合同总额 | 未回款 | 已回款 | 客户。 */
.paysum__cell.is-contract{background:var(--brand-soft);border-color:var(--brand-line);border-left:4px solid var(--brand);}
.paysum__cell.is-contract em{font-size:var(--fs-18);font-weight:700;color:var(--brand);}

/* 客户块：一级节点 */
.ptree{display:flex;flex-direction:column;gap:var(--s2);}
.ptree__grp{
  /* flex:none：.ptree 是 fill-page 面板里高度受限的滚动容器（flex:1 + overflow:auto）。
     默认 flex-shrink:1 会让每个客户块按比例被压矮，块内展开的回款明细被
     .ptree__grp 自己的 overflow:hidden 裁掉 —— 表现为「全部展开后第一项就显示不全」。
     不让收缩：超出容器高度时由 .ptree 滚动，而不是压缩子块。 */
  flex:none;
  border:1px solid var(--line);border-left:5px solid var(--line-2);
  border-radius:var(--r-sm);background:var(--surface);overflow:hidden;
}
/* 左侧竖条 = 这家客户的健康度：红=有逾期，黄=有未回款，绿=全回完。
   竖条从 3px 加粗到 5px，并让整块边框跟着染色 —— 远看就能分出"要催的"和"已收完的" */
.ptree__grp.is-danger{border-color:var(--danger-line);border-left-color:var(--danger);}
.ptree__grp.is-warn{border-color:var(--warn-line);border-left-color:var(--warn);}
.ptree__grp.is-ok{border-color:var(--ok-line);border-left-color:var(--ok);}
/* 有逾期的客户：组头再压一层淡红，不用看竖条也知道这家要优先催 */
.ptree__grp.is-danger > .ptree__head{background:var(--danger-soft);}
.ptree__grp.is-danger > .ptree__head:hover{background:#f7e2e0;}
.ptree__head{
  display:flex;align-items:center;gap:var(--s2);width:100%;height:64px;
  padding:var(--s3);background:var(--surface-2);border:0;
  font-family:inherit;font-size:var(--fs-14);color:var(--text);text-align:left;cursor:pointer;
}
.ptree__head:hover{background:var(--surface-3);}
.ptree__head:focus-visible{outline:2px solid var(--brand);outline-offset:-2px;}
.ptree__caret{
  flex:none;display:inline-block;font-size:var(--fs-12);color:var(--text-3);
  transition:transform var(--fast) var(--ease);
}
.ptree__grp.is-open .ptree__caret{transform:rotate(90deg);}
.ptree__name{font-weight:700;font-size:var(--fs-15);flex:none;max-width:34%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* 已回款占比条：一眼看出这家收回了几成。宽度即信息，旁边仍写字（不靠长度单独表意） */
.ptree__bar{
  flex:1 1 90px;min-width:64px;max-width:190px;height:7px;border-radius:999px;
  background:var(--surface-3);border:1px solid var(--line);overflow:hidden;
}
.ptree__bar i{display:block;height:100%;background:var(--ok);}
.ptree__grp.is-danger .ptree__bar i{background:var(--warn);}
.ptree__pct{
  flex:none;font-family:var(--mono);font-size:var(--fs-11);color:var(--text-3);
  font-variant-numeric:tabular-nums;
}
/* 金额块：比 .tchip 大一档 —— 这是组头真正要读的数，不能和"无未回款"这类中性标签同权重 */
.ptree__amt{
  display:inline-flex;flex-direction:column;align-items:flex-end;line-height:1.25;
  padding:3px var(--s2);border-radius:var(--r-xs);border:1px solid var(--line);background:var(--surface);
}
.ptree__amt b{
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-14);font-weight:700;
}
.ptree__amt em{font-style:normal;font-size:var(--fs-11);color:var(--text-3);white-space:nowrap;}
.ptree__amt.is-unpaid{background:var(--danger-soft);border-color:var(--danger-line);}
.ptree__amt.is-unpaid b{color:var(--danger);}
.ptree__amt.is-paid{background:var(--ok-soft);border-color:var(--ok-line);}
.ptree__amt.is-paid b{color:var(--ok);}
.ptree__chips{
  display:grid;grid-template-columns:120px 150px 150px 110px;gap:var(--s1);
  margin-left:auto;min-width:0;overflow-x:auto;align-items:stretch;
}
/* 固定四列（共x笔 / 未回款 / 已回款 / 逾期或已全部回款）：每个客户组头的同类标签落在同一竖列，
   逐行上下对齐；列宽固定，不受金额位数、笔数位数影响。空间不够时组内横向滚动。 */
/* 回款树组头：把"共X笔 / 逾期X笔 / 已全部回款"等中性标签的高度拉到与"未回款/已回款"金额块一致，
   文字居中、字号略放大(11→12)、圆角与金额块统一为 --r-xs，让四列都是同样高的标签盒 */
.ptree__chips .tchip{
  align-items:center;justify-content:center;
  padding:3px var(--s2);border-radius:var(--r-xs);font-size:var(--fs-12);
}
.ptree__go{flex:none;font-size:var(--fs-11);color:var(--brand);cursor:pointer;}
.ptree__go:hover{text-decoration:underline;}
.ptree__body{padding:var(--s1) var(--s2) var(--s2);}
/* 明细区左侧缩进 + 一条引导线，视觉上"挂"在客户下面 */
.ptree__body .table-wrap{margin-left:var(--s3);border-left:2px dashed var(--line-2);}
/* 未回款 / 已回款：底色之外再给**行首 3px 竖条**（第三重视觉锚点）。
   竖条用 box-shadow:inset 画，不额外占位，也不影响表格布局。
   类别由 table(rowClassOf) 写在 tr 上，不用 tr:has() 去猜（兼容性窄、意图也不直白） */
.ptree__body tbody tr.is-unpaid{box-shadow:inset 3px 0 0 var(--warn);}
.ptree__body tbody tr.is-unpaid.is-late{background:var(--danger-soft);box-shadow:inset 3px 0 0 var(--danger);}
.ptree__body tbody tr.is-unpaid.is-late:hover{background:#f7e2e0;}
.ptree__body tbody tr.is-unpaid.is-late td{border-top-color:var(--danger-line);}
.ptree__body tbody tr.is-paid{background:var(--ok-soft);box-shadow:inset 3px 0 0 var(--ok);}
.ptree__body tbody tr.is-paid:hover{background:#dfead0;}
.ptree__body tbody tr.is-paid td{border-top-color:var(--ok-line);}
/* 期次加粗、金额加粗：一行里最该先看到的两个数 */
.ptree__body tbody td:first-child{font-size:var(--fs-13);}
.ptree__body .paid-amt{font-size:var(--fs-13);font-weight:700;opacity:1;}
.ptree__body .cell-danger{font-weight:700;}

/* 汇总小标签（未回款 / 已回款 / 逾期）。刻意不用 .chip —— 它在别处是"可点掉的筛选态"，
   这里是纯展示，语义不同不该共用（共用会让人以为点一下能筛选） */
.tchip{
  display:inline-flex;align-items:center;gap:var(--s1);
  padding:1px var(--s2);border-radius:999px;font-size:var(--fs-11);white-space:nowrap;
  background:var(--surface-3);color:var(--text-2);border:1px solid var(--line);
  font-family:var(--mono);font-variant-numeric:tabular-nums;
}
.tchip--warn{background:var(--warn-soft);color:var(--warn);border-color:var(--warn-line);}
.tchip--ok{background:var(--ok-soft);color:var(--ok);border-color:var(--ok-line);}
.tchip--danger{background:var(--danger-soft);color:var(--danger);border-color:var(--danger-line);}

/* ============================================================
 * 项目台账 · 闭环时间轴（2026-10-13）
 * ============================================================ */
.pjlist{display:flex;flex-direction:column;gap:var(--s3);padding:var(--s3);}
.pjcard{
  border:1px solid var(--line);border-left:5px solid var(--line-2);
  border-radius:var(--r-sm);background:var(--surface);
}
.pjcard.is-danger{border-color:var(--danger-line);border-left-color:var(--danger);}
.pjcard.is-warn{border-color:var(--warn-line);border-left-color:var(--warn);}
.pjcard.is-run{border-color:var(--brand-line);border-left-color:var(--brand);}
.pjcard__head{
  display:flex;align-items:center;gap:var(--s2);width:100%;
  padding:var(--s3);background:var(--surface-2);border:0;border-radius:0;
  font-family:inherit;font-size:var(--fs-13);color:var(--text);text-align:left;cursor:pointer;
}
.pjcard__head:hover{background:var(--surface-3);}
.pjcard__head:focus-visible{outline:2px solid var(--brand);outline-offset:-2px;}
.pjcard.is-danger > .pjcard__head{background:var(--danger-soft);}
.pjcard__caret{
  flex:none;font-size:var(--fs-11);color:var(--text-3);
  transition:transform var(--fast) var(--ease);
}
.pjcard.is-open > .pjcard__head .pjcard__caret{transform:rotate(90deg);}
.pjcard__type{flex:none;}
.pjcard__name{font-size:var(--fs-15);font-weight:700;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.pjcard__cust{flex:none;font-size:var(--fs-12);color:var(--text-2);}
.pjcard__strip{margin-left:auto;flex:none;max-width:46%;}
.pjcard__sub{
  display:flex;flex-wrap:wrap;gap:var(--s1) var(--s3);
  padding:var(--s2) var(--s3) var(--s2) var(--s5);
  font-size:var(--fs-12);color:var(--text-2);border-bottom:1px solid var(--line);
}
.pjcard__body{padding:var(--s3) var(--s3) var(--s1) var(--s5);}
.pjcard__foot{
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--s2) var(--s3);
  padding:var(--s2) var(--s3);border-top:1px solid var(--line);background:var(--surface-2);
  font-size:var(--fs-12);
}
.pjcard__pay{display:flex;align-items:center;gap:var(--s2);min-width:0;}
.pjcard__paynum{font-variant-numeric:tabular-nums;}
.pjcard__tags{display:flex;flex-wrap:wrap;gap:var(--s1);}
.pjcard__go{margin-left:auto;display:flex;gap:var(--s3);flex:none;}
.pjcard__extra{padding:0 var(--s3) var(--s3) var(--s5);}
.pjcard__extra h6{font-size:var(--fs-12);color:var(--text-3);font-weight:600;margin:var(--s2) 0 var(--s1);}
.pjcard__extra ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s1);}
.pjcard__extra li{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s1) var(--s2);
  font-size:var(--fs-12);padding:3px 0;border-top:1px dashed var(--line);
}
.pjcard__extra .mono{font-family:var(--mono);font-variant-numeric:tabular-nums;}

/* 竖向时间轴：一条竖线 + 一串状态点。点的颜色之外**必带文字状态**（.tl__st），
   颜色单独表意的话，色觉障碍 / 黑白打印 / 投影偏色三种场景都会丢信息 */
.tl{list-style:none;margin:0;padding:0 0 0 18px;position:relative;}
.tl::before{
  content:"";position:absolute;left:5px;top:6px;bottom:10px;width:2px;background:var(--line-2);
}
.tl__i{position:relative;padding:0 0 var(--s3);}
.tl__i:last-child{padding-bottom:0;}
.tl__dot{
  position:absolute;left:-18px;top:3px;width:12px;height:12px;border-radius:50%;
  background:var(--surface);border:2px solid var(--line-2);box-sizing:border-box;
}
.tl__body{min-width:0;}
.tl__top{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s1) var(--s2);}
.tl__k{font-size:var(--fs-13);font-weight:600;}
.tl__date{
  font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-12);color:var(--text-2);
}
.tl__date--none{color:var(--text-3);font-style:italic;}
.tl__who{font-size:var(--fs-12);color:var(--text-2);}
.tl__who::before{content:"· ";color:var(--text-3);}
.tl__st{
  font-size:var(--fs-11);padding:0 var(--s1);border-radius:999px;
  border:1px solid var(--line);background:var(--surface-3);color:var(--text-3);
}
.tl__note{font-size:var(--fs-12);color:var(--text-2);margin-top:2px;line-height:1.5;}

.tl__i.is-done .tl__dot{background:var(--ok);border-color:var(--ok-line);}
.tl__i.is-done .tl__st{color:var(--ok);background:var(--ok-soft);border-color:var(--ok-line);}
.tl__i.is-now .tl__dot{background:var(--brand);border-color:var(--brand-line);}
.tl__i.is-now .tl__st{color:var(--brand);background:var(--brand-soft);border-color:var(--brand-line);}
.tl__i.is-warn .tl__dot{background:var(--warn);border-color:var(--warn-line);}
.tl__i.is-warn .tl__st{color:var(--warn);background:var(--warn-soft);border-color:var(--warn-line);}
.tl__i.is-warn .tl__note{color:var(--warn);}
.tl__i.is-dead .tl__dot{background:var(--surface-3);border-color:var(--line-2);}
.tl__i.is-dead .tl__k{color:var(--text-3);}
.tl__i.is-none .tl__dot{background:var(--surface);border-style:dashed;}
.tl__i.is-none .tl__k,.tl__i.is-none .tl__note{color:var(--text-3);}
@media (max-width:760px){
  .pjcard__strip{margin-left:0;max-width:100%;}
  .pjcard__go{margin-left:0;}
}

/* ============================================================
 * 列设置面板（2026-10-13）
 * ============================================================ */
.modal__panel--sm{width:min(460px,100%);}
.btn--xs{padding:1px 7px;font-size:var(--fs-11);min-height:24px;}

.colcfg{display:flex;flex-direction:column;gap:var(--s1);margin-top:var(--s2);}
.colcfg__row{
  display:grid;grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:var(--s2);
  padding:var(--s1) var(--s2);border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);
}
/* 被隐藏的列：整行淡出，让用户看清"我关掉了哪些" */
.colcfg__row.is-off{background:var(--surface-2);opacity:.55;}
.colcfg__name{display:flex;align-items:center;gap:var(--s2);font-size:var(--fs-13);min-width:0;cursor:pointer;}
.colcfg__name span,.colcfg__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.colcfg__w{
  font-family:inherit;font-size:var(--fs-12);padding:2px 6px;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);color:var(--text);
}
.colcfg__mv{display:flex;gap:2px;}
.colcfg__mv .btn:disabled{opacity:.35;cursor:default;}

/* 列宽档位：在既有 col-xs/col-md/col-lg 之上再收 / 再放一档 */
.table-wrap th.cw-n,.table-wrap td.cw-n{max-width:92px;}
.table-wrap th.cw-w,.table-wrap td.cw-w{max-width:320px;}

/* ============================================================
 * 长表单两列之间的分隔线（2026-10-13 李总提）
 *
 * 之前"左右两列不对齐"的根因不是线，而是**整行字段（备注/内容/资料…）夹在两列中间**：
 * 一个占满整行的字段插进来，后面的字段重新排布，左右两列的起始位置就错开了。
 * 所以除了画线，还要把整行字段统一沉到双列区之后（见 app.js openForm）。
 * ============================================================ */
.form-grid{position:relative;}
.form-grid::before{
  content:"";position:absolute;left:50%;top:0;width:1px;height:100%;
  transform:translateX(-.5px);pointer-events:none;z-index:0;
  /* 虚线用渐变画：border-style:dashed 的虚线长度不可控，在各浏览器里长短不一 */
  background-image:linear-gradient(var(--line-2) 55%, transparent 0);
  background-size:1px 7px;background-repeat:repeat-y;
  animation:fg-grow 420ms var(--ease) both;
}
/* 打开表单时线条自上而下生长，给一点"两列是被分开的"暗示 */
@keyframes fg-grow{from{height:0;opacity:0}to{height:100%;opacity:1}}
@media (prefers-reduced-motion:reduce){
  .form-grid::before{animation:none;height:100%;}
}
/* 整行字段已沉到双列区之后（不再夹在中间），这里只是保险：万一将来有人把整行字段
   放回双列区内，它的背景会把竖线遮住，不会出现"线穿过输入框"的怪样子。
   ⚠️ 只给 position:relative，**绝不能给 z-index**（2026-10-13 踩坑记录）：
   一旦字段带上 z-index，每个字段都成了独立层叠上下文，按 DOM 顺序绘制 ——
   前一个字段弹出的日期面板（.dsel__panel，position:fixed）会被**后一个字段的框**盖住，
   表现就是"日期选择器被下面的文本框压着"。position:relative + z-index:auto 不创建层叠上下文，
   面板能一路浮到弹窗层级；同时字段仍在 ::before 之后绘制，照样压得住竖线。
   两全其美的前提是竖线画在两列之间的沟里（left:50%），不穿过任何输入框。 */
.form-grid > .field--form{position:relative;}
/* 单列（手机 / 窄弹窗）不画线：没有两列，画了就是一条穿膛的竖线 */
@media (max-width:760px){
  .form-grid::before{display:none;}
}

/* ---- 山东省地图 ---- */
.board-map__body{display:grid;grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);gap:var(--s4);padding:var(--s4);}
/* 地图统计口径切换（2026-10-19 李总提：按项目数 / 按客户数） */
.map-toolbar{grid-column:1 / -1;display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;margin-bottom:var(--s1);}
.map-toolbar__t{font-size:var(--fs-11);color:var(--text-3);}
.map-wrap{
  position:relative;background:var(--surface-2);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:var(--s3);min-width:0;
  display:flex;align-items:center;justify-content:center;
}
.map-wrap svg{display:block;width:auto;max-width:100%;max-height:520px;height:auto;margin:0 auto;}
.sd-city{
  fill:var(--surface-3);stroke:var(--line-2);stroke-width:.8;
  transition:fill var(--fast) linear,stroke var(--fast) linear;cursor:default;
}
.sd-city.has-project{fill:var(--brand-soft);stroke:var(--brand-line);cursor:pointer;}
.sd-city.has-project:hover{fill:var(--brand);stroke:var(--brand-ink);}
.sd-city.is-hot2{fill:#cfe8dd;}
.sd-city.is-hot3{fill:#a8d6c5;}
.sd-city.is-hot4{fill:#7cbfaa;}
.sd-city.is-active{fill:var(--brand);stroke:var(--brand-ink);}
.sd-name{font-size:var(--fs-95);fill:var(--text-2);pointer-events:none;user-select:none;}
.sd-name--hot{fill:var(--brand-ink);font-weight:600;}
.sd-mark{cursor:pointer;}
.sd-mark circle{
  fill:var(--brand);stroke:#fff;stroke-width:1.4;
  transition:r var(--fast) var(--ease),fill var(--fast) linear;
}
.sd-mark:hover circle,.sd-mark.is-active circle{fill:var(--brand-ink);r:11;}
.sd-mark text{
  font-family:var(--mono);font-size:var(--fs-9);fill:#fff;font-weight:700;
  text-anchor:middle;dominant-baseline:central;pointer-events:none;user-select:none;
}
.map-legend{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s3);margin-top:var(--s2);font-size:var(--fs-11);color:var(--text-3);}
.map-legend i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:var(--s1);vertical-align:-1px;}
.map-side{display:flex;flex-direction:column;gap:var(--s2);min-width:0;max-height:560px;overflow:auto;}
.map-side h5{font-size:var(--fs-12);color:var(--text-2);}
.map-rank{display:flex;flex-direction:column;gap:var(--s0);}
.map-rank__row{
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:var(--s2);
  padding:var(--s1) var(--s2);border-radius:var(--r-xs);font-size:var(--fs-12);cursor:pointer;
  transition:background var(--fast) linear;
}
.map-rank__row:hover{background:var(--surface-2);}
.map-rank__row.is-active{background:var(--brand-soft);color:var(--brand-ink);}
.map-rank__row.is-active b{color:var(--brand-ink);}
.map-rank__row.is-active span{color:var(--brand-ink);opacity:.8;}
.map-rank__row b{font-weight:600;}
.map-rank__row span{color:var(--text-3);font-size:var(--fs-11);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.map-rank__row em{
  font-style:normal;font-family:var(--mono);font-variant-numeric:tabular-nums;
  color:var(--cnt);font-size:var(--fs-12);
}

/* ============================================================
   人员勾选控件 + 中文日期选择器
   —— 浮层一律 position:fixed（弹窗/表格内有 overflow:auto，absolute 会被裁掉）
   ============================================================ */

.picker{position:relative;}
.picker__btn{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--s2);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
  padding:var(--s2) var(--s3);font:inherit;font-size:var(--fs-13);color:var(--text);cursor:pointer;text-align:left;
  transition:border-color var(--fast) linear;
}
.picker__btn:hover{border-color:var(--brand-line);}
.picker__val{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.picker__val i{font-style:normal;color:var(--text-3);}
.picker__caret{color:var(--text-3);font-size:var(--fs-11);flex:0 0 auto;}
.picker__panel{
  position:fixed;z-index:var(--z-pop);width:270px;max-width:88vw;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-md);
  box-shadow:var(--sh3);padding:var(--s2);
}
.picker__list{max-height:230px;overflow:auto;display:flex;flex-direction:column;gap:1px;}
.popt{
  display:flex;align-items:center;gap:var(--s2);padding:6px var(--s2);
  border-radius:var(--r-xs);cursor:pointer;
}
.popt:hover{background:var(--surface-2);}
.popt.is-on{background:var(--brand-soft);}
/* 勾选框必须钉死尺寸：弹窗里有 .field--form input{width:100%}（0,2,1），
   若这里只写 .popt input（0,1,1）会被拉成整行宽 —— 2026-10-12 实测踩到 */
.picker .popt input{accent-color:var(--brand);width:16px;height:16px;flex:0 0 auto;margin:0;cursor:pointer;}
.popt__name{font-size:var(--fs-13);flex:0 0 auto;}
.popt__role{font-size:var(--fs-11);color:var(--text-3);}
.popt__flag{
  font-size:var(--fs-11);color:var(--brand);background:var(--surface);border:1px solid var(--brand-line);
  border-radius:2px;padding:0 var(--s1);margin-left:auto;white-space:nowrap;
}
.picker__foot{
  display:flex;justify-content:flex-end;gap:var(--s2);
  margin-top:var(--s2);padding-top:var(--s2);border-top:1px solid var(--line);
}
.picker__empty{font-size:var(--fs-12);color:var(--text-3);padding:var(--s2);}

.dsel{position:relative;}
.dsel__view{
  width:100%;background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-sm);
  padding:var(--s2) var(--s3);font:inherit;font-size:var(--fs-13);color:var(--text);cursor:pointer;
  font-variant-numeric:tabular-nums;
}
.dsel__view:hover{border-color:var(--brand-line);}
.dsel__panel{
  position:fixed;z-index:var(--z-pop);width:252px;max-width:88vw;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-md);
  box-shadow:var(--sh3);padding:var(--s2);
}
.dsel__head{display:flex;align-items:center;gap:var(--s1);margin-bottom:var(--s2);}
.dsel__head b{flex:1 1 auto;text-align:center;font-size:var(--fs-13);font-variant-numeric:tabular-nums;white-space:nowrap;}
.dsel__nav{
  border:1px solid var(--line);background:var(--surface-2);border-radius:var(--r-xs);
  width:24px;height:24px;cursor:pointer;font-size:var(--fs-14);line-height:1;color:var(--text-2);
}
.dsel__nav:hover{background:var(--surface-3);}
.dsel__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--s0);}
.dsel__wd{font-size:var(--fs-11);color:var(--text-3);text-align:center;padding-bottom:var(--s0);}
.dsel__day{
  border:0;background:transparent;border-radius:var(--r-xs);padding:var(--s1) 0;
  font:inherit;font-size:var(--fs-12);color:var(--text);cursor:pointer;font-variant-numeric:tabular-nums;
}
.dsel__day:hover{background:var(--surface-2);}
.dsel__day.is-out{color:var(--text-3);}
.dsel__day.is-today{box-shadow:inset 0 0 0 1px var(--brand-line);}
.dsel__day.is-sel{background:var(--brand);color:#fff;}
.dsel__foot{
  display:flex;justify-content:space-between;gap:var(--s2);
  margin-top:var(--s2);padding-top:var(--s2);border-top:1px solid var(--line);
}

/* ===== 会议看板焕新版（v2，可切换）===== */
.board-hero-wrap{margin:0;}
.board-hero{
  position:relative;overflow:hidden;color:#fff;border-radius:var(--r-lg);
  background:
    radial-gradient(120% 140% at 88% -10%, rgba(242,200,112,.20), rgba(242,200,112,0) 42%),
    radial-gradient(90% 120% at -5% 120%, rgba(15,110,86,.55), rgba(8,56,44,0) 55%),
    linear-gradient(135deg, #08382c 0%, #0b5142 46%, #0f6e56 100%);
  padding:var(--s6) var(--s6) 60px;
  display:flex;justify-content:space-between;align-items:flex-end;gap:var(--s5);flex-wrap:wrap;
}
.board-hero::after{
  content:"";position:absolute;inset:0;opacity:.07;pointer-events:none;
  background-image:
    repeating-linear-gradient(90deg, transparent 0 79px, #fff 79px 80px),
    repeating-linear-gradient(0deg, transparent 0 79px, #fff 79px 80px);
}
.board-hero__left{position:relative;z-index:var(--z-base);}
/* 第五批（2026-10-13）：下面三处副文案原来用 rgba(255,255,255,.72/.8/.82)。
   在 hero 最亮的那段底色（#0f6e56，右上角还叠了一层 20% 金色）上实测只有 4.0~4.6:1，
   13px 以下小字压着 AA 线走。统一抬到 .85 / .88 → 4.9:1 以上，肉眼看几乎没变化。 */
.board-hero__kicker{display:inline-flex;align-items:center;gap:var(--s2);font-size:var(--fs-12);letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.85);font-weight:600;}
.board-hero__dot{width:7px;height:7px;border-radius:50%;background:var(--gold);box-shadow:0 0 0 4px rgba(242,200,112,.25);}
.board-hero__title{margin:10px 0 6px;font-size:var(--fs-34);font-weight:700;letter-spacing:.02em;}
.board-hero__meta{color:rgba(255,255,255,.88);font-size:var(--fs-13);}
.board-hero__money{position:relative;z-index:var(--z-base);text-align:right;background:transparent;border:0;color:#fff;cursor:pointer;padding:0;min-width:240px;font:inherit;}
.board-hero__money .bstat__label{display:block;color:rgba(255,255,255,.85);font-size:var(--fs-13);letter-spacing:.14em;text-transform:uppercase;}
.board-hero__money .bstat__value{display:block;font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:var(--fs-56);font-weight:800;line-height:1.04;color:var(--gold);margin-top:var(--s0);text-shadow:0 6px 30px rgba(242,200,112,.35);letter-spacing:-.02em;}
.board-hero__money .bstat__foot{display:flex;justify-content:flex-end;color:rgba(255,255,255,.88);font-size:var(--fs-13);margin-top:6px;}
.board-hero__money:hover .bstat__foot{color:#fff;}
.board-kpis{
  position:relative;z-index:var(--z-board);
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s3);
  margin:-44px var(--s6) 0;
}
.board-kpis .bstat{box-shadow:var(--sh3);border:1px solid var(--line-2);background:var(--surface);}
.board-kpis .bstat:hover{box-shadow:var(--sh3);transform:translateY(-2px);}

/* ============ 医涵小智 · AI 悬浮助手 ============ */
.ai-fab{
  position:fixed;right:22px;bottom:22px;z-index:var(--z-modal);
  display:flex;align-items:center;gap:var(--s2);
  padding:10px var(--s4) 10px var(--s3);border:0;border-radius:999px;
  background:linear-gradient(135deg,var(--brand),var(--brand-ink));
  color:#fff;font-size:var(--fs-14);font-weight:600;cursor:pointer;
  box-shadow:0 8px 24px rgba(20,120,90,.35);transition:transform var(--fast) var(--ease),box-shadow var(--fast) var(--ease);
}
.ai-fab:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(20,120,90,.45);}
.ai-fab__icon{
  width:26px;height:26px;border-radius:50%;background:rgba(255,255,255,.22);
  display:grid;place-items:center;font-size:var(--fs-14);font-weight:800;
}
.ai-fab__label{letter-spacing:.5px;}

.ai-panel{
  position:fixed;right:22px;bottom:84px;z-index:var(--z-panel);
  width:440px;max-width:calc(100vw - 32px);height:620px;max-height:calc(100vh - 110px);
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:0 16px 48px rgba(15,40,30,.22);
}
.ai-panel__head{
  display:flex;align-items:center;justify-content:space-between;
  padding:var(--s3) 14px;background:linear-gradient(135deg,var(--brand),var(--brand-ink));color:#fff;
}
.ai-panel__title{font-size:var(--fs-15);font-weight:700;}
.ai-panel__title em{font-style:normal;font-size:var(--fs-11);opacity:.85;margin-left:6px;font-weight:500;}
.ai-panel__close{
  width:28px;height:28px;border:0;border-radius:var(--r-md);background:rgba(255,255,255,.18);
  color:#fff;font-size:var(--fs-18);line-height:1;cursor:pointer;
}
.ai-panel__close:hover{background:rgba(255,255,255,.32);}
.ai-panel__msgs{
  flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:var(--s3);background:var(--surface-2);
}
.ai-msg{display:flex;}
.ai-msg--user{justify-content:flex-end;}
.ai-msg--bot{justify-content:flex-start;}
.ai-msg__bubble{
  max-width:84%;padding:var(--s2) var(--s3);border-radius:var(--r-bubble);font-size:var(--fs-14);line-height:1.55;white-space:pre-wrap;word-break:break-word;
}
.ai-msg--user .ai-msg__bubble{background:var(--brand);color:#fff;border-bottom-right-radius:4px;}
.ai-msg--bot .ai-msg__bubble{background:var(--surface);border:1px solid var(--line);color:var(--text);border-bottom-left-radius:4px;}
.ai-typing{display:flex;gap:var(--s1);padding:0 var(--s4) 6px;background:var(--surface-2);}
.ai-typing span{
  width:7px;height:7px;border-radius:50%;background:var(--text-3);opacity:.5;animation:ai-bounce 1.2s infinite ease-in-out;
}
.ai-typing span:nth-child(2){animation-delay:.18s;}
.ai-typing span:nth-child(3){animation-delay:.36s;}
@keyframes ai-bounce{0%,80%,100%{transform:translateY(0);opacity:.4;}40%{transform:translateY(-5px);opacity:1;}}
.ai-panel__form{display:flex;gap:var(--s2);padding:10px;border-top:1px solid var(--line);background:var(--surface);}
.ai-panel__form textarea{
  flex:1;resize:none;max-height:200px;min-height:56px;padding:var(--s2) var(--s3);border:1px solid var(--line);border-radius:var(--r-pill);
  font:inherit;font-size:var(--fs-14);line-height:1.4;background:var(--surface-2);color:var(--text);
}
.ai-panel__form textarea:focus{outline:none;border-color:var(--brand);}
.ai-panel__form .btn{padding:0 var(--s4);align-self:flex-end;}

.ai-panel__tools{display:flex;align-items:center;gap:6px;}
.ai-panel__tool{
  width:28px;height:28px;border:0;border-radius:var(--r-md);background:rgba(255,255,255,.18);
  color:#fff;font-size:var(--fs-15);line-height:1;cursor:pointer;
}
.ai-panel__tool:hover{background:rgba(255,255,255,.32);}
.ai-panel__mic{
  flex:0 0 38px;width:38px;height:38px;align-self:flex-end;
  border:1px solid var(--line);background:var(--surface-2);border-radius:var(--r-pill);
  font-size:var(--fs-16);cursor:pointer;transition:border-color var(--fast) var(--ease),background var(--fast) var(--ease);
}
.ai-panel__mic:hover{border-color:var(--brand);}
.ai-panel__mic.is-recording{
  background:var(--danger);border-color:var(--danger);color:#fff;
  animation:ai-pulse 1.1s infinite;
}
.ai-panel__mic.is-disabled{opacity:.4;cursor:not-allowed;}
@keyframes ai-pulse{0%,100%{box-shadow:0 0 0 0 rgba(220,60,60,.5);}50%{box-shadow:0 0 0 7px rgba(220,60,60,0);}}

/* 断点 max-width:1080px（第三批 P2-16：原 4 块合并） */
@media (max-width:1080px){
  .app{grid-template-columns:minmax(0,1fr);}
  .sidebar{position:fixed;left:0;top:0;bottom:0;width:216px;z-index:var(--z-nav);transform:translateX(-100%);transition:transform var(--base) var(--ease);}
  .grid-2{grid-template-columns:1fr;}
  .login__panel{padding:var(--s5);}
  .modal{padding:var(--s3);}
  .modal__panel{max-height:92vh;}
  /* 顶栏：显示菜单按钮，允许换行，收紧内边距 */
  .btn-menu{
    display:inline-flex;align-items:center;justify-content:center;
    width:34px;height:34px;padding:0;flex:none;font-size:var(--fs-16);line-height:1;
    border:1px solid var(--line-2);border-radius:var(--r-sm);background:var(--surface);
  }
  .topbar{padding:var(--s2) var(--s3);gap:var(--s2);flex-wrap:wrap;}
  .topbar__left{gap:var(--s2);flex:1 1 auto;min-width:0;flex-wrap:nowrap;}
  .topbar__left h2{font-size:var(--fs-15);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
  .topbar__sub{display:none;}
  .topbar__right{gap:var(--s2);flex-wrap:wrap;}
  .view{padding:var(--s3);}
  /* 客户详情上下文条：跟着 .view 的内边距对齐（桌面 --s5） */
  .cd-bar{padding:var(--s2) var(--s3);}
  .section{margin-bottom:var(--s4);}

  /* 抽屉侧栏：默认移出屏幕，菜单按钮唤回 */
  .app.nav-open .sidebar{transform:translateX(0);box-shadow:var(--sh3);}
  .app.nav-open .nav-backdrop{display:block;position:fixed;inset:0;z-index:var(--z-backdrop);background:rgba(28,27,24,.34);}

  /* 看板 KPI 与大表在窄屏更紧凑 */
  .kpis{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));}
  .kpi__value{font-size:var(--fs-20);}

  /* 表格：窄屏下单元格不逐字换行，改为容器横向滚动；仅长文本字段保留换行 */
  .table-wrap table{font-size:var(--fs-13);}
  .table-wrap th,.table-wrap td{padding:var(--s1) var(--s3);white-space:nowrap;}
  .table-wrap th.cell-wrap,.table-wrap td.cell-wrap{white-space:normal;min-width:180px;}
  .board-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  /* 2 列时「合同变更」撑满一行：它只有 2 块瓦片，半行放着是个半空的格子，
     而下面的「预签约」要跨 2 列，留着半行只会让自动排布在它后面挖一个洞 */
  .mgrp[data-grp="cc"]{grid-column:span 2;}
  /* 2 列时「任务待办」也要跨 2 —— 此时没有第三列来凑数，半行就是半行 */
  .mgrp[data-grp="task"]{grid-column:span 2;}
  .board-map__body{grid-template-columns:minmax(0,1fr);}
  .map-rank{max-height:200px;overflow:auto;}
}

/* 断点 max-width:760px（第三批 P2-16：原 1 块合并） */
@media (max-width:760px){
  .board-hero{padding:var(--s5) var(--s4) 56px;}
  .board-hero__title{font-size:var(--fs-28);}
  .board-hero__money .bstat__value{font-size:var(--fs-40);}
  .board-hero__money{min-width:0;}
  .board-kpis{grid-template-columns:1fr;margin-top:-36px;}
  .sd-name{font-size:var(--fs-12);}
  .sd-mark text{font-size:var(--fs-12);}
  .sd-mark circle{r:12;}
  /* 客户详情上下文条：地区/联系人/电话在条子上让位（下方信息面板里还有完整信息） */
  .cd-bar__meta{display:none;}
}

/* 断点 max-width:560px（第三批 P2-16：原 6 块合并） */
@media (max-width:560px){
  /* ---- A3（2026-10-12 修）：手机上列表页**被裁掉且滚不动** ----
     桌面用 .fill-page{position:absolute;inset:0} 让表格区内部滚动、翻页条常驻底部；
     但手机上卡片行高是桌面的 3~4 倍，再配上 .panel{overflow:hidden} 的裁切 ——
     结果只能看到头两三张卡片；而外层 .view 因为 fill-page 是绝对定位也撑不高，
     于是"既看不到内容、也没有滚动条"。手机上改回文档流，交给 .view 自己滚。 */
  .fill-page{position:static;display:block;}
  .fill-page > .panel{flex:none;min-height:auto;}
  .panel{overflow:visible;}
  .fill-page__foot{position:static;}

  /* ---- C5：触摸目标 ≥44px ----
     只在触屏 / 窄屏下放大；桌面鼠标环境保持密度（本系统的设计语言就是密度优先）。 */
  .btn--sm{min-height:44px;padding:var(--s2) var(--s3);}
  /* 例外：工具栏按钮与「新增」齐平 —— C5 把小按钮抬到 44px，比同排的「新增」（默认
     .btn，约 34px）高出一截，一排按钮参差不齐（李总反馈：显示列 / 刷新 / 导出 Excel 偏大）。
     工具栏统一成同一高度。只收工具栏：表格行内、弹窗底部、翻页条仍保持 44px 触摸目标。 */
  .toolbar .btn,.toolbar .btn--sm{
    display:inline-flex;align-items:center;justify-content:center;
    min-height:34px;padding:var(--s1) var(--s3);font-size:var(--fs-13);
  }
  .dsel__nav{width:44px;height:44px;}
  .dsel__day{min-height:44px;}
  .ai-panel__close,.ai-panel__tool{min-width:44px;min-height:44px;}
  .btn-menu{min-width:44px;min-height:44px;}

  /* ---- C6：iOS 聚焦 font-size<16px 的输入框会自动放大整页 ----
     原来只补了弹窗 / 抽屉 / 登录三处，工具栏搜索框和合同字段复核表漏了 —— 改成全局兜底。 */
  input,select,textarea{font-size:var(--fs-16);}

  /* 工具栏在手机上换行占满，搜索框独占一行更好点 */
  .toolbar{flex-wrap:wrap;gap:var(--s2);}
  .toolbar__search{flex:1 1 100%;min-height:44px;}
  .toolbar__select{flex:1 1 100%;min-height:44px;font-size:var(--fs-16);}
  .toolbar__count{flex:1 1 100%;}

  /* 客户详情吸顶条：窄屏项目构成换行后占太宽，只保留「当前项目表」这一颗
     （项目构成在下方各表面板里本来就有标题和计数，条子上不重复） */
  .cd-bar__chips{display:none;}

  /* 回款树客户行：窄屏竖排三行 —— ① 箭头 + 客户名 ② 已回款占比条 + 百分比 ③ 金额标签 + 客户详情。
     桌面是一行排开（caret/名字/占比条/百分比/三组金额标签/入口），375px 宽度下全部挤在一行，
     标签块被压到接近 0 宽后内部自行换行，就叠成一摞。 */
  .ptree__head{flex-wrap:wrap;row-gap:var(--s2);align-items:center;height:auto;}
  .ptree__name{flex:1 1 auto;max-width:none;min-width:0;}
  .ptree__bar{order:1;flex:1 1 auto;min-width:0;max-width:none;}
  .ptree__pct{order:2;}
  .ptree__chips{order:3;margin-left:0;flex:1 1 auto;min-width:0;flex-wrap:wrap;overflow:visible;display:flex;}
  .ptree__go{order:4;flex:none;}
  /* 汇总卡：390px 下三列每格仅 ~106px，18px 金额（¥717,290 ≈ 86px）加上下内边距必然溢出，
     而溢出部分会被后一格的背景盖住 → 金额看着被"切掉」。改两行：
     第一行 未回款 / 已回款 两张大字金额卡（各占半宽，金额放得下），
     第二行 客户统计通栏、一行排开。 */
  .paysum{grid-template-columns:1fr 1fr;gap:var(--s2);}
  .paysum__cell{padding:var(--s2);}
  /* 两列 → 四格自然排成 2x2：合同总额|未回款 / 已回款|客户。
     18px 在 390px 下遇到「✓ ¥12,345,678」这类 13 位金额仍会溢出，降一档到 16px。 */
  .paysum__cell.is-unpaid em,.paysum__cell.is-paid em,.paysum__cell.is-contract em{font-size:var(--fs-16);}
  /* 金额含 ¥ 与长数字，禁换行避免被后格背景切掉 */
  .paysum__cell em{white-space:nowrap;}

  /* C20：toast 贴底时避开 iPhone 底部横条（同文件 .modal__foot 已做，这里补上） */
  .toast{bottom:calc(28px + env(safe-area-inset-bottom,0px));}

  .user-add-fab{bottom:120px;right:14px;padding:var(--s2) 14px;font-size:var(--fs-13);}
  .alert{grid-template-columns:60px 1fr;row-gap:var(--s0);}
  .alert .tag{display:none;}
  .facts{grid-template-columns:1fr 1fr;}
  /* C7：矮视口下 logo 固定 110px 会把面板顶出屏幕，改成随视口高度收缩 */
  .login__logo{max-height:clamp(64px,14vh,110px);}
  .login{padding:var(--s4);}
  .login__panel{padding:var(--s4);}
  .board-grid{grid-template-columns:minmax(0,1fr);}
  .mgrp[data-grp="cc"],.mgrp[data-grp="ps"],.mgrp[data-grp="task"]{grid-column:auto;}
  .mgrp__body{grid-template-columns:minmax(0,1fr);gap:var(--s2);}
  .mgrp__col--money{border-left:0;border-top:1px dashed var(--line-2);padding-left:0;padding-top:var(--s2);}
  .mtile__value{font-size:var(--fs-17);}
  .mtile--big .mtile__value{font-size:var(--fs-28);}
  .board-top{grid-template-columns:1fr 1fr;}
  .ai-panel{right:12px;left:12px;width:auto;bottom:76px;height:70vh;}
  .ai-fab{right:14px;bottom:14px;}
/* ============================================================
   手机端：列表表格 → 卡片列表
   —— 2026-10-12 李总定调：手机端是**正式录入入口**，不是「凑合能看」。
      560px 以下不再让用户横向拖着看一张十几列的大表，
      改为每行一张卡片：表头藏起来，列名由 app.js 的 table() 写进
      td 的 data-label，用 ::before 显示在值前面。
   —— 只改样式不动结构，桌面上表格长什么样一点没变。
   ============================================================ */
  /* 手机端不需要内部滚动，让整页跟着手指滚 */
  .table-wrap,
  .fill-page > .panel .table-wrap,
  .fill-page .grid-2 .panel .table-wrap{overflow:visible;}

  .table-wrap table{font-size:var(--fs-14);}

  /* 表头整行隐藏：列名已经在每个值前面了，再顶一行反而占掉半屏 */
  .table-wrap thead{display:none;}

  /* 一行 = 一张卡片（外框仍由 .panel 提供，这里只做分隔） */
  .table-wrap tbody tr{
    /* 原来是 display:block。改成 flex 列是为了让复选列能用 order 排到末尾 ——
       卡片模式下第一格是"卡片标题"，复选列必须让位，否则标题位置会变成一个孤零零的勾选框。 */
    display:flex;flex-direction:column;
    padding:var(--s3) var(--s4);
    border-bottom:1px solid var(--line);
  }
  .table-wrap tbody tr:last-child{border-bottom:0;}
  /* 手机上 :hover 会「粘住」不放，看起来像 bug，直接关掉 */
  .table-wrap tbody tr:hover{background:none;}

  /* 单元格 = 「标签 + 值」两列；桌面上的居中/右对齐/不换行全部让位 */
  .table-wrap tbody td{
    display:flex;align-items:flex-start;gap:var(--s3);
    padding:var(--s1) 0;
    border-bottom:0;
    white-space:normal;
    text-align:left;
    vertical-align:top;
  }
  .table-wrap tbody td::before{
    content:attr(data-label);
    flex:0 0 88px;          /* 标签定宽，右侧的值自然对成一列 */
    color:var(--text-3);
    font-size:var(--fs-12);
    line-height:1.75;
  }

  /* 列宽策略（col-xs/col-lg/num 的 nowrap 与上限）是桌面的事，卡片里全部放开 */
  .table-wrap tbody td{white-space:normal;min-width:0;max-width:none;overflow:visible;text-overflow:clip;}

  /* 第一格当卡片标题：不要标签，字号加大加粗，下面拉一条线跟明细分开 */
  .table-wrap tbody td:first-child{
    padding:0 0 var(--s2);
    margin-bottom:var(--s2);
    border-bottom:1px solid var(--line);
    font-size:var(--fs-15);
    font-weight:500;
    line-height:1.5;
  }
  .table-wrap tbody td:first-child::before{display:none;}

  /* 操作列：铺成一整行按钮，手指够得着（≥40px 高） */
  .table-wrap tbody td.col-actions{
    display:block;
    margin-top:var(--s2);
    padding:var(--s2) 0 0;
    border-top:1px solid var(--line);
  }
  /* 复选列：卡片里排到最末（order 98），排在操作行(99)之前；标签不占位 */
  .table-wrap tbody td.col-check{
    order:98;width:auto;max-width:none;padding:var(--s2) 0;margin-top:var(--s2);
    border-top:1px dashed var(--line);align-items:center;
  }
  .table-wrap tbody td.col-check::before{display:none;}
  .table-wrap tbody td.col-check::after{content:'选择本条';font-size:var(--fs-13);color:var(--text-2);}
  .table-wrap tbody td.col-check .row-check{width:22px;height:22px;display:inline-block;}
  .table-wrap tbody td.col-actions{order:99;}
  /* 手机上显示操作条里的「全选/反选」（表头整行是隐藏的） */
  .bulkbar__all{display:inline-flex;}

  .table-wrap tbody td.col-actions::before{display:none;}
  .table-wrap tbody td.col-actions .row-actions{display:flex;gap:var(--s2);flex-wrap:wrap;}
  .table-wrap tbody td.col-actions .btn{
    flex:1 1 auto;min-height:40px;padding:var(--s2) 14px;font-size:var(--fs-14);
  }

  /* 卡片模式：首列是"卡片标题"，不能再做横向吸附（固定首列 / 冻结列的规则都要撤掉） */
  .table-wrap th:first-child,
  .table-wrap td:first-child,
  .table-wrap th.is-fz,
  .table-wrap td.is-fz{position:static;left:auto;background:none;}
  .table-wrap .is-fz-edge{border-right:0;}

  /* 空值列：底下一排放最后，保证压倒上面的 display:flex/block */
  .table-wrap tbody td.is-empty{display:none;}
/* ============================================================
   手机端：录入表单
   —— 三件事：①弹窗全屏，别把本就不多的竖向空间留给留白；
              ②输入框一律 16px —— 低于 16px 时 iOS 一聚焦就放大整页，
                录一条记录要来回缩两次，这是手机上最难忍的问题；
              ③按钮/勾选项都撑到 ≥44px，手指点得准。
   ============================================================ */
  /* 弹窗铺满整屏（记录表单、账号资料编辑两个弹窗都是长表单） */
  .modal{padding:0;display:block;}
  .modal__panel{width:100%;height:100%;max-height:none;border:0;border-radius:0;}
  .modal__head{padding:var(--s3) var(--s4);border-radius:0;}
  .modal__head h3{font-size:var(--fs-15);}
  .modal__body{flex:1 1 auto;min-height:0;padding:var(--s4);overscroll-behavior:contain;}

  /* 底部操作条常驻屏幕底部：表单再长也不用滚到底找「保存」 */
  .modal__foot{
    flex:none;border-radius:0;
    padding:var(--s3) var(--s4);
    padding-bottom:calc(var(--s3) + env(safe-area-inset-bottom,0px));
  }
  /* 手机上同样靠右下：不再让两个按钮等分整行 —— 那会把"取消"做成和"保存"一样大，
     误触成本一样高。保持 44px 触摸高度即可。 */
  .modal__foot .btn{flex:0 1 auto;min-height:44px;font-size:var(--fs-15);}
  .drawer__foot .btn{flex:0 1 auto;min-height:44px;}
  .modal__foot .hint-line{display:none;}   /* 那句必填说明会把按钮挤扁，手机上省掉 */

  /* ---- 输入控件：16px 是硬要求 ---- */
  .modal__body .field--form > input,
  .modal__body .field--form > select,
  .modal__body .field--form > textarea,
  .modal__body .field--form .picker__btn,
  .modal__body .field--form .dsel__view,
  .drawer__body .field--form > input,
  .drawer__body .field--form > select{
    font-size:var(--fs-16);min-height:44px;padding:var(--s2) var(--s3);
  }
  .modal__body .field--form > textarea{min-height:72px;line-height:1.5;}
  .modal__body .field--form label,
  .drawer__body .field--form label{font-size:var(--fs-13);}
  .field--form{margin-bottom:var(--s4);}

  /* 登录/改密页同理：14px 输入框在 iOS 上一样会触发整页放大 */
  .login-form .field input{font-size:var(--fs-16);min-height:44px;}

  /* 人员勾选、日期选择器：行高和日期格子都要点得准 */
  .picker__panel,.dsel__panel{max-width:calc(100vw - 16px);}
  /* 双列栅格在手机上退回单列（P1-11） */
  .form-grid{grid-template-columns:minmax(0,1fr);}
  .form-grid > .field--wide{grid-column:auto;}
  .popt{min-height:44px;padding:var(--s2) var(--s2);}
  .picker .popt input{width:20px;height:20px;}
  .popt__name{font-size:var(--fs-14);}
  .picker__panel .btn,.dsel__panel .btn{min-height:40px;}
  .dsel__nav{width:36px;height:36px;font-size:var(--fs-16);}
  .dsel__head b{font-size:var(--fs-14);}
  .dsel__grid{gap:3px;}
  .dsel__day{min-height:38px;font-size:var(--fs-14);}

  /* 抽屉里的导航：先在手机上点得进表，才谈得上录入 */
  .nav__item{min-height:44px;font-size:var(--fs-14);}
  .sidebar__foot{flex-wrap:wrap;}
  .sidebar__foot .sidebar__user{flex:1 1 100%;}
  .sidebar__foot .btn{flex:1 1 auto;min-height:40px;padding:var(--s2) var(--s3);}
}

/* 减少动态效果：退化为静态 */
@media (prefers-reduced-motion:reduce){
/* 系统开启「减少动态效果」时退化为静态文字，不做无谓动画 */
  .login__slogan > span{
    animation:none;background:none;
    color:var(--brand);-webkit-text-fill-color:currentColor;
  }
  *{animation-duration:.001ms !important;transition-duration:.001ms !important;}
  .mtile{transition:none;}
  .mgrp__body:hover .mtile{transform:none;opacity:1;}
  .mgrp__body .mtile:hover,.mgrp__body .mtile.is-active{transform:none;}
}

/* ============================================================
   打印（C19，2026-10-12 新增）
   之前完全没有 @media print：直接 Ctrl+P 会把侧栏、AI 悬浮球、
   toast、按钮一起印出来，表格还会被截断。这里只做"能打"的最低限度：
   隐去壳层与交互件、表格不分页断开、显示全部列（取消省略号截断）。
   ============================================================ */
@media print{
  :root{--text-3:#63625b;}                      /* 打印时略加深，浅墨在纸上更糊 */
  .sidebar,.topbar,.nav-backdrop,.btn-menu,
  .ai-fab,.ai-panel,.user-add-fab,#toast,.idle-warn,
  .toolbar,.pager,.fill-page__foot,.row-actions,.bulkbar,#bulkMsg,
  .cd-bar,
  th.col-check,td.col-check,
  .modal,.drawer,.picker__panel,.dsel__panel{display:none !important;}

  body,.app,.view{position:static !important;display:block !important;overflow:visible !important;height:auto !important;}
  .view{padding:0 !important;}
  /* fill-page 在屏幕上是绝对定位铺满，打印时必须回到文档流，否则只印出第一屏 */
  .fill-page{position:static !important;display:block !important;}
  .fill-page > .panel{flex:none !important;min-height:auto !important;}
  .panel{overflow:visible !important;break-inside:avoid;box-shadow:none;}

  /* 打印时把被省略号截断的内容完整放出来（纸上看不到 title 提示） */
  .table-wrap{overflow:visible !important;max-height:none !important;}
  .col-xs,.col-md,.col-lg{max-width:none !important;white-space:normal !important;overflow:visible !important;text-overflow:clip !important;}
  .table-wrap th:first-child,.table-wrap td:first-child,
  .table-wrap th.is-fz,.table-wrap td.is-fz{position:static !important;}

  table{font-size:10pt;}
  thead{display:table-header-group;}             /* 多页时每页重复表头 */
  tr{break-inside:avoid;}
  .tag,.pill{border:1px solid var(--line-2);}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:8pt;color:var(--text-3);}
  @page{margin:12mm;}
}

/* ================= 合同附件组件 ================= */

/* 文件输入框：视觉隐藏但保留可点击（label 触发），避免 hidden 属性在某些浏览器无法唤起选择框 */
.cf-input{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;}

.cf-widget{display:block;}

.cf-drop{
  display:flex;align-items:center;gap:var(--s2);
  padding:var(--s3);border:1.5px dashed var(--line-3);border-radius:var(--r-md);
  background:var(--surface-2);cursor:pointer;color:var(--text-2);
  font-size:var(--fs-13);transition:border-color var(--fast),background var(--fast),color var(--fast);
}
.cf-drop:hover{border-color:var(--brand);background:var(--brand-soft);color:var(--brand-ink);}
.cf-drop.is-drag{border-color:var(--brand);background:var(--brand-soft);}
.cf-drop__icon{font-size:18px;line-height:1;color:var(--brand);font-weight:600;}

.cf-list{display:flex;flex-direction:column;gap:var(--s2);margin-top:var(--s3);}
.cf-row{
  display:flex;align-items:center;gap:var(--s2);flex-wrap:wrap;
  padding:var(--s2) var(--s3);background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-sm);
}
.cf-badge{
  flex:0 0 auto;min-width:44px;text-align:center;font-size:var(--fs-11);font-weight:600;
  padding:2px 6px;border-radius:var(--r-xs);border:1px solid;letter-spacing:.3px;
}
.cf-badge--pdf{color:var(--danger-ink);background:var(--danger-soft);border-color:var(--danger-line);}
.cf-badge--png,.cf-badge--jpg,.cf-badge--jpeg,.cf-badge--gif,.cf-badge--webp{color:var(--ok-ink);background:var(--ok-soft);border-color:var(--ok-line);}
.cf-badge--doc,.cf-badge--docx{color:var(--warn-ink);background:var(--warn-soft);border-color:var(--warn-line);}
.cf-name{flex:1 1 auto;min-width:80px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text);font-size:var(--fs-13);}
.cf-size{flex:0 0 auto;color:var(--text-3);font-size:var(--fs-11);}
.cf-acts{flex:0 0 auto;display:flex;gap:var(--s1);}

.cf-preview{width:100%;height:72vh;border:0;background:#fff;}
.cf-nopreview{padding:var(--s6);text-align:center;color:var(--text-2);line-height:1.7;}
.cf-nopreview .btn{margin-top:var(--s3);}
.cf-nodl{font-size:var(--fs-12);white-space:nowrap;}

/* 合同预览弹窗：更宽更高，让 PDF / 图片铺满 */
.modal__panel--wide{width:min(960px,96vw);max-height:92vh;}
.modal__panel--wide .modal__body{padding:0;flex:1 1 auto;min-height:0;}

@media print{
  .cf-drop,.cf-del{display:none !important;}
}

/* ============================================================
 * 周会跟进 · 定制表单与分组列表（2026-10-17 改版）
 * 一次周会 = ① 本周做了什么（多客户、不拆项目） + ② N 条跟进 / 协调事项
 * 表单分区用「序号圆标 + 副标题」区分两块，事项卡片左侧色条区分跟进（技术·蓝）/ 协调（商务·橙）。
 * ============================================================ */
.fw{display:flex;flex-direction:column;gap:var(--s5);}
.fw__sec{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);padding:var(--s4);}
.fw__sec:nth-of-type(1){background:var(--surface-2);}
.fw__title{
  display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s4);
  font-size:var(--fs-15);font-weight:600;color:var(--text);
}
.fw__title i{
  flex:none;width:20px;height:20px;border-radius:50%;background:var(--brand);color:#fff;
  font-style:normal;font-size:var(--fs-12);display:inline-flex;align-items:center;justify-content:center;
}
.fw__hint{
  margin-left:auto;font-size:var(--fs-12);font-weight:400;color:var(--text-3);
}
.fw__row{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);}
.fw__tags{
  display:flex;flex-wrap:wrap;gap:var(--s2);min-height:32px;align-items:center;
  padding:var(--s2);border:1px dashed var(--line-2);border-radius:var(--r-sm);background:var(--surface);
}
.fw__pick{
  margin-top:var(--s2);border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--surface);overflow:hidden;
}
.fw__search{
  width:100%;border:0;border-bottom:1px solid var(--line);padding:var(--s2) var(--s3);
  font:inherit;font-size:var(--fs-13);background:var(--surface);color:var(--text);
}
.fw__search:focus{outline:none;background:var(--brand-soft);}
.fw__list{max-height:224px;overflow:auto;padding:var(--s1);}
.fw__empty{padding:var(--s3);font-size:var(--fs-13);color:var(--text-3);text-align:center;}
.fw__tip{margin:var(--s3) 0 0;font-size:var(--fs-12);color:var(--text-3);line-height:1.6;}

/* 客户标签 */
.fwtag{
  display:inline-flex;align-items:center;gap:var(--s1);max-width:100%;
  padding:3px var(--s2);border:1px solid var(--line-2);border-radius:var(--r-pill);
  background:var(--surface);font-size:var(--fs-12);color:var(--text-2);
}
.fwtag.is-mine{border-color:var(--brand-line);background:var(--brand-soft);color:var(--brand-ink);font-weight:600;}
.fwtag--sm{padding:1px 8px;font-size:var(--fs-11);}
.fwtag__x{
  border:0;background:none;cursor:pointer;font:inherit;color:var(--text-3);
  padding:0 2px;line-height:1;border-radius:var(--r-xs);
}
.fwtag__x:hover{color:var(--danger);}

/* 候选行（客户 / 项目共用） */
.fwopt{
  display:flex;align-items:center;gap:var(--s2);width:100%;text-align:left;
  padding:var(--s2) var(--s3);border:0;border-radius:var(--r-sm);background:none;
  font:inherit;color:var(--text);cursor:pointer;
}
.fwopt:hover,.fwopt:focus-visible{background:var(--brand-soft);outline:none;}
.fwopt.is-mine .fwopt__name{color:var(--brand-ink);font-weight:600;}
.fwopt__name{flex:none;font-size:var(--fs-13);}
.fwopt__meta{flex:1;min-width:0;font-size:var(--fs-11);color:var(--text-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.fwopt__flag{
  flex:none;font-size:var(--fs-11);padding:0 6px;border-radius:var(--r-pill);
  background:var(--brand);color:#fff;
}

/* 事项卡片 */
.fwi{
  border:1px solid var(--line-2);border-left:3px solid var(--info);border-radius:var(--r-sm);
  background:var(--surface);padding:var(--s3);margin-bottom:var(--s3);
}
.fwi__head{display:flex;align-items:center;gap:var(--s2);margin-bottom:var(--s2);}
.fwi__who{font-size:var(--fs-12);color:var(--text-3);}
.fwi__head .btn{margin-left:auto;}
.fwi__grid{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:var(--s3);}
.fwi .field--form{margin-bottom:var(--s2);}
.fwproj{width:100%;justify-content:space-between;text-align:left;}
.fwproj .picker__caret{flex:none;margin-left:var(--s2);}

/* 类型分段控件：跟进 / 协调 二选一 */
.seg{display:inline-flex;border:1px solid var(--line-2);border-radius:var(--r-pill);overflow:hidden;}
.seg__b{
  border:0;background:var(--surface);padding:4px 16px;min-height:32px;
  font:inherit;font-size:var(--fs-13);color:var(--text-2);cursor:pointer;
}
.seg__b + .seg__b{border-left:1px solid var(--line-2);}
.seg__b.is-on{background:var(--brand);color:#fff;font-weight:600;}
.seg__b:focus-visible{outline:2px solid var(--brand);outline-offset:1px;}

/* ---- 列表：按会议分组 ---- */
.fwlist{display:flex;flex-direction:column;gap:var(--s2);padding:var(--s2) 0;}
.fwg{border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);overflow:hidden;}
.fwg__head{
  display:grid;grid-template-columns:16px 72px 96px minmax(120px,auto) 1fr auto auto;
  align-items:center;gap:var(--s3);padding:var(--s3);
  cursor:pointer;transition:background var(--fast) linear;
}
.fwg__head:hover{background:var(--surface-2);}
.fwg__head.is-open{background:var(--surface-2);border-bottom:1px solid var(--line);}
.fwg__caret{color:var(--text-3);font-size:var(--fs-12);}
.fwg__week{font-family:var(--mono);font-size:var(--fs-12);color:var(--text-2);}
.fwg__date{font-family:var(--mono);font-size:var(--fs-13);color:var(--text);}
.fwg__custs{display:flex;flex-wrap:wrap;gap:4px;}
.fwg__sum{font-size:var(--fs-13);color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.fwg__counts{font-size:var(--fs-12);color:var(--warn-ink);white-space:nowrap;}
.fwg__acts{display:flex;gap:var(--s1);}
.fwg__body{padding:var(--s3);background:var(--surface);}
.fwg__progress{display:flex;gap:var(--s3);margin-bottom:var(--s3);font-size:var(--fs-13);line-height:1.7;}
.fwg__k{flex:none;color:var(--text-3);font-size:var(--fs-12);}
.fwsub{
  display:grid;grid-template-columns:56px minmax(120px,1fr) 2fr auto;align-items:center;gap:var(--s3);
  padding:var(--s2) var(--s3);border-left:3px solid var(--info);border-radius:var(--r-xs);
  background:var(--surface-2);margin-bottom:var(--s1);font-size:var(--fs-13);
}
.fwsub--biz{border-left-color:var(--warn);}
.fwsub__note{color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.fwsub__who{font-size:var(--fs-12);color:var(--text-3);white-space:nowrap;}

@media (max-width:760px){
  .fw__row,.fwi__grid{grid-template-columns:1fr;}
  .fwg__head{grid-template-columns:16px 1fr auto;row-gap:var(--s2);}
  .fwg__week,.fwg__date,.fwg__sum{grid-column:2;}
  .fwg__custs{grid-column:2;}
  .fwg__counts{grid-column:2;}
  .fwg__acts{grid-column:3;grid-row:1 / span 4;align-self:start;}
  .fwsub{grid-template-columns:56px 1fr;row-gap:2px;}
  .fwsub__who{grid-column:2;}
  .seg__b{min-height:40px;padding:6px 18px;}
}
