:root { color-scheme: light dark; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", sans-serif; color: #182033; background: #f4f6fa; }
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; }
button, input, textarea, select { font: inherit; }
button { cursor: pointer; }
.hidden { display: none !important; }
.muted { color: #6f788b; }
.error { min-height: 22px; margin: 10px 0 0; color: #b42318; }
.login-view { min-height: 100vh; display: grid; place-items: center; padding: 24px; background: radial-gradient(circle at 15% 10%, #fff1f2, transparent 34%), #f4f6fa; }
.login-card { width: min(440px, 100%); padding: 42px; border: 1px solid #d9deea; border-radius: 24px; background: #fff; box-shadow: 0 22px 70px rgba(31, 42, 68, .12); }
.brand-logo { display: block; width: 190px; max-height: 72px; margin: 0 auto 28px; }
.product-kicker { margin: 0 0 6px; color: #111827; font-size: 12px; font-weight: 750; letter-spacing: .14em; }
h1, h2, p { margin-top: 0; }
.login-card label { display: grid; gap: 8px; margin-top: 20px; color: #4f586b; }
.login-card input { width: 100%; height: 48px; border: 1px solid #cfd5e1; border-radius: 10px; padding: 0 14px; outline: none; }
.login-card input:focus { border-color: #d5000d; box-shadow: 0 0 0 3px rgba(213, 0, 13, .09); }
.password-field { display: grid; grid-template-columns: 1fr 48px; gap: 8px; }
.password-toggle { border: 1px solid #cfd5e1; border-radius: 10px; background: #fff; color: #4f586b; }
.password-toggle svg { width: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.password-toggle[aria-pressed="false"] .eye-slash { display: none; }
.primary { border: 0; background: #d5000d; color: #fff; font-weight: 700; }
/* 禁用态必须有可见区别，否则「应用」在配不了的部门上仍是满血红底 + pointer 指针，看着完全能点，
   点了却什么都不发生——用户报的「点应用没有用」一半来自这里（另一半是它压根没被点，见 admin.js）。 */
button:disabled { opacity: .45; cursor: not-allowed; }
.full { width: 100%; height: 50px; margin-top: 24px; border-radius: 10px; }
.security-note { margin: 28px 0 0; color: #8991a2; font-size: 12px; text-align: center; }
.state-card { width: min(520px, calc(100% - 48px)); margin: 15vh auto; padding: 36px; border: 1px solid #d9deea; border-radius: 18px; background: #fff; text-align: center; }
.app { min-height: 100vh; display: grid; grid-template-columns: 236px 1fr; }
.sidebar { position: sticky; top: 0; height: 100vh; padding: 28px 18px; background: #171b27; color: #fff; display: flex; flex-direction: column; }
.sidebar-logo { width: 176px; max-height: 72px; margin: 0 auto 10px; padding: 8px 10px; border-radius: 10px; background: #fff; }
.environment { margin: 0 auto 30px; color: #adb5c7; font-size: 12px; }
/* 侧栏是 100vh 的 flex 列（.sidebar）。菜单项一多，它就把页脚（角色行 + 退出按钮）顶出可视区
   并被裁掉一半（2026-10-08 用户截图实测）。让**菜单自己滚动**、页脚始终可见：
   `min-height: 0` 是必须的——flex 子项默认不肯收缩到内容以下，不写它滚动条根本不会出现。 */
.sidebar nav { display: grid; gap: 6px; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.nav { padding: 12px 14px; border: 0; border-radius: 9px; background: transparent; color: #bdc5d5; text-align: left; }
.nav:hover, .nav.active { background: #2b3142; color: #fff; }
/* =============================================================================
   侧边导航分组（2026-10-09）—— 12 个一级项平铺着查阅麻烦，按用途收成 4 组。
   风格**逐项对齐前端左侧导航栏**（apps/web/platform/shell.css 的 .nav-group /
   .nav-group-title / .nav-group-items / .nav-group-items-inner）：结构、间距、
   就地折叠（grid-template-rows 1fr↔0fr）、箭头旋转 90°、子项依次淡入 —— 全部照它。
   **颜色用控制台的深色**（侧栏底色 #171b27）。本表没有设计 token，故写字面值并标注
   对应的前端 token：前端侧栏改了值，这里要跟着改。
   交互与「组织与岗位」左树同一套约定：**箭头只管收放、不切换选中**；子项选中时父标题只做弱高亮。
   ============================================================================= */
.nav-group { display: flex; flex-direction: column; gap: 3px; }
.nav-group-title {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; min-width: 0; margin: 0; padding: 8px 12px;
  border: 0; border-radius: 6px;                 /* --radius-xs */
  background: transparent; color: #bdc5d5;
  font-size: 13px; font-weight: 500; line-height: 1.3;   /* --fs-base */
  text-align: left; cursor: pointer;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  transition: background .15s ease, color .15s ease;
}
.nav-group-title:hover { background: #232838; color: #fff; }   /* --hover-bg */
/* 子项被选中时父标题只做弱高亮，不抢选中底色（与 .orgpos-group-main.branch-active 同口径）。
   这里用控制台自己的白，不抄前端的红 —— 深色侧栏上红字读不出层级。 */
.nav-group.branch-active > .nav-group-title { color: #fff; font-weight: 600; }
/* ===== 箭头：内联 svg，收起时转 -90° =====
   ⚠️ **刻意不用 mask + data: URI**（前端是那么写的）：控制台是严格 CSP，
   `img-src 'self'` **不含 `data:`** —— data URI 的 mask 会被浏览器直接挡掉，
   箭头根本画不出来（而在没有 CSP 的前端那边是正常的）。
   内联 <svg> 是标记不是资源，不受 img-src 约束，形状与前端那条 polyline 完全一致。 */
.nav-group-chevron {
  flex: none; width: 16px; height: 16px; display: block;
  color: #7b8397;                                   /* --muted：比文字淡，但看得出可点 */
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  transform-origin: center;
  transition: transform .28s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-group-title[aria-expanded="false"] .nav-group-chevron { transform: rotate(-90deg); }
.nav-group-title[aria-expanded="true"] .nav-group-chevron { transform: rotate(0deg); }
/* ===== 子项：缩进 + 1px 竖线 + 就地折叠（不是 display:none，是高度收拢） ===== */
.nav-group-items {
  display: grid; grid-template-rows: 1fr;
  position: relative; margin-left: 16px; padding-left: 8px;
  transition: grid-template-rows .28s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-group-items::before {
  content: ""; position: absolute; left: 0; top: 4px; bottom: 6px; width: 1px;
  background: #2b3142; border-radius: 1px;          /* --line */
  transition: opacity .2s ease; pointer-events: none;
}
.nav-group-title[aria-expanded="false"] + .nav-group-items {
  grid-template-rows: 0fr; visibility: hidden; pointer-events: none;
  transition: grid-template-rows .28s cubic-bezier(0.4, 0, 0.2, 1), visibility 0s .28s;
}
.nav-group-title[aria-expanded="false"] + .nav-group-items::before { opacity: 0; }
.nav-group-items-inner { min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 3px; }
/* ===== 子项依次错开淡入 ===== */
@keyframes navSubItemFadeIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > * { animation: navSubItemFadeIn .24s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > *:nth-child(1) { animation-delay: .02s; }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > *:nth-child(2) { animation-delay: .05s; }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > *:nth-child(3) { animation-delay: .08s; }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > *:nth-child(4) { animation-delay: .11s; }
.nav-group-title[aria-expanded="true"] + .nav-group-items .nav-group-items-inner > *:nth-child(n+5) { animation-delay: .14s; }
.sidebar-footer { margin-top: auto; display: grid; gap: 12px; color: #adb5c7; font-size: 12px; flex: 0 0 auto; }
.sidebar-footer button { padding: 10px; border: 1px solid #465066; border-radius: 8px; background: transparent; color: #fff; }
main { min-width: 0; padding: 36px 42px; }
header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 28px; }
header h1 { margin: 0; }
header button, .section-head button, .session-item button, .state-card button, .form-actions button, td button { padding: 9px 14px; border: 1px solid #cfd5e1; border-radius: 8px; background: #fff; color: #30384a; }
.panel { display: none; }
.panel.active { display: block; }
.kpi-grid { display: grid; grid-template-columns: repeat(5, minmax(130px, 1fr)); gap: 14px; }
.kpi { padding: 22px; border: 1px solid #dde2ec; border-radius: 14px; background: #fff; }
.kpi strong { display: block; margin-top: 8px; font-size: 30px; }
.notice { margin-top: 18px; padding: 20px; border-left: 4px solid #d5000d; border-radius: 8px; background: #fff; }
.notice p { margin: 8px 0 0; color: #6f788b; }
/* 头部原本只有一个按钮，多一个按钮就会跟标题抢宽度：按钮文字被挤成两行、还压住下面的说明文字
   （截图实测）。所以①给 gap + flex-wrap，窄屏时按钮整组落到下一行而不是互相压；②按钮自己不许换行。 */
.section-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.section-head-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.section-head-actions button { white-space: nowrap; }
.section-head h2 { margin-bottom: 6px; }
.section-head p { margin-bottom: 0; color: #6f788b; }
.ai-config-head { gap: 24px; }
.ai-master-control { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
.ai-master-label { color: #697286; font-size: 14px; font-weight: 700; }
.ai-master-label.enabled { color: #137a3d; }
.toggle-switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
.toggle-switch input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.toggle-track { position: relative; display: block; width: 46px; height: 26px; border: 1px solid #b9c1d0; border-radius: 999px; background: #d7dce5; transition: background .18s ease, border-color .18s ease; }
.toggle-thumb { position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(24, 32, 51, .25); transition: transform .18s ease; }
.toggle-switch input:checked + .toggle-track { border-color: #137a3d; background: #1d9b55; }
.toggle-switch input:checked + .toggle-track .toggle-thumb { transform: translateX(20px); }
.toggle-switch input:focus-visible + .toggle-track { outline: 3px solid rgba(213, 0, 13, .18); outline-offset: 2px; }
.toggle-switch input:disabled + .toggle-track { opacity: .5; cursor: not-allowed; }
.ai-master-status { margin: -6px 0 16px; font-size: 13px; text-align: right; }
.table-wrap { overflow: auto; border: 1px solid #dde2ec; border-radius: 12px; background: #fff; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 13px 14px; border-bottom: 1px solid #edf0f5; text-align: left; vertical-align: top; }
th { color: #697286; background: #fafbfc; font-size: 12px; white-space: nowrap; }
.badge { display: inline-block; margin: 1px 4px 1px 0; padding: 3px 7px; border-radius: 999px; background: #eef1f6; font-size: 12px; white-space: nowrap; }
.badge.active, .badge.succeeded { background: #e9f8ef; color: #137a3d; }
.badge.locked, .badge.denied, .badge.failed, .badge.disabled, .badge.inactive { background: #fff0f0; color: #b42318; }
.badge.draft, .badge.pending_activation { background: #fff6e5; color: #9a5b00; }
/* 锁定到期是个既非「正常」也非「锁定中」的中间态：锁过、也确实已经放行了（status 只有
   成功登录才写回 active）。用蓝色另立一档，不跟绿/红/黄任何一态撞色，管理员才不会看串。 */
.badge.lock-expired { background: #eaf1fb; color: #1a5fb4; }
.session-list { display: grid; gap: 12px; }
.session-item { display: flex; justify-content: space-between; gap: 20px; padding: 18px; border: 1px solid #dde2ec; border-radius: 12px; background: #fff; }
.session-item p { margin: 5px 0 0; color: #6f788b; font-size: 13px; }
.tree-list { display: grid; gap: 8px; }
.tree-node { display: flex; justify-content: space-between; gap: 18px; padding: 13px 16px; border: 1px solid #dde2ec; border-radius: 10px; background: #fff; }
.tree-node small { color: #7a8394; }
.tree-node .workspace-binding { color: #315f78; }
.tree-node .workspace-binding.missing { color: #b54708; }
/* 一级部门与二级板块只靠底色、字号和名称后的「一级/二级」标签区分：每张卡只有一层 1px 外框线，
   不再给左边单独加一条色条（那会让左右边框不一样粗、看着像套了两层框）。 */
.tree-node.level-root { background: #eef2f9; border-color: #c9d4e6; }
.tree-node.level-2 { background: #f7f9fc; border-color: #e4e9f2; }
.tree-node.level-2 strong { font-size: 14px; font-weight: 600; }
.node-level { display: inline-block; margin-right: 6px; padding: 2px 7px; border-radius: 999px; background: #eef1f6; color: #4f586b; font-size: 11px; vertical-align: 1px; }
.tree-node.level-1 .node-level { background: #e3e9f5; color: #3c4a6b; }
.tree-node.level-2 .node-level { background: #e7ecf5; color: #5b6a86; }
/* 有下级的节点可折叠：按钮与叶子节点的等宽占位对齐，折叠时箭头转成向右。 */
.tree-toggle, .tree-toggle-placeholder { display: inline-block; width: 22px; height: 22px; margin-right: 7px; vertical-align: -5px; }
.tree-toggle { padding: 0; border: 1px solid #cfd5e1; border-radius: 6px; background: #fff; color: #4f586b; line-height: 1; }
.tree-toggle:hover { border-color: #8794ad; color: #30384a; }
.tree-toggle svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform .15s ease; }
.tree-toggle.collapsed svg { transform: rotate(-90deg); }
.node-count { display: inline-block; margin-left: 6px; padding: 2px 8px; border-radius: 999px; background: #f0f2f7; color: #6f788b; font-size: 11px; vertical-align: 1px; }
.config-form { display: grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap: 18px; padding: 24px; border: 1px solid #dde2ec; border-radius: 14px; background: #fff; }
.config-form label, .dialog-form label { display: grid; gap: 7px; color: #4f586b; }
.config-form input, .config-form textarea, .dialog-form input, .dialog-form select { width: 100%; padding: 11px 12px; border: 1px solid #cfd5e1; border-radius: 9px; background: #fff; }
.config-form textarea { resize: vertical; }
.config-form .wide { grid-column: 1 / -1; }
.config-form .checkbox { display: flex; align-items: center; gap: 8px; }
.config-form .checkbox input { width: auto; }
/* 品牌 LOGO 预览。⚠️ 控制台是严格 CSP（style-src 'self'），**内联 style 会被直接拦掉** ——
   尺寸只能写在这里，不能写在标签上。 */
.brand-logo-preview { max-height: 64px; max-width: 240px; object-fit: contain; }
/* 「新增/编辑服务商」的表单默认收起：多服务商时它不该一直占着版面。
   ⚠️ .config-form 是 display:grid —— **会盖掉 UA 的 [hidden]{display:none}**，必须显式补这一条。 */
.config-form[hidden] { display: none; }
/* 模型表按服务商分组：多家服务商时一行一个分组标题，比混在一张长表里可读得多。 */
.provider-group th { text-align: left; background: #f4f6fa; font-weight: 600; color: #3b4252; padding: 8px 12px; }
/* 「可用模型与版本」默认折起：不点开就不占版面；标题上带数量，不展开也知道里面有什么。 */
.ai-fold > summary { cursor: pointer; display: flex; align-items: baseline; gap: 10px; padding: 6px 0; }
.ai-fold > summary::marker { color: #8a92a6; }
.ai-fold-title { font-size: 20px; font-weight: 600; color: #23262f; }
.ai-fold[open] > summary { margin-bottom: 12px; }
.brand-logo-preview[hidden] { display: none; }
/* 弹窗底部的动作行（取消/保存）。
   ⚠️ 必须有上间距：它是表单里唯一一个**跟在 label 后面**的块，靠 `label + label` 那种相邻规则
   拿不到间距，于是「取消/保存」会**紧贴上面的输入框**（2026-10-08 用户截图指出的就是这个：
   输入框底部到按钮只有约 6px，而表单其它间距是 14–26px）。23 个弹窗共用这一条规则。 */
.form-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.form-actions .primary { background: #d5000d; color: #fff; border-color: #d5000d; }
dialog { width: min(520px, calc(100% - 32px)); border: 0; border-radius: 16px; padding: 0; box-shadow: 0 24px 80px rgba(20, 28, 45, .3); }
dialog::backdrop { background: rgba(13, 18, 28, .55); }
/* 内容超过一屏时滚动的必须是内层这一块、不能是 dialog 自己：滚动条是方的、贴在右边缘，长在 dialog
   身上会把右边两个圆角盖成直角，看着就成了「左上左下圆、右上右下直」。让 dialog 只负责把溢出裁掉
   （裁出来的四角都是圆的），滚动交给 .dialog-form——所有 dialog 都只有一个 .dialog-form 直接子节点。 */
dialog { overflow: hidden; }
dialog > .dialog-form { max-height: calc(100vh - 38px); overflow: auto; }
.dialog-form { padding: 26px; }
.dialog-form label + label { margin-top: 14px; }
.dialog-form label small { line-height: 1.45; }
.role-options { display: grid; gap: 9px; margin: 18px 0; }
.role-options label { display: flex; align-items: center; gap: 9px; }
.dialog-form .role-options input[type="checkbox"], .dialog-form .role-options input[type="radio"] { width: 18px; height: 18px; flex: 0 0 18px; }
.node-actions { display: flex; align-items: center; gap: 8px; }
/* 这一列有 6-7 个按钮，不给表格一个下限宽度的话，其余列会被压到一个字一行。 */
.surveys-table { min-width: 1200px; }
.surveys-table td { white-space: nowrap; }
.surveys-table td:first-child { white-space: normal; }
.node-actions button { padding: 6px 10px; white-space: nowrap; }
/* 账号与权限表：人员列要放得下 4-5 个中文字，MFA 列要放得下「未要求」。这两个下拉/单元格
   不给下限宽度的话，会被「角色（部门派生）」和「操作」两列挤到一字一行。
   注意：加了「登录账号」列之后 MFA 从第 5 列变成第 6 列，这条规则要跟着挪。 */
.accounts-table th:nth-child(1), .accounts-table td:nth-child(1) { min-width: 7.5em; white-space: nowrap; }
.accounts-table th:nth-child(6), .accounts-table td:nth-child(6) { min-width: 5.5em; white-space: nowrap; }
/* 「角色（部门派生）」是系统按部门派生的默认值，不是要逐字精读的信息，却一个账号常挂 2–3 个徽章。
   这一列没有自己的规则时，徽章全挤在一行把列越撑越宽、连带挤掉「操作」。（加「登录账号」列那次
   就是把它无保护地暴露了出来：原来给 MFA 写的 nth-child(5) 平移过来给它套了 nowrap，列被撑到 364px。）
   改法是给表头一个上限宽度、并让单元格可以换行——让这一列往下长，而不是往右长。 */
.accounts-table th:nth-child(5) { width: 9em; }
.accounts-table td:nth-child(5) { white-space: normal; }
/* 激活码按钮文字长，跟「权限」「调整角色」挤在一行会把操作列撑宽。让它自己占一行。 */
.accounts-table td:last-child { white-space: nowrap; }
.accounts-table [data-issue-activation] { display: block; margin-top: 6px; }
.activation-code { display: block; margin: 18px 0; padding: 16px; border: 1px dashed #d5000d; border-radius: 10px; background: #fff7f7; color: #9f0010; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; user-select: all; }
/* 激活码要和登录账号成对发给员工，所以两者放在同一个弹窗里；账号本身也要能一键选中复制
   （单独复制一段用得上），所以跟激活码一样开 user-select: all。样式刻意比红框激活码轻，
   避免两处都像「必须照抄」的重点。 */
.activation-identity { display: grid; gap: 8px; margin: 16px 0 0; padding: 14px 16px; border: 1px solid #dde2ec; border-radius: 10px; background: #f8fafc; }
.activation-identity > div { display: grid; grid-template-columns: 5.5em 1fr; align-items: baseline; gap: 10px; }
.activation-identity dt { color: #6b7690; font-size: 13px; }
.activation-identity dd { margin: 0; color: #1f2430; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; user-select: all; }
.model-policy-form { margin-top: 20px; padding: 24px; border: 1px solid #dde2ec; border-radius: 14px; background: #fff; }
.model-policy-form > label { display: grid; gap: 7px; margin-top: 18px; color: #4f586b; }
.model-policy-form > label input { width: 100%; padding: 11px 12px; border: 1px solid #cfd5e1; border-radius: 9px; }
.model-policy-form input[type="checkbox"], .model-policy-form input[type="radio"] { width: 18px; height: 18px; }
.warehouse-scope-head { margin-top: 32px; }
.scope-warning { margin: 16px 0; padding: 12px 14px; border-left: 4px solid #b54708; border-radius: 8px; background: #fff8eb; color: #7a4500; line-height: 1.5; }
.perm-section-title { margin: 18px 0 6px; font-size: 13px; color: #4f586b; }
.perm-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.dept-tag, .grant-tag { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 999px; font-size: 12px; line-height: 1.6; }
.dept-tag { background: #eaf6ef; color: #1f6b45; border: 1px solid #cfe8d9; }
.grant-tag { background: #fff6e6; color: #8a5a12; border: 1px solid #f0dfba; }
.grant-tag button { margin-left: 2px; padding: 0 4px; border: 0; background: transparent; color: #b42318; font-size: 14px; line-height: 1; }
.grant-tag button:hover { color: #7a0006; }
.perm-add-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 2px; align-items: center; }
.perm-add-row select { width: auto; flex: 1 1 160px; min-width: 0; }
/* 一级部门没有二级板块时，二级下拉整个藏掉，而不是留一个空下拉占位。 */
.perm-add-row select[hidden] { display: none; }
/* 「重新解析角色 / 应用 / 添加授权 / 保存」这四个红色按钮原来落在浏览器默认样式上（上面的按钮
   padding 规则没覆盖 .perm-add-row）：直角、也没有图标，跟页面其它按钮不是一套。统一成圆角 +
   同一套线性图标（stroke 用 currentColor，与侧栏的密码眼睛图标同一种风格）。 */
.perm-add-row button { padding: 10px 16px; border-radius: 9px; display: inline-flex; align-items: center; gap: 7px; }
.perm-add-row button svg { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.perm-admin-row .inline-check { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: #30384a; }
.perm-admin-row .inline-check input { width: 18px; height: 18px; flex: 0 0 18px; }
.perm-admin-row small { flex: 1 1 190px; }
.perm-admin-row button { margin-left: auto; }
/* 权限对话框比通用 dialog 宽：两段都要放「部门 + 岗位 + 按钮」再加一块权限清单。 */
#permDialog { width: min(620px, calc(100% - 32px)); }
.perm-preview { margin: 8px 0 2px; padding: 10px 12px; border: 1px solid #e4e8f0; border-radius: 10px; background: #fafbfd; font-size: 12px; color: #4f586b; line-height: 1.7; }
.perm-preview-title { display: block; margin-bottom: 5px; }
.perm-ops { display: flex; flex-wrap: wrap; gap: 6px; }
.perm-op { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: 999px; border: 1px solid #dbe3f5; background: #eef2fb; color: #2c3a58; }
.perm-op-mode { padding-left: 5px; border-left: 1px solid #cdd6ea; font-size: 11px; font-style: normal; color: #6b7690; }
.perm-preview small { display: block; margin-top: 6px; }
.perm-warn { color: #b54708; }
/* 「应用 / 保存 / 添加授权」成功时要出一句话。原来只有失败才说话，成功时界面一声不响，
   用户点完看不到任何变化，报的就是「点了没有用」。 */
.perm-status { min-height: 22px; margin: 10px 0 0; color: #067647; }
/* 登录账号是管理员要核对、要照抄的一串数字，等宽字体 + 可整段选中，跟弹窗里的账号同一待遇。 */
.login-name { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; user-select: all; }
@media (max-width: 980px) { .app { grid-template-columns: 1fr; } .sidebar { position: static; height: auto; } .sidebar nav { grid-template-columns: repeat(2, 1fr); } .sidebar-footer { margin-top: 18px; } main { padding: 24px 18px; } .kpi-grid, .config-form { grid-template-columns: 1fr; } .config-form .wide { grid-column: auto; } .ai-config-head { align-items: flex-start; } .ai-master-status { text-align: left; } }

.login-card .login-trust { display: flex; align-items: center; gap: 9px; font-size: 14px; }
.login-card .login-trust input { width: 16px; height: 16px; padding: 0; flex: 0 0 16px; }

/* 问卷两个弹窗高度另有限制（88vh），滚动交给同一层内层块。 */
#surveyDialog, #surveyDataDialog { width: min(880px, calc(100% - 32px)); }
#surveyDialog > .dialog-form, #surveyDataDialog > .dialog-form { max-height: 88vh; }
.survey-form label, .survey-data label { display: grid; gap: 7px; color: #4f586b; }
.survey-time-row { display: grid; grid-template-columns: repeat(2, minmax(200px, 1fr)); gap: 14px; margin-top: 14px; }
.survey-time-row label { margin-top: 0; }
.survey-editor-head { margin-top: 26px; align-items: flex-end; }
.survey-editor-head h3 { margin: 0 0 6px; }
.survey-editor-head p { margin: 0; color: #6f788b; font-size: 13px; }
.survey-editor-buttons { display: flex; gap: 8px; flex: 0 0 auto; }
.survey-editor { display: grid; gap: 16px; }
.survey-section { padding: 16px; border: 1px solid #dde2ec; border-radius: 12px; background: #fafbfc; }
.survey-section-head { display: flex; align-items: center; gap: 10px; }
.survey-section-head input { flex: 1 1 auto; }
.survey-question { margin-top: 12px; padding: 14px; border: 1px solid #dde2ec; border-radius: 10px; background: #fff; }
.survey-question-head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.survey-question-head .survey-question-order { color: #6f788b; font-size: 13px; flex: 0 0 auto; }
.survey-question-head select { width: auto; }
.survey-question-head .survey-question-actions { display: flex; gap: 6px; margin-left: auto; }
.survey-question-actions button, .option-row button, .survey-section-head button { width: 44px; height: 44px; padding: 0; border: 1px solid #cfd5e1; border-radius: 9px; background: #fff; color: #30384a; font-size: 15px; line-height: 1; }
.option-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.option-row input[type="text"] { flex: 1 1 auto; }
.option-group { margin-top: 10px; padding: 10px 12px; border: 1px dashed #cfd5e1; border-radius: 9px; }
.survey-question .inline-check { display: flex; align-items: center; gap: 8px; margin-top: 12px; color: #4f586b; }
.survey-question .inline-check input { width: 18px; height: 18px; flex: 0 0 18px; }
.survey-add-row { margin-top: 10px; }
.survey-add-row button { padding: 9px 14px; border: 1px dashed #b9c1d0; border-radius: 9px; background: #fff; color: #30384a; }
.trend-list { display: grid; gap: 6px; }
.trend-row { display: grid; grid-template-columns: 120px 1fr 60px; align-items: center; gap: 10px; font-size: 14px; }
.trend-bar { height: 12px; border-radius: 999px; background: #eef1f6; overflow: hidden; }
.trend-bar span { display: block; height: 100%; background: #d5000d; }
.share-link { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; user-select: all; }
.survey-data .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

/* =============================================================================
   考勤与假勤设置样式
   ============================================================================= */
.attendance-subnav-buttons { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
.attendance-tab { padding: 8px 16px; border: 1px solid #cfd5e1; border-radius: 8px; background: #fff; color: #4f586b; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.15s ease; }
.attendance-tab:hover { background: #f4f6fa; border-color: #b9c1d0; color: #1e2433; }
.attendance-tab.active { background: #1e2433; border-color: #1e2433; color: #fff; }
.attendance-subtab { margin-top: 14px; }
.attendance-subtab.hidden { display: none; }
.attendance-subtab.active { display: block; }
.attendance-shifts-table, .attendance-rules-table, .attendance-nodes-table, .attendance-blocked-table { width: 100%; border-collapse: collapse; }
.primary-small { padding: 5px 12px; font-size: 13px; font-weight: 500; border-radius: 6px; background: #0052cc; color: #fff; border: none; cursor: pointer; }
.primary-small:hover { background: #0047b3; }
.danger-small { padding: 5px 12px; font-size: 13px; font-weight: 500; border-radius: 6px; background: #fff; color: #d9383a; border: 1px solid #fecdd3; cursor: pointer; }
.danger-small:hover { background: #fee2e2; border-color: #f87171; }
.btn-toggle-shift { padding: 5px 12px; font-size: 13px; border-radius: 6px; border: 1px solid #cfd5e1; background: #fff; color: #30384a; cursor: pointer; }
.btn-toggle-shift:hover { background: #f4f6fa; border-color: #b9c1d0; }
.btn-goto-node-assign { padding: 5px 12px; font-size: 13px; border-radius: 6px; border: 1px solid #cfd5e1; background: #f9f0ff; color: #6929c4; font-weight: 500; cursor: pointer; }
.btn-goto-node-assign:hover { background: #f1ddff; }
.success-row { text-align: center; padding: 24px 12px !important; color: #087443; font-weight: 500; background: #f6fcf8; border-radius: 8px; }
#attendanceRuleDialog, #attendanceNodeDialog, #attendanceShiftDialog { width: min(600px, calc(100% - 32px)); }
#attendanceRuleValueContainer { margin: 12px 0; }
#attendanceRuleValueContainer label { display: grid; gap: 7px; color: #4f586b; font-weight: 500; }
.workdays-checkbox-group { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.workdays-checkbox-group label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; font-weight: normal; }
.workdays-checkbox-group input { width: auto; margin: 0; }

/* =============================================================================
   组织与岗位：左侧组织菜单树 + 右侧岗位详情（ORBIT-20261008-171）

   样式逐项对齐**前端左侧导航栏**（apps/web/platform/shell.css 的
   .nav-group / .nav-group-title / .nav-group-items / .nav，ORBIT-20261008-168 已复用同一套值）。
   控制台这份样式表没有设计 token（:root 只有字体与底色），所以这里写**字面值**，
   括号里标出它对应的 token —— 以后前端侧栏改了值，这里要跟着改，反之亦然。
   选中的「全部岗位 / 未对应」条目与部门条目同形（.orgpos-node），不加第二套视觉。
   ============================================================================= */
.orgpos-layout { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 20px; align-items: start; }
.orgpos-rail { padding: 14px 12px; border: 1px solid #dde2ec; border-radius: 12px; background: #fff; }
/* 「新增顶级节点」按的是整棵树，所以它跟树待在一起（左栏标题行），不跟右栏的岗位动作混排。 */
.orgpos-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 0 0 8px; padding: 0 4px 0 12px; }
.orgpos-rail-head button { padding: 6px 10px; border: 1px solid #cfd5e1; border-radius: 8px; background: #fff; color: #30384a; white-space: nowrap; }
.orgpos-rail-head button:hover { background: #f4f6fa; border-color: #b9c1d0; }
.orgpos-rail-title { margin: 0; color: #6b7280; font-size: 12px; font-weight: 600; letter-spacing: .04em; } /* --fs-sm / --muted */
/* 停用节点不显示，但要在左栏底下如实说一句（静默少几个节点用户会以为数据丢了）。 */
.orgpos-rail-note { margin: 10px 4px 0; color: #6b7280; font-size: 12px; line-height: 1.5; }
/* 卡片上的持有人一行：主/兼标记要一眼可辨；取不到目录时用 muted 如实说明。 */
.orgpos-holders { display: inline-block; margin-top: 2px; color: #374151; font-size: 12px; }
.orgpos-holders.muted { color: #9ca3af; }
.orgpos-tree { display: flex; flex-direction: column; gap: 6px; } /* 树间隔 6px */
.orgpos-group { display: flex; flex-direction: column; gap: 3px; } /* 分组内间隔 3px（与 .nav-group 同值） */
.orgpos-group-title { display: flex; align-items: center; gap: 2px; border-radius: 6px; } /* --radius-xs */
.orgpos-group-main,
.orgpos-node {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
  margin: 0;
  padding: 8px 12px;          /* 与 .nav-group-title / .nav 同值 */
  border: 0;
  border-radius: 6px;         /* --radius-xs */
  background: transparent;
  color: #151b2d;             /* --text */
  font-size: 13px;            /* --fs-base */
  font-weight: 400;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background .15s ease, color .15s ease;
}
.orgpos-group-main { font-weight: 500; }
.orgpos-group-main:hover,
.orgpos-node:hover { background: #f3f4f7; } /* --hover-bg */
.orgpos-group-main.active,
.orgpos-node.active { background: #fce8ea; color: #b8000b; font-weight: 600; } /* --accent-soft / --accent-strong */
/* 分组内被选中的是子项时，父分组标题只做弱提示（与 .nav-group-title.selected-group 同口径），
   不抢选中态的底色 —— 否则会看成选中了两个。 */
.orgpos-group-main.branch-active { color: #b8000b; font-weight: 600; }
.orgpos-node-name { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.orgpos-node-count { flex: none; font-size: 11px; color: #6b7280; } /* --fs-xs / --muted */
.orgpos-group-main.active .orgpos-node-count,
.orgpos-group-main.branch-active .orgpos-node-count,
.orgpos-node.active .orgpos-node-count { color: inherit; opacity: .85; }
/* 没有下级的部门不渲染箭头：不给「点了没反应」的按钮。 */
/* 部门节点下的人员姓名（2026-10-27）：与岗位计数同色同字号，长名单换行不截断。 */
.orgpos-leaf { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.orgpos-members { margin: 0; padding: 0 12px 2px 12px; font-size: 11px; color: #6b7280; line-height: 1.5; overflow-wrap: anywhere; }
.orgpos-group-chevron {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  margin-right: 4px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #6b7280;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.orgpos-group-chevron:hover { background: #f3f4f7; color: #151b2d; }
.orgpos-group-chevron i {
  display: block;
  width: 6px;
  height: 6px;
  margin-top: -3px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);   /* 展开＝朝下 */
  transition: transform .2s ease;
}
.orgpos-group[data-expanded="false"] .orgpos-group-chevron i { transform: rotate(-45deg); margin-top: 0; } /* 收起＝朝右 */
/* 子项：缩进 + 1px 竖线 + 就地收起动画（与 .nav-group-items 同法同值）。 */
.orgpos-group-items {
  display: grid;
  grid-template-rows: 1fr;
  position: relative;
  margin-left: 16px;
  padding-left: 8px;
  transition: grid-template-rows 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.orgpos-group-items::before {
  content: "";
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 6px;
  width: 1px;
  border-radius: 1px;
  background: #e2e5ec;        /* --line */
  pointer-events: none;
}
.orgpos-group[data-expanded="false"] .orgpos-group-items { grid-template-rows: 0fr; visibility: hidden; pointer-events: none; }
.orgpos-group-items-inner { display: flex; flex-direction: column; gap: 3px; min-height: 0; overflow: hidden; }
.orgpos-node-all { font-weight: 500; }
/* 「组织架构里没有对应节点」的岗位：只在真有这种岗位时出现，且**必须**出现，
   否则这些岗位在合并后的界面里会彻底消失（只在「全部岗位」里还能看到）。 */
.orgpos-node-unmatched { color: #b54708; }
.orgpos-node-unmatched.active { background: #fff8eb; color: #7a4500; }

.orgpos-detail { min-width: 0; }
.orgpos-detail .section-head { align-items: flex-start; }
.orgpos-detail h3 { margin: 0 0 6px; }
/* 节点详情：徽章一行，编码/人数/负责人与文件空间各占一行 —— 挤成一行时
   「负责人：张三文件空间：未绑定」会被读成一串，看不出边界。 */
.orgpos-node-meta { display: grid; gap: 5px; margin: 0 0 12px; color: #6f788b; font-size: 13px; }
.orgpos-node-badges { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
/* 部门在法人主体树里找不到对应单元时的显式说明（不是空列表）。 */
/* 提示框（落位说明 / 停用岗位说明）。⚠️ 必须自带**上**间距：它前面那块是 `.node-actions`
   按钮行（文件空间/新增下级/编辑），只写 `margin-bottom` 会让提示框**紧贴按钮**（2026-10-08 用户截图）。 */
.orgpos-note { margin: 14px 0 12px; padding: 12px 14px; border-left: 4px solid #b54708; border-radius: 8px; background: #fff8eb; color: #7a4500; line-height: 1.5; }
.orgpos-note.bridged { border-left-color: #1a5fb4; background: #eef4fc; color: #1a4a8a; }

@media (max-width: 980px) {
  .orgpos-layout { grid-template-columns: 1fr; }
}


/* 调岗弹窗：候选人列表与持有人行。列表限高避免弹窗被撑爆，选中项高亮。 */
/* 候选人列表：限高滚动。内边距让行不要贴着边框被切一半（截图里那条半截行就是这么来的）。 */
.reassign-list { max-height: 232px; overflow-y: auto; padding: 4px; border: 1px solid #e5e7eb; border-radius: 9px; margin: 6px 0 0; }
.reassign-list button { display: block; width: 100%; text-align: left; padding: 6px 10px; background: none; border: 0; cursor: pointer; }
.reassign-list button.selected { background: #eff6ff; font-weight: 600; }
.reassign-holders { margin: 4px 0 0; padding: 10px 12px; border-radius: 9px; background: #f8fafc; font-size: 13px; color: #374151; }
.reassign-holders .row { display: flex; align-items: center; gap: 8px; }
.reassign-holders .row + .row { margin-top: 4px; }

/* LOGO 上传：保留原生文件选择与键盘操作，中文展示；提示独占整行。 */
#brandingLogoForm > label { align-content: start; }
.branding-file-picker { position: relative; display: flex; align-items: center; gap: 12px; min-width: 0; min-height: 44px; padding: 7px 12px; border: 1px solid #cfd5e1; border-radius: 9px; background: #fff; }
.branding-file-button { flex: 0 0 auto; padding: 4px 10px; border: 1px solid #cfd5e1; border-radius: 6px; }
.branding-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
#brandingLogoFile { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.branding-file-picker:focus-within { outline: 2px solid #d5000d; outline-offset: 2px; }

/* ORBIT-NATIVE-SELECT-INSET:START — canonical in apps/web/platform.css; sync with scripts/sync-native-select-inset.mjs */
/* !important protects only arrow geometry from legacy background/padding shorthands. Colors, borders and focus remain owned by each form. */
select:not([multiple]):not([size]), select:not([multiple])[size="0"], select:not([multiple])[size="1"] {
  appearance:none !important;
  -webkit-appearance:none !important;
  background-image:linear-gradient(45deg,transparent 42%,var(--icon-gray,#858b96) 42%,var(--icon-gray,#858b96) 58%,transparent 58%),linear-gradient(135deg,transparent 42%,var(--icon-gray,#858b96) 42%,var(--icon-gray,#858b96) 58%,transparent 58%) !important;
  background-position:right calc(var(--sp-4,16px) + 6px) center,right var(--sp-4,16px) center !important;
  background-size:6px 6px,6px 6px !important;
  background-repeat:no-repeat !important;
  padding-inline-end:var(--sp-8,40px) !important;
}
@media (forced-colors:active) {
  select:not([multiple]):not([size]), select:not([multiple])[size="0"], select:not([multiple])[size="1"] {
    appearance:auto !important;
    -webkit-appearance:auto !important;
    background-image:none !important;
  }
}
/* ORBIT-NATIVE-SELECT-INSET:END */
