/* ============================================================
   dfdp 控制台共享设计系统 · 企业级浅色（炼石 VI 蓝）
   47 个后台页 <link rel=stylesheet href=/app.css> 接入；
   态势大屏 dashboard.html 不接本文件，保留深空科技蓝（大屏就该是那样）。
   2026-08-19 换肤：原「深空科技蓝」全站暗色+霓虹青（Tailwind 默认色板）被客户
   质疑非企业级，按主流管理台惯例（浅色工作区+深蓝品牌色+降饱和语义色）重设计。
   变量名沿用旧版（--cyan 等历史名指向新别名 --accent），各页 var() 引用零改动。
   页面特有样式（canvas/svg/表单微调）留各页 <style> 覆盖；
   图谱类 canvas 画布保留深底（嵌入浅色页面的「图面」，节点配色在各 JS 不随本文件）。
   ============================================================ */
:root{
  /* 品牌主色 = 炼石 VI 实测蓝（logo 像素统计值，对外物料同源）。白底对比 ≈7:1（AA/AAA） */
  --accent:#1F538F;
  --accent-2:#2A6AB5;        /* hover/亮阶 */
  --accent-soft:#EAF1F8;     /* 主蓝浅底（选中底/信息条） */

  --bg:#F5F7FA;              /* 工作区底（浅灰） */
  --bg-2:#FFFFFF;            /* 面板/卡片 */
  --bg-3:#EFF3F8;            /* 表头/悬停 */
  --txt:#263238;             /* 主文本（on --bg ≈12:1） */
  --txt-2:#46586A;           /* 次文本（on --bg ≈7:1） */
  --muted:#5F7183;           /* 弱化（on --bg ≈5.2:1，达 AA；沿旧版纪律不低于 4.5） */

  --cyan:var(--accent);      /* 历史名（46 页选中/焦点/hover 在引用），值=主蓝；择机批量改名 */
  --link:var(--accent);
  --primary:var(--accent); --primary-2:var(--accent-2);

  /* 语义色：企业级降饱和，白底深字达 AA（旧版 Tailwind 亮色系专为深底设计，弃用） */
  --danger:#C0392B; --warn:#9A6A1B; --ok:#4F7023; --purple:#6D5FA8;
  --line:#DDE3EA;
  --r:6px; --r-lg:8px;

  /* 深藏青侧栏：导航深、工作区浅，与大屏视觉呼应 */
  --nav-bg:#16324F; --nav-line:#2A4A6E;
  --nav-txt:#B9C9DA; --nav-txt-2:#8FA6BC; --nav-muted:#6E86A0;
}
*{box-sizing:border-box}
/* 🔴 文本选中态必须显式定义，否则浏览器默认的浅蓝高亮会把深底浅字整段吞掉
   （2026-08-23 现场截图：assets 血缘面板 #1e293b 上选中文字后完全看不见）。
   影响面远不止那一处——全站主蓝按钮 #1F538F、侧边栏 #16324F、搜索框 #0F2740、
   图谱面板 #131c2e 全是深底白字，选中一律消失。定义一次全站解决。
   选 --accent-2 而非 --accent：它比主蓝亮一档，跟侧边栏深蓝(#16324F)仍有 3.03:1 可辨，
   白字落其上 5.49:1。
   ⚠️ 两条必须分开写，不能并成一个选择器组——组里只要有一个选择器不被识别，
   整条规则会被整体丢弃（CSS 选择器组的全有全无语义），那样两个浏览器都拿不到。 */
