/* ============================================================
   慵懒半径 · 骨架样式
   职责：重置 / 布局骨架 / 侧边栏 / 顶栏 / 底部菜单
        / 通用组件（工具行·按钮·Tab·胶囊·空态）/ 弹窗 / 通知
   ============================================================ */

/* ================= 1. 重置与基础 ================= */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  /* 字体栈 1:1 demo（shared.css）：-apple-system 在部分 Chrome 不生效，
     必须紧跟 BlinkMacSystemFont / system-ui 才能命中 SF —— 缺失会回退 PingFang，
     导致行盒从 1.19em 涨到 1.43em，全站文字观感整体走样。 */
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "HarmonyOS Sans SC", "Helvetica Neue", "Microsoft YaHei", sans-serif;
  background: var(--bg); color: var(--ink);
  font-size: 14px; line-height: 1.6;
  transition: background 300ms ease, color 300ms ease;
  min-height: 100vh;
}
/* -webkit-appearance:none 必须挂在全局层：Safari 对 appearance:auto 的按钮保留原生
   按钮底色/直角外壳，渐变胶囊（.pb-newbtn/.btn-primary 等）会露出异常背景块 */
button { font-family: inherit; border: none; background: none; cursor: pointer; color: inherit;
  -webkit-appearance: none; appearance: none; }
svg { display: block; }
::selection { background: color-mix(in oklch, var(--violet) 25%, transparent); }
/* 等宽数字工具类（demo 通用）：日期 / 时间 / 统计数字。全局一份，
   页面与顶栏（页头搬入后已脱离原页面作用域）共用同一实现 */
.f-mono { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; }
/* 滚动条（1:1 demo shared.css：8px 半透明圆角，轨道透明） */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: rgba(120, 130, 170, .28); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: rgba(120, 130, 170, .48); }
::-webkit-scrollbar-track { background: transparent; }

/* ================= 1.5 全局动效基座 =================
   入场：页面根块挂 .rise 统一上浮进场（原 ssRise/pmRise/mmRise/imRise/ngRise/
   gmRise/dmRise/anRise 九份同款 keyframes 收敛于此，页面 CSS 不再自带）。
   时长/曲线令牌见 variables.css（--t-* / --ease-out）；整站尊重 reduced-motion。 */
@keyframes riseIn { from { transform: translateY(10px); } }
/* 加载旋转全站唯一实现：各页 scxSpin/cactSpin/progSpin/syncSpin 等同款已并入 */
@keyframes spin { to { transform: rotate(360deg); } }
.rise { animation: riseIn .5s var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) {
  .rise, .rise * { animation: none !important; }
}

/* ================= 2. 布局骨架（1:1 demo：白色外底 + 两块同色圆角面板） =================
   桌面端滚动发生在 .content 内（demo .page 模型）：.main 定高裁剪，
   顶栏常驻面板顶部，滚动条贴面板圆角内侧。移动端由 responsive.css 覆盖。 */
.shell { display: flex; min-height: 100vh; line-height: normal; }
.main {
  flex: 1; min-width: 0;
  margin: 14px 14px 14px calc(var(--side-w) + 28px);
  height: calc(100vh - 28px);
  display: flex; flex-direction: column;
  background: var(--bg-2);
  border-radius: 18px;
  overflow: hidden;
  transition: margin-left 340ms var(--ease), background 300ms ease;
}
body.side-min .main { margin-left: calc(var(--side-w-min) + 28px); }
.content { max-width: none; margin: 0; padding: 16px 14px 16px; animation: pageIn 420ms var(--ease) both; }
/* 入场只做位移、不门控透明度：动画时钟被挂起（后台页签/省电/合成器节流）时
   内容也绝不可隐身——2026-09-30 事故：both+opacity:0 让整页停在首帧不可见 */
@keyframes pageIn { from { transform: translateY(14px); } }
/* 桌面/平板：内容区为唯一滚动容器（移动端 .main 滚动，见 responsive.css）。
   --pane-h = 滚动视口净高（100dvh − 外边距28 − 顶栏12+64），吸顶侧栏等组件取用；
   后代通过继承读取，移动端由 responsive.css 在 .main 上覆写。 */
@media (min-width: 768px) {
  /* scrollbar-gutter: 滚动条槽位常驻——内容过不过视口宽度都恒定，防经典滚动条
     环境（Windows/外接屏/系统「始终显示」）下「滚动条撑出来才出现」的整页宽度跳变 */
  .content { flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-gutter: stable; --pane-h: calc(100dvh - 104px); }
}

/* ================= 3. 侧边栏（1:1 demo：分类标题 + 分组树 + 收缩浮层） ================= */
.sidebar {
  position: fixed; inset: 14px auto 14px 14px;
  width: var(--side-w);
  display: flex; flex-direction: column;
  background: var(--bg-2);
  border-radius: 18px;
  padding: 22px 0 14px;
  transition: width 340ms var(--ease), background 300ms ease;
  z-index: 60;
}
body.side-min .sidebar { width: var(--side-w-min); }

/* 品牌区 */
.brand { display: flex; align-items: center; gap: 11px; padding: 0 22px 22px; }
/* logo 与右侧「标题 + 副标题」文字块等高（--brand-mark）：
   文字块行高钉死为 28 + 1 + 11 = 40px，logo 取同一变量 —— 三处品牌标尺寸统一，
   且不随字体回退（PingFang 1.4em / SF 1.19em）漂移 */
.brand-ic { flex: none; width: var(--brand-mark); height: var(--brand-mark); color: var(--violet); display: grid; place-items: center; }
.brand-ic svg { width: 100%; height: 100%; display: block; }
.brand-tx { font-size: 20px; line-height: 28px; font-weight: 800; letter-spacing: -.01em; color: var(--ink); white-space: nowrap; }
.brand-tx small { display: block; font-size: 9px; line-height: 11px; font-weight: 600; letter-spacing: .22em; color: var(--faint); margin-top: 1px; }

/* 导航 */
.side-nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: visible; padding-bottom: 8px; }
.side-nav::-webkit-scrollbar { width: 0; height: 0; }
body.side-min .side-nav { overflow: visible; padding-bottom: 0; }
.nav-cat { padding: 16px 22px 7px; font-size: 10.5px; font-weight: 700; letter-spacing: .16em; color: var(--faint); white-space: nowrap; }
.nav-cat:first-child { padding-top: 4px; }
.ng { position: relative; }
.ni {
  position: relative; width: calc(100% - 28px); height: 47px; margin: 1px 14px; padding: 0 13px;
  display: flex; align-items: center; gap: 12px;
  border-radius: 14px; border: 0; background: transparent;
  font-size: 14.5px; font-weight: 500; color: var(--ink-2); white-space: nowrap; text-align: left;
  transition: background .18s ease, color .18s ease;
}
.ni > svg { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ni .nlab { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ni .nchev { width: 14px; height: 14px; flex: none; opacity: .6; transition: transform .25s var(--ease); fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 侧栏网页链接叶（菜单管理「独立一级·网页链接」）：<a> 与按钮 .ni 同观感 */
a.ni { text-decoration: none; color: inherit; }
.ng.is-open > .ni .nchev { transform: rotate(180deg); }
/* 悬停 = 色卡淡染（--nav-hover）：侧栏底是 --bg-2，着色只落在底色上、图标保持中性——
   「悬停（仅淡染）」与「当前页」两种状态一眼分得开。
   当前页 = demo-users 同款白卡浮起（--card + --shadow-sm，亮暗令牌齐），必须声明在
   :hover 之后：否则悬停选中项时淡染底会盖掉卡面。 */
.ni:hover { background: var(--nav-hover); color: var(--ink); }
.ni.is-active { background: var(--card); color: var(--violet); font-weight: 650; box-shadow: var(--shadow-sm); }
.ni.is-active > svg { color: var(--violet); }
.ni.is-active::before {
  content: ''; position: absolute; left: -14px; top: 50%; transform: translateY(-50%);
  width: 5px; height: 23px; border-radius: 0 5px 5px 0; background: var(--brand-grad);
}
/* 分组头让位（仅展开态）：子项命中时白卡/亮条唯一落在真实页面（子项）上——
   「选中标记跟随子菜单」；头只留品牌色文字提示当前分组。收缩态子项整体隐藏，
   头保留完整选中卡做席位指示，故用 body:not(.side-min) 收窄。 */
body:not(.side-min) .ng:has(> .nkids .nkid.is-active) > .ni.is-active { background: transparent; box-shadow: none; }
body:not(.side-min) .ng:has(> .nkids .nkid.is-active) > .ni.is-active::before { content: none; }
body:not(.side-min) .ng:has(> .nkids .nkid.is-active) > .ni.is-active:hover { background: var(--nav-hover); }
.nbadge {
  min-width: 22px; height: 19px; padding: 0 7px; border-radius: 999px;
  display: inline-grid; place-items: center;
  font-size: 10.5px; font-weight: 700; color: #fff; background: var(--brand-grad);
  font-variant-numeric: tabular-nums;
}
.nbadge.zero { display: none; }
.ndot { position: absolute; top: 9px; right: 14px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger); box-shadow: 0 0 0 3px var(--danger-bg); display: none; }
body.side-min .ni.has-dot .ndot { display: block; }

/* 二级/三级树：展开与收起都走 grid-rows 过渡。
   原来只有展开有入场动画（navTreeIn）、收起是 display:none 瞬间塌陷——
   手风琴切换时若关闭的是上面那组，下面那组会跟着瞬移，看起来像「闪一下」。 */
.nkids {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .26s var(--ease);
}
.ng.is-open > .nkids { grid-template-rows: 1fr; }
.nkids-in {
  overflow: hidden; min-height: 0; padding: 0;
  transition: padding .26s var(--ease);
}
.ng.is-open > .nkids > .nkids-in { padding: 2px 0 4px; }
.nkid {
  position: relative;
  width: calc(100% - 44px); margin: 1px 14px 1px 30px; padding: 8px 12px;
  display: flex; align-items: center; gap: 10px;
  border-radius: 11px; border: 0; background: transparent;
  font-size: 13px; font-weight: 450; color: var(--ink-2); white-space: nowrap; text-align: left;
  transition: background .16s ease, color .16s ease;
}
.nkid svg { width: 16px; height: 16px; flex: none; opacity: .7; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 侧栏自定义图标（菜单管理配置的内置变量 / 阿里图库字形 .icon-xxx）：<i> 文本字形与 svg 同档 */
.nico-glyph { font-style: normal; flex: none; line-height: 1; opacity: .7; }
.ni .nico-glyph, .nkid .nico-glyph { font-size: 16px; }
.nfly-i .nico-glyph { font-size: 14px; opacity: .75; }
.nkid:hover { background: var(--nav-hover); color: var(--ink); }
.nkid.is-active { background: var(--card); color: var(--violet); font-weight: 650; box-shadow: var(--shadow-sm); }
/* 亮条跟随子项：挂在子项自身左缘（缩进位），尺寸比一级亮条小一档；相对 .nkid 定位
   （基类已补 position:relative），落在 .nkids-in 内不被 overflow:hidden 裁剪 */
.nkid.is-active::before {
  content: ''; position: absolute; left: -14px; top: 50%; transform: translateY(-50%);
  width: 4px; height: 18px; border-radius: 0 4px 4px 0; background: var(--brand-grad);
}
.nkid.is-active svg { color: var(--violet); opacity: 1; }
.nkid .nbadge { height: 17px; min-width: 19px; padding: 0 6px; font-size: 10px; }
.nkid.parent { color: var(--ink); font-weight: 600; pointer-events: none; }
.nkid.parent svg { opacity: .85; }
.nkid.l3 { width: calc(100% - 58px); margin-left: 44px; font-size: 12.5px; padding: 7px 11px; color: var(--ink-3); }
.nkid.l3.is-active { background: var(--card); color: var(--violet); box-shadow: var(--shadow-sm); }

/* 收缩态悬停浮层（1:1 demo .nfly/.ntip） */
.nfly {
  position: absolute; left: 100%; top: -6px; z-index: 120;
  padding-left: 12px; display: none;
}
.nfly-card {
  width: 214px; padding: 11px 9px;
  background: var(--card); border: 1px solid var(--line); border-radius: 18px;
  box-shadow: 0 34px 70px -24px rgba(10, 14, 40, .4);
  animation: navFlyIn .2s var(--ease);
}
@keyframes navFlyIn { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
.nfly-ti {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 10px 9px; margin-bottom: 4px;
  font-size: 11px; font-weight: 700; letter-spacing: .1em; color: var(--ink-3);
  border-bottom: 1px solid var(--line); white-space: nowrap;
}
.nfly-ti svg { width: 14px; height: 14px; color: var(--violet); fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.nfly-i {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border-radius: 11px; border: 0; background: transparent;
  font-size: 12.5px; color: var(--ink-2); white-space: nowrap; text-align: left;
}
.nfly-i svg { width: 14px; height: 14px; opacity: .75; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.nfly-i:hover { background: var(--card-2); color: var(--ink); }
.nfly-i .nbadge { margin-left: auto; }
.nfly-sub { padding: 7px 10px 3px; font-size: 10.5px; color: var(--ink-3); letter-spacing: .05em; white-space: nowrap; }
.nfly-l3 {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 10px 7px 26px; border: 0; background: transparent;
  font-size: 12px; color: var(--ink-3); white-space: nowrap; text-align: left;
}
.nfly-l3:hover { color: var(--ink); }
.nfly-l3 .nbadge { margin-left: auto; }
.ntip {
  position: absolute; left: 100%; top: 50%; transform: translateY(-50%);
  padding-left: 12px; display: none; z-index: 120; white-space: nowrap; pointer-events: none;
}
.ntip-card {
  padding: 8px 14px; border-radius: 12px;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 22px 46px -20px rgba(10, 14, 40, .4);
  font-size: 12.5px; font-weight: 650; color: var(--ink);
  animation: navFlyIn .16s var(--ease);
}
body.side-min .ng:hover > .nfly { display: block; }
body.side-min .ng.leaf:hover > .ntip { display: block; }

/* 收缩拉手：与侧栏同底融合，仅右侧微凸（1:1 demo）。
   悬停只动图标（变色 + 微移）——不改宽、不加底色、不加投影，否则会在侧栏边缘「鼓起」
   一块与侧栏不同底的色块，看着像贴上去的而不是长在侧栏上。 */
.rail-tab {
  position: absolute; right: -9px; top: 20px; z-index: 130;
  width: 20px; height: 44px; padding: 0; border: 0;
  border-radius: 0 13px 13px 0;
  display: grid; place-items: center;
  color: var(--ink-3); background: var(--rail-bg);
  transition: color .22s var(--ease);
}
.rail-tab:hover { color: var(--brand); }
.rail-tab svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; transition: transform .3s var(--ease); }
.rail-tab:hover .rt-fold { transform: translateX(-1.5px); }
body.side-min .rail-tab:hover .rt-open { transform: translateX(1.5px); }
.rail-tab .rt-open { display: none; }
body.side-min .rail-tab .rt-fold { display: none; }
body.side-min .rail-tab .rt-open { display: block; }

/* 收缩态 */
body.side-min .sidebar { padding-top: 24px; }
body.side-min .brand { justify-content: center; padding: 0 0 18px; }
body.side-min .brand-tx,
body.side-min .nav-cat,
body.side-min .nlab,
body.side-min .nchev,
body.side-min .side-nav .nkids { display: none; }
body.side-min .ng { margin-bottom: 5px; }
body.side-min .ni { justify-content: center; padding: 0; width: calc(100% - 28px); }
body.side-min .ni .nbadge { display: none; }
body.side-min .ni.is-active::before { left: -14px; }

/* ================= 4. 顶栏（1:1 demo：64 高 / 搜索框 / 圆形描边按钮 / 头像 meta） ================= */
.topbar {
  position: relative;
  /* 必须高于页面 loading 遮罩(.lg-page-overlay z-index:40)，否则切页瞬间遮罩会盖住
     顶栏（表现为标题/日期"丢失"）。同时低于 bottomnav(70)。 */
  z-index: 45;
  flex: none;
  display: flex; align-items: center;
  height: 64px; margin: 12px 12px 0; padding: 0 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 10px 24px -18px rgba(20, 26, 60, .35);
  transition: background 300ms ease, border-color 300ms ease;
}
.topbar-inner { width: 100%; min-width: 0; display: flex; align-items: center; gap: 12px; }

/* 顶栏页头（原搜索框位置）：由各页面 .phead 自动归位而来，随路由变化。
   宽度改为「内容自适应」——页面自己的操作区（.tb-acts）紧跟标题从左往右排，
   右侧固定按钮由 .tb-right 的 margin-left:auto 顶到最右，两边不再粘在一起。 */
/* 页头区的收缩优先级（顶栏空间不够时依次让位）：
     ① 副信息行先按省略号收窄 → ② 页面操作区再横向让位 → ③ 标题永不压缩。
   flex-shrink 加权 + 下限：带副信息行的页头（:has(p)）以远大于操作区的收缩权重先瘦身；
   下限 150px 兜住「标题（最长 4 字 ≈ 80px）+ 品牌竖条」，标题因此不会被压没。 */
.tb-page {
  flex: 0 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  white-space: nowrap; overflow: hidden;
}
/* 标题不再预留固定宽度：h1 自身 flex:none + nowrap 已不可压缩，
   原来固定 150px 会在短标题（如「通知」）后留出一大截空白，把功能按钮推远 */
.tb-page:has(h1) { min-width: 0; }
/* 无副信息行的页头：标题容器不压缩（nowrap 内容 = max-content，短标题也不占多余宽度），
   空间不足按既定顺序由 检索框收窄 → 操作区横向让位，而不是把标题硬切成半截字 */
.tb-page:has(h1):not(:has(p)) { min-width: max-content; }
.tb-page:has(p) { flex-shrink: 12; }
.tb-page > div { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* 标题：不再是一整块纯黑大字——收小一号，左侧加一条随路由色调的品牌竖条 */
.tb-page h1 {
  position: relative;
  flex: none;
  margin: 0; padding-left: 12px;
  font-size: 17px; font-weight: 750; letter-spacing: -.005em;
  color: var(--ink); white-space: nowrap;
}
.tb-page h1::before {
  content: ''; position: absolute; left: 0; top: 50%;
  width: 3.5px; height: 14px; margin-top: -7px;
  border-radius: 999px;
  /* 跟随全局色卡（原来取 --tc 会被顶栏的 route.color 覆盖成页面色） */
  background: var(--brand);
}
.tb-page p {
  display: flex; align-items: center; gap: 9px; margin: 0;
  min-width: 0; overflow: hidden;
  font-size: 11.5px; font-weight: 550; color: var(--sub); white-space: nowrap;
}
/* 副信息行是顶栏里唯一「可牺牲」的部分：空间不足时先按省略号收窄，
   而不是被 .tb-page 的 overflow:hidden 从右端硬切（会出现半截文字/空药丸） */
.tb-page p > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tb-page p .wk {
  padding: 2px 9px; border-radius: 999px;
  background: var(--tint); color: var(--brand);
  font-size: 10.5px; font-weight: 700;
}

/* 页面操作区（各页 .phead-btns 归位处）：紧跟标题，从左往右排。
   默认整块保持原尺寸（控件不被压扁），空间不足时由 .tb-page 先让位，
   再不足才轮到本区横向滚动（滚动条隐藏，与移动端同一套语法，见 responsive.css）。
   例外：含检索胶囊的页（发布/轮休明细/成员/定时任务）允许压缩检索框——
   桌面端鼠标无法横向滚动，宁可把输入框收窄，也不要把后面的主操作按钮切掉。 */
.tb-acts {
  display: flex; align-items: center; gap: 10px;
  flex: 0 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.tb-acts::-webkit-scrollbar { display: none; }
/* 页面 phead 搬入顶栏后已脱离原页面作用域，这里补齐内部布局（否则会上下堆叠） */
.tb-acts .phead-btns { display: flex; align-items: center; gap: 10px; flex: none; }
.tb-acts .phead-btns:has(.tb-search) { flex: 0 1 auto; min-width: 0; }
.tb-acts .phead-btns .tb-search { flex: 0 1 auto; min-width: 130px; }
.tb-acts .phead-scope { width: 152px; flex: none; }

/* ── 页头控件统一顺序（全站一份；页面只需沿用既有类/id，DOM 里谁先谁后都不影响显示）──
   顺序：① 部门/范围下拉 → ② 搜索框 → ③ 日期框 / 月份胶囊 → ④ 第一按钮(主) → ⑤ 第二按钮(次) → ⑥ 图标钮
   部门类控件历史上各页命名不一（*DeptSel / *DeptPick / *ScopeSel / *Scope），这里统一按 id 后缀归位；
   新增页面沿用同样命名即可自动落到「①」。 */
/* 兜底：未识别控件排在日期之后、按钮之前 */
.phead-btns { display: flex; align-items: center; gap: 10px; }
.phead-btns > * { order: 3.5; }
.phead-btns > [id$="DeptSel"],
.phead-btns > [id$="DeptPick"],
.phead-btns > [id$="ScopeSel"],
.phead-btns > [id$="Scope"],
.phead-btns > .phead-scope,
.phead-btns > .seg { order: 1; }                       /* ① 部门 / 范围 */
.phead-btns > .tb-search { order: 2; }                 /* ② 搜索框 */
.phead-btns > [id$="DateSel"],
.phead-btns > .ed-dtp,
.phead-btns > .mnav { order: 3; }                      /* ③ 日期框 / 月份胶囊 */
.phead-btns > .btn-primary,
.phead-btns > .btn-do,
.phead-btns > .pb-newbtn,
.phead-btns > .bank-new.is-study { order: 4; }         /* ④ 第一按钮（主操作） */
.phead-btns > .btn-soft,
.phead-btns > .bank-new.is-exam { order: 5; }          /* ⑤ 第二按钮（次级操作） */
.phead-btns > .tb-ic { order: 6; }                     /* ⑥ 图标圆钮（刷新等） */
/* 顶栏里的「检索框 / 部门下拉 / 日期框」统一成同一款外壳（与「新增考试题库」按钮同一语言）：
   胶囊形 + 1.5px **品牌实色**描边 + 透明底 + 品牌色文字与图标，不做淡化。
   这样顶栏一行里不再出现「方形软底下拉 / 白底胶囊 / 描边按钮」三种体系混排。 */
.tb-acts .phead-scope .ssel-field,
.tb-acts .phead-btns .ssel-field,
.tb-acts .phead-btns .tb-search,
.tb-acts .phead-btns .ed-dtp-field,
.phead-btns .ssel-field,
.phead-btns .tb-search,
.phead-btns .ed-dtp-field {
  height: var(--tb-h); border-radius: 999px; padding: 0 16px; font-size: 12.5px;
  background: transparent;
  border: 1.5px solid var(--brand);
  color: var(--brand);
  box-shadow: none;
}
.tb-acts .phead-btns .tb-search, .phead-btns .tb-search { padding: 0 6px 0 15px; }
/* 检索框占位文字也跟随品牌色（不用系统灰），与描边同色系 */
.tb-acts .phead-btns .tb-search input::placeholder,
.phead-btns .tb-search input::placeholder {
  color: color-mix(in oklch, var(--brand) 55%, var(--sub));
}
/* 内部图标（放大镜 / 下拉箭头 / 日期图标）同取品牌色 */
.tb-acts .phead-btns .tb-sic, .phead-btns .tb-sic,
.tb-acts .phead-btns .ssel-chev, .phead-btns .ssel-chev,
.tb-acts .phead-btns .ed-dtp-ic, .phead-btns .ed-dtp-ic { stroke: var(--brand); }
/* 日期框内的文字同取品牌色（含未选日期时的占位） */
.tb-acts .phead-btns .ed-dtp-txt, .phead-btns .ed-dtp-txt,
.tb-acts .phead-btns .ed-dtp-txt.is-ph, .phead-btns .ed-dtp-txt.is-ph { color: var(--brand); }
/* 占位文字：品牌色降一档，仍然明确可读（不用系统灰） */
.tb-acts .phead-btns .ssel-txt.is-ph, .phead-btns .ssel-txt.is-ph {
  color: color-mix(in oklch, var(--brand) 62%, var(--sub));
}
.tb-acts .phead-scope .ssel-field:hover:not(:disabled),
.tb-acts .phead-btns .ssel-field:hover:not(:disabled),
.tb-acts .phead-btns .tb-search:hover,
.tb-acts .phead-btns .ed-dtp-field:hover:not(:disabled),
.phead-btns .ssel-field:hover:not(:disabled),
.phead-btns .tb-search:hover,
.phead-btns .ed-dtp-field:hover:not(:disabled) {
  background: color-mix(in oklch, var(--brand) 9%, transparent);
  border-color: var(--brand);
  color: var(--brand);
}
.tb-acts .phead-scope .ssel-field.open,
.tb-acts .phead-btns .ssel-field.open,
.tb-acts .phead-btns .tb-search:focus-within,
.phead-btns .ssel-field.open,
.phead-btns .tb-search:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-tint);
}

/* 顶栏页头控件高度统一（--tb-h = 42px，与 .tb-ic 圆钮、主按钮同高）：
   输入下拉 / 日期框 / 月份胶囊 / 分段切换 / 各类按钮一律取这一档，
   不再出现「按钮 42、下拉 38、日期框 40」这种一行里高低不齐的情况。
   例外：检索胶囊内部的「搜索」提交按钮是嵌套在胶囊里的，保持比胶囊小一圈。 */
.phead-btns .btn-primary,
.phead-btns .btn-soft,
.phead-btns .btn-do,
.phead-btns .tb-search,
.phead-btns .ssel-field,
.phead-btns .ed-dtp,
.phead-btns .ed-dtp-field,
.phead-btns .bank-new,
.phead-btns .pb-newbtn,
.phead-btns .tb-ic,
.phead-btns .mlabel,
.phead-btns .mtoday,
.tb-acts > .btn-primary,
.tb-acts > .btn-soft {
  height: var(--tb-h);
}
/* 分段切换（全局 / 部门）：外壳与内钮一起压到同一档 */
.phead-btns .seg { height: var(--tb-h); padding: 3px; }
.phead-btns .seg button { height: calc(var(--tb-h) - 6px); }

/* 顶栏检索胶囊（全站唯一实现：成员管理 / 任务发布 / 轮休明细 / 通知 / 任务题库 /
   请假明细 / 积分流水 等都走这一份，不再各页自建）：
   结构 = 放大镜 + 输入框 + 「搜索」提交按钮；
   外壳 = 胶囊 + **品牌实色描边** + 透明底（与「新增考试题库」等第二按钮同一语言）。 */
.tb-search {
  display: flex; align-items: center; gap: 8px;
  height: var(--tb-h); width: 296px; max-width: 40vw;
  padding: 0 6px 0 15px;
  background: transparent; border: 1.5px solid var(--brand); border-radius: 999px;
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.tb-search:hover { background: color-mix(in oklch, var(--brand) 9%, transparent); }
.tb-search:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-tint); }
.tb-sic { width: 16px; height: 16px; flex: none; fill: none; stroke: var(--brand); stroke-width: 2; stroke-linecap: round; }
.tb-search input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  appearance: none; -webkit-appearance: none;
  font: inherit; font-size: 13px; color: var(--ink);
}
.tb-search input::placeholder { color: color-mix(in oklch, var(--brand) 55%, var(--sub)); }
.tb-search input::-webkit-search-cancel-button,
.tb-search input::-webkit-search-decoration { -webkit-appearance: none; display: none; }
.tb-go {
  flex: none; height: 30px; padding: 0 15px; margin-left: 2px;
  border-radius: 999px; background: var(--grad); color: #fff;
  font-size: 12.5px; font-weight: 700; white-space: nowrap;
  box-shadow: 0 8px 18px -9px var(--brand);
  transition: opacity .16s ease, transform .14s ease;
}
.tb-go:hover { color: #fff; opacity: .94; }
.tb-go:active { transform: scale(.97); }
@media (max-width: 1100px) {
  .tb-search { width: 250px; }
  /* 窄屏（≈1024 笔记本）：操作区本身就吃紧，副信息行让位。
     不留它，正是为了避免「检索框 / 主按钮被容器硬切」——桌面端鼠标滚不动横向区域。
     与移动端 responsive.css 里同样的处理保持一致（那里 767 起就隐藏）。 */
  .tb-page p { display: none; }
  .tb-page:has(h1) { min-width: 100px; }
}
@media (max-width: 767px) {
  .tb-search { width: 100%; max-width: 150px; height: 36px; padding: 0 5px 0 11px; gap: 6px; }
  .tb-go { height: 28px; padding: 0 12px; font-size: 12px; }
}

/* 右侧操作区 */
.tb-right { margin-left: auto; display: flex; align-items: center; gap: 11px; flex: none; }
/* 圆形描边图标按钮（主题/色调/铃铛） */
.tb-ic {
  position: relative; width: var(--tb-h); height: var(--tb-h); border-radius: 50%;
  display: grid; place-items: center;
  color: var(--ink-2); background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 6px 16px -10px rgba(20, 26, 60, .25);
  transition: color .18s ease, border-color .18s ease, transform .18s ease, background .18s ease;
}
.tb-ic svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.tb-ic:hover { color: var(--violet); border-color: color-mix(in srgb, var(--violet) 40%, var(--line)); }
.tb-ic.on { color: var(--violet); background: var(--brand-tint); border-color: color-mix(in srgb, var(--violet) 30%, var(--line)); }
.tb-ic .rdot {
  position: absolute; top: 8px; right: 9px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger); box-shadow: 0 0 0 2.5px var(--card);
  display: none; font-size: 0; line-height: 0;
}
.tb-ic.has-dot .rdot { display: block; }
.tb-theme .i-moon { display: none; }
html[data-theme="dark"] .tb-theme .i-sun { display: none; }
html[data-theme="dark"] .tb-theme .i-moon { display: block; }
.tb-div { width: 1px; height: 26px; background: var(--line); flex: none; }

/* 品牌色调按钮（色相环）+ 下拉：颜色由 data-brand 变量切换 */
/* ── 浮层（下拉/选择器弹出层）统一语法 ──────────────────────
   分两个层级，页面不得再造第三套：
     A. 锚在页面上的下拉（色卡 .tone-pop / 账号 .accpop / 选择器 .ssel-panel / 月份 .mpop）：
        圆角 16px、底色 var(--card)、1px var(--line) 描边、阴影 var(--shadow)、
        入场 opacity + translateY(-6px) scale(.98) 180ms、origin 视锚点定
     B. 叠在弹窗之上的选择器（日期 .ed-cal-panel / 时间 .stp-panel）：
        圆角 16px（窄屏贴底时 20px），阴影用更重的 var(--shadow-pop) 以压过弹窗卡片
   底色 --shell 与 --card 同值，故两处写法等价，不要因此再分叉。 */
/* 色调图标：按 BRANDS 数量均分的圆环（每色一段圆弧，段数与颜色由 core.js 依色卡生成，勿在此写死） */
.tb-ic .tone-ic { width: 19px; height: 19px; }
.tone-ic path { fill: none; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.tone-wrap { position: relative; }
.tone-pop {
  position: absolute; top: calc(100% + 10px); right: 0; width: 172px;
  padding: 7px; border-radius: 16px;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: var(--shadow);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px) scale(.98); transform-origin: top right;
  transition: opacity 180ms ease, transform 200ms var(--spring), visibility 180ms;
  z-index: 90;
}
.tone-wrap.is-open .tone-pop { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0) scale(1); }
.tone-opt {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: 10px; border: 0; background: transparent;
  font-size: 13px; font-weight: 600; color: var(--ink-2);
  transition: background 150ms ease, color 150ms ease;
}
.tone-opt:hover { background: var(--card-2); color: var(--ink); }
.tone-opt.on { background: var(--brand-tint); color: var(--brand); }
.tone-dot { flex: none; width: 15px; height: 15px; border-radius: 50%; background: var(--dot); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dot) 18%, transparent); }
.tone-ok { margin-left: auto; width: 16px; height: 16px; flex: none; fill: none; stroke: var(--brand); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: 0; }
.tone-opt.on .tone-ok { opacity: 1; }

