/* PostSup 组件样式 —— 基于 CEM 设计系统变量（base.css 定义主题 token） */
/* 状态色 */
:root, [data-bs-theme="light"], [data-bs-theme="dark"] {
  --ok: #34d399;
  --err: #f87171;
  --warn: #fbbf24;
  --accent-solid: var(--accent, #6366f1);

  /* ── 圆角令牌（20260930 建）──────────────────────────────────────
     ⚠️【为什么要有令牌】建之前实测：全文件有 **18 种**不同 `border-radius`
       （2/3/4/5/6/7/8/9/10/11/12/13/14/16/18/20/999/9999）——
       ⭐ 完全是"手写飘移"：同一个语义（如"按钮"）在不同地方取值不同。
       ⇒ 令牌把"分档"变成**可引用的事实**，而不是"每处各自估一个数"。

     ⭐ 档位来自项目设计判据（`ptrelskill cross/dev-principles.md`）：
         卡片 16 / 按钮 10 / 输入 12 / 标签 8
       ⚠️ 并补两档（判据未覆盖但实际需要）：
         `--r-menu` 导航项（比按钮略小、比标签略大 ⇒ 8，与标签同档）
         `--r-pill` 胶囊（完全圆头，如 .chip / .count）*/
  --r-card: 16px;   /* 卡片、面板、对话框 */
  --r-input: 12px;  /* 输入框、下拉、文本域 */
  --r-btn: 10px;    /* 按钮 */
  --r-menu: 8px;    /* 导航项、标签、小控件 */
  --r-pill: 999px;  /* 胶囊（完全圆头） */
}

.hidden{display:none !important}
.muted{color:var(--text-muted);font-size:13px}

/* Toast 提示弹窗 */
#toast.toast{position:fixed;bottom:24px;right:24px;z-index:9999;background:rgba(18,24,38,.92);color:#f1f5f9;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(148,163,184,.25);border-radius:10px;padding:10px 18px;font-size:13px;box-shadow:0 10px 30px rgba(0,0,0,.35);pointer-events:none;transition:opacity .2s ease,transform .2s ease;opacity:1;transform:translateY(0);display:block}
#toast.toast.hidden{opacity:0;transform:translateY(12px);display:none !important}

/* 旋转动画 */
.spin{animation:spin 1s linear infinite;display:inline-block}
@keyframes spin{100%{transform:rotate(360deg)}}

/* ---- 页签切换（路由控制显示/隐藏） ---- */
.tab{display:none}
.tab.active{display:block;animation:fadeIn .25s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

/* ---- 登录 ---- */
.login-wrap{display:flex;align-items:center;justify-content:center;min-height:100vh;padding:20px}
.login-card{width:400px;max-width:100%;padding:34px;border-radius:var(--r-card);background:var(--bg-card);border:1px solid var(--border-color);box-shadow:var(--shadow-lg)}
.brand{display:flex;gap:14px;align-items:center;margin-bottom:24px}
.brand-mark{width:52px;height:52px;border-radius:13px;filter:drop-shadow(0 6px 14px rgba(99,102,241,.35))}
.brand-mark.small{width:30px;height:30px;border-radius:8px;filter:none}
.brand h1{font-size:25px;font-weight:800;letter-spacing:-.3px;color:var(--text-primary)}
.brand h1 .hl{background:var(--accent-gradient);-webkit-background-clip:text;background-clip:text;color:transparent}
.brand-sub{color:var(--text-secondary);font-size:12.5px;margin-top:2px}
.foot{text-align:center;margin-top:20px;color:var(--text-muted);font-size:12px}

/* ---- 表单/输入（Bootstrap form-control 由 base.css 覆盖，此处补充布局）---- */
.form{display:flex;flex-direction:column;gap:14px}
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-size:12.5px;color:var(--text-secondary);font-weight:500}
.row{display:flex;gap:12px}.row .field{flex:1}
.msg{font-size:13px;padding:9px 12px;border-radius:9px}
.msg.error{background:rgba(248,113,113,.12);color:var(--err)}

/* ---- 按钮（覆盖 Bootstrap .btn 基础，叠加 PostSup 变体）---- */
.btn{font-weight:600}
.btn.primary{background:var(--accent-gradient);color:#fff;border:none}
.btn.primary:hover{filter:brightness(1.05);color:#fff}
.btn.ghost{background:var(--bg-card-hover);border:1px solid var(--border-color);color:var(--text-primary)}
.btn.ghost:hover{background:var(--bg-card);border-color:var(--accent)}
.btn.block{width:100%}
.btn.small{padding:6px 12px;font-size:12.5px;border-radius:8px}
.btn.danger{background:rgba(248,113,113,.14);color:var(--err)}
.btn.danger:hover{background:rgba(248,113,113,.22)}

/* ---- 顶栏补充 ---- */
.bar-logo{width:26px;height:26px;border-radius:7px}
.bar-brand{font-weight:800;font-size:16px;color:var(--text-primary);margin-left:4px}
.bar-brand .hl{color:var(--accent)}
.ver{font-size:11px;color:var(--text-muted);margin-left:6px}
.chip{background:var(--bg-card-hover);border:1px solid var(--border-color);border-radius:var(--r-menu);padding:4px 11px;font-size:12px;color:var(--text-secondary)}
.avatar{width:30px;height:30px;border-radius:50%;background:var(--accent-gradient);color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:13px;font-weight:700}

/* ---- 页面标题 ---- */
.page-title{font-size:21px;font-weight:700;letter-spacing:-.2px;margin-bottom:16px;display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--text-primary)}
.page-title .sub{font-size:13px;font-weight:500;color:var(--text-muted);margin-left:8px}
.title-actions{display:flex;gap:8px;align-items:center}
.bar-title{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.bar-title h3{font-size:16px;color:var(--text-primary)}

/* ---- 服务家族/卡片 ---- */
.families{display:flex;flex-direction:column;gap:26px}
.family h3{font-size:13px;color:var(--text-muted);font-weight:600;margin-bottom:12px;text-transform:uppercase;letter-spacing:.5px;display:flex;align-items:center;gap:8px}
.family h3 .sub{font-weight:400;font-size:11px;color:var(--text-muted);opacity:.65;text-transform:none;letter-spacing:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:420px;direction:rtl;text-align:left}
.family .count{font-size:11px;font-weight:600;color:var(--text-secondary);background:var(--bg-card-hover);border-radius:var(--r-menu);padding:1px 9px}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px}

/* 服务卡片：识别→理解→操作，状态顶部柔色渐变 pop-out */
.svc-card{background:var(--bg-card);border:1px solid var(--border-color);border-radius:14px;padding:16px;display:flex;flex-direction:column;gap:11px;box-shadow:var(--shadow-sm);transition:all .2s}
.svc-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.svc-card.operating{border-color:var(--accent) !important;box-shadow:0 0 16px rgba(99,102,241,.4) !important;animation:pulseCard 1.2s ease-in-out infinite alternate}
@keyframes pulseCard{0%{transform:scale(1);box-shadow:0 0 10px rgba(99,102,241,.25)}100%{transform:scale(1.01);box-shadow:0 0 20px rgba(99,102,241,.55)}}
.svc-card.running{background:linear-gradient(180deg,rgba(52,211,153,.08),var(--bg-card) 38%)}
.svc-card.fatal,.svc-card.backoff,.svc-card.error{background:linear-gradient(180deg,rgba(248,113,113,.1),var(--bg-card) 38%)}
.svc-head{display:flex;align-items:center;gap:10px;min-width:0}
.svc-icon-img{width:32px;height:32px;border-radius:8px;object-fit:contain;background:var(--mac-field);padding:2px;border:1px solid rgba(255,255,255,.08);box-shadow:0 2px 6px rgba(0,0,0,.2);flex-shrink:0}
.svc-icon-placeholder{display:flex;width:32px;height:32px;border-radius:8px;align-items:center;justify-content:center;font-size:13.5px;font-weight:700;background:var(--mac-field);border:1px solid rgba(255,255,255,.08);color:var(--text-primary);box-shadow:0 2px 6px rgba(0,0,0,.15);flex-shrink:0;text-transform:uppercase;user-select:none}
.svc-head-info{display:flex;flex-direction:column;min-width:0;flex:1;gap:3px}
.svc-head-title{display:flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap}
.svc-dot{width:9px;height:9px;border-radius:50%;background:var(--text-muted);flex:none}
.svc-card.running .svc-dot{background:var(--ok);box-shadow:0 0 8px var(--ok)}
.svc-card.fatal .svc-dot,.svc-card.backoff .svc-dot,.svc-card.error .svc-dot{background:var(--err);box-shadow:0 0 8px var(--err)}
.svc-name{font-weight:650;font-size:14.5px;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.svc-chip{font-size:10.5px;font-weight:600;color:var(--text-secondary);background:var(--bg-card-hover);border:1px solid var(--border-color);border-radius:5px;padding:1px 7px;flex:none}
.svc-status{display:flex}
.svc-note{font-size:11px;color:var(--text-muted);opacity:.85;font-style:italic;line-height:1.5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 2px;border-left:2px solid var(--border-light);padding-left:8px;margin:2px 0}
.status-badge{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:650;padding:4px 11px;border-radius:999px}
.status-badge::before{content:"";width:8px;height:8px;border-radius:50%}
.status-badge.running{background:rgba(52,211,153,.15);color:var(--ok)}.status-badge.running::before{background:var(--ok)}
.status-badge.stopped{background:rgba(148,163,184,.15);color:var(--text-muted)}.status-badge.stopped::before{background:var(--text-muted)}
.status-badge.fatal,.status-badge.backoff,.status-badge.error{background:rgba(248,113,113,.15);color:var(--err)}.status-badge.fatal::before,.status-badge.backoff::before,.status-badge.error::before{background:var(--err)}
.svc-meta{display:flex;justify-content:space-between;font-size:12px;color:var(--text-muted);border-top:1px solid var(--border-light);padding-top:9px}
.svc-actions{display:flex;gap:8px}
.svc-actions .btn{flex:1;padding:7px 0;font-size:12.5px}

/* ---- 列表 ---- */
.list{display:flex;flex-direction:column;gap:8px}
.audit-row{background:var(--bg-card);border:1px solid var(--border-color);border-radius:11px;padding:11px 14px;display:flex;gap:12px;font-size:12.5px;align-items:center}
.audit-row .t{color:var(--text-muted);font-family:monospace;white-space:nowrap}
.audit-row .u{font-weight:600;min-width:90px;color:var(--text-primary)}
.audit-row .a{font-weight:600;min-width:100px;color:var(--text-primary)}
.audit-row .d{color:var(--text-muted);flex:1}

/* ---- 弹窗/toast ---- */
.modal{position:fixed;inset:0;z-index:1080;display:grid;place-items:center;background:rgba(0,0,0,.4);backdrop-filter:blur(6px)}
.modal-card{width:380px;max-width:92vw;padding:24px;border-radius:16px;display:flex;flex-direction:column;gap:14px;background:var(--bg-card);border:1px solid var(--border-color);box-shadow:var(--shadow-lg);animation:pop .2s ease}
@keyframes pop{from{transform:scale(.94);opacity:0}to{transform:scale(1)}}
.modal-actions{display:flex;gap:10px;justify-content:flex-end}

/* ---- 终端 ---- */
.terminal-box{margin-top:14px;border-radius:12px;overflow:hidden;background:#0b1220;padding:8px}
#xterm .xterm{padding:6px}
.config-edit{width:100%;min-height:60vh;background:var(--bg-card);border:1px solid var(--border-color);border-radius:12px;padding:16px;color:var(--text-primary);outline:none;resize:vertical;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px}
/* 覆盖 bootstrap textarea.form-control 的 min-height（特异性更高，否则被压成 ~38px） */
textarea.config-edit.form-control{min-height:60vh!important}
.config-edit:focus{border-color:var(--accent)}
/* ===== 平台配置可视化表单 ===== */
.config-form{display:flex;flex-direction:column;gap:14px;max-width:860px}
.cfg-section{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;box-shadow:var(--mac-shadow);overflow:hidden}
.cfg-head{padding:12px 16px;border-bottom:1px solid var(--mac-line)}
.cfg-head h3{margin:0;font-size:14px;font-weight:700;color:var(--text-primary)}
.cfg-head p{margin:3px 0 0;font-size:11.5px}
.cfg-body{padding:14px 16px;display:flex;flex-direction:column;gap:10px}
.cfg-field{display:flex;align-items:center;gap:10px}
.cfg-field label{flex:0 0 220px;font-size:12px;font-weight:600;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cfg-field input[type="text"],.cfg-field input[type="number"],.cfg-field input[type="password"]{flex:1;min-width:0;height:32px;padding:4px 10px;border:1px solid var(--mac-line);border-radius:8px;background:var(--mac-field);color:var(--text-primary);font-size:12.5px;outline:none}
.cfg-field input:focus{border-color:var(--mac-accent)}
.cfg-field input[type="checkbox"]{width:18px;height:18px;accent-color:var(--mac-accent)}
.cfg-clear{clear:both}
.cfg-users{display:flex;flex-direction:column;gap:8px}
.cfg-user-row{display:flex;align-items:center;gap:8px}
.cfg-user-field{display:flex;align-items:center;gap:6px;flex:1;min-width:0}
.cfg-user-field label{flex:none;font-size:11px;color:var(--text-muted);width:auto}
.cfg-user-field input,.cfg-user-field select{flex:1;min-width:0;height:30px;padding:3px 8px;border:1px solid var(--mac-line);border-radius:var(--r-input);background:var(--mac-field);color:var(--text-primary);font-size:12px;outline:none}
.cfg-user-del{flex:none;width:30px;height:30px;border:1px solid transparent;border-radius:7px;background:transparent;color:var(--text-muted);cursor:pointer}
.cfg-user-del:hover{color:var(--err);background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.3)}
.cfg-version{margin-top:2px;font-size:11px;text-align:right}
/* ===== 日志查看 / 资源监控 / 配置管理（服务管理子页） ===== */
.log-view{background:var(--bg-card);border:1px solid var(--border-color);border-radius:12px;padding:12px 16px;height:calc(100vh - 260px);overflow-y:auto;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-all}
.log-view .log-line{color:var(--text-primary)}
.log-view .log-warn{color:#d29922;background:rgba(210,153,34,.08);border-radius:4px;padding:0 2px}
.log-view .log-err{color:#f85149;background:rgba(248,81,73,.1);border-radius:4px;padding:0 2px}
.monitor-summary{display:flex;gap:20px;flex-wrap:wrap;padding:12px 16px;background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;font-size:13px;margin-bottom:14px}
.monitor-summary .si{color:var(--text-secondary)}
.monitor-summary .si b{color:var(--text-primary);font-weight:600;font-family:monospace}

/* ===== 服务资产概览（层级重构 v0.32.0）=====
   设计心理学：① 主体（服务自身）用左侧强调竖条建立焦点（冯·雷斯托夫效应）；
   ② 四项指标等宽并排，横向可比（格式塔·相似律）；③ 依赖默认折叠为一行结论
   （认知卸载 + 渐进披露），异常时自动展开；④ 体积降权为脚注小字；
   ⑤ 服务用 pill 表达明确边界（可供性），异常时整块变红（前注意加工升级：点→面）。 */
.asset-card{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:14px 16px;box-shadow:var(--mac-shadow)}
.asset-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;flex-wrap:wrap}
.asset-head .asset-title{font-size:13px;font-weight:700;color:var(--text-primary)}
.asset-head .asset-meta{font-size:11px;color:var(--text-muted);font-family:ui-monospace,Menlo,Consolas,monospace}
.asset-alarm{font-weight:600}
.asset-alarm.ok{color:var(--ok)}
.asset-alarm.bad{color:var(--err)}
/* —— 焦点区：服务自身 —— */
.asset-self{border-left:2px solid var(--accent);padding-left:12px;margin-bottom:14px}
.asset-self .asset-id{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:13px;color:var(--text-secondary);margin-bottom:10px}
.asset-self .asset-id b{color:var(--text-primary);font-weight:650}
.asset-self .asset-id .num{font-family:ui-monospace,Menlo,Consolas,monospace}
/* 四项指标等宽并排：相似律 → 横向可比 */
.asset-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:10px}
.asset-metric{background:var(--mac-field);border:1px solid var(--mac-line);border-radius:10px;padding:8px 11px}
.asset-metric .k{font-size:10.5px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--text-muted);margin-bottom:3px}
.asset-metric .v{font-size:15px;font-weight:700;font-family:ui-monospace,Menlo,Consolas,monospace;color:var(--text-primary);line-height:1.2}
.asset-metric .s{font-size:10.5px;color:var(--text-muted);font-family:ui-monospace,Menlo,Consolas,monospace}
/* 指标色带：把"是否正常"的判断交给颜色，免读数字（外化认知负担） */
.asset-metric .meter{height:4px;border-radius:999px;background:rgba(127,132,142,.18);overflow:hidden;margin-top:6px}
.asset-metric .meter>i{display:block;height:100%;border-radius:999px;transition:width .5s ease}
/* —— 依赖区：默认一行结论，异常自动展开 —— */
.asset-dep{border-top:1px solid var(--mac-line);padding-top:10px;margin-bottom:10px}
.asset-dep-head{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-secondary);cursor:pointer;user-select:none}
.asset-dep-head:hover{color:var(--text-primary)}
.asset-dep-head .caret{font-size:10px;color:var(--text-muted);transition:transform .18s ease}
.asset-dep.open .asset-dep-head .caret{transform:rotate(90deg)}
.asset-dep-body{display:none;flex-wrap:wrap;gap:6px;margin-top:9px}
.asset-dep.open .asset-dep-body{display:flex}
/* pill：明确边界 + 内部结构（服务名 / pid 分层），扫读时"几个服务"一眼可数 */
.svc-pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;background:var(--mac-field);border:1px solid var(--mac-line);font-size:12px;color:var(--text-secondary);line-height:1.6}
.svc-pill .dot{width:7px;height:7px;border-radius:50%;background:var(--ok);box-shadow:0 0 6px var(--ok);flex:none}
.svc-pill small{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;color:var(--text-muted)}
.svc-pill.bad{background:rgba(248,81,73,.12);border-color:rgba(248,81,73,.35);color:var(--err);font-weight:600}
.svc-pill.bad .dot{background:var(--err);box-shadow:0 0 6px var(--err)}
/* —— 体积：降权为脚注（变化极慢的数据不占高权重位）—— */
.asset-foot{border-top:1px solid var(--mac-line);padding-top:9px;font-size:11.5px;color:var(--text-muted)}
.asset-foot b{color:var(--text-secondary);font-weight:600;font-family:ui-monospace,Menlo,Consolas,monospace}
.monitor-charts{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px}
.monitor-charts .mc-card, #mp-proc .mc-card{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:12px 16px;box-shadow:var(--mac-shadow);position:relative}
.monitor-charts .mc-title{font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:8px}
.monitor-charts canvas{width:100%;height:90px;display:block;cursor:crosshair}
#mp-proc canvas{width:100%;height:220px;display:block;cursor:crosshair}
/* 【v0.32.0 修复：canvas 宽度规则收口 —— 从"逐面板列举"改为"统一兜底"】
   实踩：`#mp-traffic`（请求流量）**从未写过 canvas 宽度规则**，导致 chartReq 退化为
   canvas 默认宽 300px（HTML 规范默认值）—— 而绘图代码用 `c.clientWidth || 900`，
   读到的正是 300 → 图表画在左上角一小块、右侧大片空白。同类隐患：#mp-disk、
   #mp-reqlog 等未来新增面板若放 canvas 会重蹈覆辙。
   根因是**规则按面板逐一列举**，漏一个就静默退化（不报错、只是难看）。
   故改为对所有监控面板内的 canvas 统一兜底；各面板的特殊尺寸仍由更具体的选择器覆盖。
   ⚠️ 层叠说明（**别被书写顺序误导**）：`.monitor-panel canvas`（特异性 0,1,1）**低于**
   `#mp-proc canvas`（1,0,1），故上面那条 220px 高度**依然生效**，与本条书写先后无关。
   本规则只设 width 不设 height，正是为了不与各面板的高度设定打架。
   ⚠️ 但 chartReq 因此**没有显示高度**：它只有 HTML 属性 `height="220"`（那是位图缓冲区
   高度，不是 CSS 显示高度）→ 宽度撑满后，浏览器按 canvas 固有比例把**显示高度也拉到 880px**，
   与 chartProc 的 220px 不一致（修复宽度后新暴露的问题，属"修好一个暴露下一个"）。
   故补 `#mp-traffic canvas` 与之对齐。 */
.monitor-panel canvas{width:100%;display:block;cursor:crosshair}
#mp-traffic canvas{height:220px}
/* v0.54.0：磁盘页新增趋势折线（#chartDiskTab）→ 必须显式给显示高度。
   与概览页 chartDisk 同级，同为 90px 紧凑曲线。 */
#mp-disk canvas{height:90px}
.chart-tooltip{position:absolute;z-index:20;background:rgba(18,24,38,.92);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(148,163,184,.25);border-radius:8px;padding:8px 12px;font-size:11.5px;color:#f1f5f9;box-shadow:0 8px 24px rgba(0,0,0,.35);pointer-events:none;transform:translate(-50%,-100%);margin-top:-10px;transition:opacity .15s ease,transform .15s ease;white-space:nowrap;opacity:0}
.chart-tooltip.show{opacity:1}
.chart-tooltip .tt-time{font-size:10px;color:#94a3b8;margin-bottom:4px;border-bottom:1px solid rgba(148,163,184,.15);padding-bottom:2px;font-family:monospace}
.chart-tooltip .tt-row{display:flex;align-items:center;gap:6px;margin:2px 0}
.chart-tooltip .tt-dot{width:8px;height:8px;border-radius:50%;display:inline-block}
.chart-tooltip .tt-label{color:#cbd5e1;max-width:140px;overflow:hidden;text-overflow:ellipsis}
.chart-tooltip .tt-val{margin-left:auto;font-weight:700;font-family:monospace;color:#fff}
/* 子视图切换 */
.monitor-tabs{display:flex;gap:6px;margin-bottom:14px;flex-wrap:wrap}
.monitor-tab{padding:6px 14px;border-radius:8px;border:1px solid var(--mac-line);background:transparent;color:var(--text-secondary);font-size:12px;cursor:pointer;transition:.15s}
.monitor-tab:hover{color:var(--text-primary);border-color:var(--accent)}
.monitor-tab.active{background:var(--accent);color:#fff;border-color:var(--accent)}
.monitor-panel.hidden{display:none}
.monitor-legend{display:flex;flex-wrap:wrap;gap:10px;margin-top:8px;font-size:12px;color:var(--text-secondary)}
.monitor-legend .lg{display:flex;align-items:center;gap:5px;cursor:pointer}
.monitor-legend .lg.dim{opacity:.35}
.monitor-legend .dot{width:10px;height:10px;border-radius:3px;display:inline-block}
/* 通用表格 (macOS 悬浮孤岛卡片行风格) */
.table-wrap{background:transparent;border:none;border-radius:0;padding:0;overflow:auto;max-height:65vh}
.grid-table{width:100%;border-collapse:separate!important;border-spacing:0 5px!important;font-size:12px;margin:0}
.grid-table th{padding:8px 12px;text-align:left;color:var(--text-muted);font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.4px;user-select:none;position:sticky;top:0;background:var(--mac-card);border:none!important;z-index:2}
.grid-table tbody tr{background:var(--mac-field);border:1px solid transparent;transition:all .15s ease}
.grid-table tbody tr:hover{background:var(--mac-card);box-shadow:0 2px 8px rgba(0,0,0,.08);transform:translateY(-0.5px)}
.grid-table tbody td{padding:9px 12px;text-align:left;border-top:1px solid var(--mac-line)!important;border-bottom:1px solid var(--mac-line)!important;border-left:none!important;border-right:none!important;color:var(--text-primary);white-space:nowrap;vertical-align:middle}
.grid-table tbody tr td:first-child{border-left:1px solid var(--mac-line)!important;border-radius:8px 0 0 8px!important}
.grid-table tbody tr td:last-child{border-right:1px solid var(--mac-line)!important;border-radius:0 8px 8px 0!important}
.grid-table .num{font-family:ui-monospace,Menlo,Consolas,monospace;text-align:right;font-weight:600}
.grid-table .bar-bg{background:rgba(127,132,142,.15);border-radius:9999px;height:6px;min-width:100px;overflow:hidden}
.grid-table .bar-fill{height:100%;border-radius:9999px;transition:width .3s ease}

/* Markdown 文档与知识库内嵌表格 (同步应用悬浮孤岛圆角行) */
.server-report-container table{width:100%;border-collapse:separate!important;border-spacing:0 6px!important;margin:12px 0 16px!important;font-size:12.5px}
.server-report-container table th{padding:6px 12px;color:var(--text-muted);font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.4px;border:none!important;background:transparent!important}
.server-report-container table tbody tr{background:var(--mac-field);transition:all .15s ease}
.server-report-container table tbody tr:hover{background:var(--mac-card)}
.server-report-container table tbody td{padding:8px 12px;border-top:1px solid var(--mac-line)!important;border-bottom:1px solid var(--mac-line)!important;border-left:none!important;border-right:none!important;color:var(--text-primary);vertical-align:middle}
.server-report-container table tbody tr td:first-child{border-left:1px solid var(--mac-line)!important;border-radius:8px 0 0 8px!important}
.server-report-container table tbody tr td:last-child{border-right:1px solid var(--mac-line)!important;border-radius:0 8px 8px 0!important}
.server-report-container{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:20px;font-size:13px;color:var(--text-primary);max-height:calc(100vh - 260px);overflow-y:auto;line-height:1.6}
.server-report-container h1{font-size:18px;font-weight:700;margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--mac-line);color:var(--text-primary)}
.server-report-container h2{font-size:15px;font-weight:700;margin:18px 0 8px;padding-bottom:4px;border-bottom:1px solid var(--mac-line);color:var(--text-primary)}
.server-report-container h3{font-size:13.5px;font-weight:600;margin:14px 0 6px;color:var(--text-secondary)}
.server-report-container blockquote{margin:8px 0 12px;padding:8px 12px;background:rgba(88,166,255,0.06);border-left:3px solid var(--mac-accent);border-radius:0 6px 6px 0;font-size:12px;color:var(--text-secondary)}
.server-report-container pre{background:var(--mac-field);border:1px solid var(--mac-line);border-radius:8px;padding:10px 14px;overflow-x:auto;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.5;margin:8px 0}
.server-report-container code{background:var(--mac-field);border-radius:4px;padding:1px 5px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
.server-report-container pre code{padding:0;background:none}
.server-report-container details{background:var(--mac-field);border:1px solid var(--mac-line);border-radius:8px;padding:8px 12px;margin:8px 0}
.server-report-container summary{cursor:pointer;user-select:none;font-size:12px;color:var(--text-secondary)}
.server-report-container summary:hover{color:var(--text-primary)}
.server-report-container details[open] summary{margin-bottom:8px;border-bottom:1px solid var(--mac-line);padding-bottom:4px}
.server-report-container .table-wrap{margin:8px 0 14px}

/* 打印与导出 PDF 专属样式 (A4 专业文档与报告排版) */
@page {
  size: A4 portrait;
  margin: 15mm 15mm 15mm 15mm;
}

@media print {
  /* 1. 强制浅色底与纯黑字，清除所有固定高度与滚动条限制 */
  html, body, .app-wrapper, .app-main, .app-content, .tab, #tab-docs, .dochub-wrap, .dochub-content, .doc-view-panel, .server-report-container {
    background: #ffffff !important;
    color: #111827 !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    box-shadow: none !important;
    border: none !important;
    padding: 0 !important;
    margin: 0 !important;
    display: block !important;
    font-size: 11pt !important;
    line-height: 1.6 !important;
  }

  /* 2. 彻底隐藏与正文无关的导航、侧边栏、调试台与操作按钮 */
  .app-header, .sidebar-wrapper, .app-footer, .brand-link, .nav, .title-actions,
  .dochub-sidebar, #docApiDebugger, #tab-docs .dashboard-header,
  .monitor-tabs, .btn, .status-bar, .tab:not(.active), .monitor-panel.hidden {
    display: none !important;
  }

  /* 3. 文档视图页眉排版 */
  .doc-view-header {
    border-bottom: 2px solid #111827 !important;
    padding-bottom: 8px !important;
    margin-bottom: 16px !important;
  }
  #docViewTitle {
    font-size: 18pt !important;
    font-weight: 800 !important;
    color: #111827 !important;
    margin-bottom: 4px !important;
  }
  #docViewPath {
    font-size: 9pt !important;
    color: #6b7280 !important;
    font-family: ui-monospace, Menlo, Consolas, monospace !important;
  }

  /* 4. 正文排版与防断裂控制 */
  .server-report-container h1 { font-size: 16pt !important; font-weight: 700 !important; color: #111827 !important; margin-top: 14pt !important; page-break-after: avoid; }
  .server-report-container h2 { font-size: 13pt !important; font-weight: 700 !important; color: #1f2937 !important; margin-top: 12pt !important; page-break-after: avoid; }
  .server-report-container h3 { font-size: 11.5pt !important; font-weight: 600 !important; color: #374151 !important; margin-top: 10pt !important; page-break-after: avoid; }

  .server-report-container p, .server-report-container ul, .server-report-container ol {
    color: #1f2937 !important;
    margin-bottom: 8pt !important;
  }

  /* 代码块在纸张上的浅色高清晰排版 */
  .server-report-container pre, pre {
    background: #f8fafc !important;
    border: 1px solid #cbd5e1 !important;
    border-radius: 4px !important;
    padding: 8pt 10pt !important;
    font-size: 9pt !important;
    line-height: 1.45 !important;
    color: #0f172a !important;
    white-space: pre-wrap !important;
    word-break: break-all !important;
    page-break-inside: avoid;
    margin: 8pt 0 !important;
  }
  .server-report-container code, code {
    background: #f1f5f9 !important;
    color: #0f172a !important;
    border-radius: 3px !important;
    padding: 1pt 3pt !important;
    font-size: 9.5pt !important;
  }
  .server-report-container pre code {
    background: none !important;
    padding: 0 !important;
  }

  /* 表格在纸张上的印刷线框 */
  .grid-table, table {
    width: 100% !important;
    border-collapse: collapse !important;
    margin: 10pt 0 !important;
    page-break-inside: avoid;
  }
  .grid-table th, table th {
    background: #f1f5f9 !important;
    color: #111827 !important;
    font-weight: 700 !important;
    border: 1px solid #94a3b8 !important;
    padding: 5pt 8pt !important;
    font-size: 9.5pt !important;
  }
  .grid-table td, table td {
    border: 1px solid #cbd5e1 !important;
    color: #1f2937 !important;
    padding: 5pt 8pt !important;
    font-size: 9.5pt !important;
  }

  /* 展开所有 details 折叠标签 */
  .server-report-container details {
    border: 1px solid #cbd5e1 !important;
    background: #ffffff !important;
    margin: 8pt 0 !important;
    page-break-inside: avoid;
  }
  .server-report-container details summary {
    font-weight: 700 !important;
    color: #111827 !important;
  }
  .server-report-container details[open] summary, .server-report-container details summary {
    margin-bottom: 4pt !important;
  }
  .server-report-container details pre {
    display: block !important;
  }
}
/* ===== 时间同步与 NTP 矩阵样式 ===== */
.timesync-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:14px}
.ts-card{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:14px 18px;box-shadow:var(--mac-shadow);display:flex;flex-direction:column}
.ts-card .ts-head{font-size:12px;font-weight:700;color:var(--text-secondary);margin-bottom:6px;display:flex;align-items:center}
.ts-card .ts-main-time{font-size:20px;font-weight:700;font-family:ui-monospace,Menlo,Consolas,monospace;color:var(--text-primary);letter-spacing:.5px;margin:2px 0}
.ts-card .ts-sub{font-size:11.5px;color:var(--text-muted);margin-top:auto}
.ts-card .ts-status{margin:4px 0}
.delta-badge{display:inline-block;padding:2px 8px;border-radius:6px;font-size:11.5px;font-family:ui-monospace,Menlo,Consolas,monospace;font-weight:700}
.delta-badge.good{background:rgba(63,185,80,.15);color:#3fb950}
.delta-badge.warn{background:rgba(210,153,34,.15);color:#d29922}
.delta-badge.bad{background:rgba(248,81,73,.15);color:#f85149}
.delta-badge.ref{background:rgba(88,166,255,.15);color:#58a6ff}
.type-tag{display:inline-block;padding:1px 6px;border-radius:4px;font-size:10.5px;font-weight:600;text-transform:uppercase}
.type-tag.ntp{background:rgba(163,113,247,.15);color:#a371f7}
.type-tag.http{background:rgba(88,166,255,.15);color:#58a6ff}

.conf-wrap{display:flex;gap:14px;align-items:flex-start}
.conf-list{width:220px;flex:none;max-height:calc(100vh - 260px);overflow-y:auto;background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:8px}
.conf-item{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:8px;font-size:12.5px;color:var(--text-secondary);cursor:pointer;transition:background .12s}
.conf-item:hover{background:var(--mac-field)}
.conf-item.active{background:var(--mac-accent);color:#fff;font-weight:600}
.conf-item i{font-size:14px}
.conf-editor{flex:1;min-width:0}
.conf-editor .config-edit{height:calc(100vh - 300px);min-height:300px}
/* ===== README 展示弹窗 + app 联动 ===== */
.readme-card{width:720px;max-width:94vw;max-height:82vh;display:flex;flex-direction:column;gap:10px}
.readme-head{display:flex;align-items:center;justify-content:space-between}
.readme-head h3{margin:0;font-size:15px}
.modal-x{background:transparent;border:none;color:var(--text-muted);font-size:22px;line-height:1;cursor:pointer;padding:2px 8px;border-radius:8px}
.modal-x:hover{color:var(--text-primary);background:var(--mac-field)}
.readme-path{font-size:11px;word-break:break-all;border-bottom:1px solid var(--mac-line);padding-bottom:8px}
.readme-body{flex:1;overflow-y:auto;padding:4px 2px;font-size:13px;line-height:1.6;color:var(--text-primary)}
.readme-body h3{font-size:15px;margin:12px 0 6px;border-bottom:1px solid var(--mac-line);padding-bottom:4px}
.readme-body h4{font-size:13.5px;margin:10px 0 4px}
.readme-body p{margin:5px 0}
.readme-body ul{margin:4px 0;padding-left:20px}
.readme-body li{margin:2px 0}
.readme-body pre{background:var(--mac-field);border:1px solid var(--mac-line);border-radius:8px;padding:10px;overflow-x:auto;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.5}
.readme-body code{background:var(--mac-field);border-radius:4px;padding:1px 5px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
.readme-body pre code{padding:0;background:none}
.svc-app-tag{margin-left:6px;font-size:13px;cursor:pointer;color:var(--mac-accent);opacity:.75;transition:opacity .15s}
.svc-app-tag:hover{opacity:1}
/* ============================================================
   卡片级物理呼吸灯动效 (Physical LED Breath & Aura)
   ============================================================ */

/* 卡片定位容器基础 */
.svc-card {
  position: relative;
  overflow: hidden;
  transition: transform 0.2s, box-shadow 0.25s, border-color 0.25s;
}

/* RUNNING 运行中卡片：卡片微光呼吸 + 左上角物理 LED 呼吸灯 */
.svc-card.is-running {
  border-color: rgba(46, 160, 67, 0.45) !important;
  box-shadow: 0 0 16px rgba(46, 160, 67, 0.16), 0 4px 16px rgba(0, 0, 0, 0.25) !important;
  animation: cardRunningBreath 3.2s infinite ease-in-out;
}

/* 顶部物理 LED 呼吸灯光束 (Physical Glowing LED Dot) */
.svc-card .card-led-indicator {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  z-index: 10;
  pointer-events: none;
}

/* 绿色运行呼吸灯 */
.svc-card.is-running .card-led-indicator {
  background: #39d353;
  box-shadow: 0 0 6px #39d353, 0 0 14px rgba(57, 211, 83, 0.85);
  animation: ledPulseGreen 2.2s infinite cubic-bezier(0.4, 0, 0.6, 1);
}

/* 琥珀色停止/异常呼吸灯 */
.svc-card.is-stopped .card-led-indicator {
  background: #d29922;
  box-shadow: 0 0 5px #d29922;
}

/* STOPPED 停止/异常卡片：琥珀微光静止提示 */
.svc-card.is-stopped {
  border-color: rgba(210, 153, 34, 0.35) !important;
  box-shadow: 0 0 12px rgba(210, 153, 34, 0.12), 0 4px 16px rgba(0, 0, 0, 0.25) !important;
}

/* 卡片极光呼吸动画：边框发光强度 + 外光晕柔和扩散 */
@keyframes cardRunningBreath {
  0%, 100% {
    border-color: rgba(46, 160, 67, 0.28);
    box-shadow: 0 0 8px rgba(46, 160, 67, 0.10), 0 0 0 1px rgba(46, 160, 67, 0.15), 0 4px 16px rgba(0, 0, 0, 0.25);
  }
  50% {
    border-color: rgba(63, 185, 80, 0.75);
    box-shadow: 0 0 22px rgba(46, 160, 67, 0.32), 0 0 0 1.5px rgba(63, 185, 80, 0.45), 0 6px 20px rgba(0, 0, 0, 0.35);
  }
}

/* LED 物理呼吸灯脉冲（亮度 + 聚光晕扩张） */
@keyframes ledPulseGreen {
  0%, 100% {
    transform: scale(1);
    opacity: 0.85;
    box-shadow: 0 0 5px #39d353, 0 0 10px rgba(57, 211, 83, 0.6);
  }
  50% {
    transform: scale(1.35);
    opacity: 1;
    box-shadow: 0 0 12px #56d364, 0 0 22px rgba(86, 211, 100, 0.95);
  }
}

/* 错位流动呼吸动画（避免整页死板同频闪烁） */
.svc-card.is-running:nth-child(2n) { animation-delay: -0.8s; }
.svc-card.is-running:nth-child(3n) { animation-delay: -1.6s; }
.svc-card.is-running:nth-child(4n) { animation-delay: -2.4s; }
.svc-card.is-running:nth-child(2n) .card-led-indicator { animation-delay: -0.6s; }
.svc-card.is-running:nth-child(3n) .card-led-indicator { animation-delay: -1.2s; }
.svc-card.is-running:nth-child(4n) .card-led-indicator { animation-delay: -1.8s; }

.app-state{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;padding:2px 10px;border-radius:20px;cursor:pointer;letter-spacing:.3px;transition:all .25s ease}
.app-state::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}

/* RUNNING: 极光绿呼吸动态光晕 (Glow Pulse) */
.app-state.run{
  background: rgba(46,160,67,0.14);
  color: #3fb950;
  border: 1px solid rgba(46,160,67,0.35);
  box-shadow: 0 0 10px rgba(46,160,67,0.2);
}
.app-state.run::before{
  background: #3fb950;
  box-shadow: 0 0 8px #3fb950, 0 0 14px rgba(63,185,80,0.6);
  animation: pulseGreenGlow 2s infinite cubic-bezier(0.4, 0, 0.6, 1);
}

/* STOPPED: 琥珀黄警告微光 */
.app-state.stop{
  background: rgba(210,153,34,0.12);
  color: #d29922;
  border: 1px solid rgba(210,153,34,0.3);
}
.app-state.stop::before{
  background: #d29922;
  box-shadow: 0 0 4px rgba(210,153,34,0.4);
}

/* NONE/未部署 */
.app-state.none{
  background: var(--mac-field);
  color: var(--text-muted);
  border: 1px solid rgba(255,255,255,0.06);
}
.app-state.none::before{
  background: var(--text-muted);
  opacity: 0.5;
}

/* 呼吸灯脉冲动画 */
@keyframes pulseGreenGlow {
  0%, 100% {
    transform: scale(1);
    opacity: 1;
    box-shadow: 0 0 6px #3fb950, 0 0 12px rgba(63,185,80,0.6);
  }
  50% {
    transform: scale(1.25);
    opacity: 0.75;
    box-shadow: 0 0 12px #3fb950, 0 0 20px rgba(63,185,80,0.8);
  }
}
.svc-card.flash{animation:flashCard 1s ease 2}
@keyframes flashCard{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 0 3px var(--mac-accent)}}
/* ===== git 检查弹窗 ===== */
.git-body{display:flex;flex-direction:column;gap:8px;padding:4px 2px;font-size:13px}
.git-row{display:flex;align-items:center;gap:10px;padding:7px 10px;background:var(--mac-field);border-radius:8px}
.git-row span{width:48px;flex:none;font-size:11px;color:var(--text-muted);font-weight:600}
.git-row b{flex:1;font-size:12.5px;color:var(--text-primary)}
.git-row code{flex:1;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;color:var(--text-primary)}
.git-warn{color:#d29922;font-weight:600}
.git-row .git-warn{flex:1}

/* ============================================================
   macOS 覆盖层（B 方案 · 双主题各自归位）
   light = mac 浅色 / dark = mac 经典深色 · 单一 accent 蓝
   ============================================================ */
/* ---- 亮色：mac 浅色（浅底深字、白/浅灰毛玻璃） ---- */
:root, [data-bs-theme="light"] {
  --mac-accent: #0071e3; --mac-accent-h: #0077ed;
  --mac-side: rgba(255,255,255,.55); --mac-top: rgba(255,255,255,.66);
  --mac-card: rgba(255,255,255,.7); --mac-field: rgba(0,0,0,.05);
  --mac-line: rgba(0,0,0,.08); --mac-btn: rgba(0,0,0,.05);
  --bg-card-hover: rgba(0,0,0,.04);
  /* ⭐ 选中态底色：accent 的**低饱和版本**（20260930 建）
     ⚠️【为什么不用 --accent 本身】它在亮色下过饱和 ⇒ 整块填充会"太重"，
        像"按钮"而不是"当前项"。⭐ 判据：**选中态是"背景"语义，不是"强调"语义** ——
        用低透明度同色相即可（与 .chip / 状态徽章同源思路）。*/
  --accent-soft: rgba(99,102,241,.12);
  --border-light: rgba(0,0,0,.06);
}
/* ---- 暗色：mac 经典深色（近黑底、深灰毛玻璃、浅字） ---- */
[data-bs-theme="dark"] {
  --mac-accent: #0a84ff; --mac-accent-h: #0a93ff;
  --mac-side: rgba(17,19,26,.88); --mac-top: rgba(255,255,255,.08);
  --mac-card: rgba(36,36,40,.72); --mac-field: rgba(255,255,255,.07);
  --mac-line: rgba(255,255,255,.09); --mac-btn: rgba(255,255,255,.10);
  --bg-card-hover: rgba(255,255,255,.06);
  /* ⚠️ 深色下要**更高一点**的透明度 —— 同样的 12% 在深底上几乎看不见（实测） */
  --accent-soft: rgba(99,102,241,.22);
  --border-light: rgba(255,255,255,.06);
}

/* ---- 背景：浅色 = 亮冷渐变；深色 = 近黑 + 极淡蓝紫光 ---- */
body, .app-wrapper {
  background:
    radial-gradient(1100px 700px at 15% 0%,  rgba(158,190,255,.32), transparent 60%),
    radial-gradient(900px 650px at 85% 10%, rgba(205,176,255,.25), transparent 60%),
    radial-gradient(1000px 850px at 50% 110%, rgba(180,205,255,.28), transparent 60%),
    linear-gradient(160deg, #eef3ff 0%, #e4edff 100%) !important;
}
[data-bs-theme="dark"] body, [data-bs-theme="dark"] .app-wrapper {
  background:
    radial-gradient(1100px 700px at 15% 0%,  rgba(28,58,118,.32), transparent 60%),
    radial-gradient(900px 650px at 85% 10%, rgba(58,36,110,.26), transparent 60%),
    radial-gradient(1000px 850px at 50% 110%, rgba(36,66,128,.28), transparent 60%),
    linear-gradient(160deg, #090a0e 0%, #0e0f15 100%) !important;
}

/* ---- 文字：浅色 = 深字；深色 = 浅字 ---- */
:root, [data-bs-theme="light"] {
  --text-primary: #1d1d1f;
  --text-secondary: #6e6e73;
  --text-muted: #9aa1b0;
}
[data-bs-theme="dark"] {
  --text-primary: #f2f4f8;
  --text-secondary: #a8b0bd;
  --text-muted: #6b7280;
  --ok: #32d74b; --err: #ff453a; --warn: #ffd60a;
}

/* 玻璃通用 */
.glass{background:var(--mac-top);backdrop-filter:blur(22px) saturate(170%);-webkit-backdrop-filter:blur(22px) saturate(170%);border-bottom:1px solid var(--mac-line)}
.glass-card{background:var(--mac-card);backdrop-filter:blur(16px) saturate(160%);-webkit-backdrop-filter:blur(16px) saturate(160%);border:1px solid var(--mac-line)}

/* 顶栏 + 侧边栏：毛玻璃。
   mac 风格不用硬边框切割，改用色差 + 超淡投影柔和分隔 */
/* ══════════════════════════════════════════════════════════════════════
   ⭐⭐「浮起面板」布局（20261010，用户要求参照 DSH）
   ══════════════════════════════════════════════════════════════════════

   ⚠️【用户原话】「我想要这种样式的卡片浮在上面的样式」——
      ⭐ 关键区分：**浮的是"整个面板"，不是"每个菜单项"**。
      我最初只给菜单项加圆角，而侧栏面板本身仍贴边
      （证据：`.app-sidebar{box-shadow:1px 0 0 0 …}` 那条右边线）
      ⇒ 面板不"浮"，内部再圆也没用。

   ⭐【「浮」的三要素】缺一不可：
      ① **四周留缝** —— 面板之间、面板与屏幕边缘之间都有空隙
      ② **大圆角**   —— 面板自己的四个角（不是内部元素的角）
      ③ **色阶差**   —— 缝隙色必须比面板色**深一档**
         ⚠️ 最易漏的一条：若缝隙与面板同色，"留缝"根本看不见。
         实测本项目：`.app-main` 与 `.app-wrapper` 原同为 `--bg-root`
         ⇒ 只加 gap 会**完全无效**。

   ⚠️⚠️【实现上踩过的坑 —— 值得记】
      第一版我只做 `gap + padding`，并给 `.app-header`/`.app-sidebar`
      加了 `top: var(--float-gap)`（以为 sticky 偏移能补缝）——
      🔴 结果：**布局占位没变、只是视觉上移** ⇒
         「侧栏比顶栏高、顶栏与内容区之间无缝、点不透"浮"的效果」。
      ⭐ 根因：AdminLTE 用 **grid-template-areas 的九宫格**：
           "sidebar | header"
           "sidebar | main"
         ⇒ sidebar **跨两行**（所以它与顶栏"焊"在一起），
           header 与 main 同列不同行（所以它们之间本该有缝，却被 sticky 抵消）。
      ⇒ ⭐ 判据：**要做"三块各自独立"，就必须改 grid 的"区域划分"，
           而不是在既有区域上叠加偏移量** —— 后者只会造成"视觉动了、布局没动"。
         这正是"用手段代替目标"的典型：我要的是"三块独立"，
         却用"加偏移"去模拟它。

   ⚠️【保留的约束】
     · 缝隙色用 `--bg-root` **再深一档**（显式指定，不依赖 body 默认色）
     · **不动** AdminLTE 的 sidebar-collapse / mini 交互逻辑
     · **不动** 任何 JS / 路由 / DOM 结构
     · 圆角复用 `--r-card`（项目圆角令牌的"卡片"档）

   ⚠️【已知边界】折叠态（sidebar-mini）下侧栏变窄，圆角仍成立；
      但若侧栏宽度 < 2×圆角，视觉上会"过圆" ——
      届时把 `--float-r` 调小即可（回退成本一行）。*/

:root, [data-bs-theme="light"], [data-bs-theme="dark"] {
  /* ⭐ 浮起布局的两个参数（局部语义，不进全局令牌表）*/
  --float-gap: 10px;                /* 面板之间的缝 —— 「浮」的关键 */
  --float-r: var(--r-card, 16px);   /* 面板圆角（复用"卡片"档）*/
}

/* ── ① 改 grid 区域划分：让 header 独占一行、sidebar 只占第二行 ──────
   ⚠️ AdminLTE 原定义（sidebar 跨两行 = 与顶栏焊在一起）：
        grid-template-areas:"lte-app-sidebar lte-app-header"
                            "lte-app-sidebar lte-app-main"
                            "lte-app-sidebar lte-app-footer";
   ⇒ 改为「顶栏横贯整行、下面左右分栏」，这样三块能各自留缝。*/
.app-wrapper {
  grid-template-areas:
    "lte-app-header lte-app-header"
    "lte-app-sidebar lte-app-main"
    "lte-app-sidebar lte-app-footer" !important;
  /* ⚠️⚠️【必须同时给 rows，否则"多一小节滚动条"（20261010 用户报）】
     AdminLTE 原定义是 `grid-template-rows: min-content 1fr min-content`
     （中间那行 `1fr` 负责**撑满视口**）。
     我第一版**只改了 areas、没保留 rows** ⇒ 行高退化为 `auto`
     ⇒ wrapper 被内容撑到 **4538px**（实测）⇒ 页面出现**双层滚动**：
        ① 浏览器窗口滚动条（整个页面 4538px）← ⭐ 用户看到的"多一小节"
        ② 内容区自己的滚动条
     ⭐ 判据：**改 `grid-template-areas` 时必须同时确认 `grid-template-rows`** ——
        行数与语义要跟着 areas 一起改，否则隐式行高会变 auto。
        这是"改了一半"的典型：改了区域划分，却漏了行定义。
     ⭐ 这里按新 areas 给三行：顶栏(auto) / 主体(1fr 撑满) / 页脚(auto)。*/
  grid-template-rows: auto 1fr auto !important;
  /* ⚠️ 高度**必须限定为视口内**，否则 `1fr` 也会被内容顶开。
     用 `100vh - 上下留缝*2`（wrapper 自身有 padding）。*/
  height: calc(100vh - var(--float-gap) * 2) !important;
  /* 溢出交给**内部面板**处理（每块自己滚），wrapper 本身不滚。*/
  overflow: hidden !important;
  gap: var(--float-gap) !important;
  padding: var(--float-gap) !important;   /* ⚠️ 与屏幕边缘也要留缝 */
  /* ⚠️⚠️【背景**故意不改** —— 这是踩过才知道的】
     本项目**本来就有一层渐变背景**（见上方 `body, .app-wrapper{background: …gradient… }`），
     它是"桌面底色"，而面板浮在它之上 —— 这**正是"浮"所需要的基础**。
     ⚠️ 我第一版在这里写了 `background:#080b10 !important` 想"加深缝隙"，
        结果：① 选择器优先级**打不过** `[data-bs-theme="dark"] .app-wrapper`（它多一个属性选择器）
              ② 即使打赢，也会**抹掉那层渐变**（把"桌面"变成纯色，反而失去空间感）
     ⭐ 判据：**"浮起"要的是「底色比面板深/有层次」，不是「把底色换成纯色」** ——
        既有的渐变恰好提供了层次，**应当保留并让面板从它上面浮出来**。*/
  /* 让网格容器自身**透明**，把渐变背景透出来当"桌面" */
  background-color: transparent;
}

/* ── 加深"桌面"与"面板"的对比（在既有渐变之上叠加一层压暗）──────────
   ⚠️ 用 `::before` 叠一层**半透明黑**，而不是替换 background ——
      这样既压暗了桌面（让浮起更明显），又**保留了原渐变的色彩层次**。*/
.app-wrapper { position: relative; }
.app-wrapper::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;      /* ⚠️ 不能挡点击 */
  z-index: 0;
  background: rgba(0, 0, 0, .28);   /* 深色主题：压暗桌面 */
}
[data-bs-theme="light"] .app-wrapper::before {
  background: rgba(0, 0, 0, .06);   /* 亮色主题：只需极淡 */
}
/* ⚠️ 面板要浮在 ::before 之上 */
.app-header, .app-sidebar, .app-main, .app-footer { position: relative; z-index: 1; }

/* ⚠️【每块面板自己滚，页面不滚】
   浮起布局下"整页滚动"是错的 —— 否则顶部/侧栏会跟着滚走，
   "浮起"变成"一起飘"。正确语义：**面板固定在视口内，内容在面板内滚**。
   ⭐ 这也是 DSH 那种布局的行为（侧栏不随内容滚）。*/
.app-sidebar { overflow-y: auto !important; overflow-x: hidden !important; }
.app-main    { overflow-y: auto !important; }

/* ── ② 三块各自成一枚浮起面板 ───────────────────────────────────── */
.app-header,
.app-sidebar,
.app-main {
  border-radius: var(--float-r) !important;
  /* 发丝边：项目判据「面 + 发丝边」表达层级（不是重阴影）*/
  border: 1px solid var(--mac-line, rgba(148,163,184,.14)) !important;
  /* 投影**极淡** —— 只做辅助，"浮"主要靠留缝与色差 */
  box-shadow: 0 1px 3px rgba(0,0,0,.22), 0 8px 24px rgba(0,0,0,.14) !important;
  overflow: hidden;   /* 让内部内容不溢出圆角 */
}

/* ⚠️ 去掉"贴边布局"遗留的分隔线 ——
   现在有留缝了，再画线就是**重复表达层级**（项目判据：面+发丝边取代粗线）。*/
.app-header { border-bottom: none !important; }
.app-sidebar { box-shadow: 0 1px 3px rgba(0,0,0,.22), 0 8px 24px rgba(0,0,0,.14) !important; }

/* ⚠️ sticky 去掉 top 偏移 —— 改 grid 区域后，
   顶栏本就在自己的行里，不需要靠偏移"假装"浮起。
   （⚠️ 我第一版正是这么做错的，见上方注释。）*/
.app-header { top: 0 !important; }
.app-sidebar { top: 0 !important; }

/* ── ③ 面板色：与深色缝隙形成色阶 ───────────────────────────────── */
[data-bs-theme="dark"] .app-main {
  /* 深色主题下内容区比缝隙**亮一档** ⇒ 浮起可见 */
  background: #141821 !important;
}


/* ── ③ 内部背景改为"面板色"，与外层深色缝形成色阶 ───────────────── */
.app-main {
  background: var(--bg-root) !important;
}
[data-bs-theme="dark"] .app-main {
  /* 深色主题下内容区比缝隙**亮一档** ⇒ 浮起可见 */
  background: #141821 !important;
}

.app-header{background:var(--mac-top) !important;backdrop-filter:blur(22px) saturate(170%);-webkit-backdrop-filter:blur(22px) saturate(170%)}
.app-sidebar{background:var(--mac-side) !important;backdrop-filter:blur(20px) saturate(170%);-webkit-backdrop-filter:blur(20px) saturate(170%);box-shadow:1px 0 0 0 var(--mac-line) !important}
.sidebar-wrapper{background:var(--mac-side) !important}
.sidebar-brand{background:var(--mac-side) !important;border-bottom:1px solid var(--mac-line) !important}
/* ── 侧边栏菜单项：**圆角卡片**（20260930 改，用户要求）─────────────────
   ⚠️【改前是什么】扁平竖条：
        .nav-link        { background: transparent }
        .nav-link:hover  { background: transparent }   ← ⚠️ hover **零反馈**
        .nav-link.active { box-shadow: inset 3px 0 0 var(--accent) }  ← 仅左侧 3px 竖条
      ⭐ 原注释写着「靠文字色+左侧指示条区分当前项」—— 那是个**有意的扁平设计**。

   【为什么改成圆角卡片】用户明确要求参照 DSH 侧栏。
     设计上的实质区别（不只是"加圆角"）：
       · **hover 要有反馈** —— 扁平版 hover 完全透明 ⇒
         用户移上去没有任何回应，**"可点性"只能靠猜**（⭐ 错误可供性）
       · **选中要整块可见** —— 3px 竖条在长列表里**扫视成本高**
         （要找"当前在哪"，得逐行看左侧细边）
       · **左右内缩** —— 贴边的色块读起来像"分隔条"，内缩后才像"卡片"
       ⭐ 判据（本项目）：**"面 + 发丝边"取代阴影分层**是现代 SaaS 的标志；
         而"导航项的选中态"属于**状态表达**，应尽量靠**整块色面**而非细线。

   ⚠️【保留的约束】
     · 圆角取 `--r-menu`（8px）—— **不是** `--r-card`(16)：导航项矮（~36px），
       16px 会显得"胖圆"，失去"列表项"的紧凑感。⭐ 圆角要随**元素高度**分档。
     · 只改**背景/圆角/内缩**，**不动尺寸与排版**（避免布局回归）。
     · ⚠️ `!important` 保留 —— AdminLTE 对 `.nav-link` 有较强默认（原代码也用它压制）。*/

/* ⚠️⚠️【选择器优先级：必须用 `.sidebar-menu > .nav-item > .nav-link`】
   实测踩到：我先写成 `.sidebar-menu .nav-link`（2 层）——
   而 **AdminLTE 用的是 3 层**：
     `.sidebar-menu>.nav-item>.nav-link{color:...}`
     `.sidebar-menu>.nav-item>.nav-link:hover{background-color:...}`
     `.sidebar-menu>.nav-item>.nav-link.active{background-color:...}`
   ⇒ 它的优先级更高 ⇒ 🔴 我的 `margin:1px 0` 与 `padding` **被压掉**
     （症状：截图里卡片仍**贴边**，看着像"色带"而不是"卡片"）
   ⭐ 判据：**覆盖第三方（AdminLTE/Bootstrap）样式时，选择器要"同等或更高优先级"
     + `!important`** —— 只加 `!important` 不够，因为对方也可能带 `!important`
     （本次对方没带，故"3 层 + !important"即可稳）。
   ⭐ 另一条：**`margin` 无效这类问题，计算样式里看得见**（实测 `marginLeft/Right = 0px`）
     —— 故验证要**读计算值**，不能只看"改过了"。*/

/* 列表本身：给卡片留出左右内边距（贴边就没有"卡片感"了） */
.sidebar-menu{padding-left:8px !important;padding-right:8px !important}

/* 普通项：透明 + 圆角 + 过渡（过渡只加在"背景/文字色"这类装饰属性上） */
.sidebar-menu > .nav-item > .nav-link{
  background:transparent !important;
  border-radius:var(--r-menu) !important;
  margin:1px 0 !important;
  transition:background .15s ease,color .15s ease;
}

/* ⭐ hover：**给反馈**（改前是"完全透明"，等于没有 hover） */
.sidebar-menu > .nav-item > .nav-link:hover{
  background:var(--bg-card-hover) !important;
  color:var(--text-primary) !important;
}

/* ⭐ 选中：**整块圆角填充**（去掉左侧竖条 —— 用户选择"无竖条"） */
.sidebar-menu > .nav-item > .nav-link.active{
  background:var(--accent-soft) !important;
  color:var(--accent-solid) !important;
  font-weight:600;
  box-shadow:none !important;
}

/* 深色主题下 hover 的对比度：浅灰在深底上会"发白"，改用极淡的提亮 */
[data-bs-theme="dark"] .sidebar-menu > .nav-item > .nav-link:hover{
  background:rgba(148,163,184,.12) !important;
}

/* ⚠️ 触屏无 hover ⇒ 不能只靠 hover 表达"可点"（本项目既有纪律） */
@media (hover:none){
  .sidebar-menu > .nav-item > .nav-link:hover{background:transparent !important}
}

/* accent：单一 mac 蓝（背景让位，蓝按钮显著度足够），激活色随主题深浅适配 */
:root, [data-bs-theme="light"] {
  --accent: var(--mac-accent); --accent-h: var(--mac-accent-h);
  --accent-gradient: linear-gradient(135deg,#0071e3,#5e5ce6 55%,#af52de);
  --sidebar-active-bg: rgba(0,113,227,.1);
  --sidebar-active-text: #0062c2;
  --sidebar-hover-bg: rgba(0,0,0,.05);
  --topbar-tab-active-color: #0062c2;
  --topbar-tab-active-bg: rgba(0,113,227,.1);
}
[data-bs-theme="dark"] {
  --accent: var(--mac-accent); --accent-h: var(--mac-accent-h);
  --accent-gradient: linear-gradient(135deg,#0a84ff,#5e5ce6 55%,#bf5af2);
  --sidebar-active-bg: rgba(10,132,255,.16);
  --sidebar-active-text: #4ea6ff;
  --sidebar-hover-bg: rgba(255,255,255,.07);
  --topbar-tab-active-color: #4ea6ff;
  --topbar-tab-active-bg: rgba(10,132,255,.16);
}

/* 卡片/面板：毛玻璃（投影随主题深浅，浅色淡、深色重） */
:root, [data-bs-theme="light"] { --mac-shadow: 0 8px 30px rgba(0,0,0,.08); }
[data-bs-theme="dark"] { --mac-shadow: 0 8px 30px rgba(0,0,0,.35); }
.svc-card, .panel, .audit-row, .modal-card, .config-edit, .card {
  background: var(--mac-card) !important;
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  box-shadow: var(--mac-shadow);
}
/* 服务卡片状态色：克制降饱和，垫玻璃底 */
.svc-card.running{background:linear-gradient(180deg,rgba(50,215,75,.12),var(--mac-card) 40%) !important}
.svc-card.fatal,.svc-card.backoff,.svc-card.error{background:linear-gradient(180deg,rgba(255,69,58,.14),var(--mac-card) 40%) !important}

/* 登录卡：玻璃 */
.login-card{background:var(--mac-card) !important;backdrop-filter:blur(22px) saturate(170%);-webkit-backdrop-filter:blur(22px) saturate(170%);box-shadow:var(--mac-shadow);border:1px solid var(--mac-line)}

/* 表单控件：玻璃字段 */
.form-control, .form-select, .config-edit{background-color:var(--mac-field) !important;border-color:var(--mac-line) !important;color:var(--text-primary)}
.form-control:focus,.form-select:focus{border-color:var(--mac-accent) !important;box-shadow:0 0 0 .25rem rgba(10,132,255,.2) !important}

/* 按钮：mac 风（主按钮蓝底） */
.btn.primary{background:var(--mac-accent) !important;border:none;color:#fff}
.btn.primary:hover{background:var(--mac-accent-h) !important;filter:none}
.btn.ghost{background:var(--mac-btn);border:1px solid var(--mac-line)}
.btn.ghost:hover{background:rgba(10,132,255,.14);border-color:var(--mac-accent)}
.avatar{background:linear-gradient(135deg,#0a84ff,#bf5af2)}

/* 顶栏模块标签：分段控件（display 由 Bootstrap d-lg-flex 控制，此处只补外观） */
.module-tabs{gap:2px;padding:3px;background:var(--mac-field);border-radius:9px}
.module-tab{padding:7px 15px;border-radius:7px;font-size:13px;font-weight:500;color:var(--text-secondary);display:inline-flex;align-items:center;gap:6px;text-decoration:none;cursor:pointer}
.module-tab:hover{color:var(--text-primary)}
.module-tab.active{background:var(--mac-card);color:var(--text-primary);font-weight:600;box-shadow:0 1px 4px rgba(0,0,0,.3)}
.module-tab i{font-size:14px}

/* SF 字体栈 */
body,.brand h1,.page-title,.svc-name,.module-tab{font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","Helvetica Neue","PingFang SC","Microsoft YaHei",sans-serif}

/* ---- 终端：文件管理器 & 监控条 ---- */
.sysinfo{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0;padding:12px;background:var(--mac-card);border-radius:12px;border:1px solid var(--mac-line)}
.sysinfo .si{display:flex;flex-direction:column;gap:2px;min-width:110px;padding:8px 12px;border-radius:9px;background:var(--mac-field)}
.sysinfo .si b{font-size:15px;color:var(--text-primary)}
.sysinfo .si span{font-size:11px;color:var(--text-muted)}
.fs-list{display:flex;flex-direction:column;gap:4px;max-height:340px;overflow-y:auto;border:1px solid var(--mac-line);border-radius:10px;padding:6px}
.fs-row{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:7px;font-size:13px;cursor:pointer}
.fs-row:hover{background:var(--mac-field)}
.fs-row.dir .fs-ico{color:var(--mac-accent)}
.fs-row.file .fs-ico{color:var(--text-muted)}
.fs-row .fs-ico{width:18px;text-align:center}
.fs-row .fs-name{flex:1;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fs-row .fs-size{color:var(--text-muted);font-size:12px;min-width:70px;text-align:right}
.fs-row .fs-op{display:flex;gap:4px;opacity:0;transition:opacity .15s}
.fs-row:hover .fs-op{opacity:1}
.fs-row .fs-op .btn{font-size:11px;padding:3px 8px}

/* ===== 设备终端页与文档中心（独占视口，对齐 CEM 布局，mac 毛玻璃风） ===== */
/* 终端页与文档页独占视口：锁定内容区高度不滚动，内层 flex 撑满（避免页面被拉长出现上下滚动条）
   高度自适应 grid：.app-main 在 AdminLTE grid 的 1fr 行已占满剩余，.app-content 用 height:100% */
body.terminal-page, body.docs-page{overflow:hidden}
body.terminal-page .app-wrapper, body.docs-page .app-wrapper{grid-template-rows:min-content minmax(0,1fr) min-content!important;height:100vh!important;overflow:hidden!important}
body.terminal-page .app-content, body.docs-page .app-content{height:100%;overflow:hidden;display:flex;flex-direction:column;padding:16px 24px;box-sizing:border-box}
body.terminal-page #tab-terminal.tab.active, body.docs-page #tab-docs.tab.active{flex:1;display:flex;flex-direction:column;min-height:0;overflow:hidden}
.terminal-container{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}
.connection-panel{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:12px;padding:10px 14px;margin-bottom:10px;display:flex;align-items:center;gap:10px;flex-wrap:wrap;box-shadow:var(--mac-shadow)}
.connection-panel .form-group{display:flex;align-items:center;gap:6px}
.connection-panel label{font-size:11px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:.5px;white-space:nowrap}
.connection-panel .form-control,.connection-panel .form-select{height:34px;font-size:13px;border-radius:var(--r-input);min-width:130px}
.ip-input-wrapper{position:relative;flex:1;min-width:180px;max-width:260px}
.ip-input-wrapper .form-control{width:100%}
.ip-history-dropdown{position:absolute;top:100%;left:0;right:0;background:var(--mac-card);border:1px solid var(--mac-line);border-radius:0 0 8px 8px;box-shadow:var(--mac-shadow);z-index:1000;max-height:200px;overflow-y:auto;display:none}
.ip-history-dropdown.show{display:block}
.ip-history-item{padding:8px 12px;font-size:13px;cursor:pointer;color:var(--text-primary);transition:background .15s;display:flex;justify-content:space-between;align-items:center}
.ip-history-item:hover{background:var(--mac-field)}
.ip-history-item .remove-btn{color:var(--text-muted);cursor:pointer;padding:2px 6px;border-radius:4px;font-size:11px}
.ip-history-item .remove-btn:hover{color:var(--err);background:rgba(239,68,68,.1)}
.btn-connect{height:34px;padding:6px 20px;font-size:13px;font-weight:600;border-radius:var(--r-btn);background:var(--mac-accent);color:#fff;border:none;transition:all .2s;white-space:nowrap;display:inline-flex;align-items:center;gap:6px}
.btn-connect:hover{opacity:.9;transform:translateY(-1px)}
.btn-connect:disabled{opacity:.5;cursor:not-allowed;transform:none}
.btn-connect.connected{background:var(--err)}
.btn-connect.danger{background:rgba(248,81,73,.9)}
.btn-config{height:34px;padding:6px 14px;font-size:12px;font-weight:500;border-radius:var(--r-btn);background:var(--mac-btn);color:var(--text-secondary);border:1px solid var(--mac-line);transition:all .2s;text-decoration:none;display:inline-flex;align-items:center;gap:6px}
.btn-config:hover{background:var(--mac-field);color:var(--text-primary)}
.main-area{flex:1;display:flex;min-height:0;gap:0}
.main-area .terminal-wrapper{flex:1;min-width:0}
.terminal-wrapper{flex:1;min-height:250px;background:#000;border:1px solid var(--mac-line);border-radius:12px;overflow:hidden;box-shadow:var(--mac-shadow)}
#xterm-container{width:100%;height:100%;padding:10px;box-sizing:border-box;overflow:hidden}
#xterm-container .xterm{height:100%;overflow:hidden}
/* 精简版 xterm.css 缺失 .xterm-rows 绝对定位，导致渲染行参与流布局撑开容器、
   fit 无限放大(rows 每轮 +1) —— 必须补回，使行层不撑开 .xterm */
#xterm-container .xterm .xterm-rows{position:absolute;left:0;top:0}
#xterm-container .xterm .xterm-viewport{overflow-y:auto}
.divider{width:5px;flex:none;cursor:col-resize;background:transparent;position:relative;z-index:5;transition:background .15s}
.divider:hover,.divider.dragging{background:var(--mac-accent)}
.file-panel{width:340px;flex:none;background:var(--mac-card);border-left:1px solid var(--mac-line);display:flex;flex-direction:column;min-height:0;transition:width .2s ease,opacity .2s ease}
.file-panel.collapsed{width:0;opacity:0;overflow:hidden;border-left:none}
.fp-head{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;border-bottom:1px solid var(--mac-line)}
.fp-head .t{font-size:12px;font-weight:700;letter-spacing:.4px;color:var(--text-secondary);display:flex;align-items:center;gap:6px}
.fp-head .t .dot{width:8px;height:8px;border-radius:50%;background:var(--mac-accent)}
.fp-toolbar{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--mac-line);align-items:center;flex-wrap:wrap}
.fp-toolbar .tbtn{height:28px;min-width:28px;padding:0 8px;background:transparent;border:1px solid transparent;border-radius:6px;color:var(--text-secondary);cursor:pointer;font-size:13px;display:inline-flex;align-items:center;justify-content:center;gap:4px;transition:all .12s}
.fp-toolbar .tbtn:hover{background:var(--mac-card);color:var(--text-primary);border-color:var(--mac-line)}
.fp-toolbar .tbtn:disabled{opacity:.35;cursor:not-allowed}
.fp-toolbar .tbtn.spacer{flex:1}
.fp-toolbar .tbtn.upload-btn{background:var(--mac-accent);color:#fff;border:none;font-weight:600}
.fp-toolbar .tbtn.upload-btn:hover{opacity:.9}
.fp-path{display:flex;align-items:center;gap:2px;padding:7px 12px;border-bottom:1px solid var(--mac-line);font-size:12px;color:var(--text-secondary);overflow-x:auto;white-space:nowrap;flex:none}
/* 路径栏可编辑：点击进入输入模式，支持直接输入绝对路径（如 /main/app） */
.fp-path.editable{cursor:text}
.fp-path.editable:hover{background:var(--mac-field)}
.fp-path .fp-edit-hint{margin-left:auto;color:var(--text-muted);opacity:.55;font-size:11px;padding:0 4px;cursor:pointer;flex:none}
.fp-path .fp-edit-hint:hover{opacity:1;color:var(--mac-accent)}
.fp-path-input{flex:1;min-width:0;background:var(--mac-field);border:none;outline:none;color:var(--text-primary);font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;padding:3px 8px;border-radius:var(--r-input)}
.fp-path .crumb{padding:2px 6px;border-radius:5px;cursor:pointer;color:var(--text-secondary)}
.fp-path .crumb:hover{background:var(--mac-card);color:var(--text-primary)}
.fp-path .crumb:last-child{color:var(--text-primary);font-weight:600}
.fp-path .sep{color:var(--text-muted)}
.fp-list{flex:1;overflow-y:auto;min-height:0}
.fp-item{display:flex;align-items:center;gap:8px;padding:6px 12px;font-size:13px;cursor:pointer;border-left:2px solid transparent;transition:background .12s}
.fp-item:hover{background:var(--mac-field)}
.fp-item .ic{width:18px;text-align:center;font-size:15px;flex:none}
.fp-item .ic.folder{color:#d29922}
.fp-item .ic.file{color:var(--text-muted)}
.fp-item .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fp-item .meta{font-size:11px;color:var(--text-muted);flex:none}
.fp-item .actions{display:none;gap:2px;flex:none}
.fp-item:hover .actions{display:inline-flex}
.fp-item .actions .a{width:24px;height:24px;border:none;background:transparent;border-radius:5px;color:var(--text-secondary);cursor:pointer;font-size:13px;display:inline-flex;align-items:center;justify-content:center}
.fp-item .actions .a:hover{background:var(--mac-field);color:var(--text-primary)}
.fp-item .actions .a.del:hover{color:var(--err);background:rgba(248,81,73,.12)}
.fp-drop{margin:8px;padding:12px 10px;border:1.5px dashed var(--mac-line);border-radius:8px;text-align:center;font-size:12px;color:var(--text-muted);cursor:pointer;transition:all .15s;flex:none}
.fp-drop i{font-size:18px;display:block;margin-bottom:4px;color:var(--text-secondary)}
.fp-drop:hover,.fp-drop.dragover{border-color:var(--mac-accent);background:rgba(63,185,80,.06);color:var(--text-primary)}
.fp-drop.dragover i{color:var(--mac-accent)}
.fp-empty{padding:30px 12px;text-align:center;color:var(--text-muted);font-size:12.5px}
.fp-empty i{font-size:34px;display:block;margin-bottom:8px;opacity:.4}
.monitor{border-top:1px solid var(--mac-line);background:var(--mac-card);display:flex;flex-direction:column;overflow:hidden;transition:max-height .25s ease;max-height:0}
.monitor.connected{max-height:34px}
.monitor.connected.open{max-height:360px}
.monitor .mon-head{display:flex;align-items:center;gap:8px;padding:7px 14px;font-size:11px;color:var(--text-muted);cursor:pointer;user-select:none;flex:none}
.monitor .mon-head .title{font-weight:700;letter-spacing:.4px;color:var(--text-secondary);display:flex;align-items:center;gap:6px}
.monitor .mon-head .title .pulse{width:8px;height:8px;border-radius:50%;background:var(--mac-accent);animation:fpulse 1.4s infinite}
@keyframes fpulse{0%,100%{opacity:1}50%{opacity:.35}}
.monitor .mon-head .live{color:var(--mac-accent);font-size:10px}
.monitor .mon-head .summary{display:none;align-items:center;gap:16px;margin-left:8px;font-size:12px;color:var(--text-secondary);font-family:monospace;overflow:hidden;white-space:nowrap;min-width:0;flex:1}
.monitor:not(.open) .mon-head .summary{display:flex}
.monitor .mon-head .summary .si{display:inline-flex;align-items:center;gap:4px}
.monitor .mon-head .summary .si b{color:var(--text-primary);font-weight:600}
.monitor .mon-head .uptime{margin-left:auto}
.monitor .mon-head .host{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mon-grid{display:none;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;padding:4px 16px 14px;overflow-y:auto;min-height:0;flex:1}
.monitor.open .mon-grid{display:grid}
.mon-card{background:var(--mac-field);border:1px solid var(--mac-line);border-radius:10px;padding:10px 12px}
.mon-card .mc-title{font-size:10.5px;font-weight:700;letter-spacing:.5px;color:var(--text-muted);text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:5px}
.mc-row{display:flex;align-items:center;gap:8px;font-size:12px;margin-bottom:5px}
.mc-row .mc-name{width:38px;font-size:10.5px;color:var(--text-secondary);flex:none}
.mc-row .mc-val{width:64px;text-align:right;font-size:11px;font-family:monospace;flex:none}
.bar{flex:1;height:6px;background:var(--mac-field);border-radius:4px;overflow:hidden;min-width:40px}
.bar>i{display:block;height:100%;border-radius:4px;transition:width .5s ease}
.bar.cpu>i{background:linear-gradient(90deg,#3fb950,#2ea043)}
.bar.mem>i{background:linear-gradient(90deg,#58a6ff,#1f6feb)}
.bar.dsk>i{background:linear-gradient(90deg,#d29922,#9e6a03)}
.core-bar{flex:1;display:flex;gap:2px;height:8px}
.core-bar i{flex:1;background:var(--mac-field);border-radius:2px;overflow:hidden;position:relative}
.core-bar i::after{content:'';position:absolute;left:0;top:0;bottom:0;width:var(--pct,0%);background:linear-gradient(90deg,#3fb950,#2ea043);border-radius:2px;transition:width .5s}
.mc-list{font-size:11.5px;line-height:1.7}
.mc-list .lp{display:flex;gap:6px;align-items:center}
.mc-list .lp .pn{width:8px;height:8px;border-radius:50%;flex:none}
.mc-list .lp b{font-family:monospace;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mc-list .lp .pv{margin-left:auto;font-family:monospace;color:var(--text-muted);flex:none}
.mc-list .lp .pv.hot{color:var(--err)}
.mctx{font-size:11.5px;color:var(--text-secondary);line-height:1.7}
.mctx .k{color:var(--text-muted);margin-right:6px}
.status-bar{display:flex;align-items:center;justify-content:space-between;padding:6px 14px;font-size:11px;color:var(--text-muted);background:var(--mac-card);border:1px solid var(--mac-line);border-radius:8px;margin-top:8px}
.status-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}
.status-dot.idle{background:#6b7280}
.status-dot.connecting{background:#f59e0b;animation:pulse 1s infinite}
.status-dot.connected{background:#34d399}
.status-dot.error{background:#ef4444}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.welcome-message{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px;gap:8px}
.welcome-message i{font-size:48px;opacity:.3}

/* ===== 主题切换动画（View Transitions）=====
   默认交叉淡化在渐变背景上会短暂发白，定制为：旧视图淡出 + 新视图淡入(轻缩放)，无白闪 */
::view-transition-old(root){animation:vtFadeOut .3s ease}
::view-transition-new(root){animation:vtFadeIn .3s ease}
@keyframes vtFadeOut{from{opacity:1}to{opacity:0}}
@keyframes vtFadeIn{from{opacity:0;transform:scale(.985)}to{opacity:1;transform:none}}

/* ===== DocHub 知识文档与 API 调试台 ===== */
#tab-docs .dashboard-header {
  flex: none;
  margin-bottom: 10px;
}
#tab-docs .dashboard-header h1 {
  font-size: 1.35rem;
  margin-bottom: 2px;
}
#tab-docs .dashboard-header p {
  font-size: 0.85rem;
  margin-bottom: 0;
}
.dochub-wrap {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 14px;
  height: 100%;
  overflow: hidden;
}
.dochub-sidebar {
  width: 300px;
  flex: none;
  display: flex;
  flex-direction: column;
  background: var(--mac-card);
  border: 1px solid var(--mac-line);
  border-radius: var(--mac-radius);
  padding: 12px;
  box-shadow: var(--mac-shadow);
  height: 100%;
  box-sizing: border-box;
}
.dochub-tree {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 4px;
}
.dochub-cat {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dochub-cat-title {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: .5px;
  padding: 4px 8px;
  margin-bottom: 2px;
}
.dochub-item {
  display: flex;
  flex-direction: column;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--mac-field);
  border: 1px solid transparent;
  cursor: pointer;
  transition: all .15s ease;
}
.dochub-item:hover {
  background: var(--mac-card);
  border-color: var(--mac-line);
}
.dochub-item.active {
  background: rgba(88,166,255,.12);
  border-color: var(--mac-accent);
}
.dochub-item-header {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}
.dochub-item-desc {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 3px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dochub-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--mac-card);
  border: 1px solid var(--mac-line);
  border-radius: var(--mac-radius);
  padding: 20px;
  box-shadow: var(--mac-shadow);
  overflow-y: auto;
}
.doc-view-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.doc-view-header {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--mac-line);
}
.api-debug-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.api-desc-box {
  background: var(--mac-field);
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--mac-line);
}
.api-param-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}
.api-param-row:last-child {
  margin-bottom: 0;
}
.type-tag.get { background: rgba(88,166,255,.2); color: #58a6ff; }
.type-tag.post { background: rgba(63,185,80,.2); color: #3fb950; }

/* 接口作用与设计意图框 */
.api-purpose-box {
  background: var(--mac-card);
  border: 1px dashed var(--mac-line);
  border-radius: 6px;
  padding: 8px 12px;
  margin-top: 8px;
}
.api-purpose-tag {
  font-size: 11px;
  font-weight: 700;
  color: var(--mac-accent);
  display: block;
  margin-bottom: 3px;
  text-transform: uppercase;
  letter-spacing: .4px;
}
.api-purpose-text {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
}

/* 响应 Tab 栏 */
.api-resp-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
}
.api-resp-tab {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text-muted);
  cursor: pointer;
  transition: all .15s ease;
}
.api-resp-tab:hover {
  background: var(--mac-field);
  color: var(--text-primary);
}
.api-resp-tab.active {
  background: var(--mac-card);
  border-color: var(--mac-line);
  color: var(--mac-accent);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0,0,0,.08);
}

/* ============================================================
   最高特异性覆盖：卡片级动态物理呼吸灯 (终极层叠修复)
   ============================================================ */

/* 卡片定位容器基础 */
body .cards .svc-card {
  position: relative !important;
  overflow: visible !important; /* 允许呼吸光晕与小圆点溢出扩散 */
}

/* RUNNING 运行中卡片：极光绿呼吸脉冲 (覆盖应用管理 .is-running 与服务管理 .running) */
body .cards .svc-card.is-running,
body .cards .svc-card.running {
  border-color: rgba(46, 160, 67, 0.45) !important;
  animation: cardRunningBreathUltra 2.8s infinite ease-in-out !important;
}

/* FATAL / BACKOFF / ERROR 异常报错卡片：警示红微光呼吸 */
body .cards .svc-card.fatal,
body .cards .svc-card.backoff,
body .cards .svc-card.error {
  border-color: rgba(248, 81, 73, 0.55) !important;
  animation: cardFatalBreathUltra 2.0s infinite ease-in-out !important;
}

/* 运行中卡片物理 LED 发光灯珠 / 头部状态圆点 */
body .cards .svc-card .card-led-indicator,
body .cards .svc-card .svc-dot {
  border-radius: 50%;
  flex-shrink: 0;
}

body .cards .svc-card .card-led-indicator {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 10px;
  height: 10px;
  z-index: 10;
  pointer-events: none;
}

body .cards .svc-card.is-running .card-led-indicator,
body .cards .svc-card.running .svc-dot {
  background: #39d353 !important;
  box-shadow: 0 0 8px #39d353, 0 0 16px rgba(57, 211, 83, 0.8) !important;
  animation: ledPulseGreenUltra 2.0s infinite cubic-bezier(0.4, 0, 0.6, 1) !important;
}

body .cards .svc-card.fatal .svc-dot,
body .cards .svc-card.backoff .svc-dot,
body .cards .svc-card.error .svc-dot {
  background: #f85149 !important;
  box-shadow: 0 0 8px #f85149, 0 0 16px rgba(248, 81, 73, 0.8) !important;
  animation: ledPulseRedUltra 1.6s infinite cubic-bezier(0.4, 0, 0.6, 1) !important;
}

body .cards .svc-card.is-stopped .card-led-indicator,
body .cards .svc-card.stopped .svc-dot {
  background: #d29922 !important;
  box-shadow: 0 0 6px rgba(210, 153, 34, 0.6) !important;
}

body .cards .svc-card.is-stopped,
body .cards .svc-card.stopped {
  border-color: rgba(210, 153, 34, 0.35) !important;
  box-shadow: 0 0 12px rgba(210, 153, 34, 0.12), 0 4px 16px rgba(0, 0, 0, 0.25) !important;
}

/* 极光微光外轮廓扩散动画 (以 !important 彻底击穿全局覆盖) */
@keyframes cardRunningBreathUltra {
  0%, 100% {
    border-color: rgba(46, 160, 67, 0.3) !important;
    box-shadow: 0 0 8px rgba(46, 160, 67, 0.12), 0 0 0 1px rgba(46, 160, 67, 0.18), 0 8px 24px rgba(0, 0, 0, 0.3) !important;
  }
  50% {
    border-color: rgba(63, 185, 80, 0.85) !important;
    box-shadow: 0 0 24px rgba(46, 160, 67, 0.4), 0 0 0 1.5px rgba(63, 185, 80, 0.6), 0 8px 30px rgba(0, 0, 0, 0.4) !important;
  }
}

/* 异常状态红色警示呼吸 */
@keyframes cardFatalBreathUltra {
  0%, 100% {
    border-color: rgba(248, 81, 73, 0.35) !important;
    box-shadow: 0 0 10px rgba(248, 81, 73, 0.15), 0 0 0 1px rgba(248, 81, 73, 0.2), 0 8px 24px rgba(0, 0, 0, 0.3) !important;
  }
  50% {
    border-color: rgba(248, 81, 73, 0.9) !important;
    box-shadow: 0 0 26px rgba(248, 81, 73, 0.45), 0 0 0 1.5px rgba(248, 81, 73, 0.7), 0 8px 30px rgba(0, 0, 0, 0.4) !important;
  }
}

/* LED 灯珠物理膨胀与强光晕扩散 */
@keyframes ledPulseGreenUltra {
  0%, 100% {
    transform: scale(1);
    opacity: 0.8;
    box-shadow: 0 0 6px #39d353, 0 0 10px rgba(57, 211, 83, 0.5) !important;
  }
  50% {
    transform: scale(1.4);
    opacity: 1;
    box-shadow: 0 0 14px #56d364, 0 0 24px rgba(86, 211, 100, 0.95) !important;
  }
}

/* 红色警示灯珠膨胀与光晕 */
@keyframes ledPulseRedUltra {
  0%, 100% {
    transform: scale(1);
    opacity: 0.85;
    box-shadow: 0 0 6px #f85149, 0 0 12px rgba(248, 81, 73, 0.6) !important;
  }
  50% {
    transform: scale(1.45);
    opacity: 1;
    box-shadow: 0 0 16px #ff7b72, 0 0 28px rgba(255, 123, 114, 0.95) !important;
  }
}

/* 错位律动延迟（支持 running 与 is-running） */
body .cards .svc-card.is-running:nth-child(2n), body .cards .svc-card.running:nth-child(2n) { animation-delay: -0.7s !important; }
body .cards .svc-card.is-running:nth-child(3n), body .cards .svc-card.running:nth-child(3n) { animation-delay: -1.4s !important; }
body .cards .svc-card.is-running:nth-child(4n), body .cards .svc-card.running:nth-child(4n) { animation-delay: -2.1s !important; }
body .cards .svc-card.is-running:nth-child(2n) .card-led-indicator, body .cards .svc-card.running:nth-child(2n) .svc-dot { animation-delay: -0.5s !important; }
body .cards .svc-card.is-running:nth-child(3n) .card-led-indicator, body .cards .svc-card.running:nth-child(3n) .svc-dot { animation-delay: -1.0s !important; }
body .cards .svc-card.is-running:nth-child(4n) .card-led-indicator, body .cards .svc-card.running:nth-child(4n) .svc-dot { animation-delay: -1.5s !important; }



/* ── select 深色适配（20260915）──
 * 背景 1：Bootstrap 给所有 select 加了 appearance:none（去原生外观），
 *   但**只有 .form-select 会补下拉箭头** —— 用 .form-control 的 select
 *   会渲染成"没有箭头的空盒子"，看起来像文本框（用户报告"颜色背景不对"）。
 *   修法：统一用 .form-select（见 index.html 6 处）。
 * 背景 2：Bootstrap 的箭头是**深色 SVG**（stroke='#343a40'），
 *   在深色主题下几乎看不见 → 用浅色箭头覆盖，保证可辨识。
 * 用 background-image 覆盖而非改 vendor（vendor 不手改，升级会被覆盖）。
 */
.form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23b9c0cc' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") !important;
  background-repeat: no-repeat !important;
  background-position: right .75rem center !important;
  background-size: 14px 11px !important;
  padding-right: 2.1rem !important;   /* 给箭头留位，避免文字压住 */
}
/* 右上角小尺寸 select（如 topCount/rlStatus）箭头也要贴边不留大白 */
.form-select.form-control-sm {
  background-size: 12px 9px !important;
  padding-right: 1.7rem !important;
}

/* ── 自绘下拉 .psel（20260915）──
 * 存在理由：原生 <select> 的展开列表在 Chromium 下**不受 CSS 控制** ——
 *   <option> 的 background 常被忽略、文字色取系统色，于是深色主题里
 *   出现「深底深字 / 浅底浅字」看不清，且各平台表现不一（无解，只能自绘）。
 * 设计：DOM 结构 = button.psel-btn（触发器）+ div.psel-pop（列表）。
 *   配色一律走语义变量（--mac-card/--mac-field/--text-primary/--mac-line），
 *   不在本文件散写十六进制，跟随 [data-bs-theme] 自动明暗。
 * 渐进增强：无 JS（.psel 为空、select 未被 hidden 或由 JS 显式接管）时，
 *   下面的 .psel:empty{display:none} + select:not([hidden]) 保证原生 select 可见可用。
 */
/* 自绘下拉容器。
 * 宽度策略：容器本身**不设 width:100%**，而是跟随其所在布局。
 *   · 块级上下文（表单字段）→ 自然撑满，与原来的 select 一致
 *   · flex 行内（工具条/表格单元）→ 由内联 width 决定，不会被强行拉满
 * 触发器才设 width:100%，保证填满容器。 */
.psel { position: relative; }

/* JS 接管后 select 被标 hidden；若 JS 没跑，空 .psel 不占位，select 照常显示。
 * 这是**渐进增强的兜底**：没 JS 时原生 select 仍可见可用（虽然配色不可控），
 * 功能不丢 —— 比"整块控件消失"好得多。 */
.psel:empty { display: none; }

.psel-btn {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; min-height: 38px; padding: 0 12px;
  font-size: 14px; line-height: 1.5; text-align: left;
  border: 1px solid var(--mac-line); border-radius: 8px;
  background-color: var(--mac-field); color: var(--text-primary);
  cursor: pointer; transition: border-color .15s, box-shadow .15s;
}
.psel-btn:hover { border-color: var(--mac-accent); }
/* 小尺寸档：对应原生 select 的 form-control-sm/form-select-sm。
 * 没有它，本该紧凑的控件（表格行内、工具条里）会突然变成 38px 高，
 * 把同行元素挤走 —— 属"改了结构没跟上样式"那类静默视觉回归。 */
.psel-btn.psel-sm {
  min-height: 28px; padding: 0 8px; font-size: 12px; border-radius: 7px; gap: 4px;
}
.psel-btn.psel-sm .psel-arrow {
  border-left-width: 3.5px; border-right-width: 3.5px; border-top-width: 4.5px;
}
/* 键盘焦点与 hover 必须可区分（焦点环 vs 底色加深），见 dev-principles §2.3 */
.psel-btn:focus-visible {
  outline: none; border-color: var(--mac-accent);
  box-shadow: 0 0 0 .25rem rgba(10,132,255,.2);
}
.psel-btn[disabled] { opacity: .55; cursor: not-allowed; }
.psel-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.psel-label.is-placeholder { color: var(--text-muted); }
/* 箭头用 CSS 边框画，不依赖背景图 —— 免去 vendor SVG 深色不可见那类坑 */
.psel-arrow {
  flex: none; width: 0; height: 0; margin-left: 2px;
  border-left: 4.5px solid transparent; border-right: 4.5px solid transparent;
  border-top: 5.5px solid var(--text-secondary);
  transition: transform .15s;
}
.psel.open .psel-arrow { transform: rotate(180deg); }

/* 列表：唯一被允许有阴影的“悬浮”元素之一（模态/Toast/下拉） */
.psel-pop {
  position: absolute; z-index: 60; top: calc(100% + 4px); left: 0;
  /* 宽度：**至少与触发器同宽，但可以更宽**（min-width 而非 left/right:0 锁死）。
   * 踩过的坑：首版写 `left:0; right:0` 把列表**锁死在触发器宽度**上 ——
   * 触发器窄（如排序框 121px）时选项文字全被截成「状态...」，看起来像内容缺失。
   * 下拉列表按内容撑开是用户预期（原生 select 也是这个行为）。 */
  min-width: 100%; width: max-content; max-width: min(420px, 90vw);
  max-height: 280px; overflow-y: auto; overflow-x: hidden; padding: 4px;
  border: 1px solid var(--mac-line); border-radius: 10px;
  /* 用不透明底色：毛玻璃在长列表上会透出后面的文字，反而更难读 */
  background: var(--bg-card, #1a1d2e); color: var(--text-primary);
  box-shadow: 0 10px 30px rgba(0,0,0,.35);
  /* 靠右对齐的容器（如页面右侧工具条）里，列表向右溢出视口 →
   * 用 max-width 限宽 + 下面这条兜住极窄场景 */
  box-sizing: border-box;
}
.psel-pop[hidden] { display: none; }
/* 边缘翻转：贴近视口右侧时改为右对齐，防止列表超出屏幕被切掉。
 * 用类名而非内联样式，保证宽度/对齐**都由 CSS 决定**（单一真源）；
 * 内联会覆盖样式表，从而掩盖"CSS 宽度写错"这类缺陷（实测踩中过）。 */
.psel-pop--flip { left: auto; right: 0; }

.psel-opt {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 10px; border-radius: 7px;
  font-size: 13.5px; color: var(--text-primary); cursor: pointer;
  /* 显式给不透明底：否则背景色为 transparent，探针（与用户）看到的是
     祖先 .psel-pop 的底色 —— 能对但脆弱，一旦列表底色变半透明就会静默失效 */
  background: var(--bg-card, #1a1d2e);
  /* ⚠️ 这里**不能**写 overflow:hidden + text-overflow:ellipsis：
     列表宽度是 `width:max-content`（按内容撑开），而 max-content 要拿
     子元素的**固有宽度**去算 —— 一旦这里把溢出裁掉，固有宽度就被
     自己限制成了「当前可用宽度」，形成**自锁**：越窄越算不宽，
     永远撑不开。实测 rlStatus（触发器 90px）的「全部状态」被截成
     「全部...」，而宽触发器（522px）看不出问题，故长期潜伏。
     截断交给最内层的 .psel-opt-name 处理（那里 min-width:0 + ellipsis），
     只在真的到了 max-width 上限时才生效。 */
  min-width: 0;
}
/* 键盘高亮（.active）与鼠标高亮（:hover）同底，避免两个"当前项"打架。
 *
 * ⚠️ 高亮底色是**唯一需要显式分主题**的地方（实测被探针咬出两次）：
 *   · 用 rgba(10,132,255,.16) 叠色 → 深色下合成出偏亮的蓝，白字仅 3.3:1；
 *   · 改用强调色 --mac-accent(#0a84ff) 实底 + 白字 → 也只有 3.65:1。
 *   两者都**低于 WCAG AA 正文 4.5:1**，意味着"被高亮的那一项反而最难读"。
 *   根因：--mac-accent 是为**浅底上的蓝字**选的（深色底上做前景色），
 *   拿它当**深色文字的背景**时对比度天然不够。
 * ⇒ 高亮态单独定义 token，按"底色深浅"选文字色：
 *   深色主题用更深的蓝配白字（#0b5ed7 → 5.84:1）；
 *   浅色主题用浅蓝配深字（#dbeafe → 13.79:1）。
 */
:root, [data-bs-theme="light"] { --psel-hl-bg: #dbeafe; --psel-hl-fg: #1d1d1f; }
[data-bs-theme="dark"] { --psel-hl-bg: #0b5ed7; --psel-hl-fg: #ffffff; }

.psel-opt:hover, .psel-opt.active { background: var(--psel-hl-bg); color: var(--psel-hl-fg); }
.psel-opt:hover .psel-meta, .psel-opt.active .psel-meta {
  color: var(--psel-hl-fg); opacity: .8;
}
.psel-opt[aria-selected="true"] { font-weight: 600; }
/* ⚠️ 勾号必须**每行都预留位置**，否则选中项比其它项少一截文字宽度
 *   —— 实测：「⚡ 状态与活跃度」被截成「状态...」而其余项完整显示，
 *      看起来像"选项名不一样"，实为布局抖动。
 *   用 ::after 固定占位（内容为空但同宽），比"只在选中项加"稳。 */
.psel-opt::after {
  content: ""; flex: none; width: 12px; text-align: right;
  color: var(--mac-accent); font-size: 12px;
}
.psel-opt[aria-selected="true"]::after { content: "✓"; }
/* 已选中项被高亮时，勾要跟着换成前景色，否则蓝底蓝勾看不见 */
.psel-opt[aria-selected="true"]:hover::after,
.psel-opt[aria-selected="true"].active::after { color: var(--psel-hl-fg); }
.psel-empty { padding: 10px; color: var(--text-muted); font-size: 13px; }

.psel-opt-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.psel-meta { flex: none; color: var(--text-muted); font-size: 12px; }

/* ===== 配置：生效方式徽章 / 密码掩码 / 重启提示条（v0.45.0）=====
   设计意图：把"改了会不会立即生效"这件**用户看不见的实现事实**变成可见标签，
   避免"改完没生效 → 以为功能坏了"的误判。语义色遵循项目既有的
   --ok（绿=无风险）/ --warn（黄橙=需注意）约定。 */
.cfg-badge{display:inline-flex;align-items:center;font-size:10px;font-weight:700;letter-spacing:.3px;
  padding:1px 6px;border-radius:5px;margin-left:6px;vertical-align:middle;white-space:nowrap;flex:none}
.cfg-badge.hot{background:color-mix(in srgb,var(--ok) 16%,transparent);color:var(--ok)}
.cfg-badge.restart{background:color-mix(in srgb,var(--warn) 20%,transparent);color:var(--warn);cursor:help}

/* 密码掩码态：只显示圆点 + 修改按钮，**DOM 里根本不存在真实密码** */
.cfg-pwd-wrap{display:flex;align-items:center;gap:8px;flex:1;min-width:0}
.cfg-pwd-mask{flex:1;min-width:0;height:30px;display:flex;align-items:center;padding:3px 10px;
  border:1px solid var(--mac-line);border-radius:7px;background:var(--mac-field);
  color:var(--text-muted);font-size:12px;letter-spacing:2px;user-select:none}
.cfg-pwd-btn{flex:none;padding:3px 10px;font-size:11.5px}

/* 需重启提示条：默认隐藏，保存后按需显示 */
.cfg-restart{display:flex;align-items:flex-start;gap:12px;padding:13px 16px;margin-bottom:12px;
  background:color-mix(in srgb,var(--warn) 10%,transparent);
  border:1px solid color-mix(in srgb,var(--warn) 32%,transparent);border-radius:11px}
.cfg-restart.hidden{display:none}
.cfg-restart-icon{flex:none;font-size:19px;color:var(--warn);line-height:1.2}
.cfg-restart-body{flex:1;min-width:0;font-size:12.5px;color:var(--text-primary);line-height:1.7}
.cfg-restart-body strong{color:var(--warn);font-weight:700}
.cfg-restart-list{margin-top:5px;display:flex;flex-wrap:wrap;gap:6px}
.cfg-restart-list>span:nth-child(odd){color:var(--text-primary);font-weight:600}
.cfg-pending-path{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10.5px;color:var(--text-muted);
  background:var(--mac-field);border:1px solid var(--mac-line);border-radius:5px;padding:0 5px;margin-right:4px}
.cfg-restart-note{font-size:11.5px;color:var(--text-muted);margin-top:6px;line-height:1.6}
.cfg-restart-go{flex:none;align-self:center}

/* ===== 用户菜单 + 修改密码卡片（v0.45.1）=====
   头像可点 → 弹出菜单（改密 / 注销）。改密走独立卡片而非配置页字段：
   权限模型不同（改自己的密码不需 admin）、心智归属不同（我的账号 ≠ 平台配置）。 */
.avatar-btn{background:none;border:none;padding:0;cursor:pointer;line-height:1;border-radius:50%;
  transition:box-shadow .18s ease}
.avatar-btn:hover{box-shadow:0 0 0 3px var(--accent-light)}
.avatar-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.user-menu{position:absolute;top:52px;right:16px;z-index:1080;width:212px;padding:6px;
  background:var(--bg-card);border:1px solid var(--border-color);border-radius:12px;
  box-shadow:var(--shadow-lg);animation:pop .14s ease}
.user-menu.hidden{display:none}
.user-menu-head{padding:9px 11px 10px;border-bottom:1px solid var(--border-light);margin-bottom:5px}
.user-menu-name{font-size:13.5px;font-weight:700;color:var(--text-primary);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.user-menu-role{margin-top:5px}
.user-menu-role .status-badge{font-size:10.5px;padding:2px 9px}
.user-menu-item{display:flex;align-items:center;gap:9px;width:100%;padding:8px 11px;border:none;
  border-radius:8px;background:transparent;color:var(--text-primary);font-size:12.5px;
  cursor:pointer;text-align:left;transition:background .14s ease}
.user-menu-item:hover{background:var(--bg-card-hover)}
.user-menu-item i{font-size:13px;color:var(--text-muted);flex:none}

.pwd-modal-head{display:flex;align-items:center;justify-content:space-between;gap:10px}
.pwd-modal-head h3{margin:0;font-size:15.5px;font-weight:700;display:flex;align-items:center;
  color:var(--text-primary)}
.pwd-modal-sub{margin:0;font-size:11.5px;color:var(--text-muted);line-height:1.65;
  padding:8px 11px;background:var(--bg-card-hover);border-radius:8px;
  border-left:2px solid var(--accent)}
.pwd-modal-sub strong{color:var(--text-secondary)}
/* ── 知识库（v0.67.0）───────────────────────────────────────────────
   分类项**复用 .conf-item 的视觉语言**（同样的圆角/悬停/激活色），
   保持"配置管理"与"知识库"两页在同一次点击后观感一致。
   额外：卡片本身加左边框色条（按 level 区分严重性），
   与告警旁的卡片保持同一套视觉编码。 */
.kb-cat{display:flex;align-items:center;justify-content:space-between;padding:7px 10px;border-radius:8px;font-size:12.5px;color:var(--text-secondary);cursor:pointer;transition:background .12s;margin-bottom:2px}
.kb-cat:hover{background:var(--mac-field)}
.kb-cat.active{background:var(--mac-accent);color:#fff;font-weight:600}
.kb-cat.active .muted{color:rgba(255,255,255,.75)}
/* 知识卡：用左色条表达级别，不依赖颜色铺底（更克制、也更好读） */
.kb-card{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:var(--r-card);padding:8px 12px;margin-bottom:6px}
.kb-card>summary{list-style:none;outline:none}
.kb-card>summary::-webkit-details-marker{display:none}
.kb-card>summary::before{content:"▸";display:inline-block;margin-right:5px;transition:transform .15s;font-size:10px;opacity:.7}
.kb-card[open]>summary::before{transform:rotate(90deg)}
.kb-card[open]{border-color:rgba(88,166,255,.35)}
/* 搜索框在暗色下的对比度 */
#kbSearch{background:var(--mac-field);border:1px solid var(--mac-line);color:inherit;border-radius:8px}
#kbSearch:focus{outline:none;border-color:var(--mac-accent)}

/* ══════════════════════════════════════════════════════════════════════
 * UI 块库（v0.68.0）
 *
 * 【为什么抽出来】此前各卡片直接内联 style="background:rgba(...)"，
 * 实测 **196 处 inline style**、其中同类背景色重复 30 次。
 * 改一个配色要改几十处 —— 故全部收敛到此处，新增卡片类型不必碰样式。
 *
 * 【语义色约定】（与项目既有色板一致）
 *   danger 红 #f85149 | warn 黄 #d29922 | info 蓝 #58a6ff | ok 绿 #3fb950
 * ══════════════════════════════════════════════════════════════════════ */

/* 色值变量（改主题只需改这里） */
:root{
  --ui-danger:#f85149;
  --ui-warn:#d29922;
  --ui-info:#58a6ff;
  --ui-ok:#3fb950;
  --ui-line:rgba(127,127,127,.25);
  --ui-soft:rgba(127,127,127,.08);
  --ui-soft-strong:rgba(127,127,127,.14);
}

/* ── 块：标题 ── */
.ui-h{margin:0 0 4px;font-size:12.5px;font-weight:600;letter-spacing:.2px}

/* ── 块：键值对 ── */
.ui-kv{display:flex;flex-direction:column;gap:2px}
.ui-kv-row{display:flex;gap:8px;font-size:12px;line-height:1.6}
.ui-kv-k{color:var(--text-muted);flex:none;min-width:64px}
.ui-kv-v{flex:1;min-width:0}

/* ── 块：提示条（左侧色条，比铺底色更克制）── */
.ui-alert{padding:8px 10px;border-radius:6px;border-left:3px solid var(--ui-info);background:rgba(88,166,255,.08);font-size:12px;line-height:1.65;margin:8px 0}
.ui-alert-title{font-weight:600;margin-bottom:2px}
.ui-alert-danger{border-left-color:var(--ui-danger);background:rgba(248,81,73,.1)}
.ui-alert-warn{border-left-color:var(--ui-warn);background:rgba(210,153,34,.12)}
.ui-alert-info{border-left-color:var(--ui-info);background:rgba(88,166,255,.08)}
.ui-alert-ok{border-left-color:var(--ui-ok);background:rgba(63,185,80,.08)}

/* ── 块：列表 ── */
.ui-list{margin:6px 0;padding-left:20px;font-size:12px;line-height:1.7}
.ui-list-danger li::marker{color:var(--ui-danger)}
.ui-list-warn li::marker{color:var(--ui-warn)}

/* ── 块：代码 ── */
.ui-code{background:rgba(0,0,0,.25);padding:10px 12px;border-radius:6px;overflow:auto;font-size:11.5px;line-height:1.55;margin:6px 0}
.ui-code-wrap code{white-space:pre-wrap;word-break:break-word}

/* ── 块：表格 ── */
.ui-table{width:100%;border-collapse:collapse;font-size:12px;margin:8px 0}
.ui-table th{text-align:left;padding:6px 8px;border-bottom:1px solid var(--ui-line);color:var(--text-muted);font-weight:600}
.ui-table td{padding:6px 8px;border-bottom:1px solid var(--ui-line)}

/* ── 容器：折叠（原生 details）── */
.ui-collapse{background:var(--mac-card);border:1px solid var(--mac-line);border-radius:var(--r-card);padding:8px 12px;margin-bottom:6px;transition:border-color .15s}
.ui-collapse[open]{border-color:rgba(88,166,255,.35)}
.ui-collapse-sum{list-style:none;outline:none;cursor:pointer;font-size:12.5px;font-weight:600;user-select:none;display:flex;align-items:center;gap:4px}
.ui-collapse-sum::-webkit-details-marker{display:none}
.ui-collapse-sum::before{content:"▸";display:inline-block;font-size:10px;opacity:.7;transition:transform .15s}
.ui-collapse[open]>.ui-collapse-sum::before{transform:rotate(90deg)}
.ui-collapse-body{padding:8px 0 2px 14px;font-size:12px;line-height:1.7}
.ui-collapse-hint{margin-left:auto;font-weight:400;font-size:11px;color:var(--text-muted)}
/* 级别色：只染标题文字，不铺底（克制） */
.ui-tone-danger>.ui-collapse-sum{color:var(--ui-danger)}
.ui-tone-warn>.ui-collapse-sum{color:var(--ui-warn)}
.ui-tone-info>.ui-collapse-sum{color:var(--ui-info)}
.ui-tone-ok>.ui-collapse-sum{color:var(--ui-ok)}

/* ── 容器：卡片 ── */
.ui-card{background:var(--mac-card);border:1px solid var(--mac-line);border-left:3px solid var(--ui-info);border-radius:10px;padding:10px 14px;margin:8px 0}
.ui-card.ui-tone-danger{border-left-color:var(--ui-danger)}
.ui-card.ui-tone-warn{border-left-color:var(--ui-warn)}
.ui-card.ui-tone-ok{border-left-color:var(--ui-ok)}
.ui-card-title{font-size:13px;font-weight:600;margin-bottom:6px}
.ui-card-body{font-size:12px;line-height:1.7}
.ui-card-foot{margin-top:8px;padding-top:8px;border-top:1px solid var(--ui-line);font-size:11px}

/* ── 容器：分栏 ── */
.ui-split{display:flex;gap:16px;align-items:center;flex-wrap:wrap;margin-top:12px}
.ui-split-l{flex:none}
.ui-split-r{flex:1;min-width:220px;font-size:12px}

/* ── 图例 ── */
.ui-legend-row{display:flex;align-items:center;gap:6px;padding:2px 0}
.ui-legend-dot{width:9px;height:9px;border-radius:2px;flex:none}
.ui-legend-val{margin-left:auto;color:var(--text-muted)}
.ui-legend-desc{font-size:11px;color:var(--text-muted);margin:0 0 4px 15px}

/* ── 徽章 ── */
.ui-badge{display:inline-block;padding:1px 6px;border-radius:4px;font-size:10.5px;font-weight:600;margin-left:4px}
.ui-badge-danger{background:rgba(248,81,73,.18);color:var(--ui-danger)}
.ui-badge-warn{background:rgba(210,153,34,.18);color:var(--ui-warn)}
.ui-badge-info{background:rgba(88,166,255,.18);color:var(--ui-info)}
.ui-badge-ok{background:rgba(63,185,80,.18);color:var(--ui-ok)}

/* ── 空状态 ── */
.ui-empty{padding:12px;color:var(--text-muted);font-size:12px}

/* ══════════════════════════════════════════════════════════════════════
 * Help Drawer：右侧帮助抽屉
 * 用于**长文档**（使用手册、排查步骤）—— 与就地折叠的分工见 drawer.js 注释
 * ══════════════════════════════════════════════════════════════════════ */
.hd-wrap{position:fixed;inset:0;z-index:1060;pointer-events:none}
.hd-mask{position:absolute;inset:0;background:rgba(0,0,0,.45);opacity:0;transition:opacity .2s;pointer-events:none}
.hd-panel{position:absolute;top:0;right:0;height:100%;width:min(520px,92vw);
  background:var(--mac-bg, #161b22);border-left:1px solid var(--mac-line);
  box-shadow:-8px 0 32px rgba(0,0,0,.35);display:flex;flex-direction:column;
  transform:translateX(100%);transition:transform .24s cubic-bezier(.4,0,.2,1)}
.hd-wrap.hd-open{pointer-events:auto}
.hd-wrap.hd-open .hd-mask{opacity:1;pointer-events:auto}
.hd-wrap.hd-open .hd-panel{transform:translateX(0)}
.hd-head{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--mac-line);flex:none}
.hd-title{font-size:14px;font-weight:600;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hd-actions{display:flex;gap:6px;flex:none}
.hd-body{padding:14px 18px 24px;overflow-y:auto;font-size:12.5px;line-height:1.75}
/* 抽屉里的 md 内容排版（h1/h2 由后端 RenderMarkdown 产出） */
.hd-body h1{font-size:16px;margin:4px 0 10px}
.hd-body h2{font-size:14px;margin:16px 0 6px;padding-bottom:4px;border-bottom:1px solid var(--ui-line)}
.hd-body h3{font-size:13px;margin:12px 0 4px}
.hd-body ul,.hd-body ol{padding-left:22px}
/* 打开时锁背景滚动（防"滚到主页面"的错位感） */
body.hd-lock{overflow:hidden}
/* 移动端：占满屏 */
@media (max-width:640px){
  .hd-panel{width:100vw;border-left:none}
}

/* ════════════════════════════════════════════════════════════════════════════
   「升级」页布局（20260929 新增，遵循 ptrelskill cross/dev-principles §二十四）
   —— 信息架构：主流程列 + 旁注列；解释文字降级；因果链合成一个视觉块。
   ⚠️ 本段**新增类**（upg-*）而非改既有类 —— 避免波及同页其它 tab。
   ════════════════════════════════════════════════════════════════════════════ */

/* 容器：主列自适应 + 旁注列定宽。
   为什么用 minmax(0,1fr) 而不是 1fr：1fr 的最小尺寸是 auto，
   长内容（如 81 项下拉的选中项名、长路径代码块）会把列**撑破**，
   反而溢出。minmax(0,1fr) 把最小宽度钉到 0，由内容自己换行/省略。
   （§二十四 §四「空间分配匹配信息价值」：主流程拿宽，解释文字只要够读。） */
.upg-layout{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(260px,320px);
  gap:16px;
  align-items:start;
}
.upg-main{min-width:0}
.upg-aside{min-width:0;display:flex;flex-direction:column;gap:16px}

/* 旁注卡片：视觉上**弱一档**（不是并列的第二主列）。
   做法只用「更弱的文字色 + 更小字号」，不降对比到读不清 —— §2.1 纪律 3
   「状态色不弱化」同理：此处弱化的是**层级**，不是可读性。 */
.upg-aside-card{padding:14px 16px}
.upg-aside-title{
  font-size:13px;font-weight:600;color:var(--text-primary);
  display:flex;align-items:center;gap:7px;margin:0 0 8px;
}
.upg-aside-title i{color:var(--accent);font-size:14px}
.upg-aside-lead{margin:0 0 10px;font-size:12.5px;line-height:1.7}
.upg-facts{margin:0;padding-left:18px;font-size:12.5px;line-height:1.75;color:var(--text-secondary)}
.upg-facts li{margin-bottom:5px}
.upg-facts code,.upg-aside-note code{font-size:11.5px}
.upg-aside-note{margin:10px 0 0;font-size:12px;line-height:1.7}
.upg-tpl-row{display:flex;gap:6px;flex-wrap:wrap}

/* 识别结果落脚点（#upgradeHint）：**完整可见，只是不再挤占主流程的纵向空间**。
   ⚠️ 这里踩过两次坑，两版都被实测否掉，原因必须留着：
     ① 初版写成「单行 + text-overflow:ellipsis」→ 后半截（目标载体路径那段）
        **被静默截掉且没有任何展开入口**，用户永远读不到；
     ② 第二版改成「-webkit-line-clamp:3」（3 行裁切）→ 实测在 postsup 自身
        这个最坏用例下（部署类型 + 10 条 hints，scrollHeight 126px）**依然截断**，
        末尾只剩「…可回滚…」半句。两者都是**以"降级"为名丢信息**，
        比原先挤一点更糟 —— 用户要的是「看得到但不挡路」，不是「看不到」。
   ⇒ 正解是**把它交给折叠**（§二十四 §一：长文本用 details 折叠块）。
      故 #upgradeHint **改由 details 包裹**（见 index.html .upg-hint-fold），
      摘要行始终显示识别结论，展开后可读全文。
      本类只负责"摘要态"的排版，不再做任何裁切。 */
.upg-hint{
  font-size:12px;
  margin-top:6px;
  min-height:18px;            /* 预留一行，防"选包前后跳一下"的布局抖动 */
  line-height:1.6;
  overflow-wrap:anywhere;     /* 长路径/长 URL 必须能断行，否则横向溢出 = 多一个滚动轴 */
}

/* ⭐ 因果块（§二十四 §五）：密码 + 获取凭证 + 上传 = 同一视觉块。
   为什么要画这个框：这三步是同一件动作的三个环节，原先散在两个 .field 里、
   中间还夹着别的行 → 用户看不出「为什么要填密码」「什么时候点获取凭证」。
   用 1px 描边 + 左侧强调条表达"这是一件事"（§2.1：层级靠描边，不靠阴影）；
   **不加 box-shadow** —— 它不是悬浮元素。 */
.upg-causal{
  border:1px solid var(--mac-line);
  border-left:3px solid var(--accent);
  border-radius:12px;
  background:var(--mac-card);
  padding:12px 14px 14px;
}
.upg-causal-cap{display:flex;gap:9px;align-items:flex-start;margin-bottom:12px}
.upg-causal-cap > i{color:var(--accent);font-size:15px;line-height:1.5;flex:none}
.upg-causal-cap strong{font-size:13px;font-weight:600;color:var(--text-primary)}
.upg-causal-cap p{margin:3px 0 0;font-size:12.5px;line-height:1.7}
.upg-causal-body{display:flex;flex-direction:column;gap:8px}
.upg-causal-label{font-size:12.5px;color:var(--text-secondary);font-weight:500}
.upg-causal-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
/* 主动作（Fitts 定律）：占满因果块宽度 —— 目标面越大越难点偏。
   ⚠️ 这是在既有 .btn.primary 上加尺寸，不改其配色（§2.1 纪律 1「功能按钮一律实心色」）。 */
.upg-submit{width:100%;margin-top:2px;height:40px;font-size:14px}

/* 回落单列：1100px 以下两栏会各自过窄（主列写不下说明、旁注列挤成一条），
   此时**旁注回到主列下方**（DOM 顺序已是 main → aside，无需改结构）。
   判据取自基线实测：1440 视口下内容区 1190px，减 320 旁注 + 16 gap 后
   主列仍有 854px；到 1100 视口时主列只剩 ~700px，已接近原来的窄柱体验。 */
@media (max-width:1100px){
  .upg-layout{grid-template-columns:minmax(0,1fr)}
}

/* 折叠容器（承 #upgradeHint 与「该传什么格式」解释）。
   为什么用 details：§二十四 §一「长文本/日志默认收起」；§四「辅助明细默认折叠」。
   ⚠️ 展开后**必须限高**（§四 的关键提醒）—— 否则折叠只是延迟了"挤满屏幕"。
      这里给 26vh，配合内部滚动；因为它是**用户主动展开**的，这一处局部滚动属
      §二十四 §二 允许的例外（"必须固定区域"），且默认收起时**不产生滚动轴**。 */
.upg-hint-fold{margin-top:8px;font-size:12px}
.upg-hint-fold > summary{
  cursor:pointer;color:var(--text-secondary);font-size:12px;
  padding:3px 0;user-select:none;list-style:none;
  display:flex;align-items:center;gap:5px;
}
.upg-hint-fold > summary::-webkit-details-marker{display:none}
.upg-hint-fold > summary::before{
  content:"▸";display:inline-block;transition:transform .15s ease;
  color:var(--text-muted);font-size:10px;
}
.upg-hint-fold[open] > summary::before{transform:rotate(90deg)}
.upg-hint-fold > summary:hover{color:var(--text-primary)}
.upg-hint-fold[open] > .upg-hint{
  max-height:26vh;overflow-y:auto;   /* 展开后限高（§四），默认收起时不产生滚动轴 */
  padding:8px 10px;margin-top:6px;
  border:1px solid var(--mac-line);border-radius:10px;
  background:var(--mac-field);
}

/* ══════════════════════════════════════════════════════════════════════
   插件页外壳（20261010）
   ══════════════════════════════════════════════════════════════════════
   ⭐ 规范 §3.3：宿主提供「页面外壳」—— 否则插件页会**贴边、与内容区不搭**。

   ⚠️ 为什么"外壳"必须由宿主提供、而不是让插件自己写：
      每个插件各写一套外边距 ⇒ 间距必然不一致（且没人能统一改）
      ⭐ 判据：**"融入"要么靠机制，要么靠可被引用的公共令牌，不要靠作者自觉**。
   ══════════════════════════════════════════════════════════════════════ */
.psup-plugin-page { max-width: 1200px; }

/* 顶部信息条：显示插件名 + 版本 —— ⚠️ 这是**必做**项，不是装饰。
   ⭐ 理由（规范 §4.4）：插件可热替换 ⇒ "当前跑的是哪个版本"必须**可判定**
     （否则用户以为改好了、其实某处还在跑旧代码 —— 与"缓存破坏"同类问题）。*/
.psup-plugin-bar {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 14px; padding: 7px 12px;
  border-radius: var(--r-menu, 8px);
  background: rgba(148,163,184,.06);
  border: 1px solid var(--mac-line, rgba(148,163,184,.14));
  font-size: 12.5px;
}
.psup-plugin-tag { font-weight: 600; color: var(--text-primary, #e6edf3); }
.psup-plugin-ver { color: var(--text-muted, #8b949e); }
.psup-plugin-bar-err {
  background: rgba(248,81,73,.12);
  border-color: rgba(248,81,73,.35);
  color: #ff7b72;
}

/* Shadow DOM 承载容器：插件自己的 HTML/CSS 被隔离在这层里。
   ⚠️ 这里**不要**给插件内容设样式 —— 那是插件的自由（规范 §1.3 的"自设计"档）。*/
.psup-plugin-shadow-host { display: block; }

/* 错误卡片：加载失败**必须可见**（不白屏、不静默）*/
.psup-plugin-err {
  padding: 14px 16px; border-radius: 12px;
  background: rgba(248,81,73,.10);
  border: 1px solid rgba(248,81,73,.32);
  color: #ff7b72; font-size: 13px; line-height: 1.7;
}
.psup-plugin-err code {
  background: rgba(255,255,255,.08); padding: 1px 6px; border-radius: 4px;
  font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px;
}
.psup-plugin-pending {
  padding: 14px 16px; border-radius: 12px;
  background: rgba(148,163,184,.08);
  border: 1px solid var(--mac-line, rgba(148,163,184,.14));
  color: var(--text-secondary, #8b949e); font-size: 13px; line-height: 1.7;
}

/* ⭐ 顶栏的插件模块：加一个小圆点作为来源标识（与宿主模块视觉区分）*/
.module-tab[data-plugin-id] { position: relative; }
.module-tab[data-plugin-id]::after {
  content: ""; position: absolute; top: 5px; right: 5px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--ok, #34d399); opacity: .85;
}

/* ══════════════════════════════════════════════════════════════════════
   声明式插件页的块样式（20261010）
   ══════════════════════════════════════════════════════════════════════
   ⭐ 这些是**宿主提供**的样式 —— 声明式插件页没有自己的 CSS，
      所以它的外观**必然**与宿主一致（这正是声明式档的价值）。
   ⚠️ 与 custom 档的区别：custom 页的样式由插件自带（跑在 Shadow DOM 里）。
   ══════════════════════════════════════════════════════════════════════ */
.pd-metrics {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px; margin: 14px 0;
}
.pd-metric {
  background: var(--bg-panel-2, rgba(148,163,184,.06));
  border: 1px solid var(--mac-line, rgba(148,163,184,.14));
  border-radius: var(--r-card, 16px);
  padding: 14px 16px; position: relative; overflow: hidden;
}
.pd-metric::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--accent-solid, #6366f1); }
.pd-metric-label { font-size: 12px; color: var(--text-muted, #8b949e); margin-bottom: 7px; }
.pd-metric-value { font-size: 28px; font-weight: 700; line-height: 1.1; letter-spacing: -.5px; }
.pd-metric-foot { font-size: 11.5px; color: var(--text-muted, #8b949e); margin-top: 7px; }
/* 语义色（tone）—— ⚠️ 用令牌而不是写死色值，这样暗色模式自动跟随 */
.pd-metric-ok::before      { background: var(--ok, #34d399); }
.pd-metric-warn::before    { background: var(--warn, #f5b544); }
.pd-metric-danger::before  { background: var(--danger, #ff5c6c); }
.pd-metric-info::before    { background: var(--accent-solid, #6366f1); }
.pd-actions { display: flex; gap: 8px; margin: 14px 0; flex-wrap: wrap; }
.ui-text { margin: 8px 0; line-height: 1.7; color: var(--text-secondary, #a9b1bd); }

/* 热重载提示按钮（20261010）—— ⚠️ 它必须**看起来像可点的东西**。
   理由：这是一个"需要用户做决定"的入口（重载 = 可能丢弃当前输入），
   若做成一个不起眼的灰字，用户不会意识到"这里有个动作"。 */
.psup-plugin-reload {
  margin-left: auto; padding: 4px 12px;
  border-radius: var(--r-pill, 999px);
  border: 1px solid rgba(245,181,68,.45);
  background: rgba(245,181,68,.14);
  color: #f5b544; font-size: 12.5px; font-weight: 600;
  cursor: pointer; transition: .15s;
}
.psup-plugin-reload:hover { background: rgba(245,181,68,.24); }