::selection{background:var(--accent-2); color:#fff}
::-moz-selection{background:var(--accent-2); color:#fff}
body{margin:0; padding:24px; font-family:system-ui,"Segoe UI",sans-serif; font-size:13px; line-height:1.5;
  color:var(--txt); min-height:100vh; background:var(--bg)}   /* 纯色，去旧版径向光晕 */
h1{font-size:20px; font-weight:700; letter-spacing:.5px; margin:8px 0 16px; color:#1B2A3A}
h2{font-size:15px; color:var(--txt-2); letter-spacing:.5px; margin:26px 0 10px}
a{color:var(--link); text-decoration:none}
/* 🔴 :not(.btn) 不可省：a:hover 特异性 (0,1,1) 压过 .btn 的 (0,1,0)，不排除的话
   <a class="btn"> 一悬停文字就跟底色同为 #2A6AB5 —— 实测 1.00:1，整个按钮变空白蓝块
   （2026-08-22 check-css-contrast.sh 逮到，collectors/inspection/upgrade-* 等 5 页命中）。
   排除后按钮文字回归 .btn 系列自己裁决，页面重定义 .btn 的场景（crossborder 浅底描边）也不受干扰。 */
a:hover:not(.btn){color:var(--accent-2)}
code{color:#175A8C; font-family:ui-monospace,monospace; font-size:12px}
/* 数据键标识芯片（签名元素）：平台核心物料 table://…#field 统一 mono 呈现 */
.datakey{font-family:ui-monospace,"SF Mono",Consolas,monospace; font-size:12px; color:#3D5A78;
  background:var(--bg); border:1px solid var(--line); border-radius:4px; padding:0 5px;
  word-break:break-all}
.muted{color:var(--muted)} .hint{color:var(--txt-2); font-size:12px; margin:10px 0}
/* 读数 vs 口径说明（T-console-readout-vs-explainer）：随数据变的读数用 .readout（正文色、13px、左侧细色条，数字由 <b> 加粗），
   不变的说明留 .hint 或收进 details.explain（默认收起、一行「口径说明」）；读数在前、说明在后。
   失败态 .readout.err 保住 .err 的标色（拉不到状态时 JS 写 'readout err'，不许再写回 'muted'）。判据⑩ 钉 19 处读数 id */
.readout{color:var(--txt); font-size:13px; margin:10px 0; padding-left:10px; border-left:3px solid var(--accent); font-variant-numeric:tabular-nums}
.readout b{font-weight:600}
span.readout{margin:0; padding:0; border:0}   /* 行内读数（标题里的计数、筛选行的条数）只靠色/字号/加粗区分，不画色条 */
.readout.err{color:var(--warn); font-weight:600}
details.explain{margin:6px 0}
details.explain>summary{cursor:pointer; color:var(--txt-2); font-size:12px; list-style:none; user-select:none}
details.explain>summary::-webkit-details-marker{display:none}
details.explain>summary::before{content:'▸ '} details.explain[open]>summary::before{content:'▾ '}

/* 导航：左侧分组侧边栏（深藏青，可折叠，状态由 auth.js + localStorage 管） */
.nav{position:fixed; left:0; top:0; bottom:0; width:210px; z-index:50; margin:0;
  display:flex; flex-direction:column; overflow-y:auto;
  background:var(--nav-bg); border-right:1px solid var(--nav-line);
  transition:transform .2s}
body.has-nav{padding-left:234px; transition:padding-left .2s}  /* 侧栏 210 + 留白 24；无侧栏的大屏页不受影响 */
body.nav-collapsed .nav{transform:translateX(-211px)}
body.has-nav.nav-collapsed{padding-left:24px}

.sb-brand{padding:16px 16px 12px; border-bottom:1px solid var(--nav-line)}
/* 企业 logo（上传则显示）在上 + 产品名标识行（始终显示）在下：logo=厂商品牌，产品名=平台标识，层级分明 */
.sb-logo{max-width:178px; max-height:44px; display:block; margin-bottom:8px}
.sb-product{font-size:11px; letter-spacing:1px; color:var(--nav-txt-2); line-height:1.35}

/* 折叠按钮：fixed，随折叠态在侧栏右上（深底样式）↔ 屏幕左上（浅底样式）间移动 */
.sb-toggle{position:fixed; top:14px; left:182px; z-index:60; width:24px; height:24px; padding:0;
  border-radius:50%; font-size:13px; line-height:22px; text-align:center; cursor:pointer;
  background:#1E3E60; color:var(--nav-txt); border:1px solid var(--nav-line); transition:left .2s}
.sb-toggle:hover{color:#fff; background:#27507A}
body.nav-collapsed .sb-toggle{left:10px; background:var(--bg-2); color:var(--txt-2); border-color:var(--line)}

.sb-groups{flex:1; padding:8px 0}
.sb-group{margin-bottom:6px}
.sb-gtitle{padding:8px 16px 4px; font-size:11px; letter-spacing:1px; color:var(--nav-muted)}
.sb-gtitle.sb-upgrade{color:#7FB0E0}   /* 升级导购组：亮蓝高亮，区别于功能组 */
.sb-item{display:block; padding:6px 16px 6px 24px; font-size:13px; color:var(--nav-txt);
  border-left:2px solid transparent}
.sb-item:hover{background:rgba(255,255,255,.06); color:#fff}
.sb-item.active{color:#fff; font-weight:600; border-left-color:#7FB0E0;
  background:rgba(255,255,255,.09)}

/* 组内子节（个人信息保护等，auth.js GROUPS.sections）：小节头可点折叠，子项缩进一级。
   收起时露计数徽标提示"里面还有东西"；当前页在子节内时 auth.js 渲染期强制展开。 */
.sb-sec{display:flex; align-items:center; gap:6px; padding:6px 16px 3px 24px; font-size:12px;
  color:var(--nav-muted); cursor:pointer; user-select:none}
.sb-sec:hover .sb-sec-t{color:#fff}
.sb-sec-n{font-size:10px; line-height:16px; color:var(--nav-txt); background:rgba(255,255,255,.10);
  border-radius:8px; padding:0 6px; display:none}
.sb-sec.closed .sb-sec-n{display:inline-block}
.sb-sec-arrow{margin-left:auto; width:0; height:0; border-left:4px solid transparent;
  border-right:4px solid transparent; border-top:5px solid var(--nav-muted);
  transition:transform .15s ease}
.sb-sec.closed .sb-sec-arrow{transform:rotate(-90deg)}
.sb-secbody.closed{display:none}
.sb-item.sb-sub{padding-left:36px}

.sb-user{padding:12px 16px; border-top:1px solid var(--nav-line); font-size:12px}
.sb-user .sb-name{color:#E2EAF2} .sb-user .sb-role{color:var(--nav-muted); margin:2px 0 6px}
/* 深藏青侧栏内的链接（登出等）不能用全局主蓝 a 色（与深底对比不足）——用亮蓝并带下划线示意可点 */
.sb-user a{color:#8FB8E8; text-decoration:underline; text-underline-offset:2px}
.sb-user a:hover{color:#fff}

/* 卡片/KPI */
.cards{display:flex; flex-wrap:wrap; gap:16px; margin-bottom:20px}
.card{position:relative; background:var(--bg-2); border:1px solid var(--line); border-radius:var(--r-lg);
  padding:16px 22px; min-width:140px; overflow:hidden; box-shadow:0 1px 3px rgba(16,38,66,.06)}
.card::after{content:""; position:absolute; left:0; top:0; width:2px; height:100%;
  background:var(--accent); opacity:.9}
.card .num{font-size:30px; font-weight:700; letter-spacing:.5px; font-variant-numeric:tabular-nums; color:#1B2A3A}
.card .label{color:var(--txt-2); font-size:12px; margin-top:4px}
.card.warn .num{color:var(--warn)} .card.danger .num{color:var(--danger)} .card.ok .num{color:var(--ok)}

/* 表格 */
table{border-collapse:collapse; width:100%; margin-top:10px; font-size:13px; font-variant-numeric:tabular-nums;
  background:var(--bg-2)}
th,td{text-align:left; padding:8px 11px; border-bottom:1px solid var(--line); vertical-align:top}
/* 表头与单元格一律不折行（2026-09-09 第二轮）。
   🔴 默认反转，这是本轮的核心改动：上一轮是「td 只禁 CJK 逐字折」+ 逐列标 class="nw" 声明「这个值不可拆」，
   实测 48 页里只有 db-audit 一页标了 9 处——全站约 305 个原子值列位分布在 44 个页面，同一条判据要求在
   几百个渲染点各写一遍，本身就是 SSOT 违规，必然漏且新页面持续漏。所以改成：不折行是默认，
   确实要折的长文本列显式加 .wrap 豁免（少数）。
   表因此变宽，由 .tbl-wrap 横向滚动接管（auth.js 运行时给每张表套壳，48 页零例外，不需逐页接线）。
   取舍写明：表格里行高不定比横向滚动致命得多——一行 300px 高会把整屏挤没，横向滚动只是多拖一下。
   🔴 明确不用「nowrap + max-width + text-overflow:ellipsis」：实测那会裁掉 index 46 格、api 36 格的内容，
   这是安全审计平台，页面上少一条就是审计线索少一条。要省地方只能用 dfdpUtil.listCell 的可展开截断。 */
th{color:var(--txt-2); font-weight:600; background:var(--bg-3); position:sticky; top:0; white-space:nowrap}
td{white-space:nowrap}
/* 显式豁免：整句中文/长描述列。keep-all 仍在——CJK 没有词边界，折也只许在空格与标点处折，不逐字竖排 */
td.wrap{white-space:normal; word-break:keep-all}
/* td.wrap 列里的整段说明再封一个宽度上下限（识别依据一句话等）：表格单元格上的 max/min-width 浏览器不保证生效，
   所以落在格内这层块上。T-idl-P3-42（playwright 实测）：
   - 下限 14em：表放不下时自动布局先挤能挤的列，没有下限这一格会被压到 50px 逐字竖排；
   - word-break:normal 覆盖 td.wrap 的 keep-all：整句话在 14em 以上的宽度里按中文标准断行，「。，」不落行首
     （keep-all 下一整串没有断点，只能靠应急断开，而应急断开不守行首禁则，句号就会单独掉到最后一行）；
   - 长串（规则标识）用 break-word 而不是 anywhere：anywhere 会把这一格的最小宽度算成一个字，正是被压扁的起因 */
.cell-prose{min-width:14em; max-width:32em; word-break:normal; overflow-wrap:break-word; line-break:strict}
/* 横跨整张表的单元格一定是消息，不是值（空态、错误文案、展开的 SQL 原文）——整句话不许拖成一行长条，
   照常折。这条免掉了在几十个空态/错误渲染点各标一次 .wrap（那正是本轮返工的病根） */
td[colspan]{white-space:normal; word-break:keep-all}
/* 表格横向滚动容器：auth.js 给每个 table 套一层，只在表比容器宽时才加 .scroll（overflow 非 visible 会让
   th 的 sticky 失效，所以不宽就不加）。宽度判定在 JS（ResizeObserver），CSS 只负责两态外观。
   容器本身不设 margin：table 自己的 margin-top 在非 scroll 态穿过容器折叠、scroll 态留在容器内，两态视觉一致 */
/* 🔴 .scroll 态必须自带垂直滚动端口，否则 th 的 sticky 直接失效：CSS 规范下两轴不能一个 visible 一个 auto，
   overflow-x:auto 会把 overflow-y 的计算值也变成 auto ⇒ wrap 成了 sticky 的滚动容器，而它高度=内容高度、
   自身无垂直滚动空间 ⇒ 表头随页面一起滚走（2026-09-09 演示栈实测：下滚 1500px 后 th 的 top = -1086）。
   给 max-height 让 wrap 真的能垂直滚，表头就吸在容器顶。只在 .scroll 态加：窄表不进这个分支，
   页面级 sticky 原样不受影响。用 75vh 不用像素值——各页头部高度不同，硬编码就是易变值散写。 */
.tbl-wrap.scroll{overflow-x:auto; -webkit-overflow-scrolling:touch; max-height:75vh; overflow-y:auto}
/* 单元格第二行小字（合并列用：类型下的类别、状态下的最新阶段、版本下的策略版本/样本档位…）。
   全站第二行小字都是原子值（驱动名 / 实例地址 / 类别），一律不折行 */
td .sub{display:block; color:var(--muted); font-size:.85em; margin-top:2px; white-space:nowrap}
/* 列表型单元格（dfdpUtil.listCell）：前 N 项 + 「等 M 个」，点开补出剩余项。
   原生 details，不接事件；去掉默认三角，虚下划线示意可点 */
.lc{display:inline-block; vertical-align:top; max-width:100%}
.lc summary{cursor:pointer; list-style:none}
.lc summary::-webkit-details-marker{display:none}
.lc-n{white-space:nowrap; text-decoration:underline dotted}
.lc-rest{margin-top:3px}
/* 展开的长文本（dfdpUtil.textCell）：原文可能是一整条 JSON，必须能折行，也不许把表撑到几千像素宽 */
.lc-text{white-space:pre-wrap; word-break:break-all; max-width:640px}
/* 展开态换提示：「等 N 个」/「展开全文」隐掉、换成「收起」（否则展开后提示过时、也没有收回去的入口）。
   两种格分开挂：.lc-fold（textCell）展开区是全文，收起态前半句 .lc-head 一并隐；
   .lc-list（listCell）展开区只放剩余项，前几项留着、只换提示——别把 .lc-head 规则并到 .lc-list 上 */
.lc-less{display:none}
.lc-fold[open] .lc-head,.lc-fold[open] .lc-more{display:none}
.lc-fold[open] .lc-less{display:inline}
.lc-fold[open] .lc-rest{margin-top:0}
.lc-list[open] .lc-more{display:none}
.lc-list[open] .lc-less{display:inline}
tr:hover td{background:#F4F8FC}
/* 行内展开行（dfdpUtil.mountInlineRow）：面板/表单/明细搬进被点行正下方的一行里；不吃行 hover 底色，搬进来的元素顶部外边距清零 */
tr.inline-row>td{background:var(--bg-2); padding:10px 12px; white-space:normal}
tr.inline-row:hover>td{background:var(--bg-2)}
tr.inline-row>td>*{margin-top:0 !important; display:flow-root}
tr.inline-row>td>.inline-row-bar{display:flex; justify-content:flex-end; margin-bottom:6px}   /* helper 统一渲的「收起」（T-console-inline-row-collapse） */
.inline-row-close{font-size:12px; padding:2px 10px; cursor:pointer}   /* 内联 margin-top 也压掉；flow-root 挡住子元素外边距穿透（h2 的 26px 会把整块顶下去） */

/* 按钮 */
button,.btn{background:var(--primary); color:#fff; border:0; border-radius:4px; padding:5px 12px;
  cursor:pointer; font-size:12px; font-weight:500; transition:background .15s; white-space:nowrap}
button:hover,.btn:hover{background:var(--primary-2)}
button:disabled{opacity:.5; cursor:not-allowed}
.btn.ghost{background:var(--bg-2); color:var(--accent); border:1px solid var(--line)}
.btn.ghost:hover{background:var(--accent-soft); border-color:var(--accent)}
button.reject,button.danger,.btn.danger{background:var(--danger)} button.reject:hover,button.danger:hover{background:#A93226}

/* 页签（dfdpUtil.tabs，T-console-page-tabs）：全站一份，页面 <style> 里不得再写 .tabs/.tab-btn/.tab-panel。
   六块整组必须放在上面按钮段之后：浏览器侧 .tab-btn(0,1,0) 本就胜过 button(0,0,1)、与顺序无关，
   但 check-css-contrast.sh 用「token 数 + 源序」的简化级联，整组放前面会把 .tab-btn.active 的文字算到 button 主蓝底上（假红）。
   底边线钉 var(--line)（--border 全仓未定义，原两页 fallback 一深一浅）。 */
.tabs{display:flex; flex-wrap:wrap; gap:0; margin:16px 0 12px; border-bottom:2px solid var(--line, #DDE3EA)}
.tab-btn{padding:8px 20px; background:none; border:none; border-bottom:3px solid transparent;
  cursor:pointer; color:var(--txt-2, var(--muted)); font-size:14px; margin-bottom:-2px}
.tab-btn:hover{background:var(--accent-soft)}
.tab-btn.active{color:var(--link, var(--primary-2)); border-bottom-color:var(--link, var(--primary-2)); font-weight:600}
.tab-panel{display:none}
.tab-panel.active{display:block}

/* RBAC 写权显隐（三权分立：业务写仅 SEC_ADMIN）。body.no-write 由 auth.js 按角色加。
   [data-write]=写按钮/控件，无权隐藏；[data-ro]=只读回退文本，无权时反向显示当前值。 */
[data-ro]{display:none}
body.no-write [data-write]{display:none!important}
body.no-write [data-ro]{display:inline}

/* 输入 */
input,select,textarea{background:var(--bg-2); color:var(--txt); border:1px solid #C9D3DE;
  border-radius:var(--r); padding:6px 10px; font-size:13px; font-family:inherit}
input:focus,select:focus,textarea:focus{outline:none; border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(31,83,143,.12)}
label{display:block; font-size:12px; color:var(--txt-2); margin:10px 0 4px}

/* 标签/状态：浅底深字（旧版深底亮字为深色主题专属，整套替换） */
.tag,.chip,.pill{display:inline-block; padding:1px 8px; border-radius:10px; font-size:11px; margin-right:4px}
.chip.lv-L4,.chip.lv-CRITICAL{background:#FDE8E6; color:#A93226}
.chip.lv-L3,.chip.lv-HIGH{background:#FBF0DB; color:#8A5E14}
.chip.lv-L2,.chip.lv-MEDIUM{background:#E8F0FA; color:#1F538F}
.chip.lv-L1,.chip.lv-LOW{background:#EBF3E1; color:#4F7023}
.chip.lv-UNKNOWN{background:#EDF1F5; color:#5B6C7D}
.badge.b-AUTO{background:#FBF0DB; color:#8A5E14} .badge.b-CONFIRMED{background:#E7F2E3; color:#2E6B2F}

/* 状态文字（统一各页 .lv-* / .st-* / .sev-* 口径；星号与斜杠间留空格，防止注释提前闭合） */
.lv-HIGH,.lv-CRITICAL,.st-FOUND,.st-FAIL,.st-NEW,.st-BLIND,.st-FAILED,.sev-CRITICAL,.sev-HIGH,.cb,.over{color:var(--danger); font-weight:600}
.lv-MEDIUM,.st-REMEDIATING,.st-WARN,.st-IDLE,.sev-MEDIUM,.sens,.pending,.missing,.err{color:var(--warn); font-weight:600}
.lv-LOW,.st-OK,.st-PASS,.st-CLOSED,.st-REPLAYED,.st-ONLINE,.sev-LOW,.ready{color:var(--ok); font-weight:600}
/* 采集器「已暂停」：平台主动下发的停采留守，进程活着——不是故障（不红），也不是在线采集（不绿） */
.st-PAUSED{color:var(--muted); font-weight:600}
/* 运行期参数对话框（collectors.html 的 <dialog>，全仓首个）：跟表单同一套间距，不走浏览器默认外观 */
dialog{border:1px solid var(--border); border-radius:6px; padding:16px 20px; min-width:360px; max-width:520px}
dialog::backdrop{background:rgba(0,0,0,.35)}
dialog label{display:block; margin:10px 0 4px}
/* select 与 input 同规则：不写进来时「样本采集档位」下拉与标签同行、与其余三项不一致（T3，2026-09-06 截图实见） */
dialog input,dialog select{display:block; width:100%; box-sizing:border-box; margin-top:4px}
dialog menu{display:flex; gap:8px; justify-content:flex-end; padding:0; margin:16px 0 0}
/* 搜索框内嵌已选标签（风险处置「补充脱敏目标」）：已选项与输入框同一行排、放不下自动换行，点标签上的 × 移除 */
.chip-input{display:flex; flex-wrap:wrap; gap:4px; align-items:center; border:1px solid var(--border); border-radius:4px; padding:3px 4px; margin-top:4px}
.chip-input .chip{background:#E8F0FA; color:#1F538F; margin:0}
.chip-input .chip button{border:none; background:none; padding:0 0 0 4px; cursor:pointer; color:inherit}
dialog .chip-input input{flex:1 1 140px; width:auto; border:none; margin:0; outline:none}
/* 勾选清单：dialog 的 input 默认整行宽，勾选框要回到行内 */
dialog .check-list label{display:flex; gap:6px; align-items:center; margin:2px 0}
dialog .check-list input[type=checkbox]{display:inline-block; width:auto; margin:0}
.st-COMPLETED,.st-IGNORED,.na{color:var(--muted)}

/* 提示条/进度条 */
.banner{background:#FCF4E2; border:1px solid #E3CD96; color:#7A5417; border-radius:var(--r);
  padding:10px 14px; font-size:12px; margin-top:10px}
.banner.err{background:#FBEAE8; border-color:#E4B2A9; color:#A93226}
/* B4b 授权降级横幅：sticky 顶置、跨左侧栏内容列，醒目不可关闭。
   底色/字色用 ID 硬指定（特异性压过各页局部 .banner 覆盖——gateway-access 等页把 .banner
   重定义成白底信息条，曾把本警示横幅洗成白底不醒目，2026-08-19 实拍抓的） */
#dfdp-license-banner{position:sticky; top:0; z-index:50; margin:0 0 14px;
  font-weight:600; box-shadow:0 2px 8px rgba(16,38,66,.12);
  background:#FCF4E2; border:1px solid #E3CD96; color:#7A5417}
#dfdp-license-banner.err{background:#FBEAE8; border-color:#E4B2A9; color:#A93226}
.bar{background:#E2E8EF; border-radius:4px; height:10px; width:200px; overflow:hidden; display:inline-block; vertical-align:middle}
.bar>div{height:100%; transition:width .3s}

/* 表单容器 */
#form{background:var(--bg-2); border:1px solid var(--line); border-radius:var(--r-lg); padding:16px; margin-top:16px; max-width:560px}
#form input,#form select,#form textarea{width:100%}

/* ── 可访问性 + 图标（UI/UX 优化批次）── */
/* 键盘焦点环（a11y CRITICAL；输入框 focus 见上方 border-color+shadow） */
a:focus-visible,button:focus-visible,.btn:focus-visible,.sb-item:focus-visible,
.sb-toggle:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--accent-2); outline-offset:2px; border-radius:3px}
/* 侧栏分组图标（inline SVG，替代 emoji；随 currentColor 着色） */
.sb-icon{width:14px; height:14px; vertical-align:-2px; margin-right:6px; opacity:.85}
/* 标题旁信息提示：小圆 i，悬浮/聚焦显示口语化说明（替代术语化副标，避免客户困惑）。
   可键盘聚焦（tabindex=0）+ aria-label 供读屏；说明文字放 data-tip。 */
.tip{display:inline-block; margin-left:8px; width:15px; height:15px; line-height:14px;
  text-align:center; font:600 11px/14px Georgia,"Times New Roman",serif; font-style:italic; vertical-align:middle;
  color:var(--txt-2); border:1px solid #C9D3DE; border-radius:50%; cursor:help; user-select:none}
.tip:hover,.tip:focus-visible{color:var(--accent); border-color:var(--accent); outline:none}
/* 行内文字挂说明（非图标）：复用下方同一个气泡，只是不套小圆 i——像「成功 98.4%」这种
   本身就是一段读得懂的文字，再挂个 i 会让表格每行多一个图标。气泡样式不另抄一份，
   .tip 与 .hint[data-tip] 共用下方 #dfdp-tip 那一个浮层（改一处两边同步）。
   🔴 必须带 [data-tip] 限定（2026-08-02 /fulltest 实测修）：`.hint` 早在本段之前就是全站
   通用的说明段落类（本文件 :48 定义了它的字号/行距），22 个页面共 105 处在用且**都不带
   data-tip**。裸写 `.hint{...}` 会给那 105 处统统加上虚线下划线 + cursor:help，
   变成「看着能悬浮、悬浮出来是空气泡」。加限定后只有真挂了说明的元素才被选中。 */
/* 🔴 说明挂哪里（T-idl-P2-32，全站判据只写这一处）：<b>解释性文字一律 `<span class="hint" data-tip="…">`</b>，
   不用原生 title——原生气泡没有任何视觉提示、还要停约 1 秒才出，用户找不到该往哪儿放鼠标（2026-09-17 走查原话）。
   原生 title 只留给<b>控件上的操作提示</b>：button / a / img / input / select / option / 充当按钮的元素
   （它们本身就长得能点，提示说的是「点了会怎样」）。守门 PageTooltipConventionGuardTest 按此判，
   确需例外的登豁免并附理由。
   用法：hint 只包住那几个字，<b>不与 .tag/.sub/.chip 等同挂一个元素</b>——本规则的 color/font-size 继承会
   压掉它们的配色；外层保留原 class、里面套一层 span.hint。
   color/font-size/margin 继承：`.hint` 的段落样式（12px、txt-2、上下 10px）是给说明段落的，挂在行内文字上会改字号。
   气泡 white-space:pre-line：表格 td 全站 nowrap，不重置的话长说明冲出 300px 框压到邻格上（playwright 实测）；
   pre-line 同时让说明里的 \n 真的换行。 */
.hint[data-tip]{cursor:help; border-bottom:1px dotted #B9C5D2; color:inherit; font-size:inherit; margin:0}
/* 🔴 气泡本体是全站唯一的浮层 #dfdp-tip（auth.js watchHints 建在 body 下、position:fixed），不是触发元素的 ::after。
   2026-10-06 用户截图：气泡曾是挂在触发元素下的 ::after（绝对定位、containing block 在表内），而上面
   .tbl-wrap.scroll 的 overflow 非 visible 会把容器矩形之外的一切后代裁掉——容器高度 = 表高，行数少的宽表
   表头 ⓘ 的说明只露一行半（网关接入两个页签 9 个表头 ⓘ 全裁）。翻向救不了：容器太矮时上下都放不下。
   浮层挂 body 下、fixed 定位，祖先链上没有任何 overflow 容器，只需对视口做避让（坐标由 auth.js placeHint 算，
   判据只写那一份）。文本由 JS 从 data-tip 填入；内容、显隐、位置都不在这里的选择器上做分支。
   z-index 要盖过 #csCredModal 这类 fixed 弹层（91）与本文件最高的 80。 */
#dfdp-tip{position:fixed; left:0; top:0; width:max-content; max-width:300px; padding:8px 11px; margin:0;
  font:400 12px/1.5 system-ui,sans-serif; letter-spacing:0; white-space:pre-line; text-align:left;
  color:var(--txt); background:var(--bg-2); border:1px solid var(--line); border-radius:6px;
  box-shadow:0 6px 20px rgba(16,38,66,.16); opacity:0; visibility:hidden; transition:opacity .15s; z-index:120; pointer-events:none}
#dfdp-tip.on{opacity:1; visibility:visible}
/* 尊重系统“减弱动画”偏好：关掉过渡/动画（侧栏滑动、进度条等） */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{transition-duration:.01ms!important; animation-duration:.01ms!important;
    animation-iteration-count:1!important; scroll-behavior:auto!important}
}

/* ── 数据表体验：空状态 / 加载骨架（P3）── */
.empty-row td{color:var(--muted); text-align:center; padding:22px 11px}
.skeleton{display:inline-block; height:12px; border-radius:3px; min-width:60px;
  background:linear-gradient(90deg,var(--bg-3) 25%,#E2E9F1 50%,var(--bg-3) 75%);
  background-size:200% 100%; animation:sk 1.2s ease-in-out infinite}
@keyframes sk{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* ── 全局搜索（侧栏顶部，深底区内元素随 nav 色系；下拉命中面板浮在浅色工作区，用浅色）── */
.sb-search{position:relative; padding:10px 12px; border-bottom:1px solid var(--nav-line)}
.sb-search-input{width:100%; background:#0F2740; border:1px solid var(--nav-line); color:#E2EAF2;
  border-radius:var(--r); padding:6px 10px; font-size:12px; font-family:inherit}
.sb-search-input::placeholder{color:var(--nav-muted)}
.sb-search-input:focus{outline:none; border-color:#7FB0E0; box-shadow:none}
.sb-search-panel{position:absolute; left:12px; right:12px; top:calc(100% - 2px); z-index:70;
  max-height:62vh; overflow-y:auto; background:var(--bg-2); border:1px solid var(--line);
  border-radius:var(--r); box-shadow:0 10px 28px rgba(16,38,66,.22); padding:4px 0}
.sb-search-group{padding:2px 0}
.sb-sg-title{display:flex; justify-content:space-between; padding:5px 12px 3px; font-size:11px;
  letter-spacing:1px; color:var(--muted)}
.sb-sg-count{color:var(--txt-2)}
.sb-hit{display:block; padding:6px 12px; color:var(--txt); border-left:2px solid transparent}
a.sb-hit:hover{background:var(--accent-soft); border-left-color:var(--accent)}
.sb-hit-nolink{color:var(--txt-2); cursor:default}
.sb-hit-name{display:block; font-size:12px; line-height:1.4}
.sb-hit-meta{display:block; font-size:11px; color:var(--muted);
  font-family:ui-monospace,monospace; word-break:break-all}
/* 资产/主体命中用数据键芯片作 meta（块级、贴名下）：签名落地 + 同名字段消歧 */
.sb-hit .datakey{display:block; margin-top:3px; color:var(--muted)}
.sb-sg-more{padding:3px 12px; font-size:11px; color:var(--txt-2)}
.sb-search-empty{padding:10px 12px; font-size:11px; color:var(--muted)}
.sb-search-degraded{padding:8px 12px; font-size:11px; color:var(--warn);
  border-top:1px solid var(--line); margin-top:2px}
/* 折叠态：屏幕左上放大镜（浅底样式），点它展开并聚焦搜索框 */
.sb-search-fab{position:fixed; top:44px; left:10px; z-index:60; width:24px; height:24px; padding:0;
  display:none; border-radius:50%; cursor:pointer; background:var(--bg-2); color:var(--txt-2);
  border:1px solid var(--line)}
.sb-search-fab:hover{color:var(--accent); border-color:var(--accent); background:var(--accent-soft)}
body.nav-collapsed .sb-search-fab{display:flex; align-items:center; justify-content:center}
.sb-search-fab .sb-icon{margin-right:0}

/* 升级引导页（upgrade-professional / upgrade-enterprise 共享，页面特有类留各自页内） */
.up-hero{margin:12px 0 20px; padding:18px 20px; border-left:4px solid var(--accent);
  background:var(--accent-soft); border-radius:8px}
.up-hero h1{margin:0 0 6px}
.up-hero .tag{color:var(--accent); font-weight:700; letter-spacing:1px}
.up-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:12px; margin:16px 0}
.up-card{padding:12px 14px; border:1px solid var(--line); border-radius:8px; background:var(--bg-2)}
.up-card b{display:block; margin-bottom:4px}
.up-card span{font-size:12px; color:var(--muted)}
.up-cta{margin:24px 0; padding:16px 18px; border:1px dashed var(--accent); border-radius:8px}
.up-cta a.btn{display:inline-block; margin-top:8px; text-decoration:none}
.up-note{font-size:12px; color:var(--muted); margin-top:8px}

/* 表格内查找（table-filter.js 唯一实现，页面只加 data-filter 属性）。
   行的隐藏用 .tf-out 而非行内 style：页面自身的正交筛选（如资产页「只看未采到流量的」）
   用的就是行内 style，两边都写行内会互相覆盖，「搜不到」与「被筛掉」就长得一样了。 */
.tf-bar{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:14px 0 -4px}
.tf-bar .tf-input{flex:0 1 320px; min-width:200px; max-width:420px}
.tf-count{font-size:12px; color:var(--txt-2)}
.tf-count.tf-zero{color:var(--warn); font-weight:600}
.tf-scope{font-size:12px; color:var(--muted)}
tr.tf-out{display:none !important}

/* 列表截断提示（util.js 的 dfdpUtil.scopeNote 唯一渲染点）：常驻在表上方，与查找框无关。
   刻意不用 .muted 的浅灰——这句话是「你看到的不是全部」，弱化到看不见就等于没有。 */
.scope-note{font-size:12px; color:var(--warn); margin:8px 0 0}
/* 需要人立刻注意的一句（单独成行）：原先 classification / datasource-admin 各有一份相同的页内定义，收口到这里 */
.warnbox{border:1px solid var(--warn); border-radius:var(--r-lg); padding:8px 12px; margin:8px 0; color:var(--warn)}
.scope-note:empty{display:none}