/* 账号区（触发器为 demo 外观，下拉仍复用 .accpop） */
.tb-user { display: flex; align-items: center; gap: 10px; border: 0; background: transparent; padding: 0; cursor: pointer; border-radius: 999px; }
.tb-ava { width: var(--tb-h); height: var(--tb-h); flex: none; border-radius: 13px; display: grid; place-items: center; font-size: 15px; font-weight: 700; color: #fff;
  /* 与内容区头像同一套身份色板（--fc 由 core.js 按 ERP 哈希下发）——
     此前这里用品牌渐变，导致「同一个人」在顶栏与账号菜单里是两种颜色。 */
  background: var(--fc, var(--brand-grad));
  box-shadow: 0 6px 16px -8px color-mix(in oklch, var(--ink) 30%, transparent);
}
.tb-user-meta { line-height: 1.35; text-align: left; }
.tb-user-meta b { display: block; font-size: 13.5px; font-weight: 650; color: var(--ink); white-space: nowrap; max-width: 132px; overflow: hidden; text-overflow: ellipsis; }
.tb-user-meta span { display: block; font-size: 11px; color: var(--ink-2); white-space: nowrap; max-width: 132px; overflow: hidden; text-overflow: ellipsis; }

/* ================= 5. 通用组件 =================
   ── 控件规范 v2（1:1 demo）────────────────────────────────
   按钮：一律「胶囊 999px + 字重 650」；页面不得再自定义 height / padding /
        font-size / border-radius，改尺寸只允许加 .btn-sm / .btn-lg。
   动效只有两种语言（旧内核的「hover 填实 + 放大 / 位移」已废弃）：
        · hover：实底按钮只加深阴影；柔底按钮只加深同色系柔底；幽灵按钮只换柔底
        · active：scale(.97) 按下回弹
   变体：.btn-primary（实底渐变）/ .btn-soft（柔色，跟随 --ac）/ .btn-soft.ghost（灰）
   ──────────────────────────────────────────────────────── */
/* 工具行 */
.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar .spacer { flex: 1; }

/* 按钮基座 + 尺寸档（默认 md 38） */
.btn-primary, .btn-soft, .btn-do {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 38px; padding: 0 18px; border-radius: 999px;
  font-size: 13px; font-weight: 650; white-space: nowrap; cursor: pointer;
  transition: transform .15s ease, box-shadow .2s ease, background .2s ease,
              color .2s ease, border-color .2s ease;
}
.btn-sm { height: 34px; padding: 0 14px; font-size: 12.5px; }
.btn-lg { height: 42px; padding: 0 22px; font-size: 13.5px; }
.btn-primary:active, .btn-soft:active, .btn-do:active { transform: scale(.97); }
.btn-primary svg, .btn-do svg { width: 16px; height: 16px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }

/* 主按钮：实底品牌渐变。底缘光晕不用模糊盒阴影——Chromium 偶发把 blur 层的
   圆角遮罩丢成直角色块（按钮两下角浮淡色直角条，2026-09-30 / 10-02 两度实锤），
   改 ::after 径向渐变模拟（无 blur 即无可丢的遮罩）；伪元素只从底缘探出、
   与字面仅叠 2px 收边带，径向渐变在叠入区已衰减至近透明，不脏渐变面。 */
.btn-primary { position: relative; z-index: 0; background: var(--brand-grad); color: #fff; }
.btn-primary::after {
  content: ''; position: absolute; z-index: -1; pointer-events: none;
  left: 9%; right: 9%; top: calc(100% - 2px); height: 16px; border-radius: 50%;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--brand) 62%, transparent), transparent);
  opacity: .9;
  transition: opacity .2s ease, transform .2s ease;
}
.btn-primary:hover::after { opacity: 1; transform: translateY(2px); }
/* 主按钮禁用 / 加载态：全站唯一口径（各页不得再自行补 .loading / .busy / :disabled） */
.btn-primary:disabled, .btn-primary.loading, .btn-primary.busy,
.btn-do:disabled, .btn-do.loading, .btn-do.busy {
  opacity: .55; box-shadow: none; cursor: not-allowed; pointer-events: none;
  transform: none; filter: none;
}
.btn-primary:disabled::after, .btn-primary.loading::after, .btn-primary.busy::after { opacity: 0; }
/* 结果胶囊 */
.res-pill { display: inline-flex; align-items: center; gap: 7px; height: 34px; padding: 0 13px; border-radius: 11px; background: var(--card); border: 1px solid var(--line); font-size: 12.5px; font-weight: 700; color: var(--ink-2); }
.res-pill b { color: var(--ink); font-size: 13.5px; }
.res-pill svg { width: 16px; height: 16px; stroke: var(--violet); stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
/* 段落 Tab（seg）：容器 + 内嵌按钮的互斥分段控件，选中态 = 品牌渐变（与 .tab 同语言）。
   尺寸档：.seg-sm 26 / 默认 38。页面不得再自建 *seg* 容器。 */
.seg { display: inline-flex; gap: 4px; padding: 4px; border-radius: 14px; background: var(--card); border: 1px solid var(--line); }
.seg button { height: 38px; padding: 0 18px; border: 0; border-radius: 11px; background: transparent; font-weight: 650; font-size: 13px; color: var(--ink-3); cursor: pointer; transition: background 200ms ease, color 200ms ease, box-shadow 200ms ease; }
.seg button:hover { color: var(--ink); }
.seg button.on { background: var(--grad); color: #fff; box-shadow: 0 6px 14px -8px var(--brand); }
.seg-sm { gap: 3px; padding: 3px; border-radius: 11px; }
.seg-sm button { height: 26px; padding: 0 12px; border-radius: 8px; font-size: 11.5px; }

/* ── Tab / 筛选胶囊（全站唯一口径）────────────────────────
   页面不得再自建 tab / pill / filter 类。统一用 .tab：
     尺寸档：.tab-sm 28 / 默认 32 / .tab-lg 36
     结构：<button class="tab on">文案<i>计数</i></button>（计数徽章 i / em 自动适配选中态）
     状态：选中加 .on
   未选中态：品牌淡染底 + 1px 品牌淡染内描边 —— 原中性灰软底（#f0f2f8）在白卡上
             对比只有 1.1:1，未选中几乎看不见；描边给胶囊一个明确边界后再谈"选中/未选中"。
   需要「容器 + 内嵌分段」形态时用上面的 .seg，不要另造。 */
.tab {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: 32px; padding: 0 13px; border: 0; border-radius: 999px;
  background: var(--tint); color: var(--sub);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 32%, transparent);
  font: inherit; font-size: 12.5px; font-weight: 650; white-space: nowrap; cursor: pointer;
  transition: background 180ms ease, color 180ms ease, box-shadow 180ms ease, transform 150ms ease;
}
.tab-sm { height: 28px; padding: 0 12px; font-size: 12px; }
.tab-lg { height: 36px; padding: 0 16px; font-size: 13px; }
.tab:hover {
  background: color-mix(in srgb, var(--brand) 15%, var(--card)); color: var(--ink);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 48%, transparent);
}
.tab:active { transform: scale(.97); }
.tab.on { background: var(--grad); color: #fff; box-shadow: 0 8px 18px -9px var(--brand); }
.tab > i, .tab > em {
  font-style: normal; display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 16px; padding: 0 5px; border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 16%, transparent); color: var(--brand);
  font-size: 10.5px; font-weight: 750; font-variant-numeric: tabular-nums;
}
/* 空徽章 / 显式 hidden（计数为 0 时）都要真正隐藏，避免覆盖 UA 的 [hidden] */
.tab > i:empty, .tab > em:empty, .tab > i[hidden], .tab > em[hidden] { display: none; }
.tab.on > i, .tab.on > em { background: rgba(255, 255, 255, .26); color: #fff; }

/* 统计胶囊（数字 + 单位，如「3 小组」「27 成员」）：
   与 .tab 共用同一套胶囊语法（32px / 淡染底 / 同色内描边 / 999px），
   只靠「数字加大加粗」区分于可点击的 Tab —— 不得再靠尺寸或描边另造一套。 */
.stat {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: 32px; padding: 0 13px; border: 0; border-radius: 999px;
  background: var(--tint); color: var(--sub);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 20%, transparent);
  font-size: 12.5px; font-weight: 650; white-space: nowrap;
}
.stat b { font-size: 15px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1; }

