:root{
  --bg:#f5f6f7;
  --panel:#ffffff;
  --panel-2:#f7f8fa;
  --panel-3:#eef0f3;
  --line:#e4e6eb;
  --line-soft:#f0f1f3;
  --text:#1a1a1a;
  --text-2:#666b73;
  --text-3:#9aa0a8;
  --brand:#1677ff;
  --brand-soft:#0958d9;
  --brand-bg:#e8f2ff;
  --green:#00b42a;
  --green-bg:#e8f8ec;
  --amber:#ff7d00;
  --amber-bg:#fff4e6;
  --red:#f53f3f;
  --red-bg:#ffeded;
  --purple:#722ed1;
  --purple-bg:#f3ebff;
  --radius:10px;
  --radius-lg:14px;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  background:var(--bg);color:var(--text);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;
  font-size:13px;line-height:1.6;-webkit-font-smoothing:antialiased;
}
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-thumb{background:#d0d3d9;border-radius:8px}
::-webkit-scrollbar-thumb:hover{background:#3d4757}
::-webkit-scrollbar-track{background:transparent}

.app{display:flex;height:100vh;overflow:hidden}

/* ============ 侧边导航 ============ */
.sidebar{
  width:212px;flex:0 0 212px;background:var(--panel);
  border-right:1px solid var(--line);display:flex;flex-direction:column;
}
.brand{
  padding:18px 18px 16px;display:flex;align-items:center;gap:10px;
  border-bottom:1px solid var(--line-soft);
}
.brand-logo{
  width:30px;height:30px;border-radius:8px;background:linear-gradient(135deg,#1677ff,#2f7cf6);
  display:flex;align-items:center;justify-content:center;flex:0 0 30px;
}
.brand-logo svg{width:17px;height:17px}
.brand-title{font-size:14px;font-weight:600;letter-spacing:.2px}
.brand-sub{font-size:11px;color:var(--text-3);margin-top:-2px}

.nav{padding:12px 10px;flex:1;overflow-y:auto}
.nav-group{font-size:11px;color:var(--text-3);padding:12px 10px 6px;letter-spacing:.5px}
.nav-item{
  display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:8px;
  color:var(--text-2);cursor:pointer;user-select:none;transition:.14s;margin-bottom:1px;
}
.nav-item:hover{background:var(--panel-2);color:var(--text)}
.nav-item.active{background:var(--brand-bg);color:#0958d9;font-weight:500}
.nav-item.active .nav-ico{color:#1677ff}
/* 无权限的入口：置灰 + 不可点（权限不足时由 refreshPermGates 打上 disabled） */
.nav-item.disabled{opacity:.42;cursor:not-allowed}
.nav-item.disabled:hover{background:transparent;color:inherit}
.nav-ico{width:16px;height:16px;flex:0 0 16px;display:flex;align-items:center;justify-content:center}
.nav-ico svg{width:15px;height:15px}
.nav-badge{
  margin-left:auto;font-size:11px;background:var(--red);color:#fff;
  border-radius:9px;padding:0 6px;line-height:16px;min-width:16px;text-align:center;
}
.side-foot{
  padding:12px 14px;border-top:1px solid var(--line-soft);
  font-size:11px;color:var(--text-3);display:flex;align-items:center;gap:7px;
}
.dot-live{width:6px;height:6px;border-radius:50%;background:var(--green);box-shadow:0 0 0 3px var(--green-bg)}
.dot-off{width:6px;height:6px;border-radius:50%;background:var(--text-3);box-shadow:0 0 0 3px rgba(134,144,156,.15)}
.dot-wait{width:6px;height:6px;border-radius:50%;background:var(--warn,#ff7d00);box-shadow:0 0 0 3px rgba(255,125,0,.15)}

/* ============ 主区 ============ */
.main{flex:1;display:flex;flex-direction:column;overflow:hidden}

/* ============ 移动端底部标签栏 ============
   桌面**不显示**；≤768px 才显示，同时把侧边栏收起来（见下方媒体查询）。
   放在 .app 里当最后一个 flex 子项，自然贴底 —— 不用 position:fixed，
   也就不需要给 .content 补 padding-bottom 去躲它。 */
.tabbar{display:none}
.tb-item{
  flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;
  padding:6px 2px;border:0;background:none;color:var(--text-3);
  font-size:10.5px;font-family:inherit;cursor:pointer;position:relative;
  transition:color .14s;user-select:none;-webkit-tap-highlight-color:transparent;
}
.tb-item.active{color:#1677ff;font-weight:500}
.tb-item.disabled{opacity:.4;pointer-events:none}
.tb-ico{display:flex;align-items:center;justify-content:center;height:20px}
.tb-ico svg{width:20px;height:20px}
.tb-lb{line-height:1.2;white-space:nowrap}
/* 标签栏上的角标贴在图标右上角（侧栏那套 .nav-badge 是 margin-left:auto，横排里会跑偏） */
.tb-item .nav-badge{position:absolute;top:1px;left:50%;margin-left:6px}

/* 「更多」弹层里的入口列表 */
.more-list{display:flex;flex-direction:column;gap:2px}
.more-item{
  display:flex;align-items:center;gap:12px;width:100%;padding:13px 10px;
  border:0;background:none;border-radius:10px;color:var(--text);
  font-size:14px;font-family:inherit;cursor:pointer;text-align:left;transition:.14s;
}
.more-item:hover{background:var(--panel-2)}
.more-item.disabled{opacity:.4;pointer-events:none}
.more-item .mi-ico{width:20px;height:20px;flex:0 0 20px;color:var(--text-2);display:flex;align-items:center;justify-content:center}
.more-item .mi-ico svg{width:19px;height:19px}
.more-item .mi-lb{flex:1}
.more-item .mi-arrow{width:16px;height:16px;color:var(--text-3);flex:0 0 16px}
.topbar{
  height:56px;flex:0 0 56px;border-bottom:1px solid var(--line);
  display:flex;align-items:center;padding:0 22px;gap:14px;background:var(--panel);
}
.crumb{font-size:13px;color:var(--text-2);display:flex;align-items:center;gap:7px}
.crumb b{color:var(--text);font-weight:500}
.top-space{flex:1}
/* 连接状态胶囊：桌面端用侧边栏底部那个（.side-foot），这里默认隐藏。
   手机端侧边栏**整条收起** → 不补一个的话，「已连接 / 离线 / 同步中…」在手机上
   就彻底看不到了（离线了用户还以为在正常同步）。两者由 updateCloudBadge() 同步更新。 */
.conn-chip{
  display:none;align-items:center;gap:6px;flex:0 0 auto;
  font-size:11px;color:var(--text-3);padding:3px 9px;
  border:1px solid var(--line);border-radius:999px;white-space:nowrap;
}
.icon-btn{
  width:32px;height:32px;border-radius:8px;border:1px solid var(--line);
  background:var(--panel-2);display:flex;align-items:center;justify-content:center;
  cursor:pointer;color:var(--text-2);transition:.14s;position:relative;
}
.icon-btn:hover{border-color:#c9ccd1;color:var(--text)}
.icon-btn svg{width:15px;height:15px}
.badge-dot{position:absolute;top:5px;right:5px;width:6px;height:6px;border-radius:50%;background:var(--red)}
.user-chip{
  display:flex;align-items:center;gap:9px;padding:4px 10px 4px 4px;border-radius:20px;
  border:1px solid var(--line);background:var(--panel-2);cursor:pointer;
}
.avatar{
  width:26px;height:26px;border-radius:50%;background:linear-gradient(135deg,#1677ff,#2f7cf6);
  display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:600;color:#fff;
}
.user-chip .nm{font-size:12px}
.user-chip .rl{font-size:10px;color:var(--text-3);margin-top:-3px}

.content{flex:1;overflow-y:auto;padding:22px 24px 40px}
.page{display:none;animation:fade .18s ease}
.page.active{display:block}
@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

/* ============ 通用组件 ============ */
.page-head{display:flex;align-items:flex-start;gap:14px;margin-bottom:18px}
.page-head h1{font-size:19px;font-weight:600;letter-spacing:.2px}
.page-head p{font-size:12.5px;color:var(--text-3);margin-top:3px}
.page-head .sp{flex:1}

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  height:32px;padding:0 13px;border-radius:8px;border:1px solid var(--line);
  background:var(--panel-2);color:var(--text);font-size:12.5px;cursor:pointer;
  transition:.14s;font-family:inherit;white-space:nowrap;
}
.btn:hover{background:var(--panel-3);border-color:#c9ccd1}
.btn svg{width:14px;height:14px}
.btn.primary{background:var(--brand);border-color:var(--brand);color:#fff}
.btn.primary:hover{background:#0958d9}
.btn.danger{color:#d92d20;border-color:#ffd0cc}
.btn.danger:hover{background:var(--red-bg)}
.btn.ghost{background:transparent}
.btn.sm{height:27px;padding:0 10px;font-size:12px}
.btn:disabled{opacity:.45;cursor:not-allowed}

.card{
  background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-lg);
}
.card-h{
  padding:14px 18px;border-bottom:1px solid var(--line-soft);
  display:flex;align-items:center;gap:10px;
}
.card-h h3{font-size:14px;font-weight:500}
.card-h .sp{flex:1}
.card-b{padding:18px}

.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:16px}
.kpi{
  background:var(--panel);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:16px 18px;position:relative;overflow:hidden;
}
.kpi-label{font-size:12px;color:var(--text-3);display:flex;align-items:center;gap:6px}
.kpi-val{font-size:24px;font-weight:600;margin-top:8px;letter-spacing:.3px;font-variant-numeric:tabular-nums}
.kpi-val small{font-size:12px;font-weight:400;color:var(--text-3);margin-left:3px}
.kpi-foot{font-size:11.5px;color:var(--text-3);margin-top:6px;display:flex;align-items:center;gap:5px}
.trend-up{color:var(--green)}
.trend-down{color:var(--red)}

.grid-2{display:grid;grid-template-columns:1.55fr 1fr;gap:14px}
.grid-2b{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.stack{display:flex;flex-direction:column;gap:14px}

.chip{
  display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 9px;
  border-radius:6px;font-size:11.5px;border:1px solid transparent;white-space:nowrap;
}
.chip.gray{background:var(--panel-3);color:var(--text-2);border-color:var(--line)}
.chip.blue{background:var(--brand-bg);color:#0958d9;border-color:#bcdcff}
.chip.green{background:var(--green-bg);color:#007a1e;border-color:#b7e9c4}
.chip.amber{background:var(--amber-bg);color:#c25c00;border-color:#ffd9a8}
.chip.red{background:var(--red-bg);color:#d92d20;border-color:#ffc9c4}
.chip.purple{background:var(--purple-bg);color:#5b21b6;border-color:#ddc9ff}

table{width:100%;border-collapse:collapse}
thead th{
  text-align:left;font-size:11.5px;font-weight:500;color:var(--text-3);
  padding:9px 14px;border-bottom:1px solid var(--line);white-space:nowrap;
  background:var(--panel-2);position:sticky;top:0;z-index:2;
}
tbody td{padding:11px 14px;border-bottom:1px solid var(--line-soft);font-size:12.5px;vertical-align:middle}
tbody tr:last-child td{border-bottom:none}
tbody tr{transition:.12s}
tbody tr:hover{background:var(--panel-2)}
tbody tr.clickable{cursor:pointer}
.num{font-variant-numeric:tabular-nums;text-align:right}
.name-cell{display:flex;align-items:center;gap:9px}
.name-cell .avatar{width:26px;height:26px;font-size:11px;flex:0 0 26px}
.name-cell .nm{font-weight:500;font-size:12.5px}
.name-cell .sm{font-size:11px;color:var(--text-3);margin-top:-3px}

.tabs{display:flex;gap:3px;border-bottom:1px solid var(--line);margin-bottom:0;padding:0 18px}
.tab{
  padding:10px 12px;font-size:12.5px;color:var(--text-2);cursor:pointer;
  border-bottom:2px solid transparent;margin-bottom:-1px;transition:.14s;white-space:nowrap;
}
.tab:hover{color:var(--text)}
.tab.active{color:#1677ff;border-bottom-color:var(--brand);font-weight:500}

.toolbar{display:flex;align-items:center;gap:10px;padding:13px 18px;flex-wrap:wrap}
/* 「筛选」折叠（手机端专用，见 @media 里的展开规则）：
   桌面端横向空间够，7 个筛选项平铺一行反而更快 —— 所以按钮不显示，
   容器用 `display:contents` 让自己「不参与布局」，子元素照旧是 .toolbar 的 flex 项。
   这样 DOM 变了、桌面视觉一个像素没变，也不用维护两套结构。 */
.ps-filter-btn{display:none}
.ps-filters{display:contents}
.ps-filter-n{display:none}
.ps-count{font-size:12px;color:var(--text-3)}
.ps-sw{margin-left:4px}
.field{
  height:31px;border:1px solid var(--line);background:var(--panel-2);border-radius:8px;
  color:var(--text);padding:0 10px;font-size:12.5px;font-family:inherit;outline:none;transition:.14s;
}
.field:focus{border-color:var(--brand);background:var(--panel)}
select.field{cursor:pointer;padding-right:26px;appearance:none;
  background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7686' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 7px center;background-size:12px}
.search{position:relative;display:flex;align-items:center}
.search svg{position:absolute;left:9px;width:13px;height:13px;color:var(--text-3);pointer-events:none}
.search .field{padding-left:29px;width:200px}

.empty{padding:52px 20px;text-align:center;color:var(--text-3)}
.empty svg{width:36px;height:36px;opacity:.35;margin-bottom:10px}
.empty .t{font-size:13px;color:var(--text-2);margin-bottom:4px}
.empty .s{font-size:12px}

.progress{height:5px;background:var(--panel-3);border-radius:4px;overflow:hidden;width:100%}
.progress > i{display:block;height:100%;background:var(--brand);border-radius:4px;transition:width .3s}

/* ============ 向导 ============ */
.steps{display:flex;align-items:center;gap:0;padding:18px 22px;border-bottom:1px solid var(--line-soft)}
.step{display:flex;align-items:center;gap:9px;flex:0 0 auto}
.step-n{
  width:25px;height:25px;border-radius:50%;border:1px solid var(--line);background:var(--panel-2);
  display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--text-3);
  font-weight:500;transition:.18s;flex:0 0 25px;
}
.step .lb{font-size:12.5px;color:var(--text-3);transition:.18s;white-space:nowrap}
.step.active .step-n{background:var(--brand);border-color:var(--brand);color:#fff}
.step.active .lb{color:var(--text);font-weight:500}
.step.done .step-n{background:var(--green-bg);border-color:#9edbaf;color:#007a1e}
.step.done .lb{color:var(--text-2)}
.step-line{flex:1;height:1px;background:var(--line);margin:0 14px;min-width:20px}

.drop{
  border:1.5px dashed #c9ccd1;border-radius:var(--radius-lg);background:var(--panel-2);
  padding:38px 20px;text-align:center;cursor:pointer;transition:.16s;
}
.drop:hover{border-color:var(--brand);background:var(--brand-bg)}
.drop.over{border-color:var(--brand);background:var(--brand-bg)}
.drop svg{width:34px;height:34px;color:var(--text-3);margin-bottom:12px}
.drop .t{font-size:13.5px;margin-bottom:5px}
.drop .t b{color:#1677ff;font-weight:500}
.drop .s{font-size:12px;color:var(--text-3)}

.req-row{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
.req{
  flex:1;min-width:170px;background:var(--panel-2);border:1px solid var(--line);
  border-radius:9px;padding:10px 12px;
}
.req .t{font-size:12px;font-weight:500;display:flex;align-items:center;gap:6px}
.req .s{font-size:11.5px;color:var(--text-3);margin-top:2px;line-height:1.5}

.notice{
  display:flex;gap:9px;padding:11px 13px;border-radius:9px;font-size:12px;line-height:1.6;
  background:var(--brand-bg);border:1px solid #bcdcff;color:#0958d9;
}
.notice svg{width:14px;height:14px;flex:0 0 14px;margin-top:2px}
.notice.warn{background:var(--amber-bg);border-color:#ffd9a8;color:#c25c00}
.notice.err{background:var(--red-bg);border-color:#ffc9c4;color:#d92d20}
.notice.ok{background:var(--green-bg);border-color:#b7e9c4;color:#007a1e}
/* 身份提示条：免登状态（沿用 notice 的配色语义） */
.sso-bar{display:flex;align-items:center;gap:10px;padding:8px 20px;font-size:12px;line-height:1.6;
  border-bottom:1px solid var(--line)}
.sso-bar .sp{flex:1}
.sso-bar.ok{background:var(--green-bg);color:#007a1e}
.sso-bar.warn{background:var(--amber-bg);color:#c25c00}
.sso-bar b{font-weight:600}
.sso-bar .btn.sm{height:24px;padding:0 10px;font-size:11.5px}

.radio-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.rcard{
  border:1px solid var(--line);background:var(--panel-2);border-radius:10px;
  padding:13px 14px;cursor:pointer;transition:.15s;position:relative;
}
.rcard:hover{border-color:#c9ccd1}
.rcard.on{border-color:var(--brand);background:var(--brand-bg)}
.rcard .t{font-size:13px;font-weight:500;display:flex;align-items:center;gap:7px}
.rcard .s{font-size:11.5px;color:var(--text-3);margin-top:4px;line-height:1.5}
.rcard .rec{font-size:10.5px;color:#007a1e;background:var(--green-bg);border-radius:4px;padding:1px 5px}
.rcard .tick{
  position:absolute;top:11px;right:12px;width:15px;height:15px;border-radius:50%;
  border:1.5px solid var(--line);display:flex;align-items:center;justify-content:center;
}
.rcard.on .tick{border-color:var(--brand);background:var(--brand)}
.rcard .tick svg{width:9px;height:9px;color:#fff;opacity:0}
.rcard.on .tick svg{opacity:1}

.form-row{display:flex;align-items:flex-start;gap:14px;padding:13px 0;border-bottom:1px solid var(--line-soft)}
.form-row:last-child{border-bottom:none}
.form-row > label{width:104px;flex:0 0 104px;font-size:12.5px;color:var(--text-2);padding-top:6px}
.form-row .ctl{flex:1;min-width:0}
.form-row .hint{font-size:11.5px;color:var(--text-3);margin-top:5px}
.switch{display:inline-flex;align-items:center;gap:9px;cursor:pointer;user-select:none}
.switch input{display:none}
.switch .track{
  width:34px;height:19px;border-radius:12px;background:var(--panel-3);
  border:1px solid var(--line);position:relative;transition:.18s;flex:0 0 34px;
}
.switch .track::after{
  content:"";position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;
  background:#c9ccd1;transition:.18s;
}
.switch input:checked + .track{background:var(--brand);border-color:var(--brand)}
.switch input:checked + .track::after{transform:translateX(15px);background:#fff}
.switch .sl{font-size:12.5px;color:var(--text-2)}

/* ============ 启动遮罩（防止「先闪模拟数据」） ============
   问题：MockSource 一构造就带着 5 个演示批次 + 演示通讯录（buildHistory / EMPLOYEES），
   而身份（免登）是**异步**拿的 —— boot() 里 await SSO.load() 要等一个网络往返。
   这段时间页面已经渲染完了，于是用户先看到「已确认 15 人 (79%)」这类**演示统计**，
   等身份回来才被真实数据替换。员工瞥见别人的（假）工资数据，性质就是数据泄露。

   解法：正式域名（非本机开发）上，**身份确定之前整页不显示任何数据**。
   用一个覆盖在内容区之上的遮罩，等 boot() 走到「身份已知」或「展示门禁」时再撤掉。
   本机开发（looksLikeLocalDev）不加这个遮罩，免得打断 npm run dev 的调试节奏。 */
#bootMask{
  position:fixed;inset:0;z-index:9000;display:none;
  flex-direction:column;align-items:center;justify-content:center;gap:14px;
  background:var(--bg);
}
body.booting #bootMask{display:flex}
#bootMask .bm-logo{font-size:15px;font-weight:600;color:var(--brand);letter-spacing:.5px}
#bootMask .bm-t{font-size:12.5px;color:var(--text-3)}
#bootMask .bm-dots{display:flex;gap:5px}
#bootMask .bm-dots i{width:6px;height:6px;border-radius:50%;background:var(--brand);opacity:.3;
  animation:bmPulse 1.1s ease-in-out infinite}
#bootMask .bm-dots i:nth-child(2){animation-delay:.18s}
#bootMask .bm-dots i:nth-child(3){animation-delay:.36s}
@keyframes bmPulse{0%,100%{opacity:.22;transform:scale(.82)}50%{opacity:.95;transform:scale(1)}}
/* 员工独立页模式：遮罩由 emp-mode 自己管（它不显示工作台），别叠两层 */
body.emp-mode #bootMask{display:none}

/* 未登录门禁：服务端开了 SSO 但当前没有会话时整页挡住 */
#loginGate{
  position:fixed;inset:0;z-index:9999;display:none;
  align-items:center;justify-content:center;background:var(--bg);
}
#loginGate.on{display:flex}
#loginGate .lg-box{text-align:center;max-width:400px;padding:40px 28px}
#loginGate .lg-logo{font-size:15px;font-weight:600;color:var(--brand);letter-spacing:.5px}
#loginGate .lg-t{font-size:19px;font-weight:600;margin:16px 0 10px}
/* pre-line：让说明里的 
 真正换行。
   注意别用 <br> —— 内容经过 esc() 转义，会原样显示成 "&lt;br&gt;"。 */
#loginGate .lg-s{font-size:13px;color:var(--text-3);line-height:1.9;white-space:pre-line}
#loginGate .lg-s b{color:var(--text-2)}
#loginGate .lg-btns{margin-top:24px;display:flex;gap:10px;justify-content:center}

/* 表格里的勾选框（工资条管理列表用来挑人批量发送） */
.cb{display:inline-flex;align-items:center;justify-content:center;cursor:pointer;user-select:none;vertical-align:middle;position:relative}
.cb input{position:absolute;opacity:0;width:0;height:0}
.cb span{
  width:16px;height:16px;border:1.5px solid var(--line);border-radius:4px;
  background:var(--panel);display:inline-block;position:relative;transition:.15s;flex:0 0 16px;
}
.cb:hover span{border-color:var(--brand)}
.cb input:checked + span,.cb input:indeterminate + span{background:var(--brand);border-color:var(--brand)}
.cb input:checked + span::after{
  content:"";position:absolute;left:4.5px;top:1.5px;width:4px;height:8px;
  border:2px solid #fff;border-top:0;border-left:0;transform:rotate(45deg);
}
.cb input:indeterminate + span::after{
  content:"";position:absolute;left:3px;top:6px;width:8px;height:2px;background:#fff;border-radius:1px;
}
tr.on > td{background:rgba(22,119,255,.06)}

.tbl-scroll{max-height:330px;overflow:auto;border:1px solid var(--line);border-radius:10px}
.tbl-scroll table thead th{background:var(--panel-3)}
.hl-warn{background:rgba(245,158,11,.07)}
.hl-err{background:rgba(239,68,68,.07)}

/* ============ 工资条预览（员工端样式） ============ */
.preview-wrap{display:flex;justify-content:center;padding:20px 0}
.slip{
  width:308px;background:#fff;border-radius:16px;overflow:hidden;
  box-shadow:0 8px 28px rgba(0,0,0,.14);
}
.slip-top{background:linear-gradient(135deg,#1677ff,#2f7cf6);padding:18px 18px 38px;color:#fff}
.slip-top .t{font-size:14px;font-weight:600}
.slip-top .s{font-size:11px;opacity:.82;margin-top:2px}
.slip-amt{margin-top:14px}
.slip-amt .lb{font-size:11px;opacity:.82}
.slip-amt .vl{font-size:27px;font-weight:700;letter-spacing:.5px;font-variant-numeric:tabular-nums}
.slip-body{background:#fff;margin-top:-24px;border-radius:16px 16px 0 0;padding:16px 18px 20px;position:relative}
.slip-sec{font-size:11px;color:#94a3b8;margin:14px 0 7px;letter-spacing:.4px}
.slip-sec:first-child{margin-top:2px}
.slip-row{display:flex;justify-content:space-between;padding:6.5px 0;font-size:12.5px;border-bottom:1px solid #f1f5f9}
.slip-row:last-child{border-bottom:none}
.slip-row .k{color:#64748b}
.slip-row .v{color:#1a1a1a;font-variant-numeric:tabular-nums;font-weight:500}
.slip-row.total{border-top:1.5px solid #e2e8f0;margin-top:6px;padding-top:9px}
.slip-row.total .k{color:#1a1a1a;font-weight:600}
.slip-row.total .v{color:#1677ff;font-weight:700;font-size:13.5px}
.slip-row.sub .v{color:#dc2626}
.slip-foot{text-align:center;font-size:10.5px;color:#94a3b8;padding:12px 0 4px;line-height:1.7}
/* 弹窗内的工资条：去掉外层留白，让卡片顶到弹窗两侧 */
.sx-slip{background:var(--panel-2);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.sx-slip .slip-top{padding:16px 18px 34px}
.sx-slip .slip-body{margin-top:-22px}

/* ============ 员工独立页（从工作通知点进来时） ============
   员工点通知卡片进来，看到的应该**只有自己的工资条**：
   没有侧栏、没有顶栏、没有管理模块入口，也不需要（也不该）先登录。
   手机端与电脑端同一套规则，区别只在卡片宽度。
   这些规则必须放在本文件 CSS 的最后 —— 否则和前面的 .sidebar/.topbar
   同为单类选择器时会被后者覆盖。 */
.emp-only{display:none}
/* 只留正中间那一块内容区：侧栏、顶栏、面包屑全部撤掉 */
body.emp-mode .emp-only{display:flex;flex-direction:column;min-height:100vh;background:var(--panel-3)}
body.emp-mode .app{display:none}
body.emp-mode #loginGate{display:none!important}   /* 看工资条不需要登录，门禁不该盖上来 */
body.emp-mode .toast-wrap{top:auto;bottom:24px}
/* 员工页头 */
.emp-bar{
  display:flex;align-items:center;gap:9px;padding:12px 16px;background:var(--panel);
  border-bottom:1px solid var(--line);
}
body.emp-mode .emp-bar{position:sticky;top:0;z-index:5}
.emp-bar .t{font-size:14px;font-weight:600}
.emp-bar .sp{flex:1}
.emp-bar .who{font-size:11.5px;color:var(--text-3);max-width:150px;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.emp-body{flex:1;padding:14px 16px 30px;display:flex;justify-content:center;align-items:flex-start}
/* 手机端：卡片占满整宽（原来是固定 308px，手机上两侧留白太多、字也偏小） */
.emp-only .slip{width:100%;max-width:420px;box-shadow:0 4px 18px rgba(0,0,0,.10)}
.emp-only .slip-top{padding:17px 18px 40px}
.emp-only .slip-body{padding:16px 18px 22px}
@media (max-width:600px){
  .emp-body{padding:10px 10px 26px}
  .emp-only .slip{box-shadow:none;border:1px solid var(--line)}
}

/* ===== 首帧防闪：把「这一页该看到什么」提前到 <html> 上 =====
   为什么需要这一段（用户反馈：「打开工资条老是闪烁一下工作台界面再进入密码输入界面」）：
     · 工作台的 DOM（<div class="app">）是**静态 HTML**，就写在本文件里
     · 而决定显示规则的主脚本在文件**末尾**（第 1600 行以后）
     · 手机打开 38 万字节的单文件，浏览器解析到 .app 那段就会先画出来 ——
       等底部脚本跑起来时，用户已经看到工作台了（哪怕只闪 100ms）
   所以规则不能只靠底部的 JS：必须在**解析到工作台之前**就把结论写进 CSS。
   做法：在 <body> 最开头放一段**同步小脚本**，读一次地址栏，
   命中「通知落地」就在 <html> 上打 data-landing="employee"，
   下面这几条规则于是在第一次绘制时就已生效 —— 工作台根本不会被画出来。
   （挂 <html> 而不是 <body>：小脚本执行时 <body> 还没开始解析。）
   真正的安全边界不在这里（前端隐藏只是 UI，服务端 auth.js 才是），
   这里解决的纯粹是「别让不该出现的界面闪一下」。 */
html[data-landing="employee"] .app{display:none}
html[data-landing="employee"] #empPreviewOnly{display:none}
html[data-landing="employee"] .emp-only{
  display:flex;flex-direction:column;min-height:100vh;background:var(--panel-3);
}
html[data-landing="employee"] #bootMask{display:none}
html[data-landing="employee"] #loginGate{display:none}
/* 首帧身份未定时（既不是通知落地、也还没免登）：连遮罩一起提前打开，
   免得「工作台先画一帧 → 遮罩才盖上来」也是一种闪。 */
html[data-landing="waiting"] #bootMask{display:flex}

/* ============ 管理权限 ============ */
.nav-chip{margin-left:auto;font-size:10px;font-weight:600;padding:1px 6px;border-radius:5px;
  background:#e6f4ff;color:#1677ff;border:1px solid #bae0ff;line-height:1.6}
.perm-tb{width:100%;font-size:12px}
.perm-tb th{font-size:11px;font-weight:600;color:#64748b;padding:10px 12px;background:#fafbfc;
  border-bottom:1px solid var(--line);white-space:nowrap}
.perm-tb td{padding:10px 12px;border-bottom:1px solid var(--line);vertical-align:middle}
.perm-tb tr:last-child td{border-bottom:0}
.perm-tb tr.hl{background:#f0f7ff}
.perm-tb .yes{color:#52c41a;font-weight:700}
.perm-tb .no{color:#cbd5e1;font-weight:700}
.perm-tb .scope{color:#1677ff;font-weight:600;font-size:11.5px}
/* 钉钉应用接入：字段对照表 */
.dt-map{margin-top:14px;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--panel)}
/* ⚠️ 表头说明用**块级 + 行内图标**，不要用 flex。
   原因（手机端实测）：flex 里那段中文是「匿名 flex 项」，会被 flex-shrink 压到
   **一个字符宽** → 整段文字变成竖排单字列，完全没法读。
   中文没有空格，min-content 就是 1 个字，所以这种「图标 + 长中文」千万别用 flex 排。 */
.dt-map-h{padding:9px 12px;font-size:11.5px;color:#475569;
  background:var(--panel-2);border-bottom:1px solid var(--line);line-height:1.6}
.dt-map-h svg{vertical-align:-2px;margin-right:5px;color:#94a3b8}
.dt-map .tb{width:100%;font-size:12px}
/* 手机端表格可能比屏幕宽 → 放进横向滚动容器，**别让右边的列被裁掉**
   （`.dt-map` 是 overflow:hidden，不套一层的话第三列直接看不见） */
.dt-map-b{overflow-x:auto}
/* 变量名很长且没有断点（DINGTALK_CLIENT_SECRET）→ 允许断行，
   否则它会把整列撑宽、手机端就得横向滚才看得到「配在哪儿」那一列 */
.dt-map .tb td .mono{word-break:break-all}
/* 窄屏：对照表改成「一条一张小卡」。
   表格在手机上必然放不下三列（实测 375px 下第三列被裁掉），
   而横向滚动会让「配在哪儿」默认看不见 —— 那恰恰是这张表最该被看到的一列。
   所以窄屏改成竖排：第一列当标题、后两列依次往下排，三样信息都能直接看到。 */
@media(max-width:600px){
  .dt-map .tb thead{display:none}
  .dt-map .tb, .dt-map .tb tbody, .dt-map .tb tr, .dt-map .tb td{display:block;width:auto}
  .dt-map .tb tr{padding:9px 12px;border-bottom:1px solid var(--line-soft)}
  .dt-map .tb tr:last-child{border-bottom:0}
  .dt-map .tb td{border-bottom:0;padding:0;line-height:1.65}
  .dt-map .tb td.mono{margin-bottom:2px}
  .dt-map .tb td + td{color:var(--text-2)}
}
.dt-map .tb th{font-size:11px;font-weight:600;color:#64748b;padding:8px 12px;background:var(--panel-2);
  border-bottom:1px solid var(--line);text-align:left;white-space:nowrap}
.dt-map .tb td{padding:9px 12px;border-bottom:1px solid var(--line-soft);vertical-align:top;line-height:1.65}
.dt-map .tb tr:last-child td{border-bottom:0}
.dt-map .tb td.mono{color:#1677ff;font-weight:600}
.perm-person{display:flex;align-items:center;gap:10px;padding:10px 12px;border:1px solid var(--line);
  border-radius:9px;background:var(--panel)}
.perm-person + .perm-person{margin-top:8px}
.perm-person .avatar{width:30px;height:30px;font-size:11px;flex:0 0 auto}
.perm-person .nm{font-size:12.5px;font-weight:600}
.perm-person .sub{font-size:10.5px;color:#94a3b8}
.perm-person .sp{flex:1}
.perm-log{display:flex;gap:10px;font-size:11.5px;padding:8px 0;border-bottom:1px dashed var(--line)}
.perm-log:last-child{border-bottom:0}
.perm-log .t{color:#94a3b8;font-size:10.5px;white-space:nowrap;font-variant-numeric:tabular-nums}

/* ============ 抽屉 / 弹窗 ============ */
.mask{
  position:fixed;inset:0;background:rgba(0,0,0,.45);backdrop-filter:blur(2px);
  z-index:80;opacity:0;visibility:hidden;transition:.2s;
}
.mask.on{opacity:1;visibility:visible}
.drawer{
  position:fixed;top:0;right:0;bottom:0;width:min(660px,94vw);background:var(--panel);
  border-left:1px solid var(--line);z-index:90;transform:translateX(101%);
  transition:transform .24s cubic-bezier(.3,.8,.3,1);display:flex;flex-direction:column;
}
.drawer.on{transform:none}
.drawer-h{
  padding:16px 20px;border-bottom:1px solid var(--line);display:flex;align-items:center;gap:12px;flex:0 0 auto;
}
.drawer-h h3{font-size:15px;font-weight:600}
.drawer-h .sp{flex:1}
.drawer-b{flex:1;overflow-y:auto;padding:20px}
.drawer-f{padding:14px 20px;border-top:1px solid var(--line);display:flex;gap:10px;justify-content:flex-end;flex:0 0 auto}

.modal{
  position:fixed;top:50%;left:50%;transform:translate(-50%,-46%) scale(.97);
  width:min(520px,92vw);background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-lg);z-index:95;opacity:0;visibility:hidden;transition:.19s;
  max-height:88vh;display:flex;flex-direction:column;
}
.modal.on{opacity:1;visibility:visible;transform:translate(-50%,-50%) scale(1)}
.modal.wide{width:min(760px,94vw)}

.toast-wrap{position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:200;display:flex;flex-direction:column;gap:8px;align-items:center}
.toast{
  background:var(--panel-3);border:1px solid var(--line);border-radius:9px;
  padding:9px 15px;font-size:12.5px;display:flex;align-items:center;gap:8px;
  box-shadow:0 6px 20px rgba(0,0,0,.14);animation:tin .2s ease;
}
@keyframes tin{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
.toast svg{width:14px;height:14px;flex:0 0 14px}
.toast.ok svg{color:#00b42a}
.toast.err svg{color:#f53f3f}
.toast.info svg{color:#1677ff}

/* 「忘记密码」→ 联系管理员重置（员工端密码门里展开的那块） */
.adm-help{
  border-top:1px dashed var(--line);padding-top:14px;margin-top:2px;
  text-align:left;max-width:300px;margin-left:auto;margin-right:auto;
}
.adm-t{font-size:12.5px;font-weight:500;margin-bottom:5px}
.adm-s{font-size:11.5px;color:var(--text-3);line-height:1.7}
.adm-priv{margin-top:10px;padding-top:8px;border-top:1px solid var(--line-soft)}
.adm-list{display:flex;flex-wrap:wrap;gap:14px;margin-top:12px}
.adm-item{display:flex;flex-direction:column;align-items:center;gap:5px;width:52px}
.adm-item .avatar{width:34px;height:34px;font-size:13px}
.adm-nm{font-size:11px;color:var(--text-2);text-align:center;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}

.info-list{display:flex;flex-direction:column;gap:0}
.info-row{display:flex;padding:9px 0;border-bottom:1px solid var(--line-soft);font-size:12.5px}
.info-row:last-child{border-bottom:none}
.info-row .k{width:96px;flex:0 0 96px;color:var(--text-3)}
.info-row .v{flex:1;color:var(--text)}

.legend{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--text-2)}
.legend span{display:flex;align-items:center;gap:6px}
.legend i{width:9px;height:9px;border-radius:2.5px;display:block}

.bar-row{display:flex;align-items:center;gap:10px;padding:7px 0}
.bar-row .lb{font-size:12px;color:var(--text-2);width:66px;flex:0 0 66px}
.bar-row .bar{flex:1;height:8px;background:var(--panel-3);border-radius:5px;overflow:hidden}
.bar-row .bar i{display:block;height:100%;border-radius:5px}
.bar-row .vl{font-size:12px;color:var(--text-2);width:74px;text-align:right;flex:0 0 74px;font-variant-numeric:tabular-nums}

.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px}
.muted{color:var(--text-3)}
.sec-title{font-size:13px;font-weight:500;margin-bottom:11px;display:flex;align-items:center;gap:8px}
.sec-title .n{font-size:11px;color:var(--text-3);font-weight:400}
.divider{height:1px;background:var(--line-soft);margin:16px 0}
@media(max-width:1180px){
  /* ⚠️ 必须写 minmax(0,1fr) 而不是 1fr：`1fr` 等价于 `minmax(auto,1fr)`，
     不会缩到比**内容最小宽度**还窄 —— KPI 里「17 人已查看 · 15 人已确认」这种长文案
     会把整列顶宽，于是整块网格溢出视口（.kpi 又有 overflow:hidden，字就被切一半）。 */
  .kpis{grid-template-columns:repeat(2,minmax(0,1fr))}
  .grid-2,.grid-2b,.grid-3{grid-template-columns:minmax(0,1fr)}
}

/* ============ 移动端适配（管理端工作台）============
   分两段看：
     ①–⑧ 「能不能用」—— 把桌面布局改造成手机形态（侧边栏收起 / 宽表横滚 / 控件换行…）；
     ⑨   「好不好看」—— 层次、触控尺寸、留白节奏、字阶对比（用户：「看起来更美观一些」）。
   桌面端一个像素都不动，全部包在 ≤768px 里。 */
@media(max-width:768px){
  /* ① 侧边栏**整条收起**，导航改由底部标签栏承担。
        以前是把侧边栏横过来当顶部滚动条 —— 8 个入口一屏只看到 2 个，
        用户得猜到右边还有东西、再横着划，这正是「页面乱」的主要来源。
        顶部只留 .topbar（品牌 + 面包屑 + 图标），底部放 4 个常用入口 + 「更多」。 */
  .app{flex-direction:column}
  .sidebar{display:none}
  .tabbar{
    display:flex;flex:0 0 auto;background:var(--panel);
    border-top:1px solid var(--line);
    /* iPhone 底部小黑条：留出安全区，别让标签被挡住 */
    padding:4px 2px calc(4px + env(safe-area-inset-bottom,0px));
  }

  /* ② 主区：侧边栏变成顶栏后，main 要撑满剩下的高度（否则 .content 滚不动） */
  .main{flex:1;min-height:0}
  .topbar{height:auto;min-height:52px;flex:0 0 auto;padding:9px 12px;gap:8px}
  .crumb{font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
  /* 顶栏变窄后品牌名 + 「/」会吃掉一半宽度，把「当前页名」挤成省略号。
     手机上先保页名 —— 品牌在启动遮罩/页头都有，而页名是判断「我在哪」的唯一信息。 */
  .crumb-brand,.crumb .muted{display:none}
  /* 顶栏太窄：用户胶囊里的角色名会折成两行（「工资条主管理」+「员」），很难看。
     手机端只留名字，角色在「管理权限」页有完整展示。 */
  .user-chip{white-space:nowrap;flex:0 0 auto}
  .user-chip .rl{display:none}
  /* 侧边栏收起了 → 连接状态改由顶栏胶囊承担 */
  .conn-chip{display:flex}
  .content{padding:16px 14px 36px}

  /* ③ 表格：**自己横向滚动**，别再被裁掉。
         `display:block` 让 table 变成可滚动块；里面仍然是正常的表格布局。
         ⚠️ 这么改之后 sticky 表头会失效，所以显式关掉 —— 不关会在滚动时错位。 */
  .card>table{display:block;overflow-x:auto;white-space:nowrap;-webkit-overflow-scrolling:touch}
  .card>table>thead th{position:static}
  /* ⚠️ 上面两条用的是「直接子元素」选择器，只对 `.card > table` 生效。
     而工资条管理 / 定时发送那两张主表外面还包了一层
     `<div style="max-height:520px;overflow:auto">`（见页面结构），
     **所以它们一直没吃到这两条规则** —— 既没 nowrap，也没有自己的横滚。
     后果：「部门 / 岗位」被按字折成三行（「人力资源」+「部」），行高翻倍。
     这里补一条后代选择器兜住：卡片里的表格一律不折行。
     横滚交给外层那层 overflow:auto 就好 —— 直接给 table 加 display:block
     会把 sticky 表头一起关掉（外层滚动时表头还能吸顶，是我们要的）。 */
  .card table{white-space:nowrap}
  thead th,tbody td{padding:10px 12px}

  /* 「员工与部门」表格：手机上必然要横向滚动（列太多），
     把「查看密码」列（状态 + 重置按钮）**吸附在右侧** ——
     否则重置入口藏在最右边，要一路滑过去才点得到。
     ⚠️ 吸附单元格必须有**不透明背景**，否则内容会从它下面穿过去；
     thead 那格是双向吸附（top 来自上面那条通用规则），z-index 要压过表头与普通单元格。 */
  #page-contacts table thead th:last-child,
  #page-contacts table tbody td:last-child{
    position:sticky; right:0; background:var(--panel);
    box-shadow:-8px 0 10px -8px rgba(0,0,0,.16);
  }
  #page-contacts table tbody tr:hover td:last-child{background:var(--panel-2)}
  #page-contacts table thead th:last-child{background:var(--panel-2); z-index:3}

  /* ④ 工具条 / 标签页：搜索框占满一行，其余控件能换行、能横滑 */
  .toolbar{padding:12px 14px;gap:10px}
  .search{flex:1 1 100%}
  .search .field{width:100%}
  .toolbar .field{min-width:0}
  .tabs{overflow-x:auto;padding:0 14px}
  .tab{padding:11px 11px;font-size:13px}

  /* ⑤ 页头：标题占一行、按钮换到下一行（.sp 撑满一整行 = 强制换行） */
  .page-head{flex-wrap:wrap;gap:10px;margin-bottom:16px}
  .page-head .sp{flex:0 0 100%;height:0}
  .page-head h1{font-size:19px;font-weight:700}
  .page-head p{font-size:12.5px;line-height:1.55}

  /* ⑥ 发放向导的步骤条：5 步在 390px 上放不下（`.step{flex:0 0 auto}` 不换行不滚动，
         第 4/5 步直接被切掉，用户看不到自己在第几步）。
         做法：**数字全留、标签只留当前步**（约 265px，5 步全部可见），
         同时留 `overflow-x:auto` 兜底（某步名字特别长时还能滑）。 */
  .steps{overflow-x:auto;padding:12px 14px}
  .step .lb{display:none}
  .step.active .lb{display:block}
  .step-line{min-width:12px;margin:0 8px}

  /* ⑦ 表单行：标签从「左侧固定 104px」改成上下堆叠 */
  .form-row{flex-direction:column;gap:7px;padding:12px 0}
  .form-row>label{width:auto;flex:0 0 auto;padding-top:0}
  .form-row .ctl{width:100%}
  .form-row .ctl .field{max-width:100%}   /* 压掉行内写死的 width，避免溢出 */

  /* ⑧ KPI / 卡片：用 minmax(0,1fr) 才能真正压缩（内联的 repeat(4,1fr) 要压掉） */
  .kpis{grid-template-columns:repeat(2,minmax(0,1fr))!important;gap:10px;margin-bottom:14px}
  .kpi{padding:14px 15px;min-width:0}
  .kpi-val{font-size:22px;font-weight:700;margin-top:6px;letter-spacing:-.2px}
  .kpi-foot,.kpi-val small{white-space:normal;overflow-wrap:anywhere}
  .card-b{padding:16px}
  /* 卡片头里的月份下拉等控件：允许换行 + 不超出卡片（否则会被切掉右半边） */
  .card-h{flex-wrap:wrap;gap:8px;padding:14px 16px}
  .card-h .field{max-width:100%}
  .notice{padding:12px 13px}
  .drawer{width:100vw}                    /* 抽屉直接占满，不再留那 6vw 缝隙 */
  .modal{width:94vw}
  /* 长文案（如「环比 2026年7月工资条 +0.6%」）在窄屏要能断行，别顶宽卡片 */
  .muted,.hint{overflow-wrap:anywhere}

  /* ============================================================
     ⑨ 手机端视觉打磨 —— 四条原则
        · 层次：浅灰底 + 纯白卡片，原来只靠 1px 描边区分，在 #f5f6f7 上几乎看不出边界，
          整页糊成一片 → 改成「去描边 + 极淡双层阴影」，把卡片托起来。
        · 触控：控件统一 40px、标签栏 ≥ 52px（原来 31/32px，低于 iOS 44 / Android 48）。
        · 节奏：左右 14px、块间 14px，别一处 12 一处 18。
        · 字阶：页名 15/600、卡片标题 14.5/600、KPI 数值 22/700，把对比拉开。
     ============================================================ */

  /* —— 卡片：去描边 + 双层阴影 —— */
  .card,.kpi{border-color:transparent;box-shadow:0 1px 2px rgba(16,24,40,.05),0 1px 4px rgba(16,24,40,.04)}
  .card{border-radius:16px;overflow:hidden}   /* overflow 让内部表头/横滚条贴合圆角 */
  .card-h h3{font-size:14.5px;font-weight:600}
  .kpi{border-radius:16px}
  .kpi-label{font-size:11.5px}
  .kpi-val small{font-size:12px}
  .kpi-foot{font-size:11px;margin-top:5px}

  /* —— 顶栏：图标去描边、点击区做大（原来 32px 的方框又小又重） —— */
  .crumb b{font-size:15px;font-weight:600}
  .icon-btn{width:38px;height:38px;border:0;background:transparent;border-radius:10px}
  .icon-btn svg{width:19px;height:19px}
  .icon-btn .badge-dot{top:6px;right:6px}
  .icon-btn:active{background:var(--panel-3)}
  .user-chip{padding:0 2px;border:0;background:transparent}
  .user-chip .avatar{width:32px;height:32px}
  .user-chip .nm{font-size:12.5px}
  .conn-chip{font-size:10.5px;padding:3px 8px;background:var(--panel-2)}

  /* —— 按钮 / 表单控件：手机上一律 40px —— */
  .btn{height:40px;padding:0 16px;border-radius:10px;font-size:13.5px}
  .btn svg{width:16px;height:16px}
  .btn.sm{height:32px;padding:0 11px;font-size:12.5px}
  .btn:active{transform:scale(.97)}
  .field{height:40px;border-radius:10px;font-size:13px}
  select.field{padding-right:28px;background-position:right 9px center;background-size:12px}
  .search .field{padding-left:34px}
  .search svg{left:11px;width:15px;height:15px}
  /* 开关也跟着放大（原来 34×19，手指很难拨） */
  .switch .track{width:40px;height:22px;flex:0 0 40px}
  .switch .track::after{width:16px;height:16px}
  .switch input:checked + .track::after{transform:translateX(20px)}

  /* —— 工具条：控件排整齐。原来宽窄参差、最后「导出」还孤立占一行 —— */
  .toolbar>.field{flex:1 1 calc(50% - 5px);width:auto}
  .toolbar>.sp{display:none}    /* 桌面靠它把按钮推到右边；手机上只会多出一行空白 */
  .toolbar>.btn{margin-left:auto}

  /* —— 筛选区重排（用户：「这整个区域有点丑」）——
     根因不是配色，是**结构**：搜索 + 部门 + 月份 + 状态 + 异常 + 条数 + 导出
     7 个控件平铺成 5 行，数据还没露头就先吃掉 1/3 屏。
     改成「搜索 + 筛选」一行，筛选项收进可展开面板 —— 默认只占 1 行。 —— */
  .ps-toolbar .search{flex:1 1 auto;min-width:0}
  .ps-filter-btn{display:inline-flex;flex:0 0 auto}
  .ps-filter-btn svg{transition:transform .18s}
  .ps-filter-btn.on{border-color:var(--brand);color:var(--brand);background:var(--brand-bg)}
  .ps-filter-btn.on svg{transform:rotate(180deg)}
  /* 已启用条件数：收起面板后全靠它告诉用户「现在不是全部数据」 */
  .ps-filter-n{display:inline-flex;align-items:center;justify-content:center;
    min-width:16px;height:16px;padding:0 4px;margin-left:2px;border-radius:8px;
    background:var(--brand);color:#fff;font-size:10.5px;font-style:normal;line-height:1}
  .ps-filter-n[hidden]{display:none}
  .ps-filters{display:none;flex:1 1 100%;flex-wrap:wrap;gap:10px;padding-top:2px}
  .ps-filters.on{display:flex}
  .ps-filters>.field{flex:1 1 calc(50% - 5px);width:auto;min-width:0}
  .ps-filters>.ps-sw{flex:1 1 100%;margin-left:0}
  /* 「工资年月」下拉和上方月份标签是**同一个状态**（双向同步，见 renderPayslips），
     而标签就在卡片顶上、还带条数 —— 手机上再放一个只是重复，收掉。
     仍留在 DOM 里，所以同步逻辑和桌面端都不受影响。 */
  .ps-filters>#psMonth{display:none}
  .ps-count{font-size:12.5px;color:var(--text-2)}

  /* —— 页头控件：最多半宽，排成整齐两列。
         原来「一键撤回 / 一键发送 / 批量催办」挤一行、第 4 个「上传工资表」孤零零占一行。
         用 `0 1`（不拉伸）而不是 `1 1`：否则只有 1 个按钮的页面（如发放向导的「重置流程」）
         会被拉成整行，一个次要操作看起来比主操作还重。 —— */
  .page-head>.field,.page-head>.btn{flex:0 1 calc(50% - 5px);min-width:0;width:auto}

  /* —— 表格：行内字号收一点、行距匀一点 —— */
  thead th{font-size:11px}
  .name-cell{gap:8px}
  .name-cell .avatar{width:30px;height:30px;font-size:11.5px;flex:0 0 30px}
  .name-cell .nm{font-size:13px}
  .name-cell .sm{font-size:10.5px}

  /* —— 图例 / 条形图：条细一点、圆一点，数字不抢戏 —— */
  .legend{gap:10px 14px;font-size:11.5px}
  .bar-row{padding:8px 0}
  .bar-row .lb{width:60px;flex:0 0 60px;font-size:11.5px}
  .bar-row .vl{width:76px;flex:0 0 76px;font-size:11.5px}
  .bar-row .bar{height:7px}
  .progress{height:6px}

  /* —— 底部标签栏：改用上浮阴影（比 1px 硬线更像原生标签栏） —— */
  .tabbar{
    display:flex;flex:0 0 auto;background:var(--panel);border-top:0;
    box-shadow:0 -1px 0 var(--line-soft),0 -6px 20px rgba(16,24,40,.05);
    padding:6px 4px calc(6px + env(safe-area-inset-bottom,0px));
  }
  .tb-item{padding:7px 2px;font-size:11px;gap:3px}
  .tb-ico{height:22px}
  .tb-ico svg{width:22px;height:22px}
  .tb-item.active{color:#1677ff;font-weight:600}
  .tb-item:active{opacity:.55}

  /* —— Toast：原来 `.toast-wrap` 是 `left:50%` 的收缩盒，可用宽度只有半个视口，
         长文案被压成 4 行、还盖住页头（「暂时连不上服务器…」那条就是）。
         改成左右各留 12px 的通栏。 —— */
  .toast-wrap{left:12px;right:12px;transform:none;align-items:stretch;top:12px}
  body.emp-mode .toast-wrap{top:auto;bottom:16px}
  .toast{border-radius:12px;padding:11px 13px;font-size:13px;line-height:1.5}

  /* —— 抽屉 / 弹窗：手机上是主角，给足圆角与投影 —— */
  .drawer{border-left:0;box-shadow:-8px 0 28px rgba(16,24,40,.12)}
  .drawer-h{padding:14px 16px}
  .drawer-b{padding:16px}
  .drawer-f{padding:12px 16px;gap:8px}
  .modal{border:0;border-radius:16px;box-shadow:0 12px 40px rgba(16,24,40,.16)}
  .mask{backdrop-filter:blur(3px)}
}

/* ---- 工资条列表的分页控件（表格底部那一行）----
   每页 200 条；实测渲染 ~125µs/行，6000 行一次性渲染要 624ms（点一下等一秒）。
   ⚠️ 分页只影响渲染 —— 批量操作（全选/一键发送/导出）始终作用于全部筛选结果。 */
.pager-row td{padding:12px;background:#fafbfc}
.pager{display:flex;align-items:center;justify-content:center;gap:12px;flex-wrap:wrap;
  font-size:13px;color:var(--text-2,#666b73)}
.pager .pg-info b{color:var(--text,#1f2329)}
.pager .pg-ctrl{display:inline-flex;align-items:center;gap:4px}
.pager .pg{min-width:30px;height:28px;padding:0 8px;border-radius:6px;cursor:pointer;
  border:1px solid var(--line,#e4e6eb);background:#fff;font-size:13px;color:inherit}
.pager .pg:hover:not(:disabled){border-color:var(--brand,#1677ff);color:var(--brand,#1677ff)}
.pager .pg:disabled{opacity:.4;cursor:not-allowed}
.pager .pg.cur{background:var(--brand,#1677ff);border-color:var(--brand,#1677ff);color:#fff}
.pager .pg-dots{color:var(--text-3,#86909c);padding:0 2px}
.pager .hint{color:var(--text-3,#86909c);font-size:12px}
