/* ============================================================
   盾 · WAF & 服务器安全面板  —— 样式表（纯手写，无外部依赖）
   ============================================================ */

/* ---------- hidden 属性必须真的隐藏（真机踩过的大坑）----------
   HTML 的 `hidden` 属性靠浏览器默认样式 `[hidden]{display:none}` 生效，
   而**作者样式里的任何 display 都会盖掉它**。
   本文件里 `.login-overlay{display:grid}` 就这么干了，后果是：
   JS 把 `overlay.hidden = true` 设得好好的、DOM 里也确实是 hidden，
   但**视觉上它照样铺满整屏**，把后面的仪表盘整个盖住——
   用户看到的就是"登录成功了、数据也回来了，屏幕上却一直是登录框"。

   更坑的是这个 bug 骗过了所有测试：DOM 探针读的是 `el.hidden` 属性
   （恒为 true），从来没读过"它看起来是不是隐藏的"。
   所以这里用 !important 兜底，并且下面的测试改成检查 **computed display**。 */
[hidden]{display:none !important}

/* ---------- 设计变量 ---------- */
:root{
  --bg:#0b0f16;
  --bg-grad:#0d121b;
  --panel:#141b26;
  --panel-2:#182130;
  --panel-3:#1d2837;
  --border:#25313f;
  --border-soft:#1e2836;
  --text:#e7edf5;
  --text-dim:#b6c2d2;
  --muted:#8695a8;
  --accent:#3b82f6;
  --accent-2:#22d3ee;
  --accent-soft:rgba(59,130,246,.14);
  --ok:#22c55e;
  --warn:#f59e0b;
  --danger:#ef4444;
  --purple:#a855f7;
  --info:#38bdf8;
  --sev-info:#7c8da3;
  --sev-low:#38bdf8;
  --sev-medium:#f5b93b;
  --sev-high:#fb923c;
  --sev-critical:#ef4444;
  --radius:10px;
  --radius-sm:7px;
  --radius-lg:14px;
  --shadow:0 10px 30px rgba(0,0,0,.45);
  --sidebar-w:224px;
  --sidebar-w-mini:66px;
  --mono:ui-monospace,SFMono-Regular,"Cascadia Mono",Consolas,"Liberation Mono",Menlo,monospace;
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;
  background:var(--bg);
  background-image:radial-gradient(1100px 600px at 12% -8%,rgba(59,130,246,.10),transparent 60%),
                   radial-gradient(900px 500px at 100% 0%,rgba(34,211,238,.06),transparent 55%);
  background-attachment:fixed;
  color:var(--text);
  font:14px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei","PingFang SC","Hiragino Sans GB",sans-serif;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--accent);text-decoration:none}
h1,h2,h3,h4{margin:0;font-weight:600}
p{margin:0}
.muted{color:var(--muted)}
.noscript{margin:40px auto;max-width:640px;padding:18px 20px;border:1px solid var(--warn);border-radius:var(--radius);background:#2a2113;color:#ffe9c2}
code,kbd,pre{font-family:var(--mono)}
::selection{background:rgba(59,130,246,.35)}

/* 滚动条 */
*::-webkit-scrollbar{width:10px;height:10px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:#2b3646;border-radius:8px;border:2px solid transparent;background-clip:content-box}
*::-webkit-scrollbar-thumb:hover{background:#39485c;background-clip:content-box}

/* ---------- 应用骨架 ---------- */
.app{display:flex;min-height:100vh}
.sidebar{
  width:var(--sidebar-w);flex:0 0 var(--sidebar-w);
  position:sticky;top:0;height:100vh;
  display:flex;flex-direction:column;
  background:linear-gradient(180deg,#101722,#0c121b);
  border-right:1px solid var(--border-soft);
  z-index:40;
}
.brand{display:flex;align-items:center;gap:10px;padding:16px 16px 14px;border-bottom:1px solid var(--border-soft)}
.brand-logo{color:var(--accent);display:flex}
.brand-text{display:flex;flex-direction:column;line-height:1.2;overflow:hidden}
.brand-text b{font-size:17px;letter-spacing:2px}
.brand-text small{color:var(--muted);font-size:11px;white-space:nowrap}

.nav{flex:1;overflow-y:auto;padding:10px 8px;display:flex;flex-direction:column;gap:2px}
.nav-item{
  display:flex;align-items:center;gap:10px;
  padding:9px 11px;border-radius:var(--radius-sm);
  color:var(--text-dim);font-size:13.5px;white-space:nowrap;
  border-left:2px solid transparent;
  transition:background .14s,color .14s;
}
.nav-item:hover{background:var(--panel-2);color:var(--text)}
.nav-item.active{background:var(--accent-soft);color:#cfe2ff;border-left-color:var(--accent)}
.nav-item svg{flex:0 0 18px}
.sidebar-foot{padding:10px 14px 14px;border-top:1px solid var(--border-soft);color:var(--muted);font-size:11.5px;display:flex;flex-direction:column;gap:3px}
.sidebar-backdrop{position:fixed;inset:0;background:rgba(4,7,12,.6);z-index:35;border:0}

.main{flex:1;min-width:0;display:flex;flex-direction:column}

/* ---------- 顶栏 ---------- */
.topbar{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;gap:12px;
  padding:12px 18px;min-height:62px;
  background:rgba(13,18,27,.86);backdrop-filter:blur(10px);
  border-bottom:1px solid var(--border-soft);
}
.topbar-title{min-width:0;flex:1}
.topbar-title h1{font-size:17px;letter-spacing:.4px}
.topbar-title p{color:var(--muted);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.topbar-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:flex-end}
.conn-state{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted);padding:4px 8px;border:1px solid var(--border);border-radius:20px}
.conn-state .dot{width:7px;height:7px;border-radius:50%;background:var(--muted);box-shadow:0 0 0 3px rgba(134,149,168,.15)}
.conn-state.ok{color:#9ce8b7;border-color:rgba(34,197,94,.35)}
.conn-state.ok .dot{background:var(--ok);box-shadow:0 0 0 3px rgba(34,197,94,.18)}
.conn-state.bad{color:#ffb4b4;border-color:rgba(239,68,68,.35)}
.conn-state.bad .dot{background:var(--danger);box-shadow:0 0 0 3px rgba(239,68,68,.18)}
.refresh-time{font-size:12px;color:var(--muted);white-space:nowrap}
.user-box{display:flex;align-items:center;gap:8px;padding-left:10px;border-left:1px solid var(--border-soft)}
.avatar{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:var(--accent-soft);color:#bcd7ff;font-size:12px;font-weight:600}
.user-name{font-size:12.5px;color:var(--text-dim);max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.icon-btn{
  display:inline-grid;place-items:center;width:32px;height:32px;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--panel);color:var(--text-dim);cursor:pointer;
  transition:background .14s,color .14s,border-color .14s;
}
.icon-btn:hover{background:var(--panel-3);color:var(--text);border-color:#33455c}
.only-narrow{display:none}

/* 开关 */
.switch-label{display:inline-flex;align-items:center;gap:7px;cursor:pointer;user-select:none}
.switch{position:relative;display:inline-block;width:36px;height:20px;flex:0 0 36px}
.switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.switch i{position:absolute;inset:0;background:#2c3849;border-radius:20px;transition:background .18s}
.switch i::after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#93a2b5;transition:transform .18s,background .18s}
.switch input:checked + i{background:rgba(59,130,246,.55)}
.switch input:checked + i::after{transform:translateX(16px);background:#fff}
.switch input:focus-visible + i{outline:2px solid var(--accent);outline-offset:2px}
.switch input:disabled + i{opacity:.5;cursor:not-allowed}
.switch-text{font-size:12px;color:var(--muted);white-space:nowrap}

/* ---------- 内容区 ---------- */
.view{flex:1;padding:16px 18px 40px;min-width:0;outline:none}
.page{display:flex;flex-direction:column;gap:14px}
.page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;flex-wrap:wrap}
.page-head .hint{color:var(--muted);font-size:12.5px}

.card{
  background:linear-gradient(180deg,var(--panel),#121924);
  border:1px solid var(--border-soft);border-radius:var(--radius-lg);
  padding:14px 16px;min-width:0;
}
.card > header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px;flex-wrap:wrap}
.card h3{font-size:13.5px;letter-spacing:.4px;display:flex;align-items:center;gap:8px}
.card h3::before{content:"";width:3px;height:13px;border-radius:2px;background:linear-gradient(180deg,var(--accent),var(--accent-2))}

.grid{display:grid;gap:14px}
.grid-kpi{grid-template-columns:repeat(4,minmax(0,1fr))}
.grid-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid-2-1{grid-template-columns:minmax(0,1.55fr) minmax(0,1fr)}
.span-2{grid-column:span 2}

/* KPI */
.kpi{display:flex;flex-direction:column;gap:6px;position:relative;overflow:hidden}
.kpi .kpi-label{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:6px}
.kpi .kpi-value{font-size:26px;font-weight:600;letter-spacing:.5px;font-variant-numeric:tabular-nums}
.kpi .kpi-sub{font-size:12px;color:var(--muted);min-height:16px}
.kpi .kpi-ico{position:absolute;right:12px;top:12px;color:var(--accent);opacity:.5}
.kpi.tone-danger .kpi-value{color:#ff9d9d}
.kpi.tone-danger .kpi-ico{color:var(--danger)}
.kpi.tone-ok .kpi-value{color:#8ee6ab}
.kpi.tone-ok .kpi-ico{color:var(--ok)}
.kpi.tone-warn .kpi-value{color:#ffd28a}
.kpi.tone-warn .kpi-ico{color:var(--warn)}

/* 组件状态 */
.comp-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
.comp-row{display:flex;align-items:flex-start;gap:10px;padding:11px 12px;background:var(--panel-2);border:1px solid var(--border-soft);border-radius:var(--radius)}
.comp-row .state-dot{width:9px;height:9px;border-radius:50%;margin-top:6px;background:#5a6678;flex:0 0 9px}
.comp-row.run .state-dot{background:var(--ok);box-shadow:0 0 0 4px rgba(34,197,94,.15)}
.comp-row.stop .state-dot{background:#5a6678;box-shadow:0 0 0 4px rgba(90,102,120,.14)}
.comp-row .comp-name{font-size:13px;display:flex;align-items:center;gap:7px}
.comp-row .comp-meta{color:var(--muted);font-size:11.5px;word-break:break-all;margin-top:2px}

/* 徽章 */
.badge{
  display:inline-flex;align-items:center;gap:4px;
  padding:1px 7px;border-radius:20px;font-size:11.5px;line-height:18px;
  border:1px solid transparent;white-space:nowrap;
}
.badge.sev-info{background:rgba(124,141,163,.16);color:#c3cedd;border-color:rgba(124,141,163,.32)}
.badge.sev-low{background:rgba(56,189,248,.14);color:#9bdcff;border-color:rgba(56,189,248,.32)}
.badge.sev-medium{background:rgba(245,185,59,.14);color:#ffd98a;border-color:rgba(245,185,59,.32)}
.badge.sev-high{background:rgba(251,146,60,.15);color:#ffc194;border-color:rgba(251,146,60,.34)}
.badge.sev-critical{background:rgba(239,68,68,.16);color:#ffb1b1;border-color:rgba(239,68,68,.38)}
.badge.neutral{background:var(--panel-3);color:var(--text-dim);border-color:var(--border)}
.badge.ok{background:rgba(34,197,94,.14);color:#96e9b3;border-color:rgba(34,197,94,.32)}
.badge.warn{background:rgba(245,158,11,.14);color:#ffd08a;border-color:rgba(245,158,11,.32)}
.badge.danger{background:rgba(239,68,68,.15);color:#ffb1b1;border-color:rgba(239,68,68,.34)}
.badge.accent{background:var(--accent-soft);color:#bcd7ff;border-color:rgba(59,130,246,.34)}
.badge.purple{background:rgba(168,85,247,.14);color:#dcc0ff;border-color:rgba(168,85,247,.3)}
.badge.sm{font-size:11px;padding:0 6px}

/* 按钮 */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:7px 13px;border-radius:var(--radius-sm);
  border:1px solid var(--border);background:var(--panel-3);color:var(--text);
  font-size:13px;cursor:pointer;white-space:nowrap;
  transition:background .14s,border-color .14s,color .14s,opacity .14s;
}
.btn:hover{background:#26344a;border-color:#33455c}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn.primary{background:linear-gradient(180deg,#3b82f6,#2563eb);border-color:#2f6fd8;color:#fff}
.btn.primary:hover{background:linear-gradient(180deg,#4c8dfa,#2f6fd8)}
.btn.danger{background:rgba(239,68,68,.13);border-color:rgba(239,68,68,.42);color:#ffb4b4}
.btn.danger:hover{background:rgba(239,68,68,.22)}
.btn.ghost{background:transparent}
.btn.ghost:hover{background:var(--panel-3)}
.btn.sm{padding:4px 9px;font-size:12px}
.btn.block{width:100%}
.btn-row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}

/* 表单 */
.field{display:flex;flex-direction:column;gap:5px;min-width:0}
.field > span{font-size:12px;color:var(--muted)}
.field > span .req{color:var(--danger)}
input[type=text],input[type=password],input[type=number],input[type=email],input[type=datetime-local],select,textarea{
  width:100%;padding:7px 10px;border-radius:var(--radius-sm);
  border:1px solid var(--border);background:#0f1621;color:var(--text);
  font-size:13px;font-family:inherit;outline:none;
  transition:border-color .14s,box-shadow .14s;
}
textarea{resize:vertical;min-height:64px;font-family:var(--mono);font-size:12.5px;line-height:1.5}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(59,130,246,.18)}
input::placeholder,textarea::placeholder{color:#5d6b7d}
input[type=checkbox]{accent-color:var(--accent);width:15px;height:15px;cursor:pointer}
select{cursor:pointer;appearance:none;background-image:linear-gradient(45deg,transparent 50%,#7f8fa3 50%),linear-gradient(135deg,#7f8fa3 50%,transparent 50%);background-position:calc(100% - 15px) 14px,calc(100% - 10px) 14px;background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:28px}
.check-label{display:inline-flex;align-items:center;gap:7px;font-size:13px;color:var(--text-dim);cursor:pointer}
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px}
.form-grid.wide{grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
.inline-fields{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end}
.inline-fields .field{flex:1 1 140px}

/* 工具条 / 筛选栏 */
.toolbar{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end}
.toolbar .field{flex:0 1 auto;min-width:132px}
.toolbar .spacer{flex:1 1 auto}
.filter-bar{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;align-items:end}

/* 统计小条 */
.stat-strip{display:flex;gap:18px;flex-wrap:wrap}
.stat-strip .stat{display:flex;flex-direction:column;gap:2px;min-width:92px}
.stat-strip .stat b{font-size:19px;font-variant-numeric:tabular-nums}
.stat-strip .stat span{font-size:11.5px;color:var(--muted)}

/* 表格 */
.table-wrap{overflow-x:auto;border-radius:var(--radius);border:1px solid var(--border-soft)}
table{width:100%;border-collapse:collapse;font-size:13px;min-width:640px}
thead th{
  position:sticky;top:0;z-index:1;
  background:#151d29;color:var(--muted);font-weight:500;font-size:12px;
  text-align:left;padding:9px 11px;border-bottom:1px solid var(--border);white-space:nowrap;
}
tbody td{padding:8px 11px;border-bottom:1px solid var(--border-soft);vertical-align:middle;color:var(--text-dim)}
tbody tr:last-child td{border-bottom:0}
tbody tr.clickable{cursor:pointer}
tbody tr.clickable:hover td{background:var(--panel-2)}
tbody tr.group-row td{background:#131b26;color:var(--muted);font-size:12px;letter-spacing:.4px}
td.nowrap,th.nowrap{white-space:nowrap}
td.mono,.mono{font-family:var(--mono);font-size:12.5px}
.trunc{max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:bottom}
.actions-cell{display:flex;gap:6px;flex-wrap:wrap}
.path-cell{max-width:340px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* 分页 */
.pager{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:space-between;margin-top:12px}
.pager .pages{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:12.5px}

/* 空/加载/错误占位 */
.placeholder{padding:26px 14px;text-align:center;color:var(--muted);font-size:13px}
.placeholder .ph-ico{font-size:22px;opacity:.5;display:block;margin-bottom:6px}
.error-box{
  display:flex;flex-direction:column;gap:8px;align-items:center;
  padding:20px 14px;border:1px dashed rgba(239,68,68,.42);border-radius:var(--radius);
  background:rgba(239,68,68,.06);color:#ffc0c0;font-size:13px;text-align:center;
}
.error-box code{font-size:12px;color:#ffd9d9;word-break:break-all}
.skeleton{position:relative;overflow:hidden;background:#1a2432;border-radius:6px;height:14px}
.skeleton::after{content:"";position:absolute;inset:0;transform:translateX(-100%);background:linear-gradient(90deg,transparent,rgba(255,255,255,.07),transparent);animation:sk 1.25s infinite}
@keyframes sk{100%{transform:translateX(100%)}}
.sk-table{display:flex;flex-direction:column;gap:9px;padding:6px 2px}
.sk-table .skeleton{height:16px}
.sk-kpi{display:flex;flex-direction:column;gap:10px}
.sk-kpi .skeleton:first-child{height:12px;width:52%}
.sk-kpi .skeleton:nth-child(2){height:26px;width:70%}
.sk-kpi .skeleton:last-child{height:11px;width:40%}

/* 条形榜 */
.bar-list{display:flex;flex-direction:column;gap:10px}
.bar-item{display:grid;grid-template-columns:minmax(84px,150px) 1fr auto;gap:10px;align-items:center;font-size:12.5px}
.bar-item .bar-label{color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bar-track{height:8px;border-radius:6px;background:#1b2534;overflow:hidden}
.bar-fill{height:100%;border-radius:6px;background:linear-gradient(90deg,#3b82f6,#22d3ee);transition:width .35s ease}
.bar-fill.tone-danger{background:linear-gradient(90deg,#ef4444,#fb923c)}
.bar-fill.tone-warn{background:linear-gradient(90deg,#f59e0b,#fbbf24)}
.bar-fill.tone-purple{background:linear-gradient(90deg,#a855f7,#6366f1)}
.bar-value{font-variant-numeric:tabular-nums;color:var(--muted);min-width:44px;text-align:right}

/* IP 榜 */
.ip-list{display:flex;flex-direction:column;gap:8px}
.ip-item{display:flex;align-items:center;gap:10px;padding:8px 10px;background:var(--panel-2);border:1px solid var(--border-soft);border-radius:var(--radius)}
.ip-item .ip-addr{font-family:var(--mono);font-size:12.5px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.ip-item .ip-count{font-size:12px;color:var(--muted);white-space:nowrap}

/* 图表 */
.chart-wrap{position:relative;width:100%;height:260px}
.chart-wrap canvas{display:block;width:100%;height:100%}
.chart-tip{
  position:absolute;pointer-events:none;z-index:5;
  min-width:132px;padding:8px 10px;border-radius:var(--radius-sm);
  background:rgba(12,18,27,.96);border:1px solid var(--border);
  box-shadow:var(--shadow);font-size:12px;color:var(--text-dim);
  transform:translate(-50%,-108%);opacity:0;transition:opacity .1s;
}
.chart-tip.on{opacity:1}
.chart-tip b{color:var(--text);display:block;margin-bottom:4px;font-size:12px}
.chart-tip .row{display:flex;justify-content:space-between;gap:12px;font-variant-numeric:tabular-nums}
.chart-legend{display:flex;gap:14px;align-items:center;font-size:12px;color:var(--muted)}
.chart-legend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:5px;vertical-align:-1px}

/* 标签页 */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--border-soft);margin-bottom:14px;flex-wrap:wrap}
.tab{
  padding:8px 14px;border:0;background:transparent;color:var(--muted);
  font-size:13.5px;cursor:pointer;border-bottom:2px solid transparent;font-family:inherit;
}
.tab:hover{color:var(--text)}
.tab.active{color:#cfe2ff;border-bottom-color:var(--accent)}

/* 代码块 */
.code-block{
  background:#0c1219;border:1px solid var(--border-soft);border-radius:var(--radius-sm);
  padding:10px 12px;font-family:var(--mono);font-size:12.5px;line-height:1.6;
  color:#d6e2f0;white-space:pre-wrap;word-break:break-all;max-height:280px;overflow:auto;
}
.code-block.hit{border-color:rgba(34,197,94,.4)}
.code-block.miss{border-color:var(--border-soft);opacity:.8}
mark{background:rgba(245,158,11,.3);color:#ffe6b8;border-radius:3px;padding:0 2px}

/* 键值列表（详情） */
.kv{display:grid;grid-template-columns:104px 1fr;gap:7px 12px;font-size:13px}
.kv dt{color:var(--muted);font-size:12px;padding-top:1px}
.kv dd{margin:0;word-break:break-all;color:var(--text-dim)}
.section-title{font-size:12.5px;color:var(--muted);margin:16px 0 8px;display:flex;align-items:center;gap:8px}
.section-title::after{content:"";flex:1;height:1px;background:var(--border-soft)}

/* 抽屉 */
.drawer-root{position:fixed;inset:0;z-index:60}
.drawer-backdrop{position:absolute;inset:0;background:rgba(4,7,12,.55)}
.drawer{
  position:absolute;top:0;right:0;height:100%;width:min(620px,94vw);
  background:var(--bg-grad);border-left:1px solid var(--border);
  display:flex;flex-direction:column;box-shadow:var(--shadow);
  animation:slideIn .2s ease;
}
@keyframes slideIn{from{transform:translateX(24px);opacity:.4}to{transform:none;opacity:1}}
.drawer-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--border-soft)}
.drawer-head h2{font-size:15px}
.drawer-head p{font-size:12px;margin-top:3px;word-break:break-all}
.drawer-body{flex:1;overflow-y:auto;padding:14px 16px 24px}
.drawer-foot{padding:12px 16px;border-top:1px solid var(--border-soft);display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;background:#101722}
.drawer-foot:empty{display:none}

/* 模态 */
.modal-root{position:fixed;inset:0;z-index:70;display:grid;place-items:center}
.modal-backdrop{position:absolute;inset:0;background:rgba(4,7,12,.62)}
.modal{
  position:relative;width:min(720px,94vw);max-height:90vh;
  background:var(--bg-grad);border:1px solid var(--border);border-radius:var(--radius-lg);
  display:flex;flex-direction:column;box-shadow:var(--shadow);
  animation:pop .16s ease;
}
.modal.wide{width:min(920px,96vw)}
@keyframes pop{from{transform:scale(.97);opacity:.5}to{transform:none;opacity:1}}
.modal-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:13px 16px;border-bottom:1px solid var(--border-soft)}
.modal-head h2{font-size:15px}
.modal-body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:14px}
.modal-foot{padding:12px 16px;border-top:1px solid var(--border-soft);display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap;background:#101722}
.modal-foot:empty{display:none}

/* 通知通道卡 */
.channel-list{display:flex;flex-direction:column;gap:12px}
.channel{border:1px solid var(--border-soft);border-radius:var(--radius);background:var(--panel-2);overflow:hidden}
.channel-head{display:flex;align-items:center;gap:10px;padding:11px 13px;flex-wrap:wrap}
.channel-head .ch-name{font-size:13.5px;display:flex;align-items:center;gap:8px;min-width:0}
.channel-head .ch-name .mono{color:var(--muted);font-size:12px}
.channel-head .spacer{flex:1}
.channel-body{padding:0 13px 13px;display:flex;flex-direction:column;gap:12px;border-top:1px solid var(--border-soft);background:#111927;padding-top:13px}
.channel-result{font-size:12px;color:var(--muted);display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.channel.collapsed .channel-body{display:none}

/* 规则测试面板 */
.test-panel{border:1px solid var(--border-soft);border-radius:var(--radius);background:#111927;padding:13px}
.test-results{display:flex;flex-direction:column;gap:8px;max-height:260px;overflow:auto}
.test-item{display:flex;flex-direction:column;gap:6px;padding:9px 10px;border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--panel-2)}
.test-item .test-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px}
.hint-text{font-size:12px;color:var(--muted)}
.hint-text.ok{color:#9ce8b7}
.hint-text.bad{color:#ffb4b4}

/* 时间线/日志行 */
.timeline-list{display:flex;flex-direction:column;gap:9px}
.tl-row{display:flex;gap:10px;align-items:flex-start;font-size:13px}
.tl-row .tl-time{color:var(--muted);font-family:var(--mono);font-size:12px;white-space:nowrap}

/* 通知/提示条 */
.toast-root{position:fixed;right:16px;bottom:16px;z-index:90;display:flex;flex-direction:column;gap:8px;max-width:min(420px,92vw)}
.toast{
  padding:10px 13px;border-radius:var(--radius);border:1px solid var(--border);
  background:rgba(20,27,38,.97);box-shadow:var(--shadow);font-size:13px;color:var(--text-dim);
  animation:pop .16s ease;
}
.toast.ok{border-color:rgba(34,197,94,.45);color:#bff0cf}
.toast.err{border-color:rgba(239,68,68,.45);color:#ffc6c6}
.toast.warn{border-color:rgba(245,158,11,.45);color:#ffe0ad}
.banner{
  display:flex;align-items:center;gap:10px;padding:10px 13px;border-radius:var(--radius);
  border:1px solid rgba(245,158,11,.4);background:rgba(245,158,11,.08);color:#ffdca8;font-size:12.5px;
}

/* 登录遮罩 */
.login-overlay{position:fixed;inset:0;z-index:100;display:grid;place-items:center;background:radial-gradient(700px 420px at 50% 0%,rgba(59,130,246,.14),transparent 60%),rgba(6,9,14,.92);backdrop-filter:blur(3px);padding:20px}
.login-card{
  width:min(400px,94vw);display:flex;flex-direction:column;gap:14px;
  background:linear-gradient(180deg,#151d2a,#101722);
  border:1px solid var(--border);border-radius:16px;padding:26px 24px;box-shadow:var(--shadow);
}
.login-head{display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;margin-bottom:4px}
.login-head h2{font-size:17px;letter-spacing:1px}
.login-logo{color:var(--accent);display:flex;margin-bottom:5px}
.login-head p{font-size:12.5px}
/* white-space:pre-line —— 登录失败的提示里有换行（比如"如果密码是浏览器
   自动填充的…"那句），<p> 默认会把 \n 折叠成空格，两行挤成一行很难读。
   抖动动画：失败时光把红字变出来不够，用户正盯着按钮，需要动一下才注意得到。 */
.login-error{margin:0;font-size:12.5px;color:#ffb4b4;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.35);border-radius:var(--radius-sm);padding:7px 10px;white-space:pre-line;line-height:1.65}
.login-error:not([hidden]){animation:dun-shake .32s ease-in-out}
@keyframes dun-shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-6px)}40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(4px)}}
.login-foot{font-size:11.5px;text-align:center;line-height:1.6}

/* 密码框的"显示/隐藏"开关。
   为什么值得加一个按钮——真机上用户被浏览器自动填充的**旧口令**
   卡住了好几次：他以为自己输的是新的，其实填进去的是改口令之前存的。
   能看见框里到底是什么，这类问题一眼就能定位。 */
.pw-wrap{position:relative;display:block}
.pw-wrap input{padding-right:40px}
.pw-toggle{
  position:absolute;right:6px;top:50%;transform:translateY(-50%);
  display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;padding:0;border:0;border-radius:6px;
  background:transparent;color:var(--muted);cursor:pointer;font-family:inherit;
}
.pw-toggle:hover{background:var(--accent-soft);color:#cfe2ff}
.pw-toggle[aria-pressed="true"]{color:var(--accent)}

/* 分段控件 */
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden}
.seg button{padding:6px 12px;border:0;background:transparent;color:var(--muted);font-size:12.5px;cursor:pointer;font-family:inherit}
.seg button.active{background:var(--accent-soft);color:#cfe2ff}
.seg button + button{border-left:1px solid var(--border)}

.tag-list{display:flex;gap:6px;flex-wrap:wrap}
.countdown{font-variant-numeric:tabular-nums;font-family:var(--mono);font-size:12.5px}
.countdown.soon{color:#ffd08a}
.countdown.done{color:var(--muted)}
.list-reset{margin:0;padding:0;list-style:none}

/* ---------- 响应式 ---------- */
@media (max-width:1200px){
  .grid-kpi{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:1200px){
  .grid-2-1,.grid-2,.grid-3{grid-template-columns:minmax(0,1fr)}
  .span-2{grid-column:auto}
}
@media (max-width:1100px){
  /* 中等屏：侧栏收成图标条 */
  .sidebar{width:var(--sidebar-w-mini);flex-basis:var(--sidebar-w-mini)}
  .brand{justify-content:center;padding:14px 8px}
  .brand-text{display:none}
  .nav-item{justify-content:center;padding:10px 0}
  .nav-item span{display:none}
  .sidebar-foot{display:none}
}
@media (max-width:900px){
  .only-narrow{display:inline-grid}
  .sidebar{
    position:fixed;left:0;top:0;width:var(--sidebar-w);flex-basis:var(--sidebar-w);
    transform:translateX(-102%);transition:transform .2s ease;
  }
  .sidebar.open{transform:none;box-shadow:var(--shadow)}
  .sidebar.open .brand{justify-content:flex-start;padding:16px}
  .sidebar.open .brand-text{display:flex}
  .sidebar.open .nav-item{justify-content:flex-start;padding:9px 11px}
  .sidebar.open .nav-item span{display:inline}
  .sidebar.open .sidebar-foot{display:flex}
  .view{padding:14px 13px 40px}
  .topbar{padding:10px 13px}
  .comp-list{grid-template-columns:minmax(0,1fr)}
  .refresh-time{display:none}
  .kv{grid-template-columns:88px 1fr}
}
@media (max-width:640px){
  .grid-kpi{grid-template-columns:minmax(0,1fr)}
  .switch-text{display:none}
  .user-name{display:none}
  .kpi .kpi-value{font-size:22px}
  .chart-wrap{height:210px}
  table{min-width:520px}
}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001s !important;transition-duration:.001s !important}
}
/* -*- coding: utf-8 -*- */
/* ==========================================================================
   数据大屏（挂在运维室墙上的那一屏）
   ========================================================================== */

/* 大屏模式下藏掉面板的外壳：侧栏、顶栏、页脚都不要，整屏交给大屏 */
body.screen-mode .sidebar,
body.screen-mode .topbar,
body.screen-mode .sidebar-backdrop,
body.screen-mode .page-head{display:none !important}
body.screen-mode .main{margin:0 !important;padding:0 !important;max-width:none !important}
body.screen-mode .view{padding:0 !important;overflow:hidden}
body.screen-mode{overflow:hidden}
/* 鼠标 3 秒不动就藏光标——大屏上不该有个箭头停在图表正中间。
 *
 * **限定 screen-mode，这是有意的，不是笔误。**
 *
 * 原来写的是 `body.sc-idle{cursor:none}`。而大屏页那个"3 秒后藏光标"
 * 的定时器，在离开大屏页时没有被清掉（句柄留在闭包里，unmount 够不着），
 * 于是它会在 3 秒后给 body 加上 sc-idle —— **在普通页面上把鼠标光标
 * 藏掉**；而监听已经移除，没有任何东西能恢复它。
 * 用户看到的就是"鼠标时有时无"。
 *
 * 加上 screen-mode 限定之后，那个类在别的页面**不可能**生效。
 * 这比"记得在 unmount 里清定时器"更可靠：清理逻辑将来总会被改坏，
 * 而一个作用域限定不会。 */
body.screen-mode.sc-idle{cursor:none}

/* 固定 1920×1080 画布，用 transform 缩放适配任意分辨率。
   流式布局总有一种尺寸会把字挤没；先排好版再整体缩放最稳。 */
.screen-fit{position:fixed;inset:0;background:#05080e;overflow:hidden}
.screen-canvas{
  position:absolute;width:1920px;height:1080px;transform-origin:0 0;
  display:flex;flex-direction:column;gap:12px;padding:16px 20px 18px;
  background:
    radial-gradient(1200px 700px at 50% -8%,rgba(56,189,248,.10),transparent 62%),
    radial-gradient(900px 600px at 8% 108%,rgba(59,130,246,.10),transparent 60%),
    linear-gradient(180deg,#070b13,#05080e);
  color:#e3ecf8;
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",
              "PingFang SC",sans-serif;
}

/* ---------------------------------------------------------------- 顶栏 */
.sc-top{display:flex;align-items:center;gap:18px;height:60px;flex:none;
  padding:0 18px;border:1px solid rgba(56,189,248,.18);border-radius:12px;
  background:linear-gradient(90deg,rgba(14,24,40,.92),rgba(10,17,29,.72));
  box-shadow:0 0 0 1px rgba(0,0,0,.3) inset,0 6px 24px rgba(0,0,0,.4)}
.sc-brand{display:flex;align-items:center;gap:10px;font-size:22px;font-weight:700;
  letter-spacing:2px;color:#cfe8ff;text-shadow:0 0 18px rgba(56,189,248,.5)}
.sc-top-mid{display:flex;gap:8px;margin-left:6px}
.sc-chip{font-size:13px;padding:3px 12px;border-radius:20px;color:#9fd8ff;
  border:1px solid rgba(56,189,248,.32);background:rgba(56,189,248,.10)}
.sc-top-right{margin-left:auto;display:flex;align-items:center;gap:14px}
.sc-clock{font-size:19px;font-variant-numeric:tabular-nums;color:#bfe3ff;
  letter-spacing:1px;font-family:ui-monospace,Consolas,monospace}
.sc-conn{display:flex;align-items:center;gap:7px;font-size:13px;color:#7ee0a8}
.sc-conn i{width:8px;height:8px;border-radius:50%;background:#22c55e;
  box-shadow:0 0 10px #22c55e;animation:scPulse 1.8s ease-in-out infinite}
.sc-conn.bad{color:#ff9c9c}
.sc-conn.bad i{background:#ef4444;box-shadow:0 0 10px #ef4444}
@keyframes scPulse{0%,100%{opacity:1}50%{opacity:.35}}
.sc-btn{font:inherit;font-size:13px;padding:5px 14px;border-radius:8px;cursor:pointer;
  color:#bfe3ff;border:1px solid rgba(56,189,248,.34);background:rgba(56,189,248,.10)}
.sc-btn:hover{background:rgba(56,189,248,.22);color:#fff}

/* ---------------------------------------------------------------- 主体三列 */
.sc-body{display:grid;grid-template-columns:440px 1fr 420px;gap:12px;
  flex:1;min-height:0}
.sc-col{display:flex;flex-direction:column;gap:12px;min-height:0}
.sc-grow{flex:1;min-height:0;display:flex;flex-direction:column}

.sc-panel{border:1px solid rgba(56,189,248,.16);border-radius:12px;
  background:linear-gradient(180deg,rgba(13,21,34,.86),rgba(9,15,25,.72));
  padding:12px 14px;min-height:0;display:flex;flex-direction:column;
  box-shadow:0 6px 22px rgba(0,0,0,.35)}
.sc-panel h4{margin:0 0 10px;font-size:14px;font-weight:600;color:#9fd8ff;
  letter-spacing:1px;display:flex;align-items:baseline;gap:10px}
.sc-h4-note{margin-left:auto;font-size:11.5px;font-weight:400;color:#7d90a8;
  letter-spacing:0}
.sc-empty{color:#6f8299;font-size:13px;padding:10px 2px}

/* ---------------------------------------------------------------- KPI */
.sc-kpis{display:grid;grid-template-columns:1fr 1fr;gap:12px;flex:none}
.sc-kpi{position:relative;border-radius:12px;padding:12px 14px 11px;overflow:hidden;
  border:1px solid rgba(56,189,248,.18);
  background:linear-gradient(160deg,rgba(16,26,42,.95),rgba(9,15,25,.8))}
.sc-kpi::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px}
.sc-tone-blue::before{background:#3b82f6}
.sc-tone-red::before{background:#ef4444}
.sc-tone-amber::before{background:#f59e0b}
.sc-tone-violet::before{background:#a855f7}
.sc-kpi-label{display:block;font-size:13px;color:#8fa4bd;letter-spacing:1px}
.sc-kpi-value{display:block;font-size:38px;font-weight:700;line-height:1.15;
  font-variant-numeric:tabular-nums;color:#eaf4ff;
  text-shadow:0 0 22px rgba(56,189,248,.35)}
.sc-tone-red .sc-kpi-value{text-shadow:0 0 22px rgba(239,68,68,.4)}
.sc-tone-amber .sc-kpi-value{text-shadow:0 0 22px rgba(245,158,11,.4)}
.sc-tone-violet .sc-kpi-value{text-shadow:0 0 22px rgba(168,85,247,.4)}
.sc-kpi-sub{display:block;font-size:12px;color:#7d90a8;margin-top:2px}

/* ---------------------------------------------------------------- 防御效率环 */
.sc-gauge-row{display:flex;align-items:center;gap:18px}
.sc-gauge{position:relative;width:132px;height:132px;flex:none}
.sc-gauge svg{width:132px;height:132px;display:block}
.sc-gauge-bg{fill:none;stroke:rgba(80,140,220,.20);stroke-width:10}
.sc-gauge-fg{fill:none;stroke:#22d3ee;stroke-width:10;stroke-linecap:round;
  transition:stroke-dashoffset .8s cubic-bezier(.22,1,.36,1);
  filter:drop-shadow(0 0 8px rgba(34,211,238,.65))}
.sc-gauge-txt{position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:2px}
.sc-gauge-txt b{font-size:25px;color:#d9f6ff;font-variant-numeric:tabular-nums}
.sc-gauge-txt span{font-size:12px;color:#7d90a8}
.sc-mini{display:flex;flex-direction:column;gap:12px}
.sc-mini>div{display:flex;flex-direction:column}
.sc-mini b{font-size:22px;color:#dceaf9;font-variant-numeric:tabular-nums}
.sc-mini span{font-size:12px;color:#7d90a8}

/* ---------------------------------------------------------------- 规则命中 */
.sc-rules{display:flex;flex-direction:column;gap:9px;overflow:hidden}
.sc-rule{display:grid;grid-template-columns:132px 1fr 56px;align-items:center;gap:10px}
.sc-rule-id{font-size:12px;color:#9fd8ff;
  font-family:ui-monospace,Consolas,monospace;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.sc-rule-bar{height:7px;border-radius:4px;background:rgba(80,140,220,.16);overflow:hidden}
.sc-rule-bar i{display:block;height:100%;border-radius:4px;
  background:linear-gradient(90deg,#3b82f6,#22d3ee);
  transition:width .8s cubic-bezier(.22,1,.36,1)}
.sc-rule-n{text-align:right;font-size:13px;color:#cfe0f5;
  font-variant-numeric:tabular-nums}

/* ---------------------------------------------------------------- 攻击态势环 */
.sc-ring-wrap{position:relative;flex:1;min-height:0;display:flex;
  align-items:center;justify-content:center}
.sc-ring{width:100%;height:100%;max-height:520px}
.sc-ring-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  display:flex;flex-direction:column;align-items:center;pointer-events:none}
.sc-ring-center b{font-size:30px;color:#bfe3ff;font-variant-numeric:tabular-nums;
  text-shadow:0 0 20px rgba(56,189,248,.6)}
.sc-ring-center span{font-size:12px;color:#7d90a8}

/* ---------------------------------------------------------------- 实时流水 */
.sc-stream-panel{flex:none;height:268px}
.sc-stream{display:flex;flex-direction:column;gap:5px;overflow:hidden}
.sc-ev{display:grid;grid-template-columns:64px 118px 1fr 44px;align-items:center;
  gap:9px;padding:5px 9px;border-radius:7px;font-size:12.5px;
  background:rgba(255,255,255,.025);border-left:3px solid transparent}
.sc-ev.fresh{animation:scSlide .45s ease-out}
@keyframes scSlide{from{opacity:0;transform:translateX(-14px)}to{opacity:1;transform:none}}
.sc-ev.sev-critical{border-left-color:#ef4444;background:rgba(239,68,68,.10)}
.sc-ev.sev-high{border-left-color:#f97316;background:rgba(249,115,22,.08)}
.sc-ev.sev-medium{border-left-color:#f59e0b}
.sc-ev.sev-low{border-left-color:#3b82f6}
.sc-ev-t{color:#7d90a8;font-family:ui-monospace,Consolas,monospace}
.sc-ev-ip{color:#9fd8ff;font-family:ui-monospace,Consolas,monospace;
  cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-ev-ip:hover{color:#fff;text-decoration:underline}
.sc-ev-r{color:#cfe0f5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sc-ev-s{color:#8fa4bd;text-align:right;font-size:11.5px}

/* ---------------------------------------------------------------- 态势评分 */
.sc-posture{display:flex;align-items:center;gap:18px}
.sc-score{font-size:56px;font-weight:700;line-height:1;color:#eaf4ff;
  font-variant-numeric:tabular-nums;text-shadow:0 0 26px rgba(56,189,248,.45)}
.sc-score-side{display:flex;flex-direction:column;gap:6px}
.sc-grade{font-size:26px;font-weight:700;color:#f59e0b;letter-spacing:2px}
.sc-problems{font-size:12.5px;color:#8fa4bd}

/* ---------------------------------------------------------------- 攻击类型 */
.sc-types{display:flex;flex-direction:column;gap:10px;overflow:hidden}
.sc-type{display:grid;grid-template-columns:96px 1fr 78px;align-items:center;gap:10px}
.sc-type-name{font-size:12.5px;color:#cfe0f5;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.sc-type-bar{height:7px;border-radius:4px;background:rgba(80,140,220,.16);overflow:hidden}
.sc-type-bar i{display:block;height:100%;border-radius:4px;
  background:linear-gradient(90deg,#f97316,#ef4444);
  transition:width .8s cubic-bezier(.22,1,.36,1)}
.sc-type-n{text-align:right;font-size:12.5px;color:#cfe0f5;
  font-variant-numeric:tabular-nums}
.sc-type-n em{color:#7d90a8;font-style:normal;font-size:11.5px}

/* ---------------------------------------------------------------- 组件状态 */
.sc-comps{display:flex;flex-direction:column;gap:9px}
.sc-comp{display:grid;grid-template-columns:10px 1fr auto;align-items:center;gap:10px;
  font-size:13px;color:#cfe0f5}
.sc-comp i{width:9px;height:9px;border-radius:50%}
.sc-comp i.on{background:#22c55e;box-shadow:0 0 9px #22c55e}
.sc-comp i.off{background:#64748b}
.sc-comp em{font-style:normal;font-size:12px;color:#7d90a8}

/* ---------------------------------------------------------------- 底部趋势 */
.sc-foot{flex:none;height:236px}
.sc-trend{height:100%}
.sc-chart-wrap{position:relative;flex:1;min-height:0}
.sc-chart{width:100%;height:100%;display:block}
.sc-chart-tip{position:absolute;display:none;pointer-events:none;z-index:5;
  padding:7px 11px;border-radius:8px;font-size:12.5px;line-height:1.6;
  background:rgba(10,17,29,.96);border:1px solid rgba(56,189,248,.4);
  color:#dceaf9;box-shadow:0 8px 26px rgba(0,0,0,.55);white-space:nowrap}
.sc-dot{display:inline-block;width:9px;height:9px;border-radius:50%;
  margin-right:5px;vertical-align:middle}
.sc-dot.blue{background:#3b82f6}
.sc-dot.red{background:#ef4444}
.sc-dot.amber{background:#f59e0b}

.sc-toast{position:absolute;left:50%;bottom:26px;transform:translate(-50%,20px);
  padding:9px 20px;border-radius:10px;font-size:13.5px;opacity:0;
  background:rgba(12,20,33,.96);border:1px solid rgba(56,189,248,.42);
  color:#cfe8ff;transition:opacity .25s,transform .25s;pointer-events:none}
.sc-toast.show{opacity:1;transform:translate(-50%,0)}

/* ==========================================================================
   IP 画像抽屉
   ========================================================================== */
.ip-prof{display:flex;flex-direction:column;gap:6px}
.ip-head{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:12px}
.ip-big{display:flex;flex-direction:column;gap:3px;padding:10px 12px;border-radius:10px;
  background:rgba(56,189,248,.07);border:1px solid rgba(56,189,248,.18)}
.ip-big b{font-size:19px;color:#eaf4ff;font-variant-numeric:tabular-nums;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ip-big b.danger{color:#ff9c9c}
.ip-big span{font-size:11.5px;color:#7d90a8}
.ip-sevbar{display:flex;height:22px;border-radius:6px;overflow:hidden;margin-bottom:14px}
.ip-sev{display:flex;align-items:center;justify-content:center;font-size:11px;
  color:#0b0f16;font-weight:600}
.sev-critical{background:#ef4444}
.sev-high{background:#f97316}
.sev-medium{background:#f59e0b}
.sev-low{background:#3b82f6}
.sev-info{background:#64748b}
.ip-h{margin:14px 0 8px;font-size:13px;color:#9fd8ff;font-weight:600;letter-spacing:.5px}
.ip-row{display:grid;grid-template-columns:110px 1fr 44px;align-items:center;gap:10px;
  margin-bottom:7px}
.ip-k{font-size:12.5px;color:#cfe0f5;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.ip-bar{height:7px;border-radius:4px;background:rgba(80,140,220,.16);overflow:hidden}
.ip-bar i{display:block;height:100%;border-radius:4px;
  background:linear-gradient(90deg,#f97316,#ef4444)}
.ip-v{text-align:right;font-size:12.5px;color:#cfe0f5;font-variant-numeric:tabular-nums}
.ip-rule{display:flex;justify-content:space-between;align-items:center;
  padding:5px 0;border-bottom:1px dashed rgba(80,140,220,.18);font-size:12.5px}
.ip-rule code{font-family:ui-monospace,Consolas,monospace;color:#9fd8ff}
.ip-rule b{color:#cfe0f5}
.ip-spark{display:flex;align-items:flex-end;gap:2px;height:56px;padding:4px 0}
.ip-spark i{flex:1;min-width:3px;border-radius:2px 2px 0 0;
  background:linear-gradient(180deg,#38bdf8,rgba(56,189,248,.25))}
.ip-evs{display:flex;flex-direction:column;gap:4px;max-height:340px;overflow:auto}
.ip-ev{display:grid;grid-template-columns:104px 44px 1fr;gap:8px;align-items:center;
  font-size:12px;padding:4px 6px;border-radius:6px;background:rgba(255,255,255,.02)}
.ip-ev-t{color:#7d90a8;font-family:ui-monospace,Consolas,monospace}
.ip-ev-r{color:#cfe0f5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ip-ev-p{display:none}
.sev-tag{display:inline-block;text-align:center;border-radius:4px;padding:1px 0;
  font-size:11px;color:#0b0f16;font-weight:600}

/* ==========================================================================
   规则效能
   ========================================================================== */
.eff-alert{padding:12px 14px;border-radius:10px;margin-bottom:14px;
  background:rgba(239,68,68,.10);border:1px solid rgba(239,68,68,.42)}
.eff-alert b{display:block;font-size:14px;color:#ffb1b1;margin-bottom:6px}
.eff-alert p{margin:0 0 10px;font-size:13px;color:var(--text-dim)}
.eff-verdict{margin:12px 0 0;font-size:13px;line-height:1.75;color:var(--text-dim);
  padding:10px 12px;border-radius:8px;background:rgba(56,189,248,.06);
  border-left:3px solid var(--accent)}
.eff-list{display:flex;flex-wrap:wrap;gap:8px}
.eff-chip{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;
  padding:4px 10px;border-radius:20px;color:var(--text-dim);
  background:var(--panel-2);border:1px solid var(--border-soft)}
.eff-chip code{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;
  color:var(--accent-2)}
.eff-chip em{font-style:normal;color:var(--muted);font-size:11.5px}
.eff-chip.danger{background:rgba(239,68,68,.12);border-color:rgba(239,68,68,.4);
  color:#ffc9c9}
.eff-chip.danger code{color:#ff9c9c}
.eff-never{margin-top:14px}
.eff-never summary{cursor:pointer;font-size:13px;color:var(--muted);
  padding:6px 0;user-select:none}
.eff-never summary:hover{color:var(--accent-2)}
.eff-never .eff-list{margin-top:10px;max-height:260px;overflow:auto}

/* ==========================================================================
   软件防护页
   ========================================================================== */
.soft-warn{padding:9px 13px;border-radius:8px;margin-top:10px;font-size:13px;
  line-height:1.7;color:var(--text-dim);
  background:rgba(245,158,11,.10);border-left:3px solid var(--warn,#f59e0b)}
.soft-group{margin-bottom:18px}
.soft-group-head{display:flex;align-items:baseline;gap:10px;margin-bottom:9px;
  padding-bottom:6px;border-bottom:1px solid var(--border-soft)}
.soft-group-head b{font-size:14px;color:var(--accent-2)}
.soft-group-head .muted{font-size:12px}
.soft-group-head em{margin-left:auto;font-style:normal;font-size:12px;
  color:var(--muted)}
.soft-finding{padding:11px 13px;border-radius:9px;margin-bottom:9px;
  background:var(--panel-2);border-left:4px solid var(--border)}
.soft-finding.sev-critical{border-left-color:#ef4444;
  background:rgba(239,68,68,.08)}
.soft-finding.sev-high{border-left-color:#f97316;
  background:rgba(249,115,22,.06)}
.soft-finding.sev-medium{border-left-color:#f59e0b}
.soft-finding.sev-low{border-left-color:#3b82f6}
.soft-f-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.soft-f-head b{font-size:13.5px;color:var(--text)}
.soft-f-head code{font-size:11.5px;color:var(--muted);
  font-family:ui-monospace,Consolas,monospace}
.soft-f-path{margin-top:6px;font-size:12px;color:var(--accent-2);
  word-break:break-all}
.soft-f-detail{margin-top:6px;font-size:12.5px;line-height:1.7;
  color:var(--text-dim)}
.soft-f-fix{margin-top:8px;display:flex;gap:9px;align-items:flex-start;
  font-size:12px}
.soft-f-fix span{color:var(--muted);white-space:nowrap;padding-top:2px}
.soft-f-fix code{flex:1;background:var(--bg-soft,#0f1420);padding:6px 9px;
  border-radius:6px;color:var(--text-dim);word-break:break-all;
  font-family:ui-monospace,Consolas,monospace;font-size:11.5px}

/* ==========================================================================
   敏感数据（PII）统计块
   ========================================================================== */
.pii-box{margin-top:12px;padding:13px 15px;border-radius:10px;
  background:linear-gradient(180deg,rgba(56,189,248,.07),rgba(56,189,248,.03));
  border:1px solid rgba(56,189,248,.25)}
.pii-head{display:flex;align-items:baseline;gap:10px;margin-bottom:11px}
.pii-head b{font-size:14px;color:var(--accent-2)}
.pii-nums{display:flex;gap:26px;margin-bottom:11px}
.pii-num{display:flex;flex-direction:column;gap:2px}
.pii-num b{font-size:26px;font-variant-numeric:tabular-nums;line-height:1.1}
.pii-num span{font-size:12px;color:var(--muted)}
.pii-num.bad b{color:#ff9c9c;text-shadow:0 0 16px rgba(239,68,68,.4)}
.pii-num.ok b{color:#7ee0a8;text-shadow:0 0 16px rgba(34,197,94,.4)}
.pii-types{display:flex;flex-wrap:wrap;gap:7px}
.pii-chip{font-size:12px;padding:3px 10px;border-radius:20px;color:var(--text-dim);
  background:var(--panel-2);border:1px solid var(--border-soft)}
.pii-chip em{font-style:normal;color:var(--accent-2);font-weight:600}
.pii-box code{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;
  color:var(--accent-2)}

/* ==========================================================================
   端口管理页
   ========================================================================== */
.pt-result{margin-top:12px;padding:12px 14px;border-radius:9px;font-size:13px;
  line-height:1.7}
.pt-result b{display:block;margin-bottom:6px;font-size:13.5px}
.pt-result p{margin:0 0 6px}
.pt-result pre{margin:8px 0 0}
.pt-result.ok{background:rgba(34,197,94,.10);border-left:3px solid #22c55e;
  color:var(--text-dim)}
.pt-result.ok b{color:#7ee0a8}
.pt-result.dry{background:rgba(56,189,248,.09);border-left:3px solid var(--accent);
  color:var(--text-dim)}
.pt-result.dry b{color:var(--accent-2)}
.pt-result.bad{background:rgba(239,68,68,.10);border-left:3px solid #ef4444;
  color:var(--text-dim)}
.pt-result.bad b{color:#ff9c9c}
/* 被硬护栏拦下 —— 用紫色区分于"出错"，因为这不是 bug，是保护 */
.pt-result.blocked{background:rgba(168,85,247,.10);
  border-left:3px solid #a855f7;color:var(--text-dim)}
.pt-result.blocked b{color:#d8b4fe}

/* ==========================================================================
   进程行为与审计（系统底层页里的卡片）
   ========================================================================== */
.hb-list{display:flex;flex-direction:column;gap:7px;margin-top:8px}
.hb-item{padding:9px 12px;border-radius:8px;background:var(--panel-2);
  border-left:3px solid var(--border)}
.hb-item.sev-critical{border-left-color:#ef4444;background:rgba(239,68,68,.08)}
.hb-item.sev-high{border-left-color:#f97316;background:rgba(249,115,22,.06)}
.hb-item.sev-medium{border-left-color:#f59e0b}
.hb-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.hb-head b{font-size:13px;color:var(--text)}
.hb-head code{font-size:11.5px;color:var(--accent-2);
  font-family:ui-monospace,Consolas,monospace}
.hb-head .muted{font-size:11.5px}
.hb-path{margin-top:5px;font-size:11.5px;color:var(--muted);
  word-break:break-all}

/* ==========================================================================
   处置中心的模式条（复用端口管理那套视觉，多一个"关闭"态）
   ========================================================================== */
.mode-bar.off{border-left-color:var(--border);background:var(--panel-2)}
.mode-bar.off .mode-state b{color:var(--muted)}
.mode-bar .mode-state b{font-size:15px}
.mode-bar .mode-state span{font-size:12.5px}

/* ==========================================================================
   大屏 · 防线总览
   --------------------------------------------------------------------------
   横跨整屏，放在 header 正下方 —— 它是"我该不该紧张"的第一眼答案，
   不该藏在角落里。

   三态用三种颜色，其中 idle（用户没开）刻意是**中性灰**而不是红：
   把"配置选择"和"出故障了"画成一个颜色，值班的人就分不清哪个
   需要半夜打电话。
   ========================================================================== */
.sc-shield{display:flex;align-items:center;gap:18px;
  padding:10px 18px;margin:0 0 10px;
  background:linear-gradient(90deg,rgba(20,28,44,.92),rgba(16,22,36,.78));
  border:1px solid rgba(90,120,180,.28);border-radius:10px;
  flex:0 0 auto;overflow:hidden}

/* ---- 风险分 ---- */
.sc-sh-risk{display:flex;align-items:baseline;gap:8px;
  padding:2px 16px 2px 4px;border-right:1px solid rgba(90,120,180,.22)}
.sc-sh-risk-num{font-size:34px;font-weight:800;line-height:1;
  font-variant-numeric:tabular-nums;letter-spacing:-1px}
.sc-sh-risk-lv{font-size:13px;font-weight:600;opacity:.85;white-space:nowrap}
.sc-sh-risk.tone-ok .sc-sh-risk-num,.sc-sh-risk.tone-ok .sc-sh-risk-lv
  {color:#4ade80}
.sc-sh-risk.tone-accent .sc-sh-risk-num,.sc-sh-risk.tone-accent .sc-sh-risk-lv
  {color:#38bdf8}
.sc-sh-risk.tone-warn .sc-sh-risk-num,.sc-sh-risk.tone-warn .sc-sh-risk-lv
  {color:#fbbf24}
.sc-sh-risk.tone-danger .sc-sh-risk-num,.sc-sh-risk.tone-danger .sc-sh-risk-lv
  {color:#ff6b6b}
.sc-sh-risk.tone-danger{animation:sc-sh-pulse 2.4s ease-in-out infinite}
@keyframes sc-sh-pulse{0%,100%{opacity:1}50%{opacity:.72}}

/* ---- 七条防线 ---- */
.sc-sh-layers{display:flex;gap:8px;flex:1 1 auto;flex-wrap:nowrap;
  overflow:hidden}
.sc-sh-layer{display:flex;align-items:center;gap:6px;
  padding:5px 10px;border-radius:7px;background:rgba(255,255,255,.035);
  border:1px solid rgba(255,255,255,.07);white-space:nowrap;
  font-size:12px;cursor:help;transition:background .2s}
.sc-sh-layer:hover{background:rgba(255,255,255,.08)}
.sc-sh-layer i{width:8px;height:8px;border-radius:50%;flex:0 0 auto}
.sc-sh-layer b{font-weight:600;color:#cbd5e1}
.sc-sh-layer span{color:#8fa3bf;font-variant-numeric:tabular-nums}
.sc-sh-layer em{font-style:normal;font-size:11px;font-weight:700;
  padding:0 5px;border-radius:9px;background:rgba(255,107,107,.2);
  color:#ffb3b3}
.sc-sh-layer.ok i{background:#4ade80;box-shadow:0 0 7px rgba(74,222,128,.65)}
.sc-sh-layer.idle i{background:#64748b}
.sc-sh-layer.idle b{color:#94a3b8}
.sc-sh-layer.down{border-color:rgba(255,107,107,.42);
  background:rgba(255,107,107,.09)}
.sc-sh-layer.down i{background:#ff6b6b;
  box-shadow:0 0 8px rgba(255,107,107,.75);
  animation:sc-sh-blink 1.6s ease-in-out infinite}
@keyframes sc-sh-blink{0%,100%{opacity:1}50%{opacity:.35}}
.sc-sh-layer.down b{color:#ffc9c9}

/* ---- 右侧：覆盖 + 处置 ---- */
.sc-sh-right{display:flex;align-items:center;gap:12px;flex:0 0 auto;
  padding-left:14px;border-left:1px solid rgba(90,120,180,.22)}
.sc-sh-cov{display:flex;flex-direction:column;align-items:center;line-height:1.15}
.sc-sh-cov b{font-size:20px;font-weight:800;color:#e2e8f0;
  font-variant-numeric:tabular-nums}
.sc-sh-cov span{font-size:11px;color:#8fa3bf}
.sc-sh-warn{font-size:12px;font-weight:600;color:#ffb3b3;
  padding:3px 9px;border-radius:6px;background:rgba(255,107,107,.14);
  white-space:nowrap}
.sc-sh-act{display:flex;gap:5px}

/* ---- 风险来源 ---- */
.sc-sh-drivers{display:flex;gap:6px;flex:0 0 auto;
  padding-left:12px;border-left:1px solid rgba(90,120,180,.22)}
.sc-sh-drivers span{font-size:11.5px;color:#fbbf24;
  background:rgba(251,191,36,.12);padding:3px 8px;border-radius:6px;
  white-space:nowrap}
.sc-sh-empty{color:#8fa3bf;font-size:13px;padding:6px 4px}

/* 窄屏（大屏一般 1920，但投影/预览可能更小）时让防线换行 */
@media (max-width:1500px){
  .sc-shield{flex-wrap:wrap;gap:10px}
  .sc-sh-layers{flex-wrap:wrap}
}

/* ==========================================================================
   攻击事件 · 聚合视图
   --------------------------------------------------------------------------
   为什么要有这个：生产实测 24 小时 11540 条事件，但只有 83 个独立的
   (规则,来源) 组合。一页塞 11540 行等于没有页面 —— 运维翻两页就关掉了。
   而检测本身全是对的，问题只在于"对的"和"有用的"是两回事。

   但反过来也不成立：**一个只显示"83 组"、不告诉你每组背后有多少条的
   页面同样没用。** 所以次数和压缩比必须显眼。
   ========================================================================== */

/* ---- 视图切换 ---- */
.seg{display:inline-flex;border:1px solid var(--line);border-radius:7px;
  overflow:hidden;margin-left:10px;vertical-align:middle}
.seg button{background:transparent;border:0;color:var(--muted);
  padding:4px 12px;font-size:12.5px;cursor:pointer;font-family:inherit}
.seg button+button{border-left:1px solid var(--line)}
.seg button:hover{color:var(--fg)}
.seg button.on{background:var(--accent);color:#04121f;font-weight:600}

/* ---- 关联模式 ---- */
.corr-pats{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
.corr-pat{display:grid;grid-template-columns:96px 1fr;gap:4px 12px;
  padding:9px 12px;border-radius:8px;
  background:rgba(255,255,255,.03);border-left:3px solid var(--muted)}
.corr-pat b{font-size:12px;font-weight:600;color:var(--fg);
  white-space:nowrap;grid-row:span 2;align-self:center}
.corr-pat .t{font-size:13px;color:var(--fg);font-weight:500}
.corr-pat .d{font-size:12px;color:var(--muted);line-height:1.5}
.corr-pat.critical{border-left-color:#ff6b6b;
  background:rgba(255,107,107,.08)}
.corr-pat.high{border-left-color:#fbbf24;
  background:rgba(251,191,36,.07)}
.corr-pat.medium{border-left-color:#38bdf8}
.corr-pat.critical b{color:#ff8f8f}
.corr-pat.high b{color:#fbbf24}

/* 截断提醒：**不能让人以为"就这么多"** */
.corr-note{font-size:12.5px;color:#fbbf24;padding:7px 12px;
  border-radius:7px;background:rgba(251,191,36,.1);margin-bottom:10px}

/* ---- 分组表 ---- */
.corr-head,.corr-row{display:grid;
  grid-template-columns:74px 1fr 132px 68px 178px 52px;
  gap:10px;align-items:center;padding:9px 12px}
.corr-head{font-size:11.5px;color:var(--muted);
  border-bottom:1px solid var(--line);text-transform:uppercase;
  letter-spacing:.4px}
.corr-head .n,.corr-row .n{text-align:right}
.corr-group{border-bottom:1px solid rgba(255,255,255,.045)}
.corr-row{cursor:pointer;transition:background .15s}
.corr-row:hover{background:rgba(255,255,255,.045)}
.corr-row .r{font-size:13px;color:var(--fg);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.corr-row .r i{font-style:normal;font-size:11px;color:var(--muted);
  margin-left:7px;font-family:ui-monospace,Menlo,Consolas,monospace}
.corr-row .ip{font-size:12.5px;color:var(--muted);
  font-family:ui-monospace,Menlo,Consolas,monospace;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
/* 次数要显眼 —— 它是"这一行背后有多少"的唯一提示 */
.corr-row .n{font-size:14px;font-weight:700;color:#fbbf24;
  font-variant-numeric:tabular-nums}
.corr-row .t{font-size:11.5px;color:var(--muted);
  font-variant-numeric:tabular-nums}
.corr-row .x{font-size:11.5px;color:var(--accent);text-align:right}

.corr-detail{padding:4px 12px 14px 12px;background:rgba(0,0,0,.16);
  border-top:1px solid rgba(255,255,255,.05)}
.corr-detail .kv{display:grid;grid-template-columns:88px 1fr;gap:10px;
  padding:5px 0;font-size:12.5px;align-items:start}
.corr-detail .kv b{color:var(--muted);font-weight:500}
.corr-detail .kv span{color:var(--fg);word-break:break-all}
.corr-detail code{display:block;background:rgba(0,0,0,.3);padding:7px 9px;
  border-radius:6px;font-size:11.5px;color:#c7d2fe;word-break:break-all;
  font-family:ui-monospace,Menlo,Consolas,monospace;max-height:120px;
  overflow:auto}
.corr-acts{display:flex;gap:8px;margin-top:10px}
.btn.sm{padding:4px 11px;font-size:12px}

@media (max-width:1180px){
  .corr-head,.corr-row{grid-template-columns:66px 1fr 68px 52px}
  .corr-row .ip,.corr-row .t,.corr-head span:nth-child(3),
  .corr-head span:nth-child(5){display:none}
}

/* ==========================================================================
   安全报告页
   --------------------------------------------------------------------------
   报告刻意**直接显示后端渲染的纯文本**，不在前端重排。
   后端那个渲染器同时也给 CLI 和通知渠道用 —— 所以"面板上看到的"
   和"发到群里的"永远一致。两套渲染必然会在某次改动后对不上，
   而对不上的时候没人知道该信哪个。
   ========================================================================== */
.rp-text{background:rgba(0,0,0,.28);border:1px solid var(--line);
  border-radius:9px;padding:16px 18px;margin:0;
  font-family:ui-monospace,"Cascadia Mono",Menlo,Consolas,"Noto Sans Mono CJK SC",monospace;
  font-size:12.5px;line-height:1.75;color:#cbd5e1;
  white-space:pre-wrap;word-break:break-word;
  max-height:none;overflow-x:auto}

/* ---- 待办 ---- */
.rp-todo{padding:11px 14px;margin-bottom:9px;border-radius:8px;
  background:rgba(255,255,255,.032);border-left:3px solid var(--muted)}
.rp-todo b{display:block;font-size:13.5px;color:var(--fg);margin-bottom:5px}
.rp-todo p{margin:0;font-size:12.5px;color:var(--muted);line-height:1.6}
.rp-todo p.how{margin-top:5px;color:#8fa3bf}
.rp-todo p.how::before{content:"→ ";color:var(--accent)}
.rp-todo.critical{border-left-color:#ff6b6b;
  background:rgba(255,107,107,.075)}
.rp-todo.critical b{color:#ff9d9d}
.rp-todo.high{border-left-color:#fbbf24;background:rgba(251,191,36,.07)}
.rp-todo.high b{color:#fbbf24}
.rp-todo.medium{border-left-color:#38bdf8}

/* ==========================================================================
   激活弹窗
   --------------------------------------------------------------------------
   登录后要求输入激活码，通过后显示接管信息。

   **它是界面环节，不是安全控制。** 激活码是产品序列号（所有客户共用），
   真正的访问控制是登录 —— 那一层没有被削弱。

   所以这里的样式刻意做得"温和"：遮住界面，但不制造压迫感；
   而且拿不到状态时前端是**放行**的（见 app.js 里的说明）。
   ========================================================================== */
.act-mask{position:fixed;inset:0;z-index:9000;display:flex;
  align-items:center;justify-content:center;padding:24px;
  background:rgba(6,10,18,.82);backdrop-filter:blur(5px);
  animation:act-in .22s ease-out}
@keyframes act-in{from{opacity:0}to{opacity:1}}

/* 遮罩期间不让点后面的东西。用 class 而不是给 body 加 overflow:hidden，
   这样滚动位置不会被重置。 */
body.act-locked{overflow:hidden}
body.act-locked .nav,body.act-locked main,body.act-locked .topbar
  {pointer-events:none}

.act-box{width:min(560px,94vw);background:linear-gradient(160deg,#131c2e,#0e1522);
  border:1px solid rgba(90,120,180,.32);border-radius:14px;
  padding:26px 28px 22px;box-shadow:0 24px 70px rgba(0,0,0,.6);
  animation:act-pop .24s cubic-bezier(.2,.9,.3,1.2)}
@keyframes act-pop{from{transform:translateY(14px) scale(.97);opacity:0}
  to{transform:none;opacity:1}}

.act-head{display:flex;align-items:center;gap:12px;margin-bottom:14px}
.act-head svg{color:var(--accent);flex:0 0 auto}
.act-head h2{margin:0;font-size:19px;font-weight:600;color:#e8eef8;
  letter-spacing:.3px}
.act-box.ok .act-head svg{color:#4ade80}
.act-box.ok .act-head h2{color:#4ade80}

.act-desc{margin:0 0 16px;font-size:13px;line-height:1.7;color:#94a6c0}

.act-row{display:flex;gap:10px}
.act-row input{flex:1 1 auto;min-width:0;background:rgba(0,0,0,.32);
  border:1px solid rgba(90,120,180,.34);border-radius:9px;
  padding:11px 14px;font-size:15px;color:#e8eef8;
  font-family:ui-monospace,Menlo,Consolas,monospace;letter-spacing:1.5px;
  outline:none;transition:border-color .15s,box-shadow .15s}
.act-row input:focus{border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(56,189,248,.15)}
.act-row .btn{flex:0 0 auto;padding:11px 22px;font-size:14px}

.act-msg{min-height:20px;margin-top:10px;font-size:12.5px}
.act-msg.err{color:#ff9d9d}
.act-hint{margin:8px 0 0;font-size:11.5px;color:#6b7d96;line-height:1.6}

/* ---- 激活成功 ---- */
.act-takeover{margin:4px 0 20px;padding:16px 18px;border-radius:10px;
  background:linear-gradient(135deg,rgba(74,222,128,.1),rgba(56,189,248,.07));
  border:1px solid rgba(74,222,128,.28)}
.act-takeover p{margin:0;font-size:14.5px;line-height:1.85;color:#cfe9d8;
  letter-spacing:.2px;text-align:center}
.act-takeover p+p{margin-top:6px}
.act-box.ok #act-ok{width:100%;padding:11px 0;font-size:14px}

/* ---- 顶栏常驻条 ---- */
#act-banner{display:flex;gap:14px;flex-wrap:wrap;justify-content:center;
  padding:7px 16px;font-size:12.5px;color:#9fe8bd;
  background:linear-gradient(90deg,rgba(74,222,128,.09),rgba(56,189,248,.06));
  border-bottom:1px solid rgba(74,222,128,.18);letter-spacing:.2px}
#act-banner[hidden]{display:none}
#act-banner span{white-space:nowrap}


/* ---- 多机总览的节点卡片 ------------------------------------------
 *
 * 左边一条竖色带表示状态。**失联用告警色**，不用灰 ——
 * 灰色会让人以为是"停用/不重要"，而失联是"中心不知道它的状况"，
 * 严重程度完全不同。
 */
.fl-node {
  border: 1px solid var(--line, #2a2f3a);
  border-left-width: 3px;
  border-radius: 8px;
  padding: 10px 12px;
  margin-bottom: 8px;
}
.fl-node.fl-ok      { border-left-color: var(--ok, #2ea043); }
.fl-node.fl-warn    { border-left-color: var(--warn, #d29922); }
.fl-node.fl-danger  { border-left-color: var(--danger, #f85149); }
.fl-node.fl-muted   { border-left-color: var(--muted, #6e7681); opacity: .72; }
.fl-node .fl-head   { display: flex; align-items: center; gap: 8px; }
.fl-node .fl-mark   { font-size: 13px; line-height: 1; }
.fl-node.fl-ok      .fl-mark { color: var(--ok, #2ea043); }
.fl-node.fl-warn    .fl-mark { color: var(--warn, #d29922); }
.fl-node.fl-danger  .fl-mark { color: var(--danger, #f85149); }
.fl-node .fl-detail { font-size: 13px; margin: 6px 0 4px; }
.fl-node .fl-bits   { font-size: 12px; color: var(--muted, #8b949e);
                      margin-bottom: 4px; }