/* ── 头像（.av）──────────────────────────────────────────
   内容区头像（成员 / 候选人 / 负责人）的全站唯一实现。
   形状统一为**圆角方块**（demo .qava 语言）；顶栏「我的账号」头像 .tb-ava 同为圆角方块（r13，仅多网络状态点），
   两者分工不同、不算重复 —— 页面不得再自建 *ava / *face / *-av 类。
   尺寸档：.av-xs 24 / .av-sm 28 / 默认 36 / .av-lg 42
   配色：用身份色板 var(--av-1…6)（见 variables.css），按 ERP 哈希取色。 */
.av {
  width: 36px; height: 36px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  background: var(--fc, var(--brand));
  color: #fff; font-size: 13px; font-weight: 700; letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
}
.av-xs { width: 24px; height: 24px; border-radius: 8px; font-size: 10.5px; }
.av-sm { width: 28px; height: 28px; border-radius: 9px; font-size: 11.5px; }
.av-lg { width: 42px; height: 42px; border-radius: 13px; font-size: 15px; }

/* ── 登录头像（.ava-img）────────────────────────────────
   小程序端授权微信头像上传后 users.avatar 有值，顶栏 .tb-ava /
   账号菜单与退出确认的 .av 内层改为 <img class="ava-img">：
   铺满父头像框（圆角继承），父级的 --fc 底色成为加载前的占位。 */
.ava-img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: inherit; user-select: none;
}

/* ── 头像堆叠（.av-stack）────────────────────────────────
   成员头像组（组卡/名单/详情弹层）的全站唯一实现，收敛原先
   perms(-8px+border)、groups(-9px+shadow)、calendar(-7px+shadow) 三套各自为政。
   结构：<div class="av-stack">
           <span class="av av-xs" style="--fc:…">徐</span>…
           <span class="av av-xs av-more">+3</span>   ← 计数徽标（灰底变体）
         </div>
   成员任意尺寸档；描边用 box-shadow 环（不占布局），默认取 --card，
   深底容器可覆写 --av-ring。 */
.av-stack { display: flex; align-items: center; min-width: 0; }
.av-stack > .av { box-shadow: 0 0 0 2px var(--av-ring, var(--card)); }
.av-stack > .av + .av { margin-left: -8px; }
.av.av-more { background: var(--soft2); color: var(--ink-3); }

/* ── 选择型选项卡（.opts / .opt）────────────────────────────
   从列表里选一项或多项（任务流选项、互换对象…）。全站唯一实现：
   原先 todo 的 .tf-opt 与 vacation 的 .sw-opt 各造一套，demo 的规范形态是
   .yl-cand/.yl-radio，此处按 demo 收敛。
   结构：<div class="opts[ multi]">
           <button class="opt[ on]"><span class="opt-ck"></span>
             <span class="opt-tx"><b>主标题</b><em>副说明</em></span>
             <span class="opt-go">操作</span>   ← 可选
           </button></div>
   状态：选中 .on（品牌描边 + 9% 品牌底）/ 禁用 .off；多选时容器加 .multi（勾选框变方）。
   主题色：取局部 --tc，未设时回落品牌色。 */
.opts { display: flex; flex-direction: column; gap: 8px; }
.opt {
  --b: var(--tc, var(--brand));
  display: flex; align-items: flex-start; gap: 11px; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: 16px;
  background: var(--card); border: 1.5px solid transparent; color: var(--ink); font: inherit;
  transition: border-color .18s ease, background .18s ease;
}
.opt:hover { border-color: var(--line); background: var(--hover); }
.opt.on { border-color: var(--b); background: color-mix(in oklch, var(--b) 9%, var(--card)); }
.opt.off { opacity: .6; cursor: not-allowed; pointer-events: none; }
.opt-ck {
  position: relative; flex: none; width: 18px; height: 18px; margin-top: 1px;
  border-radius: 999px; border: 2px solid var(--ink-3); background: var(--card);
  display: grid; place-items: center;
  transition: background .18s ease, border-color .18s ease;
}
.opts.multi .opt-ck { border-radius: 6px; }
.opt.on .opt-ck { background: var(--b); border-color: var(--b); }
.opt-ck::after {
  content: ''; width: 4px; height: 8px; margin-top: -2px;
  border: solid #fff; border-width: 0 2px 2px 0;
  transform: rotate(45deg) scale(0); transition: transform .18s ease;
}
.opt.on .opt-ck::after { transform: rotate(45deg) scale(1); }
.opt-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.opt-tx b { font-size: 13.5px; font-weight: 750; color: var(--ink); }
.opt-tx em { font-style: normal; font-size: 11px; font-weight: 600; line-height: 1.55; color: var(--ink-3); }
.opt.on .opt-tx em { color: color-mix(in oklch, var(--b) 60%, var(--ink-3)); }
.opt-go {
  flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  height: 30px; padding: 0 12px; border-radius: 999px;
  background: var(--soft); color: var(--sub); font-size: 12px; font-weight: 650;
  transition: background .16s ease, color .16s ease;
}
.opt-go svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.opt:hover .opt-go { background: var(--b); color: #fff; }

/* ── 编辑型选项行（.orow / .orows）──────────────────────────
   选项编辑器里的一行：字母徽标 + 输入框 + 操作钮。
   原先 publish 的 .ed-opt 与 bank 的 .bk-opt 是同一组件的两份拷贝
   （publish 内部还留着 12px / 14px 两代定义）。
   结构：<div class="orows"><div class="orow[ is-ok]">
           <span class="orow-letter">A</span>
           <input class="orow-input">
           <button class="orow-btn ok|del">标记 / 删除</button>
         </div></div> */
.orows { display: flex; flex-direction: column; gap: 6px; }
.orow {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px 7px 8px; border-radius: 14px;
  background: var(--soft); border: 1.5px solid transparent;
  transition: background .16s ease, border-color .16s ease;
}
.orow:hover { background: color-mix(in oklch, var(--ink) 6%, var(--soft)); }
.orow.is-ok { border-color: color-mix(in oklch, var(--mint) 45%, transparent); background: color-mix(in oklch, var(--mint) 9%, var(--card)); }
.orow-letter {
  width: 22px; height: 22px; flex: none; border-radius: 7px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--card); color: var(--ink-3);
  font-size: 11.5px; font-weight: 750;
}
.orow.is-ok .orow-letter { background: var(--mint); color: #fff; }
.orow-input {
  flex: 1; min-width: 0; height: 34px; padding: 0 11px;
  border: 0; border-radius: 9px; background: var(--card); color: var(--ink);
  font: inherit; font-size: 13px; outline: 0;
  transition: box-shadow .16s ease;
}
/* 深色档：--card 在 --soft 选项瓦片上是「反向陷坑」；换成亮一档的 --soft2，
   保持浅色档「输入比瓦片亮一阶」的同一方向（配色法对齐 .s-input） */
html[data-theme="dark"] .orow-input { background: var(--soft2); }
.orow-input::placeholder { color: var(--faint); font-weight: 400; }
.orow-input:focus { box-shadow: 0 0 0 1.5px var(--tc, var(--brand)), 0 0 0 4px color-mix(in oklch, var(--tc, var(--brand)) 14%, transparent); }
.orow.is-ok .orow-input:focus { box-shadow: 0 0 0 1.5px var(--mint), 0 0 0 4px color-mix(in oklch, var(--mint) 14%, transparent); }
.orow-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  height: 28px; padding: 0 12px; border: 0; border-radius: 999px;
  background: color-mix(in oklch, var(--ink) 6%, transparent); color: var(--ink-3);
  font: inherit; font-size: 12px; font-weight: 650; white-space: nowrap; cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.orow-btn:hover { color: var(--mint); background: color-mix(in oklch, var(--mint) 12%, transparent); }
.orow.is-ok .orow-btn.ok { background: var(--mint); color: #fff; }
.orow-btn.del { padding: 0 10px; background: transparent; }
.orow-btn.del:hover { background: var(--pink-soft); color: var(--pink); }
.orow-btn:disabled { opacity: .35; cursor: not-allowed; }
/* 「添加一项」：几何交给 .orow-btn，这里只补它作为独立一行按钮的布局 */
.orow-add { align-self: flex-start; margin-top: 2px; }

/* ── 月份选择器（顶栏「2026年9月 ▾」+ 年月弹层）────────────────
   全站唯一实现：原先 calendar 的 .cal-mlabel/.cal-month-pop 与 score 的
   .sc-mlabel/.sc-mpop 是同一组件的两套拷贝（触发胶囊 34px vs 42px、弹层列数
   3 vs 4、显隐 open vs on 全都不同），已合并到此处。
   结构：<div class="mnav">
           <button class="mlabel">2026年9月<svg class="mchev"/></button>
           <button class="mtoday">本月</button>
           <div class="mpop"><div class="mpop-year">
             <button class="mpop-nav">‹</button><b>2026</b><button class="mpop-nav">›</button>
           </div><div class="mpop-grid"></div></div>
         </div>
   状态：弹层展开加 .open；月份项选中加 .on、不可选加 .off。 */
.mnav { position: relative; display: flex; align-items: center; gap: 6px; flex: none; }
.mlabel {
  height: var(--tb-h); padding: 0 14px 0 16px; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 3px;
  background: transparent; border: 1.5px solid var(--brand); color: var(--brand);
  font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap;
  transition: border-color .18s ease, background .18s ease, box-shadow .18s ease;
}
.mlabel:hover { background: color-mix(in oklch, var(--brand) 9%, transparent); }
.mlabel b, .mlabel em { font-style: normal; font-size: 14px; font-weight: 750; }
.mlabel i { font-style: normal; font-size: 11px; font-weight: 550; color: color-mix(in oklch, var(--brand) 62%, var(--sub)); }
.mlabel .mchev { width: 14px; height: 14px; margin-left: 4px; fill: none; stroke: var(--brand); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform .24s var(--ease), stroke .18s ease; }
.mlabel.open { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-tint); }
.mlabel.open .mchev { transform: rotate(180deg); stroke: var(--brand); }
/* 「本月」按钮 = 第二按钮语言（品牌实色描边透明底，随色卡切换）；
   已是本月时直接不显示（原来只是变淡，仍占位且容易误点）。 */
.mtoday {
  height: var(--tb-h); padding: 0 18px; border-radius: 999px; flex: none;
  background: transparent; color: var(--brand); border: 1.5px solid var(--brand);
  font-size: 12.5px; font-weight: 650; white-space: nowrap;
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
.mtoday:hover { background: color-mix(in oklch, var(--brand) 10%, transparent); color: var(--brand); }
.mtoday.is-current { display: none; }
.mpop {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 90;
  width: 252px; padding: 10px;
  background: var(--shell); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: var(--shadow);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px) scale(.98); transform-origin: top right;
  transition: opacity 180ms ease, transform 200ms var(--spring), visibility 180ms;
}
.mpop.open { opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0) scale(1); }
.mpop-year { display: flex; align-items: center; justify-content: space-between; padding: 2px 2px 8px; }
.mpop-year b { font-size: 13.5px; font-weight: 750; color: var(--ink); font-variant-numeric: tabular-nums; }
.mpop-nav { width: 28px; height: 28px; border-radius: 9px; display: grid; place-items: center; background: var(--soft); color: var(--sub); transition: background .16s ease, color .16s ease; }
.mpop-nav:hover { background: var(--hover); color: var(--ink); }
.mpop-nav:disabled, .mpop-nav:disabled:hover { background: var(--soft); color: var(--faint); opacity: .45; cursor: not-allowed; }
.mpop-nav svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.mpop-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 5px; }
.mpop-m {
  height: 32px; border-radius: 10px;
  background: var(--soft); color: var(--sub);
  font-size: 12px; font-weight: 650; font-variant-numeric: tabular-nums;
  transition: background .16s ease, color .16s ease;
}
.mpop-m:hover { background: var(--hover); color: var(--ink); }
.mpop-m.on { background: var(--grad); color: #fff; }
.mpop-m.off, .mpop-m:disabled { background: var(--soft); color: var(--faint); opacity: .45; cursor: not-allowed; }
@media (max-width: 767px) {
  /* 窄屏改为贴底面板（原来只在轮休日历页做过，现两页统一） */
  .mpop { position: fixed; top: auto; left: 12px; right: 12px; bottom: 12px; width: auto; }
}
/* 柔底按钮（弹窗/卡片动作通用，跟随 --ac 变色；其次取 c-* 工具类的 --tc）：
   hover 只在同色系里加深柔底，不填实、不放大 */
.btn-soft {
  background: color-mix(in oklch, var(--ac, var(--tc, var(--violet))) 13%, var(--card));
  color: var(--ac, var(--tc, var(--violet)));
  border: 1.5px solid color-mix(in oklch, var(--ac, var(--tc, var(--violet))) 28%, transparent);
}
.btn-soft:hover {
  background: color-mix(in srgb, var(--ac, var(--tc, var(--violet))) 16%, var(--shell));
  color: var(--ac, var(--tc, var(--violet)));
  border-color: color-mix(in srgb, var(--ac, var(--tc, var(--violet))) 40%, transparent);
}
/* 按钮内图标：与文字基线对齐，描边风 currentColor */
.btn-soft svg { width: 16px; height: 16px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
/* 锁定/禁用态：灰色 + 不可点击（如本周名单确定后的放弃预选） */
.btn-soft.is-locked, .btn-soft:disabled {
  --ac: var(--ink-3); background: var(--card-2); color: var(--ink-3);
  border-color: var(--line); opacity: 0.75; cursor: not-allowed;
}
.btn-soft.is-locked:hover, .btn-soft:disabled:hover { background: var(--card-2); color: var(--ink-3); border-color: var(--line); }
.btn-soft.is-locked svg { width: 14px; height: 14px; }
/* 灰色次级按钮（取消/返回/重置）：全端统一灰源——card-2 底 + ink-3 字 + line 边 */
.btn-soft.ghost { background: var(--card-2); color: var(--ink-3); border-color: var(--line); }
.btn-soft.ghost:hover { background: var(--hover); color: var(--ink); border-color: var(--line); }
/* 页头操作区的「第二按钮」（次级操作，如 上传备份恢复 / 禁休管理 / 批量新增）：
   纯描边透明底 —— 页面底 + 品牌色描边 + 加深的品牌色文字（保证浅色底上可读），
   与主按钮（品牌渐变实心）构成「实心 / 描边」主次关系，
   取色全部来自 --brand，随全局色卡联动。 */
.phead-btns .btn-soft:not(.c-danger) {
  --ac: var(--brand);
  background: transparent;
  color: var(--brand);
  border: 1.5px solid var(--brand);
  box-shadow: none;
}
.phead-btns .btn-soft:not(.c-danger):hover {
  background: color-mix(in oklch, var(--brand) 10%, transparent);
  color: var(--brand);
  border-color: var(--brand);
}
.phead-btns .btn-soft:not(.c-danger):active { transform: scale(.97); }
.btn-soft.loading { opacity: 0.55; pointer-events: none; }

/* ================= 5.5 通用表单控件（全站唯一口径） =================
   按「使用场景 → 组件」对号入座，页面不得再自建 *-input / *-search 平行实现：
     · 表单/弹窗里的单行输入（文本/数字/搜索词）→ .inp（数字自动去 spinner）
     · 多行文本 → .inp.inp-ta；紧凑行（34 高）→ .inp.inp-sm；等宽字体（key/链接）→ .inp.inp-mono
     · 列表工具行的筛选搜索 → .search-pill（wrapper 形态：label/div 内放 svg + input；
       裸 input 也可直接挂 .search-pill，两态同一条 focus 规则）
     · 弹窗长表单字段 → .s-input（44 高历史口径，仅存量弹窗继续用，新页面不再新增）
     · 设置页卡内控件 → .set-input / .set-switch / .set-seg / .set-step（见下一节）
   规范：标准输入 42 高 / r12 / --shell 底 / 1.5px --line 描边；
   聚焦一律 border 品牌色 + var(--focus-ring)（3px 品牌柔环）。
   尺寸/间距取值见 variables.css 的 --f-h / --f-h-sm / --sp-* 刻度。 */
.inp {
  width: 100%; height: var(--f-h); padding: 0 14px;
  border: 1.5px solid var(--line); border-radius: 12px;
  /* 底色与 .s-input（用户管理·添加用户）同源：--card-2 在面板上恒有一档色阶，
     旧 --shell 深色下与弹窗面板同值，只剩 13% 描边近乎隐形 */
  background: var(--card-2); color: var(--ink);
  font: inherit; font-size: 13.5px; font-weight: 600; outline: none;
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.inp:focus { border-color: var(--brand); box-shadow: var(--focus-ring); background: var(--shell); }
.inp::placeholder { color: var(--faint); font-weight: 500; }
.inp:disabled { opacity: .6; cursor: not-allowed; }
.inp-sm { height: var(--f-h-sm); padding: 0 11px; border-radius: 10px; font-size: 12.5px; }
/* select 走 .inp 口径（全站唯一下拉样式：右缘箭头、关原生外观；users.css select.s-input 同源） */
select.inp, select.inp-sm {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center; background-size: 16px;
  padding-right: 36px; cursor: pointer;
}
select.inp-sm { background-position: right 9px center; padding-right: 30px; }
.inp-ta {
  height: auto; min-height: 96px; padding: 11px 13px;
  line-height: 1.75; resize: vertical; background: var(--soft);
}
.inp-ta:focus { background: var(--shell); }
.inp-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.inp[type="number"]::-webkit-outer-spin-button,
.inp[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.inp[type="number"] { -moz-appearance: textfield; appearance: textfield; }
/* 搜索胶囊：图标 + 透明输入；wrapper 或裸 input 皆可，:focus-within 一条规则通吃 */
.search-pill {
  display: flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 14px;
  border: 1.5px solid var(--line); border-radius: 999px;
  background: var(--soft); color: var(--ink);
  transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.search-pill:focus-within { background: var(--shell); border-color: var(--brand); box-shadow: var(--focus-ring); }
.search-pill > svg { width: 15px; height: 15px; flex: none; fill: none; stroke: var(--ink-3); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* wrapper 形态的内层输入：透明铺满；裸 input 形态只调字型（不能重置边框/底色，
   否则会盖掉 .search-pill 自身的胶囊外壳） */
.search-pill > input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: inherit;
  font: inherit; font-size: 12.5px; font-weight: 550;
}
input.search-pill { flex: 1; min-width: 0; font: inherit; font-size: 12.5px; font-weight: 550; }
.search-pill > input::placeholder, input.search-pill::placeholder { color: var(--faint); }
.search-pill > input::-webkit-search-cancel-button,
.search-pill > input::-webkit-search-decoration,
input.search-pill::-webkit-search-cancel-button,
input.search-pill::-webkit-search-decoration { -webkit-appearance: none; display: none; }

/* ================= 设置类页面共用组件（系统设置 / 任务配置 …） =================
   设计取向（产品视角）：
     · 一「张卡」= 一组设置：卡头放图标 + 标题 + 说明 + 数量徽标，卡身是设置行
     · 设置行 = 左（名称 + 一句说明）右（控件），行与行用发丝线分隔（不再一格格柔底块，
       旧版"每行一块浅底"既显矮又分不清层级）
     · 行高统一、对齐统一；窄屏自动改为上下排
     · 卡内控件走全站输入默认语言（柔底 + --line 细描边，聚焦白底 + 品牌描边 + 柔环）。
       历史：旧版控件曾用「soft 底 + 同色边框」糊进卡片看不见 → 改「白底 + 品牌粗描边」
       矫枉过正（比全站大一圈、满屏品牌色）；现用中性 --line 描边，既可见又不抢戏。 */
:root { --set-h: 38px; }
.set-surface {
  background: var(--shell); border: 1px solid var(--line);
  border-radius: 22px; overflow: hidden;
}
.set-tabs {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 13px 20px; border-bottom: 1px solid var(--line);
}
.set-body { padding: 18px 20px 22px; display: flex; flex-direction: column; gap: 16px; min-height: 280px; }
.set-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }

.set-card { background: var(--card); border: 1px solid var(--line); border-radius: 20px; overflow: hidden; }
.set-hd {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 18px; border-bottom: 1px solid var(--line); background: var(--card-2);
}
.set-hd-ic {
  width: 40px; height: 40px; border-radius: 13px; flex: none; display: grid; place-items: center;
  background: color-mix(in oklch, var(--tc, var(--brand)) 14%, var(--card));
  color: var(--tc, var(--brand));
}
.set-hd-ic svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.set-hd-ti { flex: 1; min-width: 0; }
.set-hd-ti h3 { font-size: 15.5px; font-weight: 780; letter-spacing: -.01em; color: var(--ink); }
.set-hd-ti p { margin-top: 3px; font-size: 11.5px; font-weight: 550; color: var(--sub); }
.set-hd > em {
  flex: none; font-style: normal; font-size: 11.5px; font-weight: 750;
  padding: 3px 10px; border-radius: 999px;
  background: color-mix(in oklch, var(--brand) 12%, transparent); color: var(--brand);
}

.set-rows { display: flex; flex-direction: column; }
.set-row {
  display: flex; align-items: center; gap: 18px;
  padding: 11px 18px; min-height: 56px;
  border-top: 1px solid var(--line);
  transition: background .18s ease;
}
.set-row:first-child { border-top: 0; }
.set-row:hover { background: var(--hover); }
.set-row-tx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.set-row-tx b { font-size: 13px; font-weight: 750; color: var(--ink); }
.set-row-tx b[title] { cursor: help; }
.set-row-tx span { font-size: 11px; font-weight: 500; color: var(--sub); line-height: 1.45; }
/* 说明只留一句短提示；细节用标签 + hover（title）浮现，避免整屏长句 */
.set-tags { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.set-tag {
  display: inline-flex; align-items: center; gap: 4px;
  height: 19px; padding: 0 8px; border-radius: 999px;
  font-size: 10.5px; font-weight: 750; letter-spacing: .01em; white-space: nowrap;
  color: color-mix(in oklch, var(--brand) 80%, var(--ink));
  background: color-mix(in oklch, var(--brand) 10%, transparent);
}
.set-tag.mute { color: var(--sub); background: color-mix(in oklch, var(--ink) 6%, transparent); }
.set-tag.warn { color: var(--warn); background: color-mix(in oklch, var(--warn) 13%, transparent); }
.set-ctl { flex: 0 0 auto; display: flex; align-items: center; gap: 10px; }

/* 卡内控件：统一高度；外观并轨全站输入默认语言（柔底 + 细描边，聚焦品牌环），
   不再使用旧版「白底 + 品牌粗描边 + 品牌色字」的重皮——那是设置页输入框
   看起来比全站大一圈、且与整体风格脱节的根源 */
.set-ctl .ssel-field,
.set-ctl .stp-field {
  height: var(--set-h); border-radius: 10px;
  background: var(--soft); border: 1.5px solid var(--line); color: var(--ink);
  box-shadow: none;
}
.set-ctl .ssel-field:hover:not(:disabled),
.set-ctl .stp-field:hover:not(:disabled) {
  background: var(--hover);
}
.set-ctl .ssel-field.open,
.set-ctl .stp-field.open {
  background: var(--shell); border-color: var(--brand); box-shadow: var(--focus-ring);
}
.set-ctl .ssel-chev, .set-ctl .stp-ic { stroke: var(--ink-3); }
.set-ctl .ssel-txt.is-ph, .set-ctl .stp-txt.is-ph { color: var(--faint); }

.set-input {
  width: 240px; height: var(--set-h); padding: 0 12px; border-radius: 10px;
  background: var(--soft); border: 1.5px solid var(--line); color: var(--ink);
  font: inherit; font-size: 13px; font-weight: 600; outline: 0;
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;
}
.set-input::placeholder { color: var(--faint); font-weight: 500; }
.set-input:hover { background: var(--hover); }
.set-input:focus { background: var(--shell); border-color: var(--brand); box-shadow: var(--focus-ring); }

/* ─────────────────────────────────────────────────────────────
   设置页控件体系（全站共用；两页只挂类，几何/状态都在这里）
   设计原则：
     · 每个控件只服务一种「取值形态」——布尔用开关、少量枚举用分段、数值用步进、
       时刻用时刻胶囊、文本/密钥用带动作输入框。形态对了，用户不用读说明就知道怎么改。
     · 外观一律并轨全站输入默认语言：38 高、10 圆角、柔底 + 1.5px 细描边、墨色字；
       聚焦/开启才出现品牌（白底 + 品牌描边 + --focus-ring；选中态品牌渐变实底）。
       状态差异靠"实底 vs 描边"，控件静默态不再抢戏。
     · 尺寸档：控件 38；动作钮 28；开关 46×26。                              */

/* 1) 开关（布尔） */
.set-switch {
  position: relative; width: 46px; height: 26px; flex: none;
  border: 0; padding: 0; border-radius: 999px; cursor: pointer;
  background: color-mix(in oklch, var(--ink) 16%, transparent);
  transition: background .2s ease;
}
.set-switch::after {
  content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(20, 26, 60, .28);
  transition: transform .22s cubic-bezier(.22, 1, .36, 1);
}
.set-switch.on { background: var(--brand-grad); }
.set-switch.on::after { transform: translateX(20px); }
.set-switch:focus-visible { box-shadow: 0 0 0 3px var(--brand-tint); }

/* 2) 分段控件（少量枚举：2~4 个候选直接点选，比下拉少一次交互） */
.set-seg {
  display: inline-flex; gap: 4px; padding: 4px; flex: none;
  background: var(--soft); border: 1.5px solid var(--line); border-radius: 10px;
}
.set-seg > button {
  height: calc(var(--set-h) - 11px); padding: 0 14px; border: 0; border-radius: 7px;
  background: transparent; color: var(--sub); font: inherit; font-size: 12.5px; font-weight: 700;
  cursor: pointer; transition: background .16s ease, color .16s ease, box-shadow .16s ease;
}
.set-seg > button:hover { background: color-mix(in oklch, var(--brand) 10%, transparent); }
.set-seg > button.on { background: var(--brand-grad); color: #fff; box-shadow: 0 8px 16px -10px var(--brand); }

/* 3) 步进器（数值：− 值 +，可键盘左右键 / 滚轮） */
.set-step {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  height: var(--set-h); padding: 0 5px;
  background: var(--soft); border: 1.5px solid var(--line); border-radius: 10px;
}
.set-step > button {
  width: 28px; height: 28px; flex: none; border: 0; border-radius: 8px; cursor: pointer;
  display: grid; place-items: center;
  background: color-mix(in oklch, var(--brand) 12%, transparent); color: var(--brand);
  transition: background .16s ease, transform .12s ease;
}
.set-step > button:hover { background: color-mix(in oklch, var(--brand) 20%, transparent); }
.set-step > button:active { transform: scale(.94); }
.set-step > button:disabled { opacity: .35; cursor: not-allowed; }
.set-step svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; }
.set-step-val {
  min-width: 64px; text-align: center; font-size: 14px; font-weight: 750;
  color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1;
}
.set-step-val i { font-style: normal; font-size: 11px; font-weight: 700; margin-left: 3px; color: var(--faint); }
.set-step-val input {
  width: 64px; border: 0; outline: 0; background: transparent; text-align: center;
  font: inherit; font-size: 14px; font-weight: 750; color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* 4) 时刻框：复用 STimePicker 面板；触发器回归普通输入框观感（不再做大号品牌字） */
.set-ctl .stp { width: 160px; }
.set-ctl .stp .stp-field { padding: 0 10px 0 12px; }
.set-ctl .stp .stp-txt { font-size: 13.5px; font-weight: 700; letter-spacing: .01em; }
.set-ctl .stp .stp-txt.is-ph { font-size: 12.5px; font-weight: 550; }
.set-ctl .stp .stp-ic { width: 15px; height: 15px; }

/* 4.5) 数值输入：一个输入框 + 一排推荐值
   设计意图：简单明了——不引入滑杆这类要先学的控件，推荐值本身就是"范围感" */
.set-num { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 6px; }
.set-num-box {
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 3px;
  width: 96px; height: var(--set-h); padding: 0 12px;
  background: var(--soft); border: 1.5px solid var(--line); border-radius: 10px;
  transition: background .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.set-num-box:hover { background: var(--hover); }
.set-num-box:focus-within { background: var(--shell); border-color: var(--brand); box-shadow: var(--focus-ring); }
.set-num-box input {
  flex: 1 1 auto; min-width: 0; width: 100%; text-align: right; border: 0; outline: 0;
  background: transparent; font: inherit; font-size: 14px; font-weight: 750; color: var(--ink);
  font-variant-numeric: tabular-nums; padding: 0; letter-spacing: .01em;
}
.set-num-box i {
  flex: none; font-style: normal; font-size: 11px; font-weight: 700;
  color: var(--faint);
}
.set-num-chev {
  flex: none; width: 14px; height: 14px; stroke-width: 2.4;
  color: var(--faint);
}
/* 推荐值下拉（点输入框出现）：标签 + 数值，数值随当前值浮动 */
.set-rec-pop {
  position: fixed; z-index: 96; min-width: 156px; max-width: 240px;
  padding: 6px; border-radius: 14px;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 18px 40px -14px rgba(18, 24, 60, .3), 0 2px 8px -2px rgba(18, 24, 60, .12);
  animation: setRecIn .14s ease;
}
.set-rec-h {
  padding: 4px 10px 6px; font-size: 10.5px; font-weight: 800; letter-spacing: .04em;
  color: var(--faint); text-transform: uppercase;
}
@keyframes setRecIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* 5) 带动作输入框（文本 / 密钥：掩码显示 + 显隐 + 复制） */
.set-inputwrap {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  height: var(--set-h); padding: 0 5px 0 12px; width: 280px;
  background: var(--soft); border: 1.5px solid var(--line); border-radius: 10px;
  transition: background .16s ease, box-shadow .16s ease;
}
.set-inputwrap:hover { background: var(--hover); }
.set-inputwrap:focus-within { background: var(--shell); border-color: var(--brand); box-shadow: var(--focus-ring); }
.set-inputwrap input {
  flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: transparent;
  font: inherit; font-size: 13px; font-weight: 600; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.set-inputwrap input::placeholder { color: var(--faint); font-weight: 500; }
.set-act {
  width: 28px; height: 28px; flex: none; border: 0; border-radius: 8px; cursor: pointer;
  display: grid; place-items: center;
  background: color-mix(in oklch, var(--brand) 12%, transparent); color: var(--brand);
  transition: background .16s ease, color .16s ease;
}
.set-act:hover { background: color-mix(in oklch, var(--brand) 20%, transparent); }
.set-act svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* 复制成功：短暂转成功色 */
.set-act.done { background: color-mix(in oklch, var(--ok) 18%, transparent); color: var(--ok); }

/* 6) 设置行状态：改动标记（左侧品牌竖条 + 行尾"已改"）+ 行内提示 */
.set-row { position: relative; }
.set-row.is-dirty::before {
  content: ''; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--brand);
}
.set-row.is-dirty .set-row-tx b { color: var(--brand); }
.set-row-note {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 700; color: var(--brand);
  background: color-mix(in oklch, var(--brand) 12%, transparent);
  padding: 3px 9px; border-radius: 999px;
}
.set-row-note.warn { color: var(--warn); background: color-mix(in oklch, var(--warn) 14%, transparent); }

/* 页面说明行（面板内顶部）：作用域 / 版本号 / 改动状态这类页面级信息放这里，
   **不进顶栏**（顶栏只留页面标题与操作按钮）。只用短标签 chips，不写长句。 */
.set-lead {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: 14px;
  background: color-mix(in oklch, var(--brand) 6%, var(--card));
  border: 1px solid var(--line);
  font-size: 11.5px; color: var(--sub);
}
.set-lead-k {
  font-size: 10.5px; font-weight: 800; letter-spacing: .02em; color: var(--brand);
  background: var(--shell); padding: 3px 9px; border-radius: 999px;
  border: 1px solid color-mix(in oklch, var(--brand) 24%, transparent);
}
.set-lead .set-tag { height: 20px; }

@media (max-width: 1100px) {
  .set-grid { grid-template-columns: 1fr; }
  .set-input { width: 200px; }
  .set-inputwrap { width: 240px; }
}
@media (max-width: 767px) {
  .set-row { flex-direction: column; align-items: stretch; gap: 10px; padding: 14px 16px; }
  .set-ctl { width: 100%; flex-wrap: wrap; }
  .set-ctl .ssel-field, .set-ctl .stp, .set-ctl .set-input,
  .set-inputwrap, .set-step, .set-seg { width: 100%; }
  .set-seg > button, .set-step-val { flex: 1; }
}


/* 弹窗/确认框的「确认」按钮 = demo .yl-btn.ok/.danger 的实底渐变语言（全站统一，不再按页限定）：
   c-danger → 危险（删除 / 撤回 / 解除）、c-warn → 警示（恢复 / 移出 / 禁休）。
   注意：这里只认语义色组（c-danger/c-warn），不要再用内容色 c-pink/c-orange 兼职表意。 */
.sheet-card .btn-soft.c-danger, .alert-card .btn-soft.c-danger {
  color: #fff; border-color: transparent;
  background: linear-gradient(135deg, var(--danger), color-mix(in srgb, var(--danger) 68%, #8d2544));
  box-shadow: 0 12px 24px -12px var(--danger);
}
.sheet-card .btn-soft.c-warn, .alert-card .btn-soft.c-warn {
  color: #fff; border-color: transparent;
  background: linear-gradient(135deg, var(--warn), color-mix(in srgb, var(--warn) 68%, #8a5a17));
  box-shadow: 0 12px 24px -12px var(--warn);
}
.sheet-card .btn-soft.c-danger:hover, .alert-card .btn-soft.c-danger:hover {
  color: #fff;
  background: linear-gradient(135deg, var(--danger), color-mix(in srgb, var(--danger) 68%, #8d2544));
  box-shadow: 0 18px 32px -13px var(--danger);
}
.sheet-card .btn-soft.c-warn:hover, .alert-card .btn-soft.c-warn:hover {
  color: #fff;
  background: linear-gradient(135deg, var(--warn), color-mix(in srgb, var(--warn) 68%, #8a5a17));
  box-shadow: 0 18px 32px -13px var(--warn);
}

/* ── 图标容器（弹窗头 / 卡片 / 动态列表通用）──────────────────
   全站唯一实现，替换原先 .s-ico / .al-ico / .ss-rec-ic / .fd-ic / .qcv /
   .vc-chip-ico / .pl-ic 等 7 套各写各的写法。
   尺寸档：.ico-sm 28 / 默认 36 / .ico-lg 48
   取色：沿用语义/内容色类（c-danger/c-warn/c-ok/c-brand…）提供的 --tc。
   图标本身一律描边风 currentColor，尺寸由 .ico 统一给出。 */
.ico {
  width: 36px; height: 36px; border-radius: 11px; flex: none;
  display: grid; place-items: center;
  background: color-mix(in oklch, var(--tc, var(--brand)) 14%, var(--card));
  color: var(--tc, var(--brand));
}
.ico-sm { width: 28px; height: 28px; border-radius: 9px; }
.ico-lg { width: 48px; height: 48px; border-radius: 14px; }
.ico > svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.ico-sm > svg { width: 15px; height: 15px; stroke-width: 2; }
.ico-lg > svg { width: 24px; height: 24px; stroke-width: 1.8; }
/* 实底变体：用于强调色块（如确认框顶部大图标） */
.ico.solid { background: var(--tc, var(--brand)); color: #fff; }
/* 通用胶囊标签（.tag.c-*）：纯文字，淡色相底 + 同色字，颜色由 c-* 工具类的 --tc 驱动。
   与 .btn-soft 同语言：color-mix(--tc 14%, card) 柔色底。业务专用类（pb-tag/cc-tag/cd-tag 等）互不影响 */
.tag {
  display: inline-flex; align-items: center;
  height: 22px; padding: 0 10px; border-radius: 999px;
  font-size: 11px; font-weight: 800; line-height: 1; letter-spacing: 0.02em;
  white-space: nowrap;
  background: color-mix(in oklch, var(--tc, var(--ink-2)) 14%, var(--card));
  color: var(--tc, var(--ink-2));
}
/* 带状态点的标签变体（.tag.has-dot）：轮休明细等"状态 pill"并轨 .tag 的过渡件，
   圆点跟随 currentColor（= --tc），与文字同色；gap 撑开点与文字的间距 */
.tag.has-dot { gap: 5px; }
.tag.has-dot::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* 弹窗按钮：鼠标/程序聚焦不显示 UA 焦点环（打开弹窗时焦点在 dialog 本身）；键盘 Tab 进入时给中性灰环 */
.sheet-card .btn-soft:focus, .alert-card .btn-soft:focus { outline: none; }
.sheet-card .btn-soft:focus-visible, .alert-card .btn-soft:focus-visible { outline: 2px solid color-mix(in oklch, var(--ink-3) 45%, transparent); outline-offset: 2px; }
/* 空态 */
/* 空态：居中「瞌睡脸」品牌图案（无方块/无卡片框/无虚线）
   图案直接用品牌 logo 的瞌睡脸路径，描边跟随状态色 --tc，lazyBreath 呼吸；
   全局通用——各页传 c-mint/c-violet/c-blue 即可换色 */
.empty-hint {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 300px; gap: 18px; padding: 44px 20px; text-align: center;
  /* 网格列表容器（task-list/cal-group-list/lrList 等 ≥768 多列网格）中让空态横跨整行居中，
     避免被塞进第一列轨道内导致整体贴左；对 flex/block 容器此属性无效，不影响原布局 */
  grid-column: 1 / -1;
}
.eh-face {
  position: relative; width: 96px; height: 96px; flex-shrink: 0;
  /* 跟随全局色卡（原取 --tc 会被各页传入的 c-* 固定成该页颜色） */
  color: var(--brand);
  animation: ehIn 420ms var(--ease) both;
}
/* 极淡状态色光晕（氛围，非实体底板） */
.eh-face::before {
  content: ''; position: absolute; inset: -14px; border-radius: 50%; z-index: -1;
  background: radial-gradient(circle, color-mix(in oklch, var(--brand) 16%, transparent) 0%, transparent 70%);
}
.eh-face svg {
  width: 100%; height: 100%; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  animation: lazyBreath 3.6s ease-in-out infinite;
}
/* 腮红：品牌图案内原为白色，空态无方块底，跟随状态色半透明 */
.eh-face svg ellipse { fill: currentColor; opacity: .25; stroke: none; }
.eh-tx { display: flex; flex-direction: column; gap: 5px; }
.eh-tx b { font-size: 16px; font-weight: 900; color: var(--ink); letter-spacing: -0.01em; animation: ehFade 380ms var(--ease) 300ms both; }
.eh-tx span { font-size: 13px; font-weight: 600; color: var(--ink-3); line-height: 1.5; animation: ehFade 380ms var(--ease) 380ms both; }
@keyframes ehIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes ehFade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@media (max-width: 767px) {
  .empty-hint { min-height: 240px; gap: 14px; padding: 36px 16px; }
  .eh-face { width: 78px; height: 78px; }
  .eh-tx b { font-size: 15px; }
}

/* ================= 骨架屏（全站唯一实现） =================
   各业务页此前各写一套（pb-skeleton / nt-skel / u-skel / sc-skel / cal-shimmer …），
   形状与动画各不相同。统一为下面一套语法：.sk 负责底与流光，容器只负责排布。
   用法见 core.js 的 window.skel（bars / cards / band / sections / month）。
   · 一律用 background 流光（不叠 ::after），子元素共用同一 background-size，
     因此同一屏里的多个占位会同步流动，不会各闪各的。 */
.sk {
  display: block; flex: none;
  background: linear-gradient(100deg, var(--soft) 30%, var(--soft2) 50%, var(--soft) 70%);
  background-size: 200% 100%;
  animation: skShimmer 1.4s ease-in-out infinite;
}
@keyframes skShimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
/* 块级占位：一列整块（列表行 / 明细卡 / 图表块），高度由调用方按真实内容给。
   .sk-bars / .sk-grid 只做分组，用 display:contents 让占位直接落进页面自己的容器——
   骨架永远注入「将来放真实内容的那个容器」，间距与列数因此天然一致，不需要各页再写一套。 */
.sk-bars { display: contents; }
.sk-bars > .sk { border-radius: 16px; }
.sk-bars > .sk:nth-child(2) { animation-delay: .12s; }
.sk-bars > .sk:nth-child(3) { animation-delay: .24s; }
.sk-bars > .sk:nth-child(4) { animation-delay: .36s; }
.sk-bars > .sk:nth-child(5) { animation-delay: .48s; }
.sk-bars > .sk:nth-child(6) { animation-delay: .6s; }
/* 卡片网格：与各页真实卡片同构（任务卡 / 小组卡 / 题库卡）；同样直落容器 */
.sk-grid { display: contents; }
.sk-card { display: flex; flex-direction: column; gap: 11px; padding: 16px; border-radius: 18px; background: var(--shell); border: 1px solid var(--line); }
/* 文字行占位：宽度档位对齐真实文案（标题 / 正文 / 辅助说明） */
.sk-line { height: 12px; border-radius: 6px; }
.sk-line.lg { height: 15px; }
.sk-line.sm { height: 10px; }
.sk-w40 { width: 40%; } .sk-w55 { width: 55%; } .sk-w70 { width: 70%; } .sk-w85 { width: 85%; } .sk-w100 { width: 100%; }
/* 整段替换（容器不负责排版，如 score 的 .sc-body）：由骨架自带纵向排布 */
.sk-stack { display: flex; flex-direction: column; gap: 18px; }
/* 顶部指标带：执行分析 / 部门数据的 KPI 区 */
.sk-band { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.sk-band > .sk { border-radius: 16px; }
/* 分组块：任务配置这类「小标题 + 若干设置行」的形态 */
.sk-sections { display: flex; flex-direction: column; gap: 30px; }
.sk-sec { display: flex; flex-direction: column; gap: 12px; }
.sk-sec > .sk:first-child { width: 180px; height: 18px; border-radius: 9px; }
.sk-sec > .sk { height: 40px; border-radius: 12px; }
/* 日历月网格：星期行 + 7×6 格（轮休日历用；格子高度取真实日历格） */
.sk-month { display: flex; flex-direction: column; gap: 10px; padding: 16px; border-radius: 18px; background: var(--shell); border: 1px solid var(--line); }
.sk-month-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 6px; }
.sk-month-head > .sk { height: 16px; }
.sk-month-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
.sk-month-week > .sk { height: 26px; border-radius: 9px; }
.sk-month-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
.sk-month-grid > .sk { height: 88px; border-radius: 12px; }
@media (max-width: 900px) { .sk-band { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px) {
  .sk-band { grid-template-columns: repeat(2, 1fr); }
  .sk-sections { gap: 22px; }
  .sk-stack { gap: 14px; }
  .sk-month { padding: 12px; }
  .sk-month-week > .sk { height: 22px; }
  .sk-month-grid > .sk { height: 74px; border-radius: 11px; }
}
@media (prefers-reduced-motion: reduce) { .sk { animation: none; } }

/* ================= 6. 弹窗（卡片式，非玻璃） ================= */
dialog.pub-mask {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  max-width: none; max-height: none;
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border: none; padding: 0; outline: none;
  overflow: visible;
}
dialog.pub-mask:not([open]) { display: none; }
dialog.pub-mask::backdrop { background: transparent; }
.mask-dim { position: absolute; inset: 0; background: var(--mask); backdrop-filter: blur(8px) saturate(1.1); -webkit-backdrop-filter: blur(8px) saturate(1.1); animation: maskIn 260ms ease both; }
@keyframes maskIn { from { opacity: 0; } }
.mask-dim.leaving { animation: maskOut 320ms var(--ease) both; }
@keyframes maskOut { to { opacity: 0; } }

/* 通用弹窗外壳（sheet）：demo .yl-modal 语言 —— 白面 + 22 圆角 + 细线分段（头/体/底） */
.sheet-card {
  width: min(560px, calc(100vw - 48px));
  max-height: calc(100dvh - 48px);
  overflow: hidden;           /* 裁剪圆角 · 滚动交给内层 .sheet-scroll */
  background: var(--shell); border: 1px solid var(--line);
  border-radius: 22px;
  box-shadow: 0 30px 70px -30px rgba(20, 26, 60, .42);
  padding: 0; margin: auto;
  display: flex; flex-direction: column;
  animation: popIn 340ms var(--spring) both;
}
html[data-theme="dark"] .sheet-card { box-shadow: 0 32px 64px -26px rgba(0, 0, 0, .75); }
@keyframes popIn { from { opacity: 0; transform: translateY(22px) scale(0.95); } }
.sheet-card.leaving { animation: popOut 340ms var(--ease) both; }
@keyframes popOut { to { opacity: 0; transform: translateY(6px) scale(0.96); } }
/* PC/平板：内容溢出时弹窗加宽加高（移动端由 responsive.css 覆盖为贴底全屏） */
.sheet-card.is-full { width: min(820px, calc(100vw - 32px)); max-height: calc(100dvh - 16px); }
/* 头部：标题 + 一句话说明 + 圆角方形关闭钮；与内容之间用一条细线分段 */
.sheet-head {
  display: flex; align-items: flex-start; gap: 12px; flex-shrink: 0;
  padding: 20px 20px 16px; border-bottom: 1px solid var(--line);
}
/* 滚动容器：唯一允许滚动的层，滚动条自然缩在 card 圆角内 */
.sheet-scroll {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 18px 20px 20px;
  scrollbar-width: thin; scrollbar-color: rgba(120, 130, 170, .28) transparent;
}
.sheet-head h3 { font-size: 16px; font-weight: 750; letter-spacing: -0.01em; color: var(--ink); line-height: 1.35; }
.sheet-head p { margin-top: 3px; font-size: 11.5px; font-weight: 550; color: var(--sub); }
.sheet-x {
  /* 全站统一圆角方形关闭钮（对齐 pb-modal/ug-modal 新弹窗族规格；
     旧基准 999px 圆形已废弃——曾被误认为页面 CSS 干扰，实为基准本身） */
  margin-left: auto; width: 32px; height: 32px; flex: none; border-radius: 10px;
  background: var(--soft); color: var(--sub);
  display: flex; align-items: center; justify-content: center;
  transition: background 160ms ease, color 160ms ease;
}
.sheet-x:hover { background: var(--soft2); color: var(--ink); }
.sheet-x svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; fill: none; }

/* ---- 搜索下拉 SSearchSelect（规范 6.7）：自定义样式 + 可搜索，面板 portal 到 body ---- */
.ssel { position: relative; }
.ssel-field {
  display: flex; align-items: center; gap: 8px; width: 100%;
  height: 44px; padding: 0 15px; border-radius: 14px;
  background: var(--soft); border: 1px solid var(--line);
  /* 选中值文字对齐 .s-input 口径用 --ink；--ink-2 深色下在 --soft 底上发暗（占位仍走 .is-ph --faint） */
  font: inherit; font-size: 13px; font-weight: 650; color: var(--ink);
  text-align: left; cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
}
.ssel-field:hover:not(:disabled) { border-color: color-mix(in srgb, var(--brand) 38%, var(--line)); }
.ssel-field.open {
  border-color: var(--brand); background: var(--shell);
  box-shadow: 0 0 0 3px var(--brand-tint);
}
.ssel-field:disabled { opacity: .55; cursor: not-allowed; }
.ssel-txt { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ssel-txt.is-ph { color: var(--faint); font-weight: 600; }
.ssel-chev {
  width: 14px; height: 14px; flex-shrink: 0;
  stroke: var(--sub); stroke-width: 2; fill: none;
  stroke-linecap: round; stroke-linejoin: round;
  transition: transform 220ms var(--ease), stroke 160ms ease;
}
.ssel-field.open .ssel-chev { transform: rotate(180deg); stroke: var(--brand); }

/* 面板：--shell 底 + 细描边 + demo 的柔和投影（原 --shadow-pop 太重，
   像浮起一块厚板；dark 下换 demo .tone-pop 的深色投影） */
.ssel-panel {
  position: fixed; z-index: 1200;
  display: flex; flex-direction: column;
  background: var(--shell); border: 1px solid var(--line);
  border-radius: 16px; box-shadow: var(--shadow);
  overflow: hidden; transform-origin: top left;
  animation: sselIn 180ms var(--ease) both;
}
html[data-theme="dark"] .ssel-panel { box-shadow: 0 26px 52px -22px rgba(0, 0, 0, .7); }
.ssel-panel.up { transform-origin: bottom left; animation-name: sselInUp; }
@keyframes sselIn { from { opacity: 0; transform: translateY(-6px) scale(.97); } }
@keyframes sselInUp { from { opacity: 0; transform: translateY(6px) scale(.97); } }
/* 搜索行：demo .search 的内嵌柔底输入（不再是一条带下边框的工具行） */
.ssel-search {
  display: flex; align-items: center; gap: 8px; flex-shrink: 0;
  margin: 8px 8px 2px; padding: 9px 12px;
  background: var(--soft); border: 1px solid transparent; border-radius: 12px;
  transition: border-color 200ms ease, background 200ms ease;
}
.ssel-search:focus-within { border-color: color-mix(in srgb, var(--brand) 45%, transparent); background: var(--shell); }
.ssel-search svg { width: 14px; height: 14px; flex-shrink: 0; stroke: var(--sub); stroke-width: 2; fill: none; stroke-linecap: round; }
.ssel-search input {
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
  font: inherit; font-size: 12.5px; font-weight: 550; color: var(--ink);
}
.ssel-search input::placeholder { color: var(--faint); font-weight: 500; }
.ssel-list {
  flex: 1; min-height: 0; overflow-y: auto;
  padding: 6px; display: flex; flex-direction: column; gap: 2px;
}
/* 选项：demo .tone-op 语言（柔底悬停 / 品牌淡底选中 + 右侧对钩），字重收敛到 600 */
.ssel-opt {
  display: flex; align-items: center; gap: 8px; width: 100%;
  min-height: 38px; padding: 7px 10px;
  border: none; border-radius: 10px; background: transparent;
  font: inherit; text-align: left; cursor: pointer;
  transition: background 140ms ease;
}
.ssel-opt .o-l { font-size: 13px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ssel-opt .av { flex: none; }
.ssel-opt .o-m { font-size: 11px; font-weight: 550; color: var(--sub); white-space: nowrap; }
.ssel-opt .o-ck {
  width: 14px; height: 14px; margin-left: auto; flex-shrink: 0;
  stroke: var(--brand); stroke-width: 2; fill: none;
  stroke-linecap: round; stroke-linejoin: round; opacity: 0;
  transition: opacity 140ms ease;
}
.ssel-opt:hover, .ssel-opt.act { background: var(--soft); }
.ssel-opt.on { background: var(--brand-tint); }
.ssel-opt.on .o-l { color: var(--brand); }
.ssel-opt.on .o-ck { opacity: 1; }
.ssel-empty { padding: 18px 10px; text-align: center; font-size: 12px; font-weight: 550; color: var(--sub); }

/* ================= 日期时间选择器 SDatetimePicker（规范 6.10） =================
   触发器复用 .ssel-field；step1 日历面板选日期 → step2 STimePicker 选时间。
   .ed-dtp-* = 触发器；.ed-cal-* = 日历面板。组件本体在 core.js，
   样式原在 publish.css，移此以便 /analytics 等非 publish 路由复用同一组件时样式一致。 */
.ed-dtp { position: relative; display: block; }
/* 日期框：描边与图标一律取全局色卡色（--brand），不再用中性 --line / 内容色 --violet */
.ed-dtp-field { width: 100%; height: 40px; justify-content: flex-start; border-color: var(--brand); color: var(--brand); }
.ed-dtp-ic { width: 16px; height: 16px; margin-right: 8px; stroke: var(--brand); fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }
.ed-dtp-txt { font-size: 12.5px; font-weight: 700; color: var(--ink); }
.ed-dtp-txt.is-ph { color: var(--ink-3); font-weight: 600; }
.ed-dtp-time { display: block; }
.ed-cal-panel {
  position: fixed; z-index: 1200; width: 280px;
  background: var(--card); border: 1px solid var(--line); border-radius: 16px;
  box-shadow: var(--shadow-pop); overflow: hidden;
  animation: dpCardIn 200ms var(--ease) both;
}
.ed-cal-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; gap: 6px; }
.ed-cal-head b { font-size: 13px; font-weight: 900; color: var(--ink); font-variant-numeric: tabular-nums; }
.ed-cal-nav {
  width: 28px; height: 28px; border-radius: 8px; border: none; background: transparent;
  font-size: 18px; line-height: 1; color: var(--ink-2); cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.ed-cal-nav:hover { background: var(--hover); color: var(--violet); }
.ed-cal-wk { display: grid; grid-template-columns: repeat(7, 1fr); padding: 0 10px 4px; gap: 4px; }
.ed-cal-wk span { text-align: center; font-size: 11px; font-weight: 700; color: var(--ink-3); padding: 4px 0; }
.ed-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; padding: 0 10px 10px; }
.ed-cal-day {
  height: 36px; border-radius: 8px; border: none; background: transparent;
  font-size: 12px; font-weight: 700; color: var(--ink); cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.ed-cal-day.other { color: var(--ink-3); opacity: 0.4; pointer-events: none; cursor: default; background: transparent; }
.ed-cal-day.past { color: var(--ink-3); opacity: 0.4; cursor: not-allowed; }
.ed-cal-day:not(.other):not(.past):hover { background: color-mix(in oklch, var(--violet) 12%, var(--card)); color: var(--violet); }
.ed-cal-day.on { background: var(--violet); color: #fff; }
.ed-cal-day.on:hover { background: color-mix(in oklch, var(--violet) 88%, black); color: #fff; }
.ed-cal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 8px 12px; border-top: 1px solid var(--line); background: var(--card-2); }
.ed-cal-foot .btn-soft { height: 30px; padding: 0 12px; font-size: 12px; }
@media (max-width: 767px) {
  .ed-cal-panel { left: 12px !important; right: 12px; width: auto !important; }
}

/* ================= 时间选择器 STimePicker（规范 6.8） =================
   触发器复用 .ssel-field（同语言）。
   面板 = 滚筒式双列时:分（scroll-snap mandatory + 中心高亮带）。
   移动端：全宽贴底 sheet + 遮罩 + 确定按钮；桌面：popover 定位。 */
.stp-field .stp-ic { width: 16px; height: 16px; flex-shrink: 0; stroke: var(--ink-3); stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; transition: stroke 160ms ease; }
.stp-field.open .stp-ic { stroke: var(--violet); }
.stp-txt { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.stp-txt.is-ph { color: var(--ink-3); font-weight: 600; }

/* ---- 遮罩（移动端 sheet） ---- */
.stp-backdrop {
  position: fixed; inset: 0; z-index: 1150;
  background: var(--mask);
  backdrop-filter: blur(8px) saturate(1.1); -webkit-backdrop-filter: blur(8px) saturate(1.1);
  animation: maskIn 260ms ease both;
}
.stp-backdrop.leaving { animation: maskOut 300ms var(--ease) both; }
body.stp-open { overflow: hidden; }

/* ---- 面板（桌面 popover） ---- */
.stp-panel {
  position: fixed; z-index: 1200;
  width: 260px;
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--line);
  border-radius: 16px; box-shadow: var(--shadow-pop);
  overflow: hidden; transform-origin: top left;
  animation: sselIn 180ms var(--ease) both;
}
.stp-panel.up { transform-origin: bottom left; animation-name: sselInUp; }
.stp-panel.leaving { animation: sselOut 260ms var(--ease) both; }
.stp-panel.leaving.up { animation-name: sselOutUp; }
@keyframes sselOut { to { opacity: 0; transform: translateY(-6px) scale(.97); } }
@keyframes sselOutUp { to { opacity: 0; transform: translateY(6px) scale(.97); } }

/* ---- 面板（移动端底部 sheet） ---- */
.stp-panel.stp-sheet {
  position: fixed;
  left: 0 !important; right: 0 !important; bottom: 0 !important; top: auto !important;
  width: 100%; min-width: 0 !important; max-height: 85dvh;
  box-sizing: border-box;
  border-radius: 22px 22px 0 0; border: none;
  animation: stpSheetUp 320ms var(--ease) both;
}
.stp-panel.stp-sheet.stp-in-dialog { position: absolute; }
.stp-panel.stp-sheet.leaving { animation: stpSheetDown 340ms var(--ease) both; }
@keyframes stpSheetUp { from { transform: translateY(100%); } to { transform: none; } }
@keyframes stpSheetDown { to { transform: translateY(100%); opacity: 0.4; } }

/* ---- 拖动条 + 头部（移动端） ---- */
.stp-handle {
  width: 36px; height: 4px; border-radius: 999px;
  background: var(--line); margin: 8px auto 4px; flex-shrink: 0;
}
.stp-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 18px 12px; flex-shrink: 0;
}
.stp-title { font-size: 15px; font-weight: 900; color: var(--ink); }
.stp-done {
  font: inherit; font-size: 13px; font-weight: 800;
}

/* ---- 滚筒列容器 ---- */
.stp-cols {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1fr) 20px minmax(0, 1fr); align-items: stretch; gap: 0;
  flex: 0 0 220px; height: 220px; min-height: 220px;
  overflow: hidden;
}

/* 中心高亮带（滚筒选中行指示） */
.stp-band {
  position: absolute; left: 8px; right: 8px; top: 50%;
  transform: translateY(-50%);
  height: 40px; border-radius: 10px;
  background: color-mix(in oklch, var(--violet) 10%, var(--card-2));
  border-top: 1px solid color-mix(in oklch, var(--violet) 22%, var(--line));
  border-bottom: 1px solid color-mix(in oklch, var(--violet) 22%, var(--line));
  pointer-events: none; z-index: 0;
}

/* ---- 滚筒列（scroll-snap mandatory） ---- */
.stp-col {
  flex: 1 1 0; min-width: 0; height: 100%; position: relative; z-index: 1;
  overflow-y: auto; overflow-x: hidden; scroll-snap-type: y mandatory;
  -webkit-overflow-scrolling: touch; scroll-behavior: auto;
  box-sizing: border-box;
  /* 上下留白 = (容器高 - 项高) / 2，使首/末项可达中心 */
  padding: 90px 4px;
  display: flex; flex-direction: column; gap: 2px;
  scrollbar-width: none;
}
.stp-col::-webkit-scrollbar { display: none; }

/* 滚筒项（大触摸目标 + snap-align center） */
.stp-opt {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-width: 0; height: 40px; flex-shrink: 0; scroll-snap-align: center;
  border: none; border-radius: 10px; background: transparent;
  font: inherit; font-size: 18px; font-weight: 800; color: var(--ink-3);
  cursor: pointer; opacity: .45;
  transition: opacity 160ms ease, color 160ms ease;
  font-variant-numeric: tabular-nums;
}
.stp-opt span { pointer-events: none; }
.stp-opt:hover { opacity: .7; color: var(--ink); }
.stp-opt.disabled, .stp-opt:disabled { opacity: .18; color: var(--ink-3); cursor: not-allowed; filter: grayscale(.6); }
.stp-opt.disabled:hover, .stp-opt:disabled:hover { opacity: .18; color: var(--ink-3); }
.stp-opt.on { opacity: 1; color: var(--violet); }

.stp-sep {
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 900; color: var(--ink-3);
  padding: 0; width: 20px; min-width: 20px;
  z-index: 1;
}

/* ---- 底部操作行 ---- */
.stp-foot {
  flex-shrink: 0; padding: 10px 14px;
  border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: 10px;
}
.stp-now { height: 34px; padding: 0 14px; font-size: 12.5px; }
.stp-done { height: 34px; padding: 0 16px; font-size: 13px; }

/* ---- 移动端微调 ---- */
@media (max-width: 767px) {
  body.stp-open .bottomnav { display: none; }
  .stp-panel.stp-sheet .stp-cols { flex-basis: 308px; height: 308px; min-height: 308px; }
  .stp-col { padding: 132px 4px; }
  .stp-opt { height: 44px; font-size: 20px; border-radius: 12px; }
  .stp-band { height: 44px; left: 12px; right: 12px; border-radius: 12px; }
  /* 底部操作栏：此刻（左）+ 确定（右）等宽分布 */
  .stp-foot { padding: 12px 16px 20px; justify-content: space-between; }
  .stp-foot .stp-now,
  .stp-foot .stp-done { flex: 1; height: 44px; font-size: 14px; }
}

/* ---- 无障碍：减弱动效 ---- */
@media (prefers-reduced-motion: reduce) {
  .stp-panel, .stp-panel.stp-sheet, .stp-backdrop { animation: none; }
  .stp-opt { transition: none; }
}

/* ================= 简易下拉 SDropdown（规范 6.9） =================
   触发器/面板/选项全部复用 .ssel-* 同语言，仅去掉搜索行。
   .sdd-list 替代 .ssel-list，无搜索行上方遮挡。 */
.sdd-list { display: flex; flex-direction: column; gap: 1px; overflow-y: auto; padding: 5px; flex: 1; min-height: 0; }
.sdd-panel:focus { outline: none; }

/* ---- 强提醒弹窗 alert-card：桌面/移动端均居中（移动端贴底规则见 responsive，仅作用于 sheet-card） ---- */
.alert-card {
  width: min(340px, calc(100vw - 48px));
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow-pop);
  padding: 26px 22px 20px; margin: auto;
  text-align: center;
  animation: popIn 340ms var(--spring) both;
}
.alert-card.leaving { animation: popOut 300ms var(--ease) both; }
.alert-card .ico { margin: 0 auto 15px; }
/* 强提醒大警示图标（线上保留口径）：实底语义色轻渐变瓦片 —— 高关注场景分量到位（不用 blur 光晕，防直角阴影坑） */
.alert-card .al-ico { width: 54px; height: 54px; border-radius: 17px;
  background: linear-gradient(150deg, color-mix(in srgb, var(--tc, var(--brand)) 80%, #fff), var(--tc, var(--brand)));
  color: #fff; box-shadow: 0 12px 24px -11px var(--tc, var(--brand)); }
.alert-card .al-ico > svg { width: 26px; height: 26px; stroke-width: 2; }
.alert-card h3 { font-size: 17px; font-weight: 900; letter-spacing: -0.01em; margin-bottom: 8px; }
.alert-card .al-desc { font-size: 13.5px; line-height: 1.7; color: var(--ink-3); font-weight: 600; margin-bottom: 20px; }
.alert-card .al-btns { display: flex; gap: 9px; }
/* 弹窗信息分组块：同类信息归集为一张卡，组间留白 */
.sgroup { background: var(--card-2); border: 1px solid var(--line); border-radius: 12px; margin-bottom: 10px; overflow: hidden; }
.sgroup .s-row { background: transparent; border: none; border-radius: 0; padding: 10px 14px; margin: 0; }
.sgroup .s-row + .s-row { border-top: 1px solid var(--line); }
.s-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 11px 14px; border-radius: 12px; background: var(--card-2); border: 1px solid var(--line); font-size: 13px; font-weight: 700; color: var(--ink-2); }
.s-row b { color: var(--ink); font-weight: 800; }
.s-row .warn { color: var(--orange); font-weight: 800; }
.s-input { width: 100%; height: 44px; padding: 0 14px; border-radius: 12px; border: 1.5px solid var(--line); background: var(--card-2); color: var(--ink); font-family: inherit; font-size: 14px; font-weight: 600; margin-bottom: 14px; outline: none; transition: border-color 180ms ease, box-shadow 180ms ease; }
.s-input:focus { border-color: var(--violet); box-shadow: 0 0 0 3px color-mix(in oklch, var(--violet) 14%, transparent); }
.s-input::placeholder { color: var(--ink-3); font-weight: 500; }
.sheet-btns { display: flex; gap: 9px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); position: sticky; bottom: 0; background: var(--shell); z-index: 5; }
/* 弹窗底栏三种形态，按钮共用同一尺寸口径（44px / 13px），页面不得再自建脚注容器：
   .sheet-btns（表单弹窗，按钮撑满等分）/ .sheet-foot（轻量脚注，按钮右对齐）/ .al-btns（确认框，撑满等分） */
.sheet-foot { display: flex; justify-content: flex-end; gap: 9px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.sheet-btns .btn-soft, .sheet-btns .btn-primary, .sheet-btns .btn-do,
.sheet-foot .btn-soft, .sheet-foot .btn-primary, .sheet-foot .btn-do,
.alert-card .al-btns .btn-soft, .alert-card .al-btns .btn-primary, .alert-card .al-btns .btn-do {
  height: 44px; font-size: 13px; justify-content: center;
}
.sheet-btns .btn-soft, .sheet-btns .btn-primary, .sheet-btns .btn-do,
.alert-card .al-btns .btn-soft, .alert-card .al-btns .btn-primary, .alert-card .al-btns .btn-do {
  flex: 1 1 0;   /* 等分撑满：用 1 1 0 而非 1，避免按钮宽度随文案长短不等 */
}

/* ================= 7. 底部菜单（移动端，display 由 responsive 控制） ================= */
.bottomnav { display: none; }

/* ================= 8. 通知系统 =================
   通知家族 v2（2026-10 重设计，提案稿 demo-notice.html 落地）：
   母语取自站内组件 —— 页标题凹槽（类型色 4×24 圆头竖条）/
   统计带发丝线（卡底倒计时）/ 站内动态瓦片（图标容器）/
   胶囊动作（撤销 · 去处理）。类型色对齐语义组（审计口径：
   语义组件用 --ok/--danger/--warn/--info，不再用 mint/orange）。
   机制不变：Top Layer 通知栈 / 堆叠缩放重叠 / 横滑关闭 /
   hover 暂停倒计时 / dialog 强提醒。 */
#ntStack { position: fixed; display: flex; flex-direction: column; z-index: 400; pointer-events: none;
  /* popover(manual) Top Layer 化：inset 简写同时完成 UA 重置与右上角定位（top right bottom left），避免 inset:auto 覆盖 top/right */
  inset: 16px 16px auto auto; margin: 0; border: none; padding: 0; overflow: visible;
  width: 380px; height: auto; background: transparent; color: inherit; }
.nt-toast {
  --nt-c: var(--ok);
  position: relative; display: grid; grid-template-columns: auto 1fr auto; align-items: center; column-gap: 12px;
  padding: 14px 14px 17px 16px; border-radius: 16px; overflow: hidden;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 24px 48px -20px color-mix(in srgb, var(--ink) 30%, transparent), 0 2px 8px rgba(28,31,48,.05);
  pointer-events: auto; cursor: pointer;
  transition: opacity 360ms var(--spring), transform 420ms var(--spring), translate 300ms var(--spring), margin 320ms var(--spring), scale 320ms var(--spring), box-shadow 240ms ease, border-color 240ms ease, display 420ms allow-discrete, overlay 420ms allow-discrete;
}
/* 类型凹槽：站内页标题起笔的那根圆头竖条，静态即可辨认类型 */
.nt-toast::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 4px; height: 24px; border-radius: 0 99px 99px 0; background: var(--nt-c); }
.nt-toast:hover { border-color: color-mix(in oklch, var(--nt-c) 34%, var(--line)); }
.nt-toast .t-icon { position: relative; display: flex; align-items: center; justify-content: center; width: 38px; height: 38px;
  border-radius: 12px; background: color-mix(in oklch, var(--nt-c) 13%, var(--card)); color: var(--nt-c); flex-shrink: 0; }
.nt-toast .t-icon svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* 描边入场：通用虚线取 60（≥ 全站最长图标路径 58，虚线短了会永久缺一段） */
.nt-toast .t-icon path { stroke-dasharray: 60; stroke-dashoffset: 60; animation: nt-draw 460ms 120ms var(--spring) forwards; }
.nt-toast .t-icon path:nth-of-type(2) { animation-delay: 300ms; }
.nt-toast .t-icon circle { stroke-dasharray: 64; stroke-dashoffset: 64; animation: nt-draw 420ms 80ms ease-out forwards; }
@keyframes nt-draw { to { stroke-dashoffset: 0; } }
.nt-toast.warning .t-icon path:first-of-type { stroke-dasharray: 58; stroke-dashoffset: 58; animation-duration: 560ms; }
.nt-toast.warning .t-icon path:last-of-type { stroke-dasharray: 0; stroke-dashoffset: 0; stroke-width: 1.8; animation: nt-dot-in 240ms 500ms ease-out both; }
@keyframes nt-dot-in { from { opacity: 0; } to { opacity: 1; } }
.nt-toast.error .t-icon { animation: nt-shake 480ms 380ms ease-in-out; }
@keyframes nt-shake { 0%,100% { transform: rotate(0); } 25% { transform: rotate(-9deg); } 50% { transform: rotate(8deg); } 75% { transform: rotate(-5deg); } }
.nt-toast .t-body { min-width: 0; }
.nt-toast .t-title { font-size: 14px; font-weight: 800; color: var(--ink); line-height: 1.4; letter-spacing: -.005em; }
.nt-toast .t-desc { font-size: 12.5px; color: var(--ink-2); line-height: 1.55; margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* 操作胶囊（可选）：「撤销 / 去处理」—— 卡片动作胶囊语言，类型软染 */
.nt-toast .t-act { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 11px; margin-top: 8px;
  border-radius: 999px; background: color-mix(in oklch, var(--nt-c) 12%, var(--card)); color: var(--nt-c);
  font-size: 11.5px; font-weight: 800; line-height: 1; transition: filter var(--t-fast) ease, transform var(--t-press) ease; }
.nt-toast .t-act:hover { filter: brightness(.95); }
.nt-toast .t-act:active { transform: scale(.95); }
.nt-toast .t-act svg { width: 12px; height: 12px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; fill: none; }
/* 发丝倒计时线：统计带 hairline 语言，走完即自动收（hover 暂停） */
.nt-toast .t-bar { position: absolute; left: 16px; right: 14px; bottom: 6px; height: 2px; border-radius: 99px;
  background: color-mix(in oklch, var(--nt-c) 15%, transparent); }
.nt-toast .t-bar i { display: block; height: 100%; border-radius: inherit; background: var(--nt-c);
  transform-origin: left center; animation: nt-bar var(--nt-dur, 2100ms) linear forwards; }
@keyframes nt-bar { from { transform: scaleX(1); } to { transform: scaleX(0); } }
#ntStack:hover .t-bar i, #ntStack:focus-within .t-bar i { animation-play-state: paused; }
.nt-toast .t-close { position: absolute; top: 10px; right: 10px; display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 8px; background: color-mix(in oklch, var(--ink) 6%, transparent); color: var(--ink-3);
  opacity: 0; scale: .85; transition: opacity 150ms ease, scale 150ms var(--spring); }
.nt-toast:hover .t-close { opacity: 1; scale: 1; }
.nt-toast .t-close svg { width: 14px; height: 14px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; fill: none; }
#ntStack .nt-toast { z-index: 5; }
#ntStack .nt-toast:nth-child(2) { z-index: 4; margin-top: -46px; scale: .965; filter: brightness(.985); }
#ntStack .nt-toast:nth-child(3) { z-index: 3; margin-top: -46px; scale: .93; filter: brightness(.97); }
#ntStack .nt-toast:nth-child(4) { z-index: 2; margin-top: -46px; scale: .895; filter: brightness(.955); opacity: .88; }
#ntStack .nt-toast:nth-child(5) { z-index: 1; margin-top: -46px; scale: .86; filter: brightness(.94); opacity: .6; }
#ntStack:hover .nt-toast, #ntStack:focus-within .nt-toast { margin-top: 10px; scale: 1; filter: none; opacity: 1; }
#ntStack .nt-toast:nth-child(n+6) { display: none; }
@starting-style { .nt-toast { opacity: 0; transform: translateX(30px) scale(.9); } }
.nt-toast.leaving { opacity: 0; transform: translateX(60px) scale(0.92); pointer-events: none; }
.nt-toast.success { --nt-c: var(--ok); } .nt-toast.error { --nt-c: var(--danger); }
.nt-toast.warning { --nt-c: var(--warn); } .nt-toast.info { --nt-c: var(--info); }

/* ---- 8.1 通知中心下拉（顶栏铃铛；行 = 站内动态行语言） ----
   瓦片 + 粗体正文 + mono 时间；未读亮显 + 品牌点，已读整行淡化。
   分组「今天 / 更早」沿用侧栏 MENU 小字距排版；未读数与红点同源。 */
.nc-wrap { position: relative; }
.nc-pop { display: none; position: absolute; top: calc(100% + 10px); right: -24px; width: 396px; max-width: calc(100vw - 24px); z-index: 60;
  background: var(--card); border: 1px solid var(--line); border-radius: 18px; box-shadow: var(--shadow-pop);
  overflow: hidden; transform-origin: top right;
  animation: ncIn var(--t-med) var(--spring) both; }
.nc-wrap.is-open .nc-pop { display: block; }
@keyframes ncIn { from { opacity: 0; transform: translateY(-8px) scale(.96); } }
.nc-pop.leaving { animation: ncOut var(--t-fast) var(--ease) both; }
@keyframes ncOut { to { opacity: 0; transform: translateY(-6px) scale(.97); } }
.nc-head { display: flex; align-items: center; gap: 9px; padding: 14px 16px 10px; }
.nc-head .nc-notch { flex: none; width: 4px; height: 16px; border-radius: 99px; background: var(--grad); }
.nc-head b { font-size: 14.5px; font-weight: 800; }
.nc-count { min-width: 19px; height: 19px; padding: 0 6px; border-radius: 999px; background: var(--danger); color: #fff;
  font-family: var(--mono, ui-monospace); font-size: 10.5px; font-weight: 800; display: grid; place-items: center; }
.nc-count.hide { display: none; }
.nc-head .nc-read { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; height: 29px; padding: 0 12px;
  border-radius: 999px; background: var(--soft); color: var(--ink-2); font-size: 11.5px; font-weight: 700;
  transition: background var(--t-fast) ease, color var(--t-fast) ease; }
.nc-head .nc-read:hover { background: var(--soft2); color: var(--ink); }
.nc-head .nc-read svg { width: 13px; height: 13px; }
.nc-group { padding: 7px 16px 4px; font-size: 10px; font-weight: 700; letter-spacing: .18em; color: var(--ink-3); }
.nc-list { max-height: 340px; overflow-y: auto; overflow-x: hidden; padding-bottom: 4px; }
.nc-it { position: relative; display: flex; gap: 11px; width: 100%; text-align: left; padding: 11px 16px;
  transition: background var(--t-fast) ease; }
.nc-it:hover { background: var(--hover); }
.nc-it .nc-ic { width: 36px; height: 36px; border-radius: 11px; flex: none; display: grid; place-items: center;
  background: color-mix(in oklch, var(--nc-c, var(--brand)) 12%, var(--card)); color: var(--nc-c, var(--brand)); }
.nc-it .nc-ic svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nc-it .nc-main { min-width: 0; flex: 1; }
.nc-it .nc-t { display: flex; align-items: center; gap: 6px; min-width: 0; }
.nc-it .nc-t b { font-size: 13px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nc-it .nc-t .nc-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--brand); }
/* 正文块级两行截断：nc-d 是行内 span，overflow/text-overflow 对行内元素不生效，
   长文案会横向溢出把列表撑出横向滚动 —— 必须 display:-webkit-box 折行 */
.nc-it .nc-d { font-size: 12px; color: var(--ink-2); margin-top: 1px; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  word-break: break-all; }
.nc-it .nc-time { flex: none; font-size: 10.5px; font-family: var(--mono, ui-monospace); font-variant-numeric: tabular-nums; color: var(--ink-3); margin-top: 2px; }
.nc-it.is-read { opacity: .6; }
.nc-it.is-read .nc-dot { display: none; }
.nc-it.is-read .nc-ic { background: var(--soft); color: var(--ink-3); }
.nc-empty { padding: 34px 0 30px; text-align: center; }
.nc-empty .ne-ring { width: 52px; height: 52px; margin: 0 auto 10px; border-radius: 17px; background: var(--soft); color: var(--ink-3); display: grid; place-items: center; }
.nc-empty .ne-ring svg { width: 24px; height: 24px; }
.nc-empty p { font-size: 12.5px; color: var(--ink-3); font-weight: 600; }
.nc-foot { border-top: 1px solid var(--line); padding: 9px; }
.nc-foot button { width: 100%; height: 38px; border-radius: 11px; font-size: 12.5px; font-weight: 700; color: var(--ink-2);
  display: flex; align-items: center; justify-content: center; gap: 6px; transition: background var(--t-fast) ease, color var(--t-fast) ease; }
.nc-foot button:hover { background: var(--soft); color: var(--ink); }
.nc-foot svg { width: 14px; height: 14px; }

/* ---- 8.2 公告横幅（Web 公告触达面，数据 = /api/announce/active） ----
   落位 = 顶栏之下内容首卡位；对齐小程序公告条的语义：
   tag 公告 + marquee 标题 + 正文一行截断 + 查看详情/知道了。 */
#announceSlot { padding: 0 20px; margin-top: -6px; }
#announceSlot:empty { display: none; }
.nb-card { --nb-c: var(--info); display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border-radius: 16px; background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow-sm);
  animation: nbIn var(--t-slow) var(--ease-out) both; }
@keyframes nbIn { from { transform: translateY(-8px); } }
.nb-card.out { transform: translateY(-8px); opacity: 0; transition: all var(--t-med) var(--ease); }
.nb-card .nb-ico { width: 40px; height: 40px; border-radius: 12px; flex: none; display: grid; place-items: center;
  background: color-mix(in oklch, var(--nb-c) 12%, var(--card)); color: var(--nb-c); }
.nb-card .nb-ico svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nb-card .nb-main { min-width: 0; flex: 1; }
.nb-card .nb-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
.nb-card .nb-tag { flex: none; display: inline-flex; align-items: center; gap: 5px; height: 21px; padding: 0 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 800; background: color-mix(in oklch, var(--nb-c) 12%, var(--card)); color: var(--nb-c); }
.nb-card .nb-tag::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.nb-card b.nb-title { font-size: 13.5px; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nb-card .nb-text { font-size: 12.5px; color: var(--ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nb-card .nb-go { flex: none; height: 31px; padding: 0 13px; border-radius: 999px; background: var(--soft); color: var(--ink-2);
  font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; gap: 4px;
  transition: background var(--t-fast) ease, color var(--t-fast) ease, transform var(--t-press) ease; }
.nb-card .nb-go:hover { background: var(--soft2); color: var(--ink); }
.nb-card .nb-go:active { transform: scale(.95); }
.nb-card .nb-go svg { width: 13px; height: 13px; }
.nb-card .nb-x { flex: none; width: 27px; height: 27px; border-radius: 50%; display: grid; place-items: center; color: var(--ink-3);
  transition: background var(--t-fast) ease, color var(--t-fast) ease; }
.nb-card .nb-x:hover { background: var(--soft); color: var(--ink); }
.nb-card .nb-x svg { width: 13px; height: 13px; stroke: currentColor; stroke-width: 2.1; stroke-linecap: round; fill: none; }

/* ================= 9. 主题切换 View Transition（圆形扩散） ================= */
::view-transition-old(root), ::view-transition-new(root) { animation: none; mix-blend-mode: normal; }
::view-transition-old(root) { z-index: 1; }
::view-transition-new(root) { z-index: 2; animation: vt-in 420ms ease both; }
@keyframes vt-in { from { clip-path: circle(0 at var(--vt-x, 90%) var(--vt-y, 40px)); } to { clip-path: circle(140% at var(--vt-x, 90%) var(--vt-y, 40px)); } }

/* ================= 10. 全局 Loading（瞌睡脸 + 进度条） ================= */
/* ================= 页面级 Loading（仅覆盖内容区，顶栏/侧栏/底栏不动） =================
   注意：不复用 .lg-loading（其 inset:0 + z-index:9999 会盖全屏），独立定位。 */
.lg-page-overlay {
  position: fixed;
  top: var(--top-h);
  left: var(--side-w);
  right: 0;
  bottom: 0;
  z-index: 40;   /* 低于 topbar(50) / bottomnav(70)，头部和底部菜单保持可见可点 */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 22px;
  background: var(--bg);
  /* 立即不透明：若走 opacity 淡入，本地/缓存快速环境下新页面可能在淡入的透明/半透明帧里
     先被渲染出来 → “内容闪现一霎那 → loading 才盖上来”。进入动效交给内部 lg-ring / lg-logo 等动画。 */
  opacity: 1;
}
.lg-page-overlay.show { opacity: 1; }   /* 兼容旧调用（JS 已同步加 show） */
body.side-min .lg-page-overlay { left: var(--side-w-min); }
.lg-page-overlay .lg-svg { --m: 116px; }
.lg-page-overlay .lg-text { transition: opacity 180ms ease; }
.lg-page-overlay.hiding {
  opacity: 0;
  transition: opacity 320ms ease;
  pointer-events: none;
}
@media (max-width: 767px) {
  .lg-page-overlay .lg-svg { --m: 100px; }
}

.lg-loading {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 20px;
  background: var(--bg);
  will-change: opacity;
  opacity: 1;
  transition: opacity 300ms ease;
}
.lg-loading.hiding { opacity: 0; pointer-events: none; }
.lg-loading.hiding * { animation: none !important; }

/* —— 标记：品牌 Logo 自身两条轮廓「拉成圆」→ 两圈反向流光 ——
   两条 path 已改写成同结构（外轮廓 M+2C+A+Z、内轮廓 M+4C），
   因此 CSS `d` 可逐段插值：外轮廓的顶部缺口合上、内轮廓两条直臂向外弧起
   并在顶端合拢 —— 是「logo 自己变成圈」的真形变，不是淡出换圈。
   --m = 尺寸，--cycle = 一个循环时长 */
.lg-svg {
  --m: 128px; --cycle: 4.4s;
  display: block; width: var(--m); height: var(--m);
  overflow: visible; color: var(--brand);
}
.lg-g { transform-box: view-box; transform-origin: 12px 11.22px; }
.lg-p { fill: none; stroke: currentColor; stroke-width: 1.65; stroke-linecap: round; stroke-linejoin: round; }
.lg-hi {
  fill: none; stroke: var(--brand2); stroke-width: 1.65; stroke-linecap: round;
  stroke-dasharray: 16 100; opacity: 0;
}

/* ① 形变：0-16% 保持 Logo → 30% 拉成整圆 → 86-100% 收回 */
.lg-p-out { animation: lgMorphOut var(--cycle) ease-in-out infinite; }
.lg-p-in  { animation: lgMorphIn  var(--cycle) ease-in-out infinite; }
@keyframes lgMorphOut {
  0%, 16%  { d: path("M12 4.24 C13.49 4.24 14.97 2.5 16.46 2.5 C17.26 2.5 17.83 2.65 18.63 3.32 A10.31 10.31 0 1 1 5.37 3.32 C6.17 2.65 6.74 2.5 7.54 2.5 C9.03 2.5 10.51 4.24 12 4.24 Z"); }
  30%, 86% { d: path("M12 0.91 C14.42 0.91 16.77 1.76 18.63 3.32 C20.49 4.88 21.73 7.04 22.15 9.43 A10.31 10.31 0 1 1 1.85 9.43 C2.27 7.04 3.51 4.88 5.37 3.32 C7.23 1.76 9.58 0.91 12 0.91 Z"); }
  100%     { d: path("M12 4.24 C13.49 4.24 14.97 2.5 16.46 2.5 C17.26 2.5 17.83 2.65 18.63 3.32 A10.31 10.31 0 1 1 5.37 3.32 C6.17 2.65 6.74 2.5 7.54 2.5 C9.03 2.5 10.51 4.24 12 4.24 Z"); }
}
@keyframes lgMorphIn {
  0%, 16%  { d: path("M15.62 7.84 A4.95 4.95 0 1 1 8.38 7.84"); }
  /* 大弧+顺时针下 SVG 圆心解在弦上方：端点须大者在前，整圆才同心于 12,11.22（反写圆心会跳到 12,1.32 → 内圈绕外圈公转） */
  30%, 86% { d: path("M12.01 6.27 A4.95 4.95 0 1 1 12 6.27"); }
  100%     { d: path("M15.62 7.84 A4.95 4.95 0 1 1 8.38 7.84"); }
}

/* ② 成形后再转：外圈顺时针、内圈逆时针（±720° 保证循环首尾对齐） */
.lg-g-out { animation: lgTurnOut var(--cycle) ease-in-out infinite; }
.lg-g-in  { animation: lgTurnIn  var(--cycle) ease-in-out infinite; }
@keyframes lgTurnOut { 0%, 30% { transform: rotate(0deg); } 86%, 100% { transform: rotate(720deg); } }
@keyframes lgTurnIn  { 0%, 30% { transform: rotate(0deg); } 86%, 100% { transform: rotate(-720deg); } }

/* ③ 流光：一段亮弧沿圈流动，两圈方向相反 */
.lg-hi-out { animation: lgFlowOut var(--cycle) linear infinite; }
.lg-hi-in  { animation: lgFlowIn  var(--cycle) linear infinite; }
@keyframes lgFlowOut {
  0%, 30% { opacity: 0; stroke-dashoffset: 0; }
  38%     { opacity: .92; }
  84%     { opacity: .92; stroke-dashoffset: -100; }
  100%    { opacity: 0; stroke-dashoffset: -100; }
}
@keyframes lgFlowIn {
  0%, 30% { opacity: 0; stroke-dashoffset: 0; }
  38%     { opacity: .92; }
  84%     { opacity: .92; stroke-dashoffset: 100; }
  100%    { opacity: 0; stroke-dashoffset: 100; }
}

.lg-text {
  font-size: 13px; font-weight: 700;
  color: var(--brand); letter-spacing: 0.03em;
}
@media (max-width: 767px) {
  .lg-loading .lg-svg { --m: 108px; }
}

/* ================= 富文本文案展示（.rich-txt）=================
   发布端 contenteditable 微编辑器与查看端（pb-vf-text / tf-copy）共用。
   存储子集：b/u/s/mark.h-*、span.t-*、ol>li、br（见 core.js window.richText）。
   划重点 = 色卡 soft 浅色胶囊（描边克隆换行不断开）；文字颜色 = 色卡品牌色。 */
.rich-txt b, .rich-txt strong { font-weight: 800; }
/* 划重点对齐 demo-rte-tiptap（2026-10-04 用户定稿「设置了样式每行显示效果应该跟 demo 一样」）：
   纯色圆角胶囊，不带「重点」徽章（inline-block 徽章参与行盒计算，带划重点的行比普通行高，
   与气泡/普通文字混排时逐行不齐）；形状也弃 999px 全胶囊改 demo 的 6px 圆角矩形 */
.rich-txt mark {
  background: transparent; color: inherit;
  padding: 1px 7px; border-radius: 6px;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}
.rich-txt mark[data-hl] { margin: 0 2%; }   /* 划重点前后文字等距 2%（与卡片同口径）；v203c 的「左缘 0 贴齐」废止：前无间距后有问题被指 */
.rich-txt mark[data-hl]:first-child,
.rich-txt mark.bub:first-child,
.rich-txt .mention:first-child { margin-left: 0; }   /* 行首吸边：前面无内容时不留前间距（用户定稿） */
.rich-txt .is-edge { margin-left: 0 !important; }   /* 行首吸边的引擎状态层兜底：PM 判定段落首节点（卡/图/划重点/胶囊）打标，!important 压过一切页级规则 */
.rich-txt mark.bub[data-w="100"] { display: block; margin: 2px 0; }   /* 通栏档：整行铺满且左右贴边无间隔 */
.rich-txt .h-y      { --hl-bg: #FFE8BD; --hl-fg: #7E5200; --hl-deep: #B8790A; }
.rich-txt .h-mint   { --hl-bg: #C9F2DE; --hl-fg: #0A6647; --hl-deep: #0E8A62; }
.rich-txt .h-pink   { --hl-bg: #FFD8E7; --hl-fg: #9D2F63; --hl-deep: #C24E80; }
.rich-txt .h-blue   { --hl-bg: #D3E3FF; --hl-fg: #1A4F94; --hl-deep: #2B69C4; }
.rich-txt .h-violet { --hl-bg: #E3DBFC; --hl-fg: #5237B8; --hl-deep: #6E4BD9; }
.rich-txt .h-orange { --hl-bg: #FFE1C7; --hl-fg: #8A4A12; --hl-deep: #C06A1E; }
.rich-txt .h-red    { --hl-bg: #FFD9D4; --hl-fg: #9B2620; --hl-deep: #CC3B33; }
.rich-txt .h-cyan   { --hl-bg: #C9ECF7; --hl-fg: #0E5E74; --hl-deep: #1587A8; }
.rich-txt .h-teal   { --hl-bg: #C6EFE6; --hl-fg: #0B5F4C; --hl-deep: #128068; }
.rich-txt .h-brown  { --hl-bg: #EAD9BD; --hl-fg: #5F4A1E; --hl-deep: #8A6B2A; }
.rich-txt .h-gray   { --hl-bg: #E4E7EC; --hl-fg: #3D4654; --hl-deep: #5C6875; }
.rich-txt .h-custom { --hl-bg: #E3DBFC; --hl-fg: #5237B8; --hl-deep: #6E4BD9; }   /* 自定义：变量内联在元素上，此处仅兜底 */
.rich-txt .h-y, .rich-txt .h-mint, .rich-txt .h-pink, .rich-txt .h-blue, .rich-txt .h-violet,
.rich-txt .h-orange, .rich-txt .h-red, .rich-txt .h-cyan, .rich-txt .h-teal, .rich-txt .h-brown, .rich-txt .h-gray, .rich-txt .h-custom {
  background: var(--hl-bg); color: var(--hl-fg);
}
html[data-theme="dark"] .rich-txt .h-y      { --hl-bg: #553F0D; --hl-fg: #FFD98F; --hl-deep: #FFC44D; }
html[data-theme="dark"] .rich-txt .h-mint   { --hl-bg: #0D4630; --hl-fg: #93E8C4; --hl-deep: #55DAA6; }
html[data-theme="dark"] .rich-txt .h-pink   { --hl-bg: #55203C; --hl-fg: #FFAFCE; --hl-deep: #F880B4; }
html[data-theme="dark"] .rich-txt .h-blue   { --hl-bg: #16315E; --hl-fg: #A8C8FF; --hl-deep: #6FA3F5; }
html[data-theme="dark"] .rich-txt .h-violet { --hl-bg: #2F2160; --hl-fg: #C9BAFF; --hl-deep: #A78CFA; }
html[data-theme="dark"] .rich-txt .h-orange { --hl-bg: #4E320F; --hl-fg: #FFCB96; --hl-deep: #F5A35C; }
html[data-theme="dark"] .rich-txt .h-red    { --hl-bg: #4E1A16; --hl-fg: #FFB3A8; --hl-deep: #F87B6E; }
html[data-theme="dark"] .rich-txt .h-cyan   { --hl-bg: #123A47; --hl-fg: #9ADFF2; --hl-deep: #55C4E8; }
html[data-theme="dark"] .rich-txt .h-teal   { --hl-bg: #0D3E33; --hl-fg: #96E5CE; --hl-deep: #4FD4B0; }
html[data-theme="dark"] .rich-txt .h-brown  { --hl-bg: #3A2F17; --hl-fg: #E2C98F; --hl-deep: #C9A55C; }
html[data-theme="dark"] .rich-txt .h-gray   { --hl-bg: #2B323C; --hl-fg: #C3CBD6; --hl-deep: #8B98A8; }
.rich-txt .t-violet { color: #6C59E4; }
/* 「重点」前缀章（2026-10-04 用户定稿恢复）：深底白字小圆章悬浮胶囊头部——
   曾按 demo 对齐整块删除（demo 无章），用户实测「重点的标签没有了=样式丢了」打回。
   inline-block 13px 高 < 25px 行高不参与撑行；中文 baseline 偏下以 top:-1.5px 居中补偿 */
.rich-txt .h-y::before, .rich-txt .h-mint::before, .rich-txt .h-pink::before, .rich-txt .h-blue::before, .rich-txt .h-violet::before,
.rich-txt .h-orange::before, .rich-txt .h-red::before, .rich-txt .h-cyan::before, .rich-txt .h-teal::before, .rich-txt .h-brown::before, .rich-txt .h-gray::before, .rich-txt .h-custom::before {
  content: '重点';
  display: inline-block;
  font-size: 10px; line-height: 1.1; font-weight: 900; letter-spacing: .08em;
  padding: 1px 6px; margin: 0 4px 0 0;
  background: var(--hl-deep); color: #fff;
  border-radius: 999px;
  vertical-align: middle;
  position: relative; top: -1.5px;
}
/* 空胶囊不显示孤立章 */
.rich-txt .h-y:empty::before, .rich-txt .h-mint:empty::before, .rich-txt .h-pink:empty::before, .rich-txt .h-blue:empty::before, .rich-txt .h-violet:empty::before,
.rich-txt .h-orange:empty::before, .rich-txt .h-red:empty::before, .rich-txt .h-cyan:empty::before, .rich-txt .h-teal:empty::before, .rich-txt .h-brown:empty::before, .rich-txt .h-gray:empty::before, .rich-txt .h-custom:empty::before { content: none; }
.rich-txt .t-blue   { color: #3B76E0; }
.rich-txt .t-mint   { color: #159A72; }
.rich-txt .t-pink   { color: #D9568F; }
.rich-txt .t-orange { color: #DE8430; }
html[data-theme="dark"] .rich-txt .t-violet  { color: #A396FF; }
html[data-theme="dark"] .rich-txt .t-blue    { color: #88AFF8; }
html[data-theme="dark"] .rich-txt .t-mint    { color: #43D3A4; }
html[data-theme="dark"] .rich-txt .t-pink    { color: #F58BB8; }
html[data-theme="dark"] .rich-txt .t-orange  { color: #F2A45C; }
/* 文字大小（v197）：span.fs-* 绝对字号（预览 publish-preview.css / 小程序 rich.js 同值换算） */
.rich-txt .fs-s { font-size: 12px; }
.rich-txt .fs-l { font-size: 16px; }
.rich-txt .fs-xl { font-size: 20px; }
.rich-txt ol { margin: 2px 0; padding-left: 22px; }
.rich-txt ol li { margin: 3px 0; padding-left: 2px; }
.rich-txt ol li::marker { color: var(--violet); font-weight: 800; font-variant-numeric: tabular-nums; }
/* ---- Tiptap 引擎渲染件（2026-10 换底）：NodeView 图片外壳 + @提及 ----
   样式必须与 demo-rte-tiptap 一致（用户定稿）：取值照抄 demo，.rich 换 .rich-txt，
   令牌沿用站点 --violet/--violet-soft（品牌绿别名，随主题/品牌色自适应，勿写死字面值） */
.rich-txt ::selection { background: color-mix(in oklch, var(--violet) 16%, transparent); }
.rich-txt .mention { display: inline-flex; align-items: center; gap: 4px;
  vertical-align: middle;   /* 基线解耦：真头像 img 无文本基线（inline-flex 基线落到盒底会把行撑高），首字/头像两态统一居中 */
  line-height: 1.2;         /* 胶囊内文字行高收紧：最高项=16px 头像而非 19.8px 文字行，胶囊才压得矮 */
  margin: -1px 4px;         /* 前后等距 4px；行盒贡献 18-2=16px：胶囊完整落在行内不外溢，上下相邻两行的胶囊间保有真实间隙 */
  background: var(--violet-soft); color: var(--violet); border-radius: 999px; padding: 1px 8px 1px 2px;
  font-weight: 600; font-size: .94em; }
.rich-txt .mav { width: 16px; height: 16px; border-radius: 50%; background: var(--violet); color: #fff;
  display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-style: normal; }
/* @chip 真实头像（NodeView 按名册渲染）：铺满圆框不变形；img 不可原生拖拽（拖拽归 PM 原子节点） */
.rich-txt img.mav { object-fit: cover; -webkit-user-drag: none; }
/* 图片：任意占位（左浮/右浮/铺满），其余元素让位（NodeView 外壳承担浮动，img 只管显示） */
.rich-txt .rte-imgwrap { display: inline-block; vertical-align: top; position: relative;
  float: left; margin: 3px 2.5% 8px 0; transition: box-shadow .15s; }
/* 内联跟随态（align=n，粘贴默认）：不浮动、就地渲染在插入点（内容之后）；上下图片行留 3px 垂直间隔 */
.rich-txt .rte-imgwrap.inline { float: none; display: inline-block; vertical-align: middle; margin: 3px 2px; }
.rich-txt .rte-imgwrap.right { float: right; margin: 3px 0 8px 2.5%; }
.rich-txt .rte-imgwrap.full { float: none; display: block; margin: 6px 0; }
.rich-txt .rte-imgwrap img { width: 100%; max-width: 100%; border-radius: 12px; display: block;
  margin: 0; -webkit-user-drag: none; }
.rich-txt .rte-imgwrap.ProseMirror-selectednode { box-shadow: 0 0 0 2px var(--violet); border-radius: 13px; }
/* 拖拽缩放手柄：hover/选中浮现，横向拖动改宽（引擎内 snap 40/60/100） */
.rich-txt .rte-resize { position: absolute; right: 7px; bottom: 7px; width: 20px; height: 20px; border-radius: 5px;
  background: var(--violet); border: 2px solid #fff; box-shadow: 0 1px 6px rgba(0,0,0,.3);
  display: flex; align-items: center; justify-content: center;
  cursor: nwse-resize; opacity: 0; transition: opacity .15s; touch-action: none; }
.rich-txt .rte-resize svg { width: 11px; height: 11px; stroke: #fff; }
.rich-txt .rte-imgwrap:hover .rte-resize, .rich-txt .rte-imgwrap.ProseMirror-selectednode .rte-resize { opacity: 1; }
/* 图片（v172+）：行内排版（v182）——小图文字绕排、多图自动同行；点选出现选框 + 拖拽手柄（data-w 百分比走行内 style） */
.rich-txt img { max-width: 100%; border-radius: 12px; margin: 6px 6px 6px 0; display: inline-block; vertical-align: middle; cursor: pointer; }
/* 图片选中框 + 拖拽手柄：absolute 挂在画布滚动容器内，随内容滚动、被容器裁剪不出界（v181） */
.rte-img-frame { position: absolute; z-index: 30; pointer-events: none;
  border: 1.5px solid var(--violet); border-radius: 4px;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--violet) 18%, transparent); }
.rte-img-frame .rte-img-handle { position: absolute; right: -7px; bottom: -7px; width: 15px; height: 15px;
  border-radius: 4px; background: var(--violet); border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.3); cursor: nwse-resize; pointer-events: auto; }
/* 气泡卡片（v172+）：选中一段文字包成色块卡片——**选的颜色就是卡片背景**，
   文字按底色自动配同族深阶保证可读（浅/深主题各一套，与划重点色卡同源）。
   v186 带标志卡片：附加 ic-* 类时升级为「标志卡」（左侧色条 + 首行图标字形），
   用作警示条/广播条等强调块；图标以 ::before emoji 渲染，与小程序端 emoji 文本节点同源。 */
.rich-txt mark.bub {
  display: inline-block; vertical-align: middle;   /* demo-rte-tiptap 同构：卡比行高，middle 垂直居中 */
  background: var(--bub-bg, #EFECFB); color: var(--bub-fg, #4F35AE);
  border: 1px solid var(--bub-bd, var(--bub-bg, #EFECFB));
  border-radius: 14px; padding: 4px 12px; margin: 2px 2%; line-height: 1.7; overflow: hidden;   /* 前后等距 2%（左缘原 0 与划重点同批废止）；卡的自有行高+内边距=药丸观感，行距均匀协调 */
}
/* 注意：不在基类声明 --bub-deep 兜底——基类特异性会压过命名色规则，
   徽章/标志条取色用 var(--bub-deep, var(--bub-fg, …)) 逐处兜底（v191 实障修复：
   此前基类 --bub-deep 使所有命名色气泡的标志恒为雾紫）。 */
.rich-txt .bub-y      { --bub-bg: #FFE8BD; --bub-fg: #7E5200; --bub-deep: #B8790A; }
.rich-txt .bub-mint   { --bub-bg: #C9F2DE; --bub-fg: #0A6647; --bub-deep: #0E8A62; }
.rich-txt .bub-pink   { --bub-bg: #FFD8E7; --bub-fg: #9D2F63; --bub-deep: #C24E80; }
.rich-txt .bub-blue   { --bub-bg: #D3E3FF; --bub-fg: #1A4F94; --bub-deep: #2B69C4; }
.rich-txt .bub-violet { --bub-bg: #E3DBFC; --bub-fg: #5237B8; --bub-deep: #6E4BD9; }
.rich-txt .bub-orange { --bub-bg: #FFE1C7; --bub-fg: #8A4A12; --bub-deep: #C06A1E; }
.rich-txt .bub-red    { --bub-bg: #FFD9D4; --bub-fg: #9B2620; --bub-deep: #CC3B33; }
.rich-txt .bub-cyan   { --bub-bg: #C9ECF7; --bub-fg: #0E5E74; --bub-deep: #1587A8; }
.rich-txt .bub-teal   { --bub-bg: #C6EFE6; --bub-fg: #0B5F4C; --bub-deep: #128068; }
.rich-txt .bub-brown  { --bub-bg: #EAD9BD; --bub-fg: #5F4A1E; --bub-deep: #8A6B2A; }
.rich-txt .bub-gray   { --bub-bg: #E4E7EC; --bub-fg: #3D4654; --bub-deep: #5C6875; }
.rich-txt .bub-custom { --bub-bg: #EFECFB; --bub-fg: #4F35AE; }   /* 自定义：bg/fg 变量内联在元素上；deep 禁止在此声明——自定义气泡内联只有 bg/fg，类写死 deep 会压死徽章自适应（徽章按兜底链退 --bub-fg） */
html[data-theme="dark"] .rich-txt mark.bub {
  background: var(--bub-bg, #2A2447); color: var(--bub-fg, #C9BAFF);
  border-color: var(--bub-bd, var(--bub-bg, #2A2447));
}
html[data-theme="dark"] .rich-txt .bub-y      { --bub-bg: #553F0D; --bub-fg: #FFD98F; --bub-deep: #FFC44D; }
html[data-theme="dark"] .rich-txt .bub-mint   { --bub-bg: #0D4630; --bub-fg: #93E8C4; --bub-deep: #55DAA6; }
html[data-theme="dark"] .rich-txt .bub-pink   { --bub-bg: #55203C; --bub-fg: #FFAFCE; --bub-deep: #F880B4; }
html[data-theme="dark"] .rich-txt .bub-blue   { --bub-bg: #16315E; --bub-fg: #A8C8FF; --bub-deep: #6FA3F5; }
html[data-theme="dark"] .rich-txt .bub-violet { --bub-bg: #2F2160; --bub-fg: #C9BAFF; --bub-deep: #A78CFA; }
html[data-theme="dark"] .rich-txt .bub-orange { --bub-bg: #4E320F; --bub-fg: #FFCB96; --bub-deep: #F5A35C; }
html[data-theme="dark"] .rich-txt .bub-red    { --bub-bg: #4E1A16; --bub-fg: #FFB3A8; --bub-deep: #F87B6E; }
html[data-theme="dark"] .rich-txt .bub-cyan   { --bub-bg: #123A47; --bub-fg: #9ADFF2; --bub-deep: #55C4E8; }
html[data-theme="dark"] .rich-txt .bub-teal   { --bub-bg: #0D3E33; --bub-fg: #96E5CE; --bub-deep: #4FD4B0; }
html[data-theme="dark"] .rich-txt .bub-brown  { --bub-bg: #3A2F17; --bub-fg: #E2C98F; --bub-deep: #C9A55C; }
html[data-theme="dark"] .rich-txt .bub-gray   { --bub-bg: #2B323C; --bub-fg: #C3CBD6; --bub-deep: #8B98A8; }
/* 深色基类 (0,3,2) 的 border-color 会压过下方标志条规则 (0,3,1)——左色条被拍成 bg 色（命名色丢 deep 强调、自定义退 bg）。
   故深主题补一条更高特异性的左色条取色，兜底链与亮色一致（命名 deep → 自定义 fg）。 */
html[data-theme="dark"] .rich-txt mark.bub[class*=' ic-'], html[data-theme="dark"] .rich-txt mark.bub[class^='ic-'] {
  border-left-color: var(--bub-deep, var(--bub-fg, currentColor));
}
.rich-txt mark.bub[class*=' ic-'], .rich-txt mark.bub[class^='ic-'] {
  border-left: 3px solid var(--bub-deep, var(--bub-fg, currentColor));   /* demo 原样：图标变体只加左色条 */
}/* 多行卡绕排（v203 用户定稿）：卡内含 <br>/块子元素（回车几次占几行）→ 左浮动——
   气泡占用的行数视作已占位，右侧空档保持逐行可输入：回车拆出的新行贴着气泡续排，
   占满卡高后自然落到卡下（与 v183 左浮小图同一模型；inline-block 的空档只是一根
   加高行盒、永远只有一行，是「气泡旁只能输一行」的根因）。
   :not 放行通栏档；第二选择器抬特异性压过 ic- 标志卡的 inline-block。
   小程序端浮动被忽略属有意降级（与图片同口径）；mp-rich 手机预览不挂本规则 */
/* 多行卡绕排（v203 用户定稿）+ 行网格锁定（v206 用户定稿「回车几次占几行」）：
   卡内含 <br>/块子元素（跨行）→ 左浮动，右侧空档保持逐行可输入（与 v183 左浮小图同一模型）。
   v206：卡高必须严格 = 行数×正文行高——基类 line-height:1.7 与正文行距（继承值）不成
   整数比、上下 padding/border 各加零头，卡面第 N 行与旁边第 N 行逐行错位、卡底剩零头带
   （v205 只能用下边距向上吸整行，垫出空底）。修法：卡内行距 inherit 正文、垂直 padding
   与上下 border 归零（border 与底同色本无观感），卡面行天然落在正文行脊上，占用行数=
   内容行数，N 行卡旁正好排 N 行、下一行贴卡底全宽开始（与图片锁行同一效果）。
   :not 放行通栏档；第二选择器抬特异性压过 ic- 标志卡的 inline-block/padding。
   左色条（border-left）不在本规则触碰范围，ic- 标志卡保留。
   小程序端浮动被忽略属有意降级（与图片同口径）；mp-rich 手机预览不挂本规则 */
.rich-txt mark.bub:has(br, div):not([data-w="100"]),
.rich-txt mark.bub[class*=' ic-']:has(br, div):not([data-w="100"]) {
  float: left;
  margin: 0 2% 0 0;   /* 浮卡绕排沟：卡旁逐行可输入（2% 是布局沟，非文字间距；盒模型已与基类同构，此处只管浮动） */
  padding: 0 12px;    /* v206 行网格锁定（换 Tiptap 引擎时曾丢失，2026-10-04 对齐 demo 补回）：垂直 padding/border 归零 + 行距 inherit，
                         卡高严格 = 行数×正文行高——卡面第 N 行与旁边第 N 行逐行对齐、卡底无零头带 */
  line-height: inherit;
  border-top: 0; border-bottom: 0;
}
/* 正文以多行卡收尾的容器包含浮动（编辑器 .ed-rte-body 本就 flow-root，
   此处兜查看端 .rich-txt 画板：卡不溢出压到下一块） */
.rich-txt:has(mark.bub:has(br, div)) { display: flow-root; }
/* 通栏档（v201）：data-w="100" = 铺满整行（浮动开关切换，与图片 data-w 调宽同模式）；
   置于 ic- 规则之后——同特异性靠源序压过 inline-block */
.rich-txt mark.bub[data-w="100"] {
  display: block; width: 100%; box-sizing: border-box;
  margin-right: 0;   /* v203c：通栏已满宽，抵消基类 2% 间隔防溢出 */
}
/* 多行卡（v203 定稿）：行内盒改左浮绕排（见上方 v203 规则）；曾试 :has(br) 块化
   导致光标移不出卡（块级无处可停）已回退，浮动模型无此问题（绕排区有文本位可停） */
/* 徽章字形真图标（v187）：圆角/圆形色块内语义字符，不用 emoji——rich-text 端以同构 span 复刻 */
.rich-txt mark.bub.ic-warn::before   { content: '!'; }
.rich-txt mark.bub.ic-forbid::before { content: '×'; }
.rich-txt mark.bub.ic-info::before   { content: 'i'; }
.rich-txt mark.bub.ic-ok::before     { content: '✓'; }
.rich-txt mark.bub.ic-star::before   { content: '★'; }
.rich-txt mark.bub.ic-ask::before    { content: '?'; }
.rich-txt mark.bub.ic-horn::before   { content: '»'; }
.rich-txt mark.bub.ic-reward::before { content: '¥'; }
.rich-txt mark.bub.ic-warn::before, .rich-txt mark.bub.ic-forbid::before, .rich-txt mark.bub.ic-info::before,
.rich-txt mark.bub.ic-ok::before, .rich-txt mark.bub.ic-star::before, .rich-txt mark.bub.ic-ask::before,
.rich-txt mark.bub.ic-horn::before, .rich-txt mark.bub.ic-reward::before {
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; margin: -3px 7px -3px 0;
  overflow: hidden;   /* inline 级盒 overflow 非 visible → 以底边对齐基线（规范行为），负下边距抬回视觉居中 */
  background: var(--bub-deep, var(--bub-fg, currentColor)); color: #fff;
  font-size: 10.5px; font-weight: 900; line-height: 1;
  border-radius: 5px;
}
.rich-txt mark.bub.ic-forbid::before, .rich-txt mark.bub.ic-info::before,
.rich-txt mark.bub.ic-ok::before, .rich-txt mark.bub.ic-ask::before { border-radius: 50%; }
/* 图片宽度百分比小徽标：absolute 挂在画布滚动容器内，随内容滚动不出界（v181） */
.rte-img-badge {
  position: absolute; z-index: 30;
  padding: 4px 10px; border-radius: 999px;
  background: var(--violet); color: #fff;
  font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums;
  box-shadow: 0 4px 14px rgba(0,0,0,.22);
  pointer-events: none;
}

/* ================= 日期选择器 v2（新 demo 风格 · 胶囊日历） =================
   快捷今天/明天/后天 + 圆形日期格 + 渐变选中 + 已选预览提示；
   逻辑限制在 core.js：pastDisabled 禁过去日期与过去月份翻页、今天限时不早于当前。 */
.ed-cal-panel { width: 304px; border-radius: 20px; }
.ed-cal-head { padding: 14px 14px 6px; }
.ed-cal-head b { font-size: 14px; font-weight: 800; letter-spacing: -.01em; }
.ed-cal-nav {
  width: 30px; height: 30px; border-radius: 999px; border: none; background: transparent;
  display: grid; place-items: center; color: var(--ink-2); cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.ed-cal-nav svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.ed-cal-nav:hover { background: var(--hover); color: var(--violet); }
.ed-cal-nav:disabled { opacity: .3; cursor: not-allowed; }
.ed-cal-quick { display: flex; gap: 6px; padding: 0 14px 10px; }
.ed-cal-chip {
  height: 28px; padding: 0 14px; border-radius: 999px; border: 0;
  background: var(--soft); color: var(--ink-2);
  font: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer;
  transition: background 160ms ease, color 160ms ease;
}
.ed-cal-chip:hover { color: var(--ink); background: var(--hover); }
.ed-cal-chip.on { background: color-mix(in srgb, var(--violet) 13%, var(--card)); color: var(--violet); }
.ed-cal-wk { padding: 0 14px 6px; }
.ed-cal-grid { padding: 0 12px 12px; gap: 3px; }
.ed-cal-day { height: 36px; border-radius: 999px; font-size: 12.5px; font-weight: 650; }
.ed-cal-day.is-today {
  color: var(--violet);
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--violet) 42%, transparent);
}
.ed-cal-day.is-today.past { color: var(--ink-3); box-shadow: none; opacity: .4; }
.ed-cal-day:not(.other):not(.past):hover {
  background: color-mix(in srgb, var(--violet) 10%, var(--card)); color: var(--violet);
}
.ed-cal-day.on {
  background: var(--brand-grad, var(--violet)); color: #fff;
  box-shadow: 0 8px 16px -9px var(--violet);
}
.ed-cal-day.on:hover { background: var(--brand-grad, var(--violet)); color: #fff; }
.ed-cal-day.on.is-today { box-shadow: 0 8px 16px -9px var(--violet); }
.ed-cal-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 14px; border-top: 1px solid var(--line); background: transparent;
}
.ed-cal-hint { font-size: 10.5px; font-weight: 600; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.ed-cal-foot-btns { display: flex; gap: 6px; flex: none; }
.ed-cal-ghost {
  height: 28px; padding: 0 13px; border-radius: 999px; border: 0;
  background: var(--soft); color: var(--ink-2);
  font: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer;
  transition: background 160ms ease, color 160ms ease;
}
.ed-cal-ghost:hover { background: var(--hover); color: var(--ink); }

/* ================= 时间滚轮 v2（新 demo 风格 · 胶囊高亮带 + 渐变确定） =================
   桌面 popover：20px 圆角、胶囊中心带、mono 大数字；确定 = 渐变胶囊。
   仅桌面尺寸覆盖；移动端 sheet 规则（308px 滚筒）不受影响。 */
@media (min-width: 768px) {
  .stp-panel { width: 276px; border-radius: 20px; }
  .stp-cols { flex-basis: 216px; height: 216px; min-height: 216px; }
  .stp-col { padding: 86px 4px; }
  .stp-band {
    left: 14px; right: 14px; height: 44px; border-radius: 999px;
    background: color-mix(in srgb, var(--violet) 9%, var(--card));
    border-top: 0; border-bottom: 0;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--violet) 20%, transparent);
  }
  .stp-opt { height: 44px; font-size: 19px; border-radius: 999px; }
  .stp-sep { color: var(--faint); font-weight: 700; }
}
.stp-foot { padding: 10px 14px 12px; }
.stp-now {
  height: 34px; padding: 0 15px; border-radius: 999px; border: 0;
  background: var(--soft); color: var(--ink-2);
}
.stp-now:hover { background: var(--hover); color: var(--ink); }
.stp-done {
  height: 34px; padding: 0 18px; border-radius: 999px; border: 0;
  background: var(--grad); color: #fff;
  box-shadow: 0 10px 20px -12px var(--brand);
}
.stp-done:hover { filter: brightness(1.06); background: var(--grad); color: #fff; }

/* ================= 时间选择器 v3 · 时:分 双段网格 =================
   与日历同语言：软胶囊 chip + 渐变选中 + mono 预览；滚筒旧样式已退役。
   超过 12 个分钟项（step=1）时分钟网格内部滚动。 */
.stp-head2 {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 16px 10px;
}
.stp-head2 .t { font-size: 13px; font-weight: 800; color: var(--ink); }
.stp-preview {
  height: 30px; padding: 0 13px; border-radius: 999px;
  display: inline-flex; align-items: center;
  background: color-mix(in srgb, var(--violet) 10%, var(--card));
  color: var(--violet);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.stp-grp { padding: 0 14px; }
.stp-grp + .stp-grp { margin-top: 10px; }
.stp-grp-l {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0 2px 6px;
  font-size: 10.5px; font-weight: 700; color: var(--faint);
}
.stp-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px; }
.stp-grid.scroll { max-height: 150px; overflow-y: auto; scrollbar-width: none; padding-right: 2px; }
.stp-grid.scroll::-webkit-scrollbar { display: none; }
.stp-chip {
  height: 31px; border-radius: 999px; border: 0;
  background: var(--soft); color: var(--ink-2);
  font: inherit; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, box-shadow 180ms ease, transform 120ms ease;
}
.stp-chip:hover { background: var(--hover); color: var(--ink); }
.stp-chip:active { transform: scale(.93); }
.stp-chip.on {
  background: var(--brand-grad, var(--violet)); color: #fff;
  box-shadow: 0 8px 16px -9px var(--violet);
}
.stp-chip.on:hover { background: var(--brand-grad, var(--violet)); color: #fff; }
.stp-chip:disabled { opacity: .28; cursor: not-allowed; background: var(--soft); color: var(--ink-3); }
.stp-chip:disabled:active { transform: none; }
.stp-foot2 {
  display: flex; align-items: center; gap: 8px;
  margin-top: 12px; padding: 11px 14px 13px;
  border-top: 1px solid var(--line);
}
.stp-now2 {
  height: 32px; padding: 0 15px; border-radius: 999px; border: 0;
  background: var(--soft); color: var(--ink-2);
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.stp-now2:hover { background: var(--hover); color: var(--ink); }
.stp-done2 {
  margin-left: auto; height: 34px; padding: 0 20px; border-radius: 999px; border: 0;
  background: var(--grad); color: #fff;
  font: inherit; font-size: 12.5px; font-weight: 750; cursor: pointer;
  box-shadow: 0 10px 20px -12px var(--brand);
  transition: filter 160ms ease, transform 120ms ease;
}
.stp-done2:hover { filter: brightness(1.06); }
.stp-done2:active { transform: scale(.96); }

/* 移动端 sheet：同网格，加大触摸目标 */
.stp-panel.stp-sheet .stp-head2 { padding: 6px 18px 12px; }
.stp-panel.stp-sheet .stp-head2 .t { font-size: 15px; }
.stp-panel.stp-sheet .stp-grp { padding: 0 18px; }
.stp-panel.stp-sheet .stp-grid { gap: 7px; }
.stp-panel.stp-sheet .stp-chip { height: 40px; font-size: 13.5px; }
.stp-panel.stp-sheet .stp-grid.scroll { max-height: 220px; }
.stp-panel.stp-sheet .stp-foot2 { padding: 12px 18px 22px; }
.stp-panel.stp-sheet .stp-now2 { height: 40px; flex: 1; }
.stp-panel.stp-sheet .stp-done2 { height: 40px; flex: 1.4; margin-left: 0; }

/* 时间面板 v3.1：弹性骨架 —— 空间不足时网格内部滚动，确定按钮永不被裁 */
.stp-panel { display: flex; flex-direction: column; }
.stp-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-bottom: 2px; }
.stp-body .stp-grid.scroll { max-height: none; overflow: visible; }
.stp-foot2 { flex: none; }
.stp-panel.stp-sheet .stp-body { padding-bottom: 4px; }

/* ================= 经典页码分页（全站统一组件） =================
   成员管理 / 任务发布历史 / 系统设置 / 数据管理共用同一套呈现：
   单行居中 「共 N 条 · 第 x/y 页  ‹ 1 2 3 … ›」，30px 胶囊钮 + 渐变选中。
   容器 .pager-bar 自带顶部分隔线与内距，直接放进卡片底部即可 */
.pager-bar {
  display: flex; align-items: center; justify-content: center;
  padding: 13px 16px; border-top: 1px solid var(--line);
}
.u-pager { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.u-pager-total { font-size: 11.5px; font-weight: 650; color: var(--faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.u-pager-nums { display: inline-flex; align-items: center; gap: 4px; }
.u-pg {
  min-width: 30px; height: 30px; padding: 0 8px; border: 0; border-radius: 999px;
  background: var(--soft); color: var(--sub); cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 750; font-variant-numeric: tabular-nums;
  display: inline-grid; place-items: center;
  transition: background .15s ease, color .15s ease, box-shadow .18s ease, transform .12s ease;
}
.u-pg svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.u-pg:hover:not(:disabled):not(.on) { background: var(--hover); color: var(--ink); }
.u-pg:active:not(:disabled) { transform: scale(.94); }
.u-pg.on { background: var(--grad); color: #fff; box-shadow: 0 8px 16px -9px var(--brand); }
.u-pg:disabled { opacity: .3; cursor: not-allowed; }
.u-pg-ellipsis { font-size: 12px; font-weight: 700; color: var(--faint); padding: 0 2px; }

/* ================= 图标映射（图标管理页） =================
   命中映射的 svg 内部以 iconfont 字形（<text>）绘制：
   容器对 svg 的尺寸规则原样生效（大小不变）；字形用 currentColor 填充，
   不吃容器的线性描边规则（fill:none/stroke 会把字形打没）。 */
svg.ic-cus text {
  fill: currentColor !important;
  stroke: none !important;
  stroke-width: 0 !important;
}
