/* ==========================================================
   Server Monitor Panel - tech dashboard theme
   ========================================================== */
:root {
    --bg:        #04091a;
    --bg2:       #071126;
    --panel:     rgba(11, 24, 48, .72);
    --panel-2:   rgba(9, 20, 40, .85);
    --line:      rgba(0, 174, 255, .22);
    --line-str:  rgba(0, 229, 255, .55);
    --cyan:      #00e5ff;
    --blue:      #2f7bff;
    --purple:    #a855f7;
    --green:     #22e39a;
    --amber:     #ffb020;
    --red:       #ff4d6d;
    --txt:       #dceaff;
    --txt-2:     #9fb6d8;
    --mut:       #6b86ab;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
    margin: 0;
    background: var(--bg);
    color: var(--txt);
    font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", system-ui, sans-serif;
    font-size: 14px;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

/* ---------- animated background ---------- */
.bg-layer { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
.bg-layer.grid {
    background-image:
        linear-gradient(rgba(0, 174, 255, .05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 174, 255, .05) 1px, transparent 1px);
    background-size: 46px 46px;
    mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 40%, transparent 100%);
}
.bg-layer.glow {
    background:
        radial-gradient(900px 520px at 12% -8%, rgba(0, 132, 255, .22), transparent 60%),
        radial-gradient(900px 520px at 88% 8%, rgba(168, 85, 247, .16), transparent 60%),
        radial-gradient(1200px 700px at 50% 110%, rgba(0, 229, 255, .12), transparent 60%);
}
.bg-layer.scan::after {
    content: '';
    position: absolute; left: 0; right: 0; top: -180px; height: 180px;
    background: linear-gradient(180deg, transparent, rgba(0, 229, 255, .05), transparent);
    animation: scan 9s linear infinite;
    will-change: transform;     /* 只动 transform：全屏扫描线不再每帧触发重排重绘 */
}
@keyframes scan { 0% { transform: translateY(0); } 100% { transform: translateY(calc(100vh + 360px)); } }

/* ---------- layout: fit 1920x1080 without page scrolling ---------- */
.app {
    position: relative; z-index: 1; display: flex; flex-direction: column;
    min-height: 100vh; padding: 0 14px 8px;
}
/* on a real dashboard screen (1080p and up) lock to exactly one viewport */
@media (min-height: 900px) {
    html, body { overflow: hidden; }
    .app { height: 100vh; min-height: 0; }
}

/* ---------- header ---------- */
.topbar {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: space-between;
    height: 62px; padding: 0 6px; position: relative;
}
.topbar::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 6px; height: 1px;
    background: linear-gradient(90deg, transparent, var(--line-str), rgba(168,85,247,.5), var(--line-str), transparent);
}
.demo-strip {
    display: none; align-items: center; justify-content: center; gap: 8px;
    padding: 6px 12px; margin: 0 0 8px;
    background: rgba(255,176,32,.10); border: 1px dashed rgba(255,176,32,.45);
    border-radius: 8px; color: #ffd28a; font-size: 12px; letter-spacing: .5px;
}
.demo-strip.show { display: flex; }
.demo-strip svg { width: 14px; height: 14px; color: #ffd28a; }
.brand { display: flex; align-items: center; gap: 14px; }
.logo {
    width: 42px; height: 42px; border-radius: 10px; display: grid; place-items: center;
    background: linear-gradient(135deg, rgba(0,229,255,.18), rgba(47,123,255,.25));
    border: 1px solid rgba(0,229,255,.4);
    box-shadow: 0 0 18px rgba(0,229,255,.25), inset 0 0 14px rgba(0,229,255,.12);
}
.logo svg { width: 24px; height: 24px; }
/* 高科技盾牌 LOGO 动效 */
.shield-logo { width: 24px; height: 24px; overflow: visible; }
.shield-glow {
    filter: drop-shadow(0 0 3px rgba(0,229,255,.85));
    transform-origin: center; animation: shieldPulse 2.6s ease-in-out infinite;
}
.shield-mid { animation: shieldFloat 4.2s ease-in-out infinite; transform-origin: center; }
.shield-check {
    stroke-dasharray: 16; stroke-dashoffset: 0;
    animation: shieldDraw 3.4s ease-in-out infinite;
}
.shield-scan { animation: shieldScan 2.8s linear infinite; }
@keyframes shieldPulse {
    0%, 100% { opacity: .82; filter: drop-shadow(0 0 3px rgba(0,229,255,.7)); }
    50%      { opacity: 1;   filter: drop-shadow(0 0 7px rgba(0,229,255,1)); }
}
@keyframes shieldFloat { 0%, 100% { opacity: .4; } 50% { opacity: .62; } }
@keyframes shieldDraw {
    0%   { stroke-dashoffset: 16; }
    28%  { stroke-dashoffset: 0; }
    78%  { stroke-dashoffset: 0; }
    100% { stroke-dashoffset: -16; }
}
@keyframes shieldScan {
    0%   { transform: translateY(2px);  opacity: 0; }
    12%  { opacity: .6; }
    88%  { opacity: .6; }
    100% { transform: translateY(20px); opacity: 0; }
}
.brand h1 { margin: 0; font-size: 22px; letter-spacing: 3px; font-weight: 700;
    background: linear-gradient(90deg, #ffffff, #8fdcff 60%, #00e5ff);
    -webkit-background-clip: text; background-clip: text; color: transparent; }
.brand .sub { font-size: 10px; letter-spacing: 4px; color: var(--mut); margin-top: 2px; }

.clock { text-align: center; }
.clock .t { font-size: 26px; font-weight: 300; letter-spacing: 3px; font-variant-numeric: tabular-nums; text-shadow: 0 0 16px rgba(0,229,255,.45); }
.clock .d { font-size: 11px; color: var(--mut); letter-spacing: 2px; }

.topstat { display: flex; gap: 10px; align-items: center; }
.chip {
    display: flex; align-items: center; gap: 8px; padding: 7px 14px; border-radius: 20px;
    background: var(--panel-2); border: 1px solid var(--line); font-size: 12px; color: var(--txt-2);
}
.chip b { color: var(--cyan); font-size: 15px; font-variant-numeric: tabular-nums; }
.chip.online b { color: var(--green); }
.chip.offline b { color: var(--red); }
.chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 8px var(--green); animation: pulse 1.6s infinite; }
.chip.link.ok  { color: #9ff3ff; border-color: rgba(0,229,255,.45); }
.chip.link.ok .dot { background: var(--cyan); box-shadow: 0 0 8px var(--cyan); }
.chip.link.bad { color: #ffbecb; border-color: rgba(255,77,109,.55); background: rgba(255,77,109,.10); }
.chip.link.bad .dot { background: var(--red); box-shadow: 0 0 8px var(--red); animation-duration: .7s; }
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }

.btn {
    padding: 7px 16px; border-radius: 8px; cursor: pointer; font-size: 13px;
    background: linear-gradient(135deg, rgba(0,229,255,.16), rgba(47,123,255,.18));
    border: 1px solid rgba(0,229,255,.45); color: #bff2ff; letter-spacing: 1px;
    transition: .18s; display: inline-flex; align-items: center; gap: 6px;
}
.btn:hover { background: linear-gradient(135deg, rgba(0,229,255,.35), rgba(47,123,255,.35)); box-shadow: 0 0 16px rgba(0,229,255,.3); }
.btn.ghost { background: rgba(255,255,255,.04); border-color: var(--line); color: var(--txt-2); }
.btn.ghost:hover { color: var(--cyan); border-color: rgba(0,229,255,.5); }
.btn.danger { background: rgba(255,77,109,.12); border-color: rgba(255,77,109,.45); color: #ffb3c0; }
.btn.danger:hover { background: rgba(255,77,109,.28); }
.btn.sm { padding: 4px 10px; font-size: 12px; }
/* 顶栏四个导航按钮高度一致（实心按钮与 ghost 下拉按钮对齐） */
.topstat > .btn, .topstat > .tmenu > .btn { height: 36px; box-sizing: border-box; }

/* ---------- main grid ---------- */
.main {
    flex: 1 1 auto; min-height: 0; display: grid; gap: 12px;
    grid-template-columns: 384px minmax(0, 1fr) 330px;
    /* 关键：行高必须受容器高度限制，否则右栏（6 个面板）会按内容把行撑到 2500px，
       底栏被顶出视口，且 .right 的 overflow-y:auto 永远不会真正滚动 */
    grid-template-rows: minmax(0, 1fr);
    align-items: stretch;
}
.main > aside, .main > .center { min-height: 0; }
.main > aside.panel { display: flex; flex-direction: column; }
@media (max-width: 1600px) { .main { grid-template-columns: 356px minmax(0,1fr) 316px; } }
@media (max-width: 1400px) { .main { grid-template-columns: 336px minmax(0,1fr) 300px; } }
@media (max-width: 1200px) { .main { grid-template-columns: 1fr; } }

.panel {
    position: relative; background: rgba(10, 22, 44, .9); border: 1px solid var(--line);
    border-radius: 12px; padding: 14px 16px 16px;
    box-shadow: inset 0 1px 0 rgba(255,255,255,.04), 0 8px 30px rgba(0,0,0,.35);
    /* 这里原来有 backdrop-filter: blur(4px)。页面上有十几个 .panel，每一个都会
       让浏览器为它单独做一次背景采样 + 高斯模糊，是整页掉帧最大的单项开销。
       改成把面板底色做实（.9 不透明），视觉上和"模糊后的底纹"几乎一样，
       但省掉了十几层 backdrop-filter。 */
}
.panel::before, .panel::after {
    content: ''; position: absolute; width: 13px; height: 13px; border: 2px solid var(--line-str);
}
.panel::before { top: -1px; left: -1px; border-right: 0; border-bottom: 0; border-radius: 12px 0 0 0; }
.panel::after  { bottom: -1px; right: -1px; border-left: 0; border-top: 0; border-radius: 0 0 12px 0; }

.panel-title {
    display: flex; align-items: center; justify-content: space-between;
    font-size: 13px; letter-spacing: 2px; color: #cfe6ff; margin-bottom: 12px;
}
.panel-title .t { display: flex; align-items: center; gap: 8px; }
.panel-title .t::before {
    content: ''; width: 3px; height: 13px; background: var(--cyan);
    box-shadow: 0 0 8px var(--cyan); border-radius: 2px;
}
.panel-title .en { font-size: 9px; color: var(--mut); letter-spacing: 2px; }

/* ---------- globe panel ---------- */
.globe-wrap { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 1100px) { .globe-wrap { grid-template-columns: 1fr; } }

/* the map panel soaks up whatever vertical space is left over */
.globe-panel { flex: 1 1 auto; min-height: 280px; display: flex; flex-direction: column; }
.globe-wrap { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 12px; }
.globe-canvas-box {
    position: relative; height: 100%; min-height: 240px; border-radius: 10px; overflow: hidden;
    background:
        radial-gradient(600px 320px at 50% 55%, rgba(0,90,200,.18), transparent 70%),
        rgba(3,9,22,.5);
    border: 1px solid rgba(0,174,255,.14);
}
.globe-canvas-box::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background-image:
        linear-gradient(rgba(0,174,255,.05) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0,174,255,.05) 1px, transparent 1px);
    background-size: 34px 34px;
}
#globeCanvas { position: relative; width: 100%; height: 100%; display: block; touch-action: none; }
#globeCanvas.hide { display: none; }
#chinaCanvas { position: absolute; inset: 0; width: 100%; height: 100%; display: none; touch-action: none; }
#chinaCanvas.show { display: block; }
.globe-hint {
    position: absolute; left: 12px; bottom: 10px; z-index: 2; pointer-events: none;
    font-size: 10px; letter-spacing: .5px; color: rgba(150,200,240,.55);
}
.globe-btns { position: absolute; right: 10px; top: 10px; z-index: 3; display: flex; flex-direction: column; gap: 6px; }
.globe-reset {
    padding: 4px 10px; font-size: 11px; border-radius: 6px; cursor: pointer;
    /* 画布上的节点名/省名会从按钮底下透出来，所以要够实 + 背景虚化：
       模糊把背后的文字糊成光斑，按钮自己的字才读得清 */
    /* 底色已经 0.94 不透明，模糊基本看不出来，但每帧都要为它重算一次 */
    background: rgba(6,15,32,.94); border: 1px solid rgba(0,229,255,.35);
    color: #bff2ff; letter-spacing: .5px; white-space: nowrap;
}
.globe-reset:hover { background: rgba(0,229,255,.16); border-color: rgba(0,229,255,.6); }
.globe-reset.alt { border-color: rgba(255,196,92,.4); color: #ffe4b0; }
.globe-reset.alt:hover { background: rgba(255,196,92,.16); border-color: rgba(255,196,92,.7); }
/* 「开」状态要能压住画布上透出来的节点名/省名，所以底色仍是实色，
   靠色相（青色 vs 琥珀）和边框来区分开关，而不是靠透明度 */
.globe-reset.on { background: rgba(10,40,56,.94); border-color: rgba(0,229,255,.7); }
.globe-reset.alt.on { background: rgba(56,39,13,.94); }
.map-mode-tag {
    position: absolute; left: 12px; top: 10px; z-index: 2; pointer-events: none;
    font: 600 10px/1 ui-monospace, Consolas, monospace; letter-spacing: 1.6px;
    color: rgba(0,229,255,.6); padding: 4px 7px; border-radius: 5px;
    background: rgba(6,18,38,.5); border: 1px solid rgba(0,229,255,.18);
}
/* NOTE: the small-screen block below pins .globe-canvas-box to 318px, so the
   fullscreen rules need the higher specificity they get from the :fullscreen /
   .fs-fake class — do not "simplify" them into a plain .globe-canvas-box rule. */
/* .fs-on 是原生全屏时由 JS 挂的类：个别内核不认 :fullscreen、或选择器被样式压缩工具
   改写时，全屏布局靠它兜底 —— 右侧栏同样被隐藏，保证全屏只有地球/地图 */
.globe-panel:fullscreen,
.globe-panel.fs-fake,
.globe-panel.fs-on {
    width: 100vw; height: 100vh;
    margin: 0; border-radius: 0; border: none;
    padding: 14px 16px;
    background: #040b18;
    display: flex; flex-direction: column;
}
/* 只有页面内全屏退化（fs-fake）需要自己固定定位；原生全屏由浏览器 top-layer 负责 */
.globe-panel.fs-fake { position: fixed; inset: 0; z-index: 9000; }
.globe-panel:fullscreen::backdrop { background: #040b18; }
.globe-panel:fullscreen .globe-wrap,
.globe-panel.fs-fake .globe-wrap,
.globe-panel.fs-on .globe-wrap {
    flex: 1 1 auto; min-height: 0; height: 100%;
    /* 全屏只展示地球/地图：右侧 KPI 与「实时访问来源」整块隐藏，地球占满 */
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
}
.globe-panel:fullscreen .globe-canvas-box,
.globe-panel.fs-fake .globe-canvas-box,
.globe-panel.fs-on .globe-canvas-box { height: 100%; min-height: 0; }
.globe-panel:fullscreen .globe-side,
.globe-panel.fs-fake .globe-side,
.globe-panel.fs-on .globe-side { display: none !important; }

/* small screens go back to normal page scrolling with a fixed-height map */
@media (max-height: 899px), (max-width: 1100px) {
    html, body { overflow: auto; }
    .app { height: auto; min-height: 100vh; }
    .globe-panel { flex: 0 0 auto; }
    .globe-canvas-box { height: 318px; }
    .visitor-list { max-height: 232px; }
}

.globe-side { display: flex; flex-direction: column; min-height: 0; }
.globe-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.gk {
    padding: 10px 12px; border-radius: 10px; text-align: center;
    background: rgba(6,16,32,.6); border: 1px solid var(--line);
}
.gk b { display: block; font-size: 22px; font-weight: 600; color: var(--cyan);
        text-shadow: 0 0 14px rgba(0,229,255,.5); font-variant-numeric: tabular-nums; }
.gk span { font-size: 10px; color: var(--mut); letter-spacing: 1px; }

.visitor-head {
    display: flex; align-items: center; justify-content: space-between;
    margin: 12px 0 8px; font-size: 12px; letter-spacing: 2px; color: #cfe6ff;
}
.visitor-head .vt::before {
    content: ''; display: inline-block; width: 3px; height: 12px; margin-right: 8px;
    background: var(--green); box-shadow: 0 0 8px var(--green); border-radius: 2px;
    vertical-align: -1px;
}
.visitor-head .en { font-size: 9px; color: var(--mut); letter-spacing: 2px; }

.visitor-list { flex: 1 1 auto; min-height: 60px; max-height: none; overflow-y: auto; padding-right: 4px; }
.visitor-list::-webkit-scrollbar { width: 5px; }
.visitor-list::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
/* 访问来源：一行放下（IP · 属地 · 传输协议 · 服务/端口 · → 节点 · 连接数），过窄时属地/节点名省略 */
.vi {
    display: flex; align-items: center; gap: 5px; white-space: nowrap;
    padding: 6px 8px; margin-bottom: 6px; border-radius: 8px;
    background: rgba(10,24,46,.55); border: 1px solid rgba(0,174,255,.12);
    transition: border-color .35s ease, background .35s ease;
}
/* 只有新出现的行才播入场动画：整块重绘时若每行都重播，列表每次轮询都在闪 */
.vi.fresh { animation: slideIn .3s ease; }
.vi .ico { flex: none; color: var(--green); font-size: 9px; }
.vi .ip { flex: none; font-family: Consolas, monospace; font-size: 11px; color: #eaf5ff; }
.vi .loc { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; color: #9fb6d8; font-size: 10.5px; }
.vi .way {
    flex: none; font-size: 9.5px; letter-spacing: .3px; padding: 1px 5px; border-radius: 4px;
    color: #8fdcff; background: rgba(0,229,255,.12); border: 1px solid rgba(0,229,255,.28);
}
.vi .way b { font-weight: 600; margin-left: 3px; font-size: 9px; color: #5f8fc0; }
/* 传输层协议位：TCP 绿 / UDP 黄 / 其他紫，一眼看清走的是什么连接 */
.vi .way.proto { font-weight: 600; letter-spacing: .4px; }
.vi .way.proto.tcp { color: #8ff0c8; background: rgba(34,227,154,.12); border-color: rgba(34,227,154,.30); }
.vi .way.proto.udp { color: #ffd28a; background: rgba(255,176,32,.12); border-color: rgba(255,176,32,.32); }
.vi .way.proto.oth { color: #d5b3ff; background: rgba(168,85,247,.12); border-color: rgba(168,85,247,.32); }
.vi .way.unk { color: #9fb6d8; background: rgba(159,182,216,.10); border-color: rgba(159,182,216,.24); }
.vi .route { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; color: var(--mut); }
.vi .route b { color: var(--cyan); font-weight: 600; }
.vi .cnt { flex: none; font-size: 10.5px; color: var(--amber); font-variant-numeric: tabular-nums; }
.vi .cnt::after { content: ' 连接'; font-size: 9px; color: #7d97b8; }
.vi-empty { text-align: center; color: var(--mut); font-size: 12px; padding: 26px 0; }

/* ---------- server list ---------- */
/* 「全部汇总」已从按钮行移到面板标题右侧（NODES 旁边），所以原来的 .srv-tools 行取消 */
.panel-title .pt-r { display: inline-flex; align-items: center; gap: 9px; }
.panel-title .btn.sm { padding: 3px 9px; font-size: 11px; letter-spacing: .5px; }
#btnAll { gap: 5px; }
#btnAll.on {
    background: linear-gradient(135deg, rgba(0,229,255,.30), rgba(47,123,255,.30));
    border-color: rgba(0,229,255,.75); color: #eafcff;
    box-shadow: 0 0 14px rgba(0,229,255,.22);
}
.srv-list { flex: 1 1 auto; min-height: 160px; max-height: none; overflow-y: auto; padding-right: 4px; }
.srv-list::-webkit-scrollbar, .modal-body::-webkit-scrollbar { width: 5px; }
.srv-list::-webkit-scrollbar-thumb, .modal-body::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }

.srv-card {
    position: relative; padding: 11px 12px 10px; margin-bottom: 10px; border-radius: 12px; cursor: pointer;
    background: linear-gradient(135deg, rgba(14,30,58,.72), rgba(9,20,40,.58));
    border: 1px solid rgba(0,174,255,.16);
    transition: transform .22s cubic-bezier(.2,.8,.3,1), border-color .22s, box-shadow .22s;
    overflow: hidden;
}
/* 右上角氛围光斑：hover 时亮起（只动 opacity，不引起重排） */
.srv-card::after {
    content: ''; position: absolute; right: -30px; top: -30px; width: 86px; height: 86px; border-radius: 50%;
    background: radial-gradient(circle, rgba(0,229,255,.13), transparent 70%);
    opacity: 0; transition: opacity .3s; pointer-events: none;
}
.srv-card:hover {
    border-color: rgba(0,229,255,.45); transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0,10,26,.45), 0 0 14px rgba(0,229,255,.10);
}
.srv-card:hover::after { opacity: 1; }
/* 四角科技感边框：左上 + 右下两对青色 L 形，mix-blend 让它在深色卡面上发亮 */
.srv-frame {
    position: absolute; inset: 0; border-radius: 12px; pointer-events: none; opacity: .55;
    background:
        linear-gradient(var(--cyan), var(--cyan)) 0 0 / 15px 2px no-repeat,
        linear-gradient(var(--cyan), var(--cyan)) 0 0 / 2px 15px no-repeat,
        linear-gradient(var(--cyan), var(--cyan)) 100% 100% / 15px 2px no-repeat,
        linear-gradient(var(--cyan), var(--cyan)) 100% 100% / 2px 15px no-repeat;
    mix-blend-mode: screen; transition: opacity .3s;
}
.srv-card:hover .srv-frame { opacity: .95; }
.srv-card.active .srv-frame { opacity: 1; }
/* hover 时一道斜向高光扫过卡面（只动 transform/opacity，无重排） */
.srv-scan {
    position: absolute; top: -30%; left: -60%; width: 55%; height: 160%; pointer-events: none;
    background: linear-gradient(105deg, transparent, rgba(120,245,255,.16), transparent);
    transform: skewX(-18deg) translateX(0); opacity: 0; transition: opacity .3s;
}
.srv-card:hover .srv-scan { opacity: 1; animation: cardSweep .7s cubic-bezier(.2,.8,.3,1) both; }
@keyframes cardSweep { from { left: -60%; } to { left: 130%; } }
.srv-card.active { border-color: var(--cyan); box-shadow: 0 0 0 1px rgba(0,229,255,.35), 0 0 22px rgba(0,229,255,.18); }
.srv-card.active::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--cyan); box-shadow: 0 0 10px var(--cyan); animation: actPulse 2.4s ease-in-out infinite; }
@keyframes actPulse { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }
.srv-card.off { opacity: .58; }
/* 入场：只有 keyed 首建的 .fresh 播一次，轮询复用元素不会重放（无闪烁约定） */
.srv-card.fresh { animation: cardIn .38s cubic-bezier(.2,.9,.3,1) both; }
@keyframes cardIn { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
.srv-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.srv-name { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; color: #eaf5ff; }
.srv-name .st { position: relative; width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 8px var(--green); }
/* 在线状态点外圈的呼吸波纹：小面积常驻动画（氛围感，非整屏闪烁） */
.srv-name .st::after {
    content: ''; position: absolute; inset: -4px; border-radius: 50%;
    border: 1px solid rgba(34,227,154,.55); opacity: 0;
    animation: stPulse 2.8s ease-out infinite;
}
@keyframes stPulse { 0% { transform: scale(.5); opacity: .85; } 70%, 100% { transform: scale(1.8); opacity: 0; } }
.srv-card.off .srv-name .st { background: var(--red); box-shadow: 0 0 8px var(--red); }
.srv-card.off .srv-name .st::after { animation: none; }
.srv-chev { color: #3d5a80; font-size: 15px; line-height: 1; transition: transform .22s, color .22s; }
.srv-card:hover .srv-chev { transform: translateX(3px); color: var(--cyan); }
.srv-card.active .srv-chev { color: var(--cyan); }
.srv-ip { font-size: 10px; color: var(--mut); font-family: Consolas, monospace; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srv-meta { display: flex; align-items: center; gap: 6px; }
.srv-tags { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; flex-wrap: nowrap; }
.srv-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }
.srv-metrics .m { font-size: 10px; color: var(--mut); }
.srv-metrics .ml { display: inline-flex; align-items: center; gap: 4px; letter-spacing: .4px; }
.srv-metrics .ml svg { width: 11px; height: 11px; opacity: .75; }
/* 数值位与进度条常驻，只改 textContent / width，所以宽度变化能走 CSS 过渡（不再跳变） */
.srv-metrics .v {
    float: right; font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--txt);
    transition: color .4s, text-shadow .4s;
}
.srv-metrics .v.ok   { color: var(--cyan);  text-shadow: 0 0 10px rgba(0,229,255,.35); }
.srv-metrics .v.warn { color: var(--amber); text-shadow: 0 0 10px rgba(255,176,32,.35); }
.srv-metrics .v.crit { color: var(--red);   text-shadow: 0 0 10px rgba(255,77,109,.4); }
.srv-metrics .bar { height: 4px; border-radius: 3px; margin-top: 4px; }
.srv-net { margin-top: 8px; display: flex; gap: 14px; font-size: 10px; font-variant-numeric: tabular-nums; }
.srv-net .ut { margin-left: auto; color: var(--mut); white-space: nowrap; }
.srv-net .ut b { color: #9fc0e6; font-weight: 600; }
.srv-net .up { color: var(--amber); }
.srv-net .dn { color: #8fdcff; }
.srv-net b { font-weight: 500; color: #9fb6d8; }
.srv-err { margin-top: 5px; font-size: 10px; color: #ff8fa3; }
.srv-err[hidden] { display: none; }
.bar { height: 3px; border-radius: 2px; background: rgba(255,255,255,.09); margin-top: 3px; overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 2px; background: var(--cyan); transition: width .6s cubic-bezier(.25,.8,.35,1), background .4s, box-shadow .4s; box-shadow: 0 0 8px var(--cyan); }
.bar.warn i { background: var(--amber); box-shadow: 0 0 8px var(--amber); }
.bar.crit i { background: var(--red); box-shadow: 0 0 8px var(--red); }
.srv-actions { display: flex; gap: 6px; margin-top: 9px; opacity: 0; transition: .18s; }
.srv-card:hover .srv-actions { opacity: 1; }

/* ---------- center ---------- */
.center { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.kpi-row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; flex: 0 0 auto; }
@media (max-width: 1500px) { .kpi-row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.kpi {
    position: relative; padding: 10px 12px 14px; border-radius: 12px; overflow: hidden;
    background: linear-gradient(135deg, rgba(13,29,56,.85), rgba(8,18,36,.7));
    border: 1px solid var(--line);
    transition: transform .35s cubic-bezier(.2,.8,.3,1), border-color .35s, box-shadow .35s;
}
.kpi.c-cyan   { --kc: #00e5ff; --kc-rgb: 0,229,255; }
.kpi.c-green  { --kc: #22e39a; --kc-rgb: 34,227,154; }
.kpi.c-amber  { --kc: #ffb020; --kc-rgb: 255,176,32; }
.kpi.c-purple { --kc: #a855f7; --kc-rgb: 168,85,247; }
.kpi:hover {
    transform: translateY(-2px);
    border-color: rgba(var(--kc-rgb), .42);
    box-shadow: 0 9px 24px rgba(0,0,0,.34), 0 0 20px rgba(var(--kc-rgb), .13);
}
.kpi .k-label { font-size: 10px; color: var(--mut); letter-spacing: 1px; white-space: nowrap; }
.kpi .k-val {
    font-size: 22px; font-weight: 600; margin-top: 4px; font-variant-numeric: tabular-nums;
    letter-spacing: 0; white-space: nowrap;
    color: var(--kc); text-shadow: 0 0 18px rgba(var(--kc-rgb), .45);
    transition: color .5s, text-shadow .5s;
}
.kpi .k-val small { font-size: 12px; font-weight: 500; opacity: .82; margin-left: 1px; }
.kpi .k-sub { font-size: 10px; color: var(--txt-2); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* 右上角图标：常驻微呼吸；hover 时放大量、转一点角度 */
.kpi .k-ico {
    position: absolute; right: 10px; top: 10px; width: 26px; height: 26px; border-radius: 8px;
    display: grid; place-items: center; color: var(--kc);
    background: rgba(var(--kc-rgb), .13); border: 1px solid rgba(var(--kc-rgb), .18);
    animation: kIcoBreath 4.2s ease-in-out infinite;
    transition: transform .35s cubic-bezier(.2,.8,.3,1), box-shadow .35s, background .35s;
}
.kpi:nth-child(3n) .k-ico { animation-delay: -1.4s; }
.kpi:nth-child(3n+2) .k-ico { animation-delay: -2.8s; }
.kpi .k-ico svg { width: 15px; height: 15px; }
.kpi:hover .k-ico {
    transform: translateY(-1px) scale(1.1) rotate(-5deg);
    background: rgba(var(--kc-rgb), .24); box-shadow: 0 0 15px rgba(var(--kc-rgb), .32);
}
@keyframes kIcoBreath { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }

/* 底部：CPU / 内存 = 真实占比条；速率 / 流量 = 数据流动光带 */
.kpi .k-tk {
    position: absolute; left: 12px; right: 12px; bottom: 7px; height: 2px; border-radius: 2px;
    background: rgba(255,255,255,.07); overflow: hidden;
}
.kpi .k-tk i {
    display: block; height: 100%; width: 0; border-radius: 2px;
    background: var(--kc); box-shadow: 0 0 10px var(--kc);
    transition: width .7s cubic-bezier(.25,.8,.35,1);
}
.kpi .k-flow {
    position: absolute; left: 12px; right: 12px; bottom: 7px; height: 2px; border-radius: 2px; overflow: hidden;
    background: linear-gradient(90deg, transparent, rgba(var(--kc-rgb), .18), transparent);
}
.kpi .k-flow::after {
    content: ''; position: absolute; top: 0; bottom: 0; width: 26%; border-radius: 2px;
    background: linear-gradient(90deg, transparent, var(--kc), transparent);
    box-shadow: 0 0 8px var(--kc);
    animation: kFlow 2.6s linear infinite;
}
.kpi:nth-child(2n) .k-flow::after { animation-duration: 3.4s; animation-delay: -1.1s; }
@keyframes kFlow { 0% { transform: translateX(-120%); } 100% { transform: translateX(430%); } }

/* 光扫：六张卡错相位，避免整屏同时刷一下 */
.kpi .k-shine {
    position: absolute; top: 0; bottom: 0; width: 46%; pointer-events: none;
    background: linear-gradient(100deg, transparent, rgba(var(--kc-rgb), .13), transparent);
    transform: translateX(-170%);
    animation: kSweep 6s ease-in-out infinite;
}
.kpi:nth-child(6n+2) .k-shine { animation-delay: -1s; }
.kpi:nth-child(6n+3) .k-shine { animation-delay: -2s; }
.kpi:nth-child(6n+4) .k-shine { animation-delay: -3s; }
.kpi:nth-child(6n+5) .k-shine { animation-delay: -4s; }
.kpi:nth-child(6n)   .k-shine { animation-delay: -5s; }
@keyframes kSweep { 0%, 58% { transform: translateX(-170%); } 86%, 100% { transform: translateX(250%); } }

/* 入场：只有首次创建（.fresh）才播，轮询重建元素时不会重放 */
.kpi.fresh { animation: kpiIn .5s cubic-bezier(.2,.9,.3,1) both; }
@keyframes kpiIn { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }

/* compact resource bars (replaced the old big gauges) */
.res-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
@media (max-width: 1100px) { .res-row { grid-template-columns: 1fr; } }
.res-item {
    padding: 10px 14px 12px; border-radius: 10px;
    background: linear-gradient(135deg, rgba(13,29,56,.85), rgba(8,18,36,.7));
    border: 1px solid var(--line);
}
.res-head { display: flex; justify-content: space-between; align-items: baseline; }
.res-head .n { font-size: 11px; color: var(--mut); letter-spacing: 1px; display: flex; align-items: center; gap: 6px; }
.res-head .rc { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.res-head .v { font-size: 19px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: .5px; }
.res-bar {
    margin-top: 9px; height: 7px; border-radius: 4px; position: relative; overflow: hidden;
    background:
        linear-gradient(90deg, transparent calc(70% - 1px), rgba(255,176,32,.35) calc(70% - 1px), rgba(255,176,32,.35) 70%, transparent 70%),
        linear-gradient(90deg, transparent calc(90% - 1px), rgba(255,64,64,.4) calc(90% - 1px), rgba(255,64,64,.4) 90%, transparent 90%),
        rgba(255,255,255,.07);
}
.res-bar i {
    display: block; height: 100%; border-radius: 4px; width: 0;
    transition: width .7s cubic-bezier(.25,.8,.35,1), background .4s, box-shadow .4s;
}

/* 主机运行日志：终端风格 */
.log-box {
    height: 118px; border-radius: 8px; overflow: hidden;
    background: rgba(4,12,24,.92); border: 1px solid rgba(0,174,255,.20);
    box-shadow: inset 0 0 24px rgba(0,0,0,.55);
    position: relative;
}
.log-box::before {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: repeating-linear-gradient(
        0deg, rgba(0,0,0,0) 0px, rgba(0,0,0,0) 2px,
        rgba(0,229,255,.025) 2px, rgba(0,229,255,.025) 4px
    );
    opacity: .6;
}
/* 两栏布局：内容先填满左栏再进右栏，超出的列被 overflow 裁掉，
   只要 DOM 里始终保持 2×N 行，删头加尾就等价于"整体上滚一行"，不存在整块重绘 */
.log-lines {
    height: 100%; overflow: hidden; padding: 8px 10px;
    font: 11px/1.45 Consolas, 'Courier New', monospace;
    color: #8ff0c8; text-shadow: 0 0 5px rgba(34,227,154,.28);
    column-count: 2; column-gap: 18px; column-fill: auto;
    column-rule: 1px solid rgba(0,229,255,.10);
}
.log-line {
    display: flex; gap: 8px; white-space: nowrap; overflow: hidden;
    break-inside: avoid;
}
/* 只有新追加的那一行做入场动画，避免整块重写时所有行同时闪一下 */
.log-line.fresh { animation: logIn .18s ease both; }
.log-line .ts { color: #5d7a9c; font-variant-numeric: tabular-nums; flex: none; }
.log-line .node { color: #8fdcff; flex: none; max-width: 74px; overflow: hidden; text-overflow: ellipsis; }
.log-line .tag {
    flex: none; padding: 0 5px; border-radius: 3px; font-size: 9px;
    background: rgba(0,229,255,.12); color: #8fdcff; letter-spacing: .4px;
}
.log-line .tag.warn { background: rgba(255,176,32,.14); color: #ffd28a; }
.log-line .tag.err  { background: rgba(255,77,109,.16); color: #ffb3c0; }
.log-line .tag.ok   { background: rgba(34,227,154,.14); color: #8ff0c8; }
.log-line .tag.sys  { background: rgba(168,85,247,.14); color: #d4b5ff; }
.log-line .tag.net  { background: rgba(0,133,255,.14); color: #8ec7ff; }
.log-line .msg { color: #c7e0ff; overflow: hidden; text-overflow: ellipsis; }
.log-line .msg b { color: #eafcff; font-weight: 600; }
@keyframes logIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }

.chart { width: 100%; height: 148px; }
.chart.lg { height: 262px; }
.chart.sm { height: 124px; }

.chart-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; flex: 0 0 auto; }
@media (max-width: 1100px) { .chart-row { grid-template-columns: 1fr; } }

/* big total numbers */
.totals { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.total-box {
    padding: 12px 14px; border-radius: 10px; background: rgba(6,16,32,.6);
    border: 1px solid var(--line); position: relative; overflow: hidden;
}
.total-box .lbl { font-size: 11px; color: var(--mut); letter-spacing: 1px; display: flex; align-items: center; gap: 6px; }
.total-box .val { font-size: 22px; font-weight: 600; margin-top: 5px; font-variant-numeric: tabular-nums; }
.total-box.up  .val { color: var(--amber); }
.total-box.dn  .val { color: var(--cyan); }
.total-box .sub { font-size: 10px; color: var(--txt-2); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* ---------- right column ---------- */
.right { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.right > .panel { display: flex; flex-direction: column; min-height: 0; }
.right > .panel:nth-child(1) { flex: 1 1 30%; }
.right > .panel:nth-child(3) { flex: 1 1 42%; }
#volBox, #infoBox, .right .rank { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 4px; }
#volBox::-webkit-scrollbar, #infoBox::-webkit-scrollbar, .right .rank::-webkit-scrollbar { width: 5px; }
#volBox::-webkit-scrollbar-thumb, #infoBox::-webkit-scrollbar-thumb, .right .rank::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
.vol-item { margin-bottom: 10px; }
.vol-head { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px; }
.vol-head .n { color: #e6f2ff; font-family: Consolas, monospace; }
.vol-head .v { color: var(--txt-2); font-variant-numeric: tabular-nums; font-size: 11px; }
.vol-bar { height: 8px; border-radius: 5px; background: rgba(255,255,255,.07); overflow: hidden; }
.vol-bar i { display: block; height: 100%; border-radius: 5px; transition: width .6s; }

.rank { display: flex; flex-direction: column; gap: 9px; }
.rank-item { display: grid; grid-template-columns: 22px 1fr 62px; align-items: center; gap: 8px; font-size: 12px; }
.rank-item .no { width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; font-size: 11px; color: var(--mut); background: rgba(255,255,255,.05); }
.rank-item:nth-child(1) .no { background: rgba(255,77,109,.2); color: #ff8fa3; }
.rank-item:nth-child(2) .no { background: rgba(255,176,32,.18); color: var(--amber); }
.rank-item:nth-child(3) .no { background: rgba(0,229,255,.16); color: var(--cyan); }
.rank-item .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #dceaff; }
.rank-item .rb { height: 5px; border-radius: 3px; background: rgba(255,255,255,.07); overflow: hidden; margin-top: 4px; }
.rank-item .rb i { display: block; height: 100%; background: linear-gradient(90deg, var(--blue), var(--cyan)); }
.rank-item .vv { text-align: right; font-variant-numeric: tabular-nums; color: var(--txt); }

/* ---------- empty state ---------- */
.empty { text-align: center; padding: 40px 20px; color: var(--mut); }
.empty svg { width: 56px; height: 56px; opacity: .5; margin-bottom: 14px; }
.empty p { margin: 8px 0 18px; font-size: 13px; }

/* ---------- modal ---------- */
/* 2026-09-26：弹层重做 —— 遮罩毛玻璃 + 弹窗半透明玻璃质感 + 入场动效 */
.modal-mask {
    position: fixed; inset: 0; background: rgba(2,6,16,.56);
    backdrop-filter: blur(9px) saturate(1.18);
    -webkit-backdrop-filter: blur(9px) saturate(1.18);
    display: none; align-items: center; justify-content: center; z-index: 99;
    animation: maskIn .22s ease both;
}
@keyframes maskIn { from { opacity: 0; } to { opacity: 1; } }
.modal-mask.show { display: flex; }
.modal {
    width: 520px; max-width: 92vw;
    /* 半透明 + 毛玻璃：背后的地球/列表会透出来一层，但内容依然清晰 */
    background: linear-gradient(160deg, rgba(15,32,62,.86), rgba(7,16,35,.9));
    backdrop-filter: blur(16px) saturate(1.2);
    -webkit-backdrop-filter: blur(16px) saturate(1.2);
    border: 1px solid rgba(0,229,255,.3); border-radius: 14px; padding: 22px 24px;
    box-shadow: 0 26px 80px rgba(0,0,0,.62), 0 0 54px rgba(0,229,255,.11), inset 0 1px 0 rgba(255,255,255,.06);
}
.modal h3 { margin: 0 0 16px; font-size: 16px; letter-spacing: 2px; color: #eaf5ff; }
.modal-body { max-height: 62vh; overflow-y: auto; }
.field { margin-bottom: 13px; }
.field label { display: block; font-size: 11px; color: var(--mut); margin-bottom: 5px; letter-spacing: 1px; }
.field input, .field select, .field textarea {
    width: 100%; padding: 9px 11px; border-radius: 8px; background: rgba(4,12,26,.8);
    border: 1px solid var(--line); color: var(--txt); font-size: 13px; outline: none; font-family: inherit;
}
.field textarea { resize: vertical; min-height: 60px; line-height: 1.65; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--cyan); box-shadow: 0 0 0 3px rgba(0,229,255,.1); }
.field .hint { font-size: 11px; color: var(--mut); margin-top: 5px; line-height: 1.5; }
.modal-foot { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }
/* 按钮文字在自己框内居中：.btn 是 inline-flex，遇到 min-width 撑宽时文字会贴左 */
.modal-foot .btn { justify-content: center; }
.test-out { margin-top: 10px; font-size: 12px; padding: 9px 11px; border-radius: 8px; display: none; }
.test-out.ok  { display: block; background: rgba(34,227,154,.1);  border: 1px solid rgba(34,227,154,.35); color: #7df3c4; }
.test-out.bad { display: block; background: rgba(255,77,109,.1);  border: 1px solid rgba(255,77,109,.35); color: #ffa8b6; }

/* ---------- toast ---------- */
.toast-wrap { position: fixed; top: 20px; right: 20px; z-index: 200; display: flex; flex-direction: column; gap: 10px; }
.toast {
    padding: 11px 18px; border-radius: 9px; font-size: 13px; color: #eaf5ff;
    background: rgba(10,22,44,.95); border: 1px solid var(--line-str);
    box-shadow: 0 8px 30px rgba(0,0,0,.5); animation: slideIn .25s ease;
}
.toast.err { border-color: rgba(255,77,109,.6); }
.toast.ok  { border-color: rgba(34,227,154,.6); }
@keyframes slideIn { from { transform: translateX(40px); opacity: 0 } to { transform: none; opacity: 1 } }

.foot-note { flex: 0 0 auto; text-align: center; font-size: 10px; color: var(--mut); padding: 6px 0 2px; letter-spacing: 1px; }
.tag { font-size: 10px; padding: 2px 7px; border-radius: 10px; background: rgba(0,229,255,.12); color: var(--cyan); border: 1px solid rgba(0,229,255,.3); }
.tag.off { background: rgba(255,77,109,.12); color: #ff8fa3; border-color: rgba(255,77,109,.3); }

/* ==========================================================
   ops extensions: alarm badge / filters / range / drawer / settings
   ========================================================== */

/* ---------- alarm button + badge ---------- */
.btn.ghost.alarm { position: relative; }
.btn.ghost.alarm .badge {
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    font-size: 11px; line-height: 18px; text-align: center; font-weight: 700;
    background: rgba(255,255,255,.08); color: var(--txt-2);
}
.btn.ghost.alarm.hot { border-color: rgba(255,77,109,.55); color: #ffc2cc; background: rgba(255,77,109,.10); }
.btn.ghost.alarm.hot .badge { background: var(--red); color: #fff; box-shadow: 0 0 10px rgba(255,77,109,.7); animation: pulse 1.4s infinite; }

/* ---------- node list filters ---------- */
.srv-filters { margin-bottom: 10px; }
.srv-filters input, .srv-filters select {
    width: 100%; padding: 6px 9px; border-radius: 7px; font-size: 12px;
    background: rgba(4,12,26,.8); border: 1px solid var(--line); color: var(--txt);
    outline: none; font-family: inherit;
}
.srv-filters input:focus, .srv-filters select:focus { border-color: var(--cyan); }
.srv-filters .fr { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 6px; }

/* ---------- history range bar ---------- */
.range-bar { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.range-bar .rl { font-size: 11px; color: var(--mut); letter-spacing: 1px; }
.range-bar .rsp { flex: 1 1 auto; }
.range-bar .rbs { display: flex; gap: 4px; background: rgba(4,12,26,.6); border: 1px solid var(--line); border-radius: 8px; padding: 3px; }
.rb {
    padding: 4px 12px; font-size: 12px; border-radius: 6px; cursor: pointer; border: 0;
    background: transparent; color: var(--txt-2); transition: .15s; font-family: inherit;
}
.rb:hover { color: var(--cyan); }
.rb.on { background: linear-gradient(135deg, rgba(0,229,255,.22), rgba(47,123,255,.24)); color: #bff2ff; box-shadow: 0 0 12px rgba(0,229,255,.2); }

/* ---------- drawer ---------- */
.drawer-bg {
    position: fixed; inset: 0; background: rgba(2,6,16,.55); backdrop-filter: blur(2px);
    opacity: 0; pointer-events: none; transition: .22s; z-index: 120;
}
.drawer-bg.show { opacity: 1; pointer-events: auto; }
.drawer {
    position: fixed; top: 0; right: 0; bottom: 0; width: 480px; max-width: 94vw; z-index: 130;
    display: flex; flex-direction: column; transform: translateX(102%); transition: transform .26s cubic-bezier(.4,0,.2,1);
    background: linear-gradient(200deg, #0c1a34, #060e1e);
    border-left: 1px solid rgba(0,229,255,.3); box-shadow: -18px 0 60px rgba(0,0,0,.6);
}
.drawer.show { transform: none; }
.drawer-head {
    flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
    padding: 16px 18px 12px; border-bottom: 1px solid var(--line);
}
.drawer-head .dt { font-size: 15px; font-weight: 600; color: #eaf5ff; letter-spacing: 1px; }
.dw-close { background: transparent; border: 0; color: var(--mut); font-size: 16px; cursor: pointer; padding: 2px 6px; }
.dw-close:hover { color: var(--cyan); }
.drawer-tabs { flex: 0 0 auto; display: flex; gap: 6px; padding: 10px 18px 0; }
.dwt {
    padding: 6px 14px; font-size: 12px; border-radius: 7px 7px 0 0; cursor: pointer;
    background: rgba(255,255,255,.03); border: 1px solid transparent; color: var(--mut);
}
.dwt:hover { color: var(--txt-2); }
.dwt.on { color: #bff2ff; background: rgba(0,229,255,.12); border-color: rgba(0,229,255,.35); }
.drawer-body { flex: 1 1 auto; overflow-y: auto; padding: 14px 18px; }
.drawer-body::-webkit-scrollbar { width: 5px; }
.drawer-body::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
.drawer-foot {
    flex: 0 0 auto; display: flex; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--line);
}
.drawer-foot:empty { display: none; }
.drawer-foot .btn { flex: 1; justify-content: center; }

/* ---------- alert items ---------- */
.al-item {
    position: relative; padding: 11px 12px 11px 14px; margin-bottom: 9px; border-radius: 9px;
    background: linear-gradient(135deg, rgba(14,30,58,.7), rgba(9,20,40,.5));
    border: 1px solid rgba(0,174,255,.16);
}
.al-item::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; border-radius: 9px 0 0 9px; }
.al-item.warn::before  { background: var(--amber); box-shadow: 0 0 8px var(--amber); }
.al-item.crit::before  { background: var(--red);   box-shadow: 0 0 8px var(--red); }
.al-item.ok::before    { background: var(--green); box-shadow: 0 0 8px var(--green); }
.al-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.al-name { font-size: 13px; font-weight: 600; color: #eaf5ff; }
.al-type { font-size: 10px; padding: 2px 7px; border-radius: 10px; border: 1px solid; }
.al-type.crit { background: rgba(255,77,109,.12); color: #ff8fa3; border-color: rgba(255,77,109,.35); }
.al-type.warn { background: rgba(255,176,32,.12); color: #ffd28a; border-color: rgba(255,176,32,.35); }
.al-type.ok   { background: rgba(34,227,154,.12); color: #7df3c4; border-color: rgba(34,227,154,.35); }
.al-msg { margin-top: 6px; font-size: 12px; color: var(--txt-2); line-height: 1.5; }
.al-meta { margin-top: 6px; font-size: 10px; color: var(--mut); font-variant-numeric: tabular-nums; }
.al-act { margin-top: 8px; display: flex; gap: 6px; }
.al-empty { text-align: center; color: var(--mut); font-size: 12px; padding: 40px 0; }
.al-empty svg { width: 44px; height: 44px; opacity: .45; margin-bottom: 10px; display: block; margin: 0 auto 10px; }

/* ---------- node detail ---------- */
.nd-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; margin-bottom: 12px; }
.nd-cell { padding: 9px 11px; border-radius: 8px; background: rgba(6,16,34,.6); border: 1px solid rgba(0,174,255,.14); }
.nd-cell .k { font-size: 10px; color: var(--mut); letter-spacing: 1px; }
.nd-cell .v { font-size: 15px; color: #eaf5ff; font-variant-numeric: tabular-nums; margin-top: 3px; }
.nd-cell .v small { font-size: 11px; color: var(--txt-2); }
.nd-bar { margin-top: 5px; }
.nd-sec { margin: 14px 0 7px; font-size: 11px; color: var(--cyan); letter-spacing: 1px; }
.nd-sec:first-child { margin-top: 0; }
.nd-row { display: flex; justify-content: space-between; padding: 5px 0; font-size: 12px; border-bottom: 1px dashed rgba(0,174,255,.1); }
.nd-row .k { color: var(--mut); }
.nd-row .v { color: var(--txt); font-variant-numeric: tabular-nums; }
.nd-pill { font-size: 10px; padding: 2px 7px; border-radius: 10px; }
.nd-pill.ok   { background: rgba(34,227,154,.14); color: #7df3c4; }
.nd-pill.bad  { background: rgba(255,77,109,.14); color: #ff8fa3; }
.nd-pill.mut  { background: rgba(255,255,255,.06); color: var(--mut); }
.proc-row { display: flex; justify-content: space-between; font-size: 11px; padding: 4px 0; color: var(--txt-2); font-variant-numeric: tabular-nums; }
.proc-row .pn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 62%; }

/* ---------- settings modal extras ---------- */
.modal.wide { width: 620px; }
.sec-title {
    margin: 16px 0 10px; font-size: 12px; color: var(--cyan); letter-spacing: 1px;
    display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
    border-left: 2px solid var(--cyan); padding-left: 8px;
}
.sec-title span { font-size: 10px; color: var(--mut); letter-spacing: 0; line-height: 1.55; }
.sec-title.danger { color: var(--red); border-left-color: var(--red); }
/* 添加弹层：两字段并排（分组 / 节点位置），避免纵向过长又错位 */
.field-row { display: flex; gap: 12px; }
.field-row .field { flex: 1 1 0; min-width: 0; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
.grid2 .field { margin-bottom: 11px; }
.sil-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sil-txt { font-size: 11px; color: var(--mut); margin-left: 4px; }
.sil-txt.on { color: var(--amber); }
.imp-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
/* 添加弹层：两种接入方式切换卡 */
.add-tabs { display: flex; gap: 8px; margin: 2px 0 16px; }
.atab {
    flex: 1 1 0; display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
    padding: 10px 14px; border-radius: 10px; cursor: pointer;
    background: rgba(4,12,26,.6); border: 1px solid var(--line); color: var(--mut);
    font-family: inherit; text-align: left; transition: all .16s ease;
}
.atab b { font-size: 13px; color: var(--txt); letter-spacing: .5px; }
.atab span { font-size: 11px; color: var(--mut); }
.atab:hover { border-color: rgba(0,229,255,.45); }
.atab.on { border-color: rgba(0,229,255,.85); background: linear-gradient(135deg, rgba(0,229,255,.16), rgba(0,140,255,.06)); box-shadow: 0 0 16px rgba(0,229,255,.14); }
.atab.on b { color: var(--cyan); }
.add-panel { animation: fadeUp .22s ease both; }
.method-hero { display: flex; gap: 13px; align-items: flex-start; margin-bottom: 12px; }
.mh-ico { width: 40px; height: 40px; flex: 0 0 auto; margin-top: 2px; opacity: .95; }
.method-hero b { font-size: 13.5px; color: #eaf5ff; }
.method-hero p { margin: 5px 0 0; font-size: 11.5px; line-height: 1.6; color: var(--mut); }
.method-hero p b { color: #cfe9ff; font-weight: 600; }
.mh-steps { margin: 0 0 14px; padding-left: 20px; font-size: 11.5px; line-height: 1.9; color: var(--txt); }
.mh-steps li { margin-bottom: 2px; }
/* 方法 1 下载按钮：整行宽、左对齐，作为推荐项高亮 */
.add-dl {
    justify-content: flex-start; text-align: left; width: 100%;
    border-color: rgba(0,229,255,.5); background: linear-gradient(135deg, rgba(0,229,255,.14), rgba(0,140,255,.06));
    color: #d8f6ff; box-shadow: 0 0 14px rgba(0,229,255,.12);
}
.add-dl:hover { background: linear-gradient(135deg, rgba(0,229,255,.24), rgba(0,140,255,.12)); border-color: rgba(0,229,255,.8); }
.add-dl em { font-style: normal; color: var(--mut); font-size: 10.5px; margin-left: 6px; }
.add-dl i.ic { color: var(--cyan); }
.modal-foot.in-panel { margin-top: 14px; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* 新用户默认权限：一行勾选 */
.perm-def { display: flex; flex-wrap: wrap; gap: 10px 16px; margin: 4px 0 10px; }
.perm-def .pd { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--txt-2); cursor: pointer; }
.perm-def .pd input { width: auto; }
.filebtn { position: relative; overflow: hidden; cursor: pointer; }
.filebtn input { position: absolute; left: -9999px; }
.chk { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--txt-2); cursor: pointer; }
.chk input { width: auto; }

/* ---------- 客户端接入（push 模式 / enrollment keys） ---------- */
.enroll { display: flex; flex-direction: column; gap: 9px; }
.ek-new { display: flex; gap: 8px; flex-wrap: wrap; }
.ek-new input {
    flex: 1 1 170px; min-width: 150px; height: 30px; padding: 0 10px;
    background: rgba(0, 0, 0, .25); border: 1px solid var(--line);
    border-radius: 6px; color: var(--txt); font-size: 12px; outline: none;
}
.ek-new input:focus { border-color: var(--line-str); }
.ek-list { display: flex; flex-direction: column; gap: 10px; }
.ek-item {
    border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px;
    background: rgba(0, 20, 45, .35);
}
.ek-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ek-name { font-size: 13px; font-weight: 600; color: var(--txt); }
.ek-meta { font-size: 11px; color: var(--mut); }
.ek-key {
    font-family: ui-monospace, Consolas, Menlo, monospace; font-size: 11px;
    background: rgba(0, 229, 255, .08); border: 1px solid var(--line);
    color: var(--cyan); padding: 2px 8px; border-radius: 4px;
    word-break: break-all; cursor: pointer;
}
.ek-cmd {
    margin: 8px 0 6px; padding: 9px 10px; border-radius: 6px;
    background: rgba(0, 0, 0, .45); border: 1px dashed var(--line);
    font-family: ui-monospace, Consolas, Menlo, monospace;
    font-size: 11px; line-height: 1.55; color: #9fe8ff;
    white-space: pre-wrap; word-break: break-all; user-select: all;
}
.ek-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.ek-empty { font-size: 12px; color: var(--mut); padding: 6px 2px; }
 .nd-pill.push { background: rgba(168, 85, 247, .16); color: #d5b3ff; }

/* ==========================================================
   顶部「客户端下载」下拉菜单
   ========================================================== */
 .tmenu { position: relative; display: inline-block; }
 .tmenu-pop {
     display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
     min-width: 276px; padding: 6px; border-radius: 10px;
     background: linear-gradient(135deg, rgba(13, 29, 56, .98), rgba(7, 17, 38, .98));
     border: 1px solid var(--line);
     box-shadow: 0 14px 40px rgba(0, 0, 0, .6), 0 0 0 1px rgba(0, 229, 255, .08);
 }
 .tmenu-pop.show { display: block; }
 .tmenu-i {
     display: block; width: 100%; text-align: left; gap: 8px;
     padding: 9px 11px; margin-bottom: 2px; border: 0; border-radius: 7px;
     background: transparent; color: var(--txt); font-size: 12.5px;
     font-family: inherit; cursor: pointer; text-decoration: none; transition: .15s;
 }
 .tmenu-i:last-child { margin-bottom: 0; }
 .tmenu-i:hover { background: rgba(0, 229, 255, .13); color: #fff; }
 .tmenu-i em { font-style: normal; font-size: 10.5px; color: var(--mut); }
 .tmenu-i:hover em { color: var(--txt-2); }
 .tmenu-sep { height: 1px; margin: 5px 4px; background: var(--line); }

/* ==========================================================
   左侧天气预报
   ========================================================== */
 .wx { position: relative; flex: 0 0 auto; margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--line); }
 .wx-title { margin-bottom: 10px; }
 .wx-tbar { display: flex; align-items: center; gap: 5px; }
 .wx-city {
     width: 104px; padding: 3px 7px; font-size: 11px; font-family: inherit;
     color: var(--txt); background: rgba(0, 0, 0, .35); cursor: pointer;
     border: 1px solid var(--line); border-radius: 5px; outline: none;
     text-overflow: ellipsis;
 }
 .wx-city:focus, .wx-city:hover { border-color: var(--cyan); color: #fff; }
 .wx-ref {
     width: 22px; height: 22px; padding: 0; line-height: 1; font-size: 13px;
     color: var(--txt-2); background: rgba(0, 0, 0, .35);
     border: 1px solid var(--line); border-radius: 5px; cursor: pointer; transition: .15s;
 }
 .wx-ref:hover { color: var(--cyan); border-color: var(--cyan); }
 .wx-ref.spin { animation: wxspin .8s linear infinite; }
 @keyframes wxspin { to { transform: rotate(360deg); } }

 .wx-now { position: relative; display: flex; align-items: center; gap: 12px; overflow: hidden; }
 .wx-now > * { position: relative; z-index: 1; }
 .wx-ico {
     width: 42px; height: 42px; flex: 0 0 42px; line-height: 1;
     filter: drop-shadow(0 0 9px rgba(0, 229, 255, .28));
 }
 .wx-svg { display: block; overflow: visible; }
 .wx-temp {
     font-size: 27px; font-weight: 600; line-height: 1; color: #fff;
     font-variant-numeric: tabular-nums; letter-spacing: -1px;
 }
 .wx-temp i { font-style: normal; font-size: 14px; color: var(--txt-2); margin-left: 2px; }
 .wx-desc { margin-top: 5px; font-size: 11.5px; color: var(--txt-2); }
 .wx-desc b { color: var(--cyan); font-weight: 600; }
 .wx-place { margin-left: auto; text-align: right; }
 .wx-place .c { font-size: 12.5px; color: #eaf5ff; font-weight: 600; }
 .wx-place .r { font-size: 10px; color: var(--mut); margin-top: 3px; }

 .wx-metrics {
     display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; margin-top: 11px;
 }
 .wx-metrics .m {
     padding: 6px 7px; border-radius: 7px; text-align: center;
     background: rgba(0, 0, 0, .28); border: 1px solid rgba(0, 174, 255, .12);
 }
 .wx-metrics .m span { display: block; font-size: 9.5px; color: var(--mut); letter-spacing: .5px; }
 .wx-metrics .m b {
     display: block; margin-top: 3px; font-size: 12.5px; font-weight: 600; color: var(--txt);
     font-variant-numeric: tabular-nums;
 }

 .wx-days { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin-top: 9px; }
 .wx-day {
     padding: 8px 4px 7px; border-radius: 8px; text-align: center; overflow: hidden;
     background: rgba(0, 0, 0, .26); border: 1px solid rgba(0, 174, 255, .12);
     animation: wx-in .45s cubic-bezier(.2, .8, .3, 1) both;
 }
 @keyframes wx-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
 .wx-day .d { display: block; font-size: 10px; color: var(--mut); }
 .wx-day .i { display: flex; justify-content: center; height: 20px; margin: 1px 0 2px; }
 .wx-day .r { display: block; font-size: 11px; font-variant-numeric: tabular-nums; }
 .wx-day .r b { color: #ff9a6b; font-weight: 600; }
 .wx-day .r i { font-style: normal; color: var(--txt-2); margin-left: 3px; }
 .wx-day .bar {
     position: relative; display: block; height: 3px; margin: 4px 3px 3px;
     border-radius: 2px; background: rgba(255, 255, 255, .08);
 }
 .wx-day .bar i {
     position: absolute; top: 0; bottom: 0; border-radius: 2px;
     background: linear-gradient(90deg, #4cc9f0, #ffb020, #ff6b6b);
     transform-origin: left; animation: wx-grow .7s cubic-bezier(.2, .8, .3, 1) both;
 }
 @keyframes wx-grow { from { transform: scaleX(.2); opacity: .3; } to { transform: scaleX(1); opacity: 1; } }
 .wx-day .p { display: block; font-size: 9.5px; color: var(--cyan); margin-top: 1px; }

 .wx-foot {
     display: flex; align-items: center; justify-content: space-between;
     margin-top: 9px; font-size: 10px; color: var(--mut);
 }
 .wx-foot .src { letter-spacing: .5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
 .wx-foot .live {
     display: inline-block; width: 5px; height: 5px; margin-right: 5px; border-radius: 50%;
     background: var(--green); box-shadow: 0 0 6px var(--green);
     animation: wx-live 2s ease-in-out infinite;
 }
 @keyframes wx-live { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
 .wx-msg { padding: 18px 6px; text-align: center; font-size: 11.5px; color: var(--mut); }
 .wx-msg .e { color: var(--amber); }

/* ---------- 动态天气图标（SVG + CSS 动画） ---------- */
 .w-sun { fill: #ffd166; animation: w-pulse 3.2s ease-in-out infinite; transform-origin: center; }
 .w-rays { stroke: #ffd166; stroke-width: 2; stroke-linecap: round; opacity: .85; animation: w-spin 16s linear infinite; transform-origin: center; }
 .w-moon { fill: #dbe7ff; animation: w-pulse 5s ease-in-out infinite; transform-origin: center; }
 .w-star { fill: #cfe0ff; animation: w-twinkle 2.4s ease-in-out infinite; }
 .w-cloud > * { fill: #b9d3ef; }
 .w-cloud { animation: w-drift 6s ease-in-out infinite; }
 .w-drop { fill: #4cc9f0; animation: w-fall 1.15s linear infinite; }
 .w-flake { fill: #eaf5ff; animation: w-fall 2.4s linear infinite; }
 .w-bolt { fill: #ffd166; animation: w-flash 1.9s ease-in-out infinite; filter: drop-shadow(0 0 5px rgba(255, 209, 102, .7)); }
 .w-fog { fill: #9fb6d8; opacity: .75; animation: w-fog 4.5s ease-in-out infinite; }
 @keyframes w-spin { to { transform: rotate(360deg); } }
 @keyframes w-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.07); opacity: .9; } }
 @keyframes w-drift { 0%, 100% { transform: translateX(-1.6px); } 50% { transform: translateX(1.6px); } }
 @keyframes w-fall {
     0% { transform: translateY(-2px); opacity: 0; }
     20% { opacity: 1; }
     80% { opacity: 1; }
     100% { transform: translateY(9px); opacity: 0; }
 }
 @keyframes w-flash { 0%, 42%, 100% { opacity: .28; } 48%, 62% { opacity: 1; } }
 @keyframes w-twinkle { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
 @keyframes w-fog { 0%, 100% { transform: translateX(-3px); } 50% { transform: translateX(3px); } }
 @media (prefers-reduced-motion: reduce) {
     .w-sun, .w-rays, .w-moon, .w-star, .w-cloud, .w-drop, .w-flake, .w-bolt, .w-fog,
     .wx-day, .wx-day .bar i, .wx-foot .live { animation: none; }
 }

/* ---------- 省 / 市 / 区 选择器 ---------- */
 .wx-pick {
     position: absolute; left: -6px; right: -6px; top: 30px; z-index: 40;
     padding: 8px; border-radius: 10px;
     background: linear-gradient(135deg, rgba(13, 29, 56, .99), rgba(6, 15, 34, .99));
     border: 1px solid var(--line);
     box-shadow: 0 16px 44px rgba(0, 0, 0, .62), 0 0 0 1px rgba(0, 229, 255, .08);
     animation: wx-in .18s ease-out both;
 }
 .wx-pick[hidden] { display: none; }
 .wx-pick-top { display: flex; gap: 6px; margin-bottom: 6px; }
 .wx-pick-top input {
     flex: 1; min-width: 0; padding: 4px 8px; font-size: 11.5px; font-family: inherit;
     color: var(--txt); background: rgba(0, 0, 0, .4);
     border: 1px solid var(--line); border-radius: 5px; outline: none;
 }
 .wx-pick-top input:focus { border-color: var(--cyan); }
 .wx-pick-crumb {
     display: flex; flex-wrap: wrap; gap: 4px; align-items: center;
     padding: 0 2px 6px; font-size: 10.5px;
     border-bottom: 1px dashed rgba(0, 174, 255, .18); margin-bottom: 5px;
 }
 .wx-pick-crumb .cb { color: var(--mut); }
 .wx-pick-crumb .cb.on { color: var(--cyan); }
 .wx-pick-crumb .cb.up { cursor: pointer; }
 .wx-pick-crumb .cb.up:hover { color: #fff; text-decoration: underline; }
 .wx-pick-list { max-height: 216px; overflow-y: auto; padding-right: 3px; }
 .wx-pick-list::-webkit-scrollbar { width: 5px; }
 .wx-pick-list::-webkit-scrollbar-thumb { background: rgba(0, 229, 255, .35); border-radius: 4px; }
 .wx-pick-i {
     display: block; padding: 6px 8px; border-radius: 6px; cursor: pointer;
     font-size: 12px; color: var(--txt); transition: .12s;
 }
 .wx-pick-i:hover { background: rgba(0, 229, 255, .14); color: #fff; }
 .wx-pick-i.all { color: var(--cyan); border-bottom: 1px dashed rgba(0, 174, 255, .18); border-radius: 6px 6px 0 0; }
 .wx-pick-i em { display: block; font-style: normal; font-size: 10px; color: var(--mut); margin-top: 1px; }
 .wx-pick-i:hover em { color: var(--txt-2); }
 .wx-pick-empty { padding: 22px 6px; text-align: center; font-size: 11.5px; color: var(--mut); }

/* ==========================================================
   天气卡：氛围场景层 + 更夸张的图标动效
   ========================================================== */
 .wx-body-wrap { position: relative; }
 #wxBody { position: relative; }
 .wx-scene {
     position: absolute; inset: -4px -4px -4px; z-index: 0; pointer-events: none;
     overflow: hidden; border-radius: 10px; opacity: .95;
 }
 /* 雨：一排细线从上往下掉，速度各不相同，看得出"在下" */
 .wx-scene .sd {
     position: absolute; top: -14%; width: 1.5px; height: 12px; border-radius: 1px;
     background: linear-gradient(180deg, rgba(140, 210, 255, 0), rgba(140, 210, 255, .85));
     animation-name: wx-rain; animation-timing-function: linear; animation-iteration-count: infinite;
 }
 @keyframes wx-rain { 0% { transform: translateY(0) scaleY(.6); opacity: 0; } 12% { opacity: .9; } 100% { transform: translateY(150px) scaleY(1.3); opacity: 0; } }
 /* 雪：小圆点左右摇摆着往下飘 */
 .wx-scene .sf {
     position: absolute; top: -8%; width: 3.5px; height: 3.5px; border-radius: 50%;
     background: #eaf5ff; box-shadow: 0 0 6px rgba(220, 240, 255, .8);
     animation-name: wx-snowfall; animation-timing-function: linear; animation-iteration-count: infinite;
 }
 @keyframes wx-snowfall {
     0%   { transform: translate(0, 0); opacity: 0; }
     10%  { opacity: 1; }
     50%  { transform: translate(9px, 70px); }
     100% { transform: translate(-6px, 150px); opacity: 0; }
 }
 /* 云：三团半透明云横向漂过，速度不同 */
 .wx-scene .sc {
     position: absolute; height: 26px; border-radius: 999px;
     background: radial-gradient(ellipse at 30% 60%, rgba(185, 211, 239, .30), rgba(185, 211, 239, 0) 70%),
                 radial-gradient(ellipse at 70% 40%, rgba(185, 211, 239, .22), rgba(185, 211, 239, 0) 72%);
     animation-name: wx-slide; animation-timing-function: linear; animation-iteration-count: infinite;
 }
 .wx-scene .sc.c1 { top: 8%;  width: 90px; animation-duration: 17s; }
 .wx-scene .sc.c2 { top: 42%; width: 62px; animation-duration: 23s; animation-delay: -6s; }
 .wx-scene .sc.c3 { top: 68%; width: 110px; animation-duration: 29s; animation-delay: -13s; }
 @keyframes wx-slide { from { left: -40%; } to { left: 118%; } }
 /* 霾：一层缓慢呼吸的灰黄雾 */
 .wx-scene.s-haze .sh {
     position: absolute; inset: 0;
     background: linear-gradient(180deg, rgba(190, 180, 150, .22), rgba(190, 180, 150, .06));
     animation: wx-haze 6s ease-in-out infinite;
 }
 @keyframes wx-haze { 0%, 100% { opacity: .45; transform: translateX(-6px); } 50% { opacity: .85; transform: translateX(6px); } }
 /* 晴天 / 夜晚：底色渐变也在缓慢呼吸 */
 .wx-scene.s-sunny {
     background: linear-gradient(180deg, rgba(255, 209, 102, .16), rgba(255, 209, 102, 0) 62%);
     animation: wx-glow 5s ease-in-out infinite;
 }
 .wx-scene.s-night {
     background: linear-gradient(180deg, rgba(90, 130, 220, .18), rgba(90, 130, 220, 0) 62%);
     animation: wx-glow 7s ease-in-out infinite;
 }
 .wx-scene.s-storm {
     background: linear-gradient(180deg, rgba(90, 110, 200, .22), rgba(90, 110, 200, 0) 70%);
     animation: wx-flashbg 4.5s ease-in-out infinite;
 }
 @keyframes wx-glow { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
 @keyframes wx-flashbg {
     0%, 44%, 100% { opacity: .35; }
     48% { opacity: 1; }
     52% { opacity: .5; }
     56% { opacity: .95; }
 }
 .wx-now, .wx-metrics, .wx-aq, .wx-days, .wx-foot { position: relative; z-index: 1; }

 /* 图标动效加码：转得更快、漂得更远、落得更急 */
 .w-sun { fill: #ffd166; animation: w-pulse 2.2s ease-in-out infinite; transform-origin: center;
          filter: drop-shadow(0 0 6px rgba(255, 209, 102, .75)); }
 .w-halo { fill: rgba(255, 209, 102, .16); animation: w-halo 2.8s ease-in-out infinite; transform-origin: center; }
 .w-halo.moon { fill: rgba(200, 220, 255, .14); }
 .w-halo.sm { fill: rgba(255, 209, 102, .13); }
 @keyframes w-halo { 0%, 100% { transform: scale(.92); opacity: .5; } 50% { transform: scale(1.12); opacity: 1; } }
 .w-rays { stroke: #ffd166; stroke-width: 2.2; stroke-linecap: round; opacity: .9;
           animation: w-spin 7s linear infinite; transform-origin: center; }
 .w-moon { fill: #dbe7ff; animation: w-pulse 4s ease-in-out infinite; transform-origin: center; }
 .w-star { fill: #cfe0ff; animation: w-twinkle 1.8s ease-in-out infinite; }
 .w-cloud > * { fill: #b9d3ef; }
 .w-cloud { animation: w-drift 3.4s ease-in-out infinite; }
 .w-drop { fill: #4cc9f0; animation-name: w-fall; animation-timing-function: linear; animation-iteration-count: infinite; }
 .w-flake { fill: #eaf5ff; animation: w-fall 2.1s linear infinite; }
 .w-bolt { fill: #ffd166; animation: w-flash 1.4s ease-in-out infinite; filter: drop-shadow(0 0 7px rgba(255, 209, 102, .9)); }
 .w-fog { fill: #9fb6d8; opacity: .8; animation: w-fog 3.2s ease-in-out infinite; }
 @keyframes w-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.12); opacity: .88; } }
 @keyframes w-drift { 0%, 100% { transform: translateX(-4px); } 50% { transform: translateX(4px); } }
 @keyframes w-fall {
     0% { transform: translateY(-4px); opacity: 0; }
     18% { opacity: 1; }
     82% { opacity: 1; }
     100% { transform: translateY(13px); opacity: 0; }
 }
 @keyframes w-flash { 0%, 40%, 100% { opacity: .2; } 46%, 62% { opacity: 1; } }
 @keyframes w-twinkle { 0%, 100% { opacity: .2; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.25); } }
 @keyframes w-fog { 0%, 100% { transform: translateX(-5px); opacity: .45; } 50% { transform: translateX(5px); opacity: .9; } }
 .wx-ico { width: 58px; height: 58px; flex: 0 0 58px; line-height: 1;
           filter: drop-shadow(0 0 14px rgba(0, 229, 255, .4)); animation: wx-float 4.5s ease-in-out infinite; }
 @keyframes wx-float { 0%, 100% { transform: translateY(-2.5px); } 50% { transform: translateY(2.5px); } }
 .wx-temp { font-size: 32px; }
 .wx-day { animation: wx-pop .5s cubic-bezier(.2, .9, .3, 1.3) both; }
 @keyframes wx-pop { from { opacity: 0; transform: translateY(14px) scale(.86); } to { opacity: 1; transform: none; } }
 .wx-day .bar i { animation: wx-grow .9s cubic-bezier(.2, .8, .3, 1) both; }

/* ==========================================================
   空气质量 AQI
   ========================================================== */
 .wx-aq {
     margin-top: 10px; padding: 9px 11px 10px; border-radius: 9px;
     background: rgba(0, 0, 0, .3); border: 1px solid rgba(0, 174, 255, .16);
     overflow: hidden;
 }
 .wx-aq.na { display: flex; align-items: center; justify-content: space-between; }
 .wx-aq.na .aq-x { font-size: 11px; color: var(--mut); }
 .aq-h { display: flex; align-items: baseline; justify-content: space-between; }
 .aq-t { font-size: 10.5px; color: var(--mut); letter-spacing: 1px; }
 .aq-v { display: flex; align-items: baseline; gap: 3px; }
 .aq-v b { font-size: 22px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
 .aq-v i { font-style: normal; font-size: 9.5px; color: var(--mut); letter-spacing: 1px; }
 .aq-bar {
     position: relative; height: 5px; margin: 7px 0 6px; border-radius: 3px; overflow: visible;
     /* 底轨：六级色带压到很淡，只做刻度参考；上面的 i 才是当前实测值 */
     background: linear-gradient(90deg,
         rgba(61, 220, 151, .28) 0 17%, rgba(255, 209, 102, .28) 17% 33%,
         rgba(255, 154, 60, .28) 33% 50%, rgba(255, 77, 109, .3) 50% 67%,
         rgba(168, 85, 247, .3) 67% 90%, rgba(224, 104, 127, .35) 90% 100%);
     box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .05);
 }
 .wx-aq.aq1 .aq-bar i { background: linear-gradient(90deg, #0f8f63, #3ddc97); box-shadow: 0 0 8px rgba(61, 220, 151, .6); }
 .wx-aq.aq2 .aq-bar i { background: linear-gradient(90deg, #b8860b, #ffd166); box-shadow: 0 0 8px rgba(255, 209, 102, .6); }
 .wx-aq.aq3 .aq-bar i { background: linear-gradient(90deg, #c25e10, #ff9a3c); box-shadow: 0 0 8px rgba(255, 154, 60, .6); }
 .wx-aq.aq4 .aq-bar i { background: linear-gradient(90deg, #a3173a, #ff4d6d); box-shadow: 0 0 8px rgba(255, 77, 109, .6); }
 .wx-aq.aq5 .aq-bar i { background: linear-gradient(90deg, #6b21a8, #a855f7); box-shadow: 0 0 8px rgba(168, 85, 247, .6); }
 .wx-aq.aq6 .aq-bar i { background: linear-gradient(90deg, #4a0f22, #c2415f); box-shadow: 0 0 8px rgba(224, 104, 127, .6); }
 .aq-bar i {
     position: absolute; left: 0; top: 0; bottom: 0; border-radius: 3px;
     transform-origin: left; animation: aq-fill .9s cubic-bezier(.2, .8, .3, 1) both;
 }
 .aq-bar u {
     position: absolute; top: 50%; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px;
     border-radius: 50%; background: #fff; box-shadow: 0 0 8px rgba(255, 255, 255, .8);
     animation: aq-dot 1.8s ease-in-out infinite;
 }
 @keyframes aq-fill { from { transform: scaleX(.04); opacity: .4; } to { transform: scaleX(1); opacity: 1; } }
 @keyframes aq-dot { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.35); opacity: .7; } }
 .aq-l { display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--txt-2); line-height: 1.4; }
 .aq-l .tag { flex: 0 0 auto; padding: 1px 7px; border-radius: 999px; font-size: 10px; font-weight: 600; }
 .aq-m { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 3px 6px; margin-top: 7px; }
 .aq-m span { font-size: 10px; color: var(--mut); font-variant-numeric: tabular-nums; }
 .aq-m b { color: var(--txt); font-weight: 600; margin-left: 2px; }
 .aq-f { margin-top: 6px; font-size: 9.5px; color: var(--mut); letter-spacing: .3px; }

 /* 六个等级：数值 / 徽标 / 描边 同色 */
 .wx-aq.aq1 .aq-v b, .wx-aq.aq1 .aq-l b { color: #3ddc97; } .wx-aq.aq1 .aq-l .tag { background: rgba(61, 220, 151, .18); color: #8ff3c8; }
 .wx-aq.aq2 .aq-v b, .wx-aq.aq2 .aq-l b { color: #ffd166; } .wx-aq.aq2 .aq-l .tag { background: rgba(255, 209, 102, .18); color: #ffe3a3; }
 .wx-aq.aq3 .aq-v b, .wx-aq.aq3 .aq-l b { color: #ff9a3c; } .wx-aq.aq3 .aq-l .tag { background: rgba(255, 154, 60, .18); color: #ffc79a; }
 .wx-aq.aq4 .aq-v b, .wx-aq.aq4 .aq-l b { color: #ff6b81; } .wx-aq.aq4 .aq-l .tag { background: rgba(255, 77, 109, .18); color: #ffb3c0; }
 .wx-aq.aq5 .aq-v b, .wx-aq.aq5 .aq-l b { color: #c084fc; } .wx-aq.aq5 .aq-l .tag { background: rgba(168, 85, 247, .18); color: #dcc0ff; }
 .wx-aq.aq6 .aq-v b, .wx-aq.aq6 .aq-l b { color: #e0687f; } .wx-aq.aq6 .aq-l .tag { background: rgba(122, 31, 61, .35); color: #ffc2cc; }
 .wx-aq.aq1 { border-color: rgba(61, 220, 151, .3); }
 .wx-aq.aq2 { border-color: rgba(255, 209, 102, .3); }
 .wx-aq.aq3 { border-color: rgba(255, 154, 60, .34); }
 .wx-aq.aq4 { border-color: rgba(255, 77, 109, .38); }
 .wx-aq.aq5 { border-color: rgba(168, 85, 247, .42); }
 .wx-aq.aq6 { border-color: rgba(224, 104, 127, .5); }

/* ==========================================================
   登录 / 注册（2026-09-25 重做：去掉默认账号提示 + 整体美化）
   ========================================================== */
 .modal.auth { width: 428px; padding: 0; overflow: hidden; position: relative; }
 /* 顶部一条流动的青色高光，和顶栏/底栏呼应 */
 .modal.auth::before {
     content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px; border: 0; border-radius: 0;
     background: linear-gradient(90deg, transparent, rgba(0,229,255,.85), rgba(168,85,247,.6), transparent);
 }
 .auth-cover {
     position: relative; padding: 22px 26px 18px; border-bottom: 1px solid rgba(0,174,255,.14);
     background:
         radial-gradient(420px 160px at 22% -30%, rgba(0,132,255,.22), transparent 70%),
         radial-gradient(320px 140px at 96% 10%, rgba(168,85,247,.16), transparent 70%),
         rgba(4,11,24,.6);
 }
 /* logo 与说明文字整体居中（竖向堆叠）：原来左对齐，视觉重心偏左 */
.auth-head { display: flex; flex-direction: column; align-items: center; gap: 11px; text-align: center; }
 .auth-logo {
     width: 46px; height: 46px; flex: 0 0 46px; border-radius: 13px; position: relative;
     display: flex; align-items: center; justify-content: center;
     background: linear-gradient(160deg, rgba(0,229,255,.18), rgba(47,123,255,.1));
     border: 1px solid rgba(0,229,255,.4);
     box-shadow: 0 0 22px rgba(0,229,255,.18), inset 0 0 14px rgba(0,229,255,.12);
 }
 .auth-logo svg { width: 26px; height: 26px; filter: drop-shadow(0 0 6px rgba(0,229,255,.55)); }
 .auth-head-txt { min-width: 0; text-align: center; }
 .auth-head h3 {
     margin: 0 0 4px; font-size: 17px; letter-spacing: 1.4px;
     background: linear-gradient(92deg, #eaf9ff, #9fe6ff 55%, #c9b6ff);
     -webkit-background-clip: text; background-clip: text; color: transparent;
 }
 .auth-head .sub { font-size: 11.5px; color: var(--mut); line-height: 1.5; }
 .auth-tabs { position: relative; display: flex; gap: 6px; margin: 16px 26px 2px; padding: 3px;
              background: rgba(0,0,0,.3); border: 1px solid rgba(0,174,255,.14); border-radius: 10px; }
 .at {
     flex: 1; position: relative; z-index: 1; padding: 8px 0; font-size: 12.5px; font-family: inherit; cursor: pointer;
     color: var(--txt-2); background: transparent; border: 0; border-radius: 8px; transition: color .18s;
 }
 .at:hover { color: var(--cyan); }
 .at.on { color: #eafcff; }
 /* 页签底下的滑块（位置由 JS 算，避免两段渐变按钮拼不齐） */
 .auth-tabs-ink {
     position: absolute; top: 3px; left: 3px; height: calc(100% - 6px); width: 0;
     border-radius: 8px; pointer-events: none;
     background: linear-gradient(135deg, rgba(0,229,255,.3), rgba(47,123,255,.3));
     border: 1px solid rgba(0,229,255,.5); box-shadow: 0 0 14px rgba(0,229,255,.18);
     transition: transform .22s cubic-bezier(.4,0,.2,1), width .22s cubic-bezier(.4,0,.2,1);
 }
 .modal.auth .modal-body { padding: 16px 26px 4px; }
 .modal.auth .field { margin-bottom: 13px; }
 .modal.auth .field > label { display: block; margin-bottom: 6px; font-size: 11.5px; color: var(--mut); letter-spacing: .6px; }
 /* 带图标的输入框 */
 .auth-input { position: relative; display: flex; align-items: center; }
 .auth-input > .ic {
     position: absolute; left: 12px; width: 15px; height: 15px; color: rgba(125,151,189,.9);
     pointer-events: none; transition: color .18s;
 }
 .auth-input input {
     width: 100%; padding: 11px 12px 11px 36px; font-size: 13.5px; font-family: inherit;
     color: var(--txt); background: rgba(3,10,22,.72);
     border: 1px solid var(--line); border-radius: 10px; outline: none; transition: .18s;
 }
 .auth-input input::placeholder { color: rgba(125,151,189,.6); }
 .auth-input input:hover { border-color: rgba(0,229,255,.35); }
 .auth-input input:focus {
     border-color: rgba(0,229,255,.7); background: rgba(3,12,26,.9);
     box-shadow: 0 0 0 3px rgba(0,229,255,.12), 0 0 18px rgba(0,229,255,.1);
 }
 .auth-input:focus-within > .ic { color: var(--cyan); }
 .auth-input input[type=password] { padding-right: 40px; }
 .auth-eye {
     position: absolute; right: 6px; width: 28px; height: 28px; padding: 0; cursor: pointer;
     display: flex; align-items: center; justify-content: center;
     color: var(--mut); background: transparent; border: 0; border-radius: 7px; transition: .15s;
 }
 .auth-eye .ic { width: 15px; height: 15px; }
 .auth-eye:hover { color: var(--cyan); background: rgba(0,229,255,.1); }
 .auth-eye.on { color: var(--cyan); }
 /* 提示槽：只在需要时（例如"登录后继续添加节点"）出现，空的时候完全不占位 */
 .auth-tip { margin-top: 4px; padding: 9px 11px; border-radius: 9px; font-size: 11.5px; line-height: 1.65;
             color: #a9c6e8; background: rgba(0,110,190,.12); border: 1px solid rgba(0,174,255,.24); }
 .auth-tip b { color: var(--cyan); font-weight: 600; }
 .auth-tip[hidden] { display: none; }
 #authOut { display: block; margin-top: 10px; padding: 9px 11px; border-radius: 9px; font-size: 12px;
            color: #ffa8b6; background: rgba(255, 77, 109, .1); border: 1px solid rgba(255, 77, 109, .32); }
 #authOut:empty { display: none; padding: 0; border: 0; }
 /* 登录弹窗底部两个按钮：整组相对弹窗居中（原来是靠右） */
 .modal.auth .modal-foot { padding: 6px 26px 22px; justify-content: center; }
 .modal.auth .modal-foot .btn { min-width: 104px; }
 .btn.auth-ok {
     background: linear-gradient(92deg, #00d0ff, #2f7bff); color: #041021; font-weight: 700;
     border: 0; box-shadow: 0 6px 20px rgba(0,180,255,.28);
 }
 .btn.auth-ok:hover { filter: brightness(1.08); box-shadow: 0 8px 26px rgba(0,200,255,.4); }
 .btn.user.on { color: #fff; background: linear-gradient(135deg, rgba(0, 229, 255, .26), rgba(47, 123, 255, .26)); border-color: rgba(0, 229, 255, .5); }
@media (max-width: 520px) {
    .modal.auth { width: calc(100vw - 28px); }
}

/* ---------- 登录 / 注册弹窗：玻璃质感 + 动效（2026-09-26） ---------- */
 .modal.auth {
     background:
         radial-gradient(520px 240px at 18% -20%, rgba(0,150,255,.2), transparent 72%),
         radial-gradient(420px 220px at 100% 6%, rgba(168,85,247,.15), transparent 72%),
         linear-gradient(160deg, rgba(16,34,66,.8), rgba(6,14,32,.88));
     backdrop-filter: blur(20px) saturate(1.25);
     -webkit-backdrop-filter: blur(20px) saturate(1.25);
     box-shadow: 0 30px 90px rgba(0,0,0,.66), 0 0 70px rgba(0,229,255,.13),
                 inset 0 1px 0 rgba(255,255,255,.07);
 }
 /* 顶沿那条高光改成流动的光带 */
 .modal.auth::before {
     background: linear-gradient(90deg, transparent, rgba(0,229,255,.9), rgba(168,85,247,.7), transparent);
     background-size: 220% 100%;
     animation: authSheen 3.8s linear infinite;
 }
 @keyframes authSheen { 0% { background-position: -130% 0; } 100% { background-position: 230% 0; } }
 /* 玻璃表面缓慢扫过的一层斜向反光 */
 .modal.auth::after {
     content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
     background: linear-gradient(115deg, transparent 32%, rgba(255,255,255,.055) 46%, transparent 60%);
     background-size: 260% 100%;
     animation: authGlass 7.5s ease-in-out infinite;
 }
 @keyframes authGlass { 0%, 100% { background-position: 125% 0; } 50% { background-position: -65% 0; } }
 /* logo 轻微浮动 + 呼吸光晕 */
 .modal.auth .auth-logo { animation: authLogoFloat 4.4s ease-in-out infinite; }
 @keyframes authLogoFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
 /* 两个页签与表单错峰入场，避免"啪"一下全出来 */
 .modal.auth .auth-tabs { animation: authRise .4s cubic-bezier(.16,.84,.24,1) both .05s; }
 .modal.auth .modal-body > .field,
 .modal.auth .modal-body > .auth-tip { animation: authRise .44s cubic-bezier(.16,.84,.24,1) both .1s; }
 .modal.auth .modal-body > .field:nth-child(2) { animation-delay: .15s; }
 .modal.auth .modal-body > .field:nth-child(3) { animation-delay: .2s; }
 @keyframes authRise { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
 /* 主按钮：hover 时一道高光扫过 */
 .btn.auth-ok { position: relative; overflow: hidden; }
 .btn.auth-ok::after {
     content: ''; position: absolute; top: 0; bottom: 0; width: 34%;
     background: linear-gradient(100deg, transparent, rgba(255,255,255,.5), transparent);
     transform: translateX(-160%) skewX(-18deg);
 }
 .btn.auth-ok:hover::after { animation: authBtnSheen .7s ease; }
 @keyframes authBtnSheen { from { transform: translateX(-160%) skewX(-18deg); } to { transform: translateX(420%) skewX(-18deg); } }

/* ---------- 注册验证码 ---------- */
 .auth-cap { display: flex; gap: 9px; align-items: stretch; }
 .auth-cap .auth-input { flex: 1 1 auto; min-width: 0; }
 .cap-img {
     flex: 0 0 116px; width: 116px; height: 40px; border-radius: 10px; cursor: pointer;
     border: 1px solid rgba(0,174,255,.32); background: rgba(3,10,22,.8);
     transition: border-color .18s, box-shadow .18s, transform .14s;
 }
 .cap-img:hover { border-color: rgba(0,229,255,.72); box-shadow: 0 0 16px rgba(0,229,255,.22); }
 .cap-img:active { transform: scale(.97); }
 .cap-hint b { color: var(--cyan); font-weight: 600; cursor: pointer; }
 .cap-hint b:hover { text-decoration: underline; }

/* 系统「减少动态效果」时关掉装饰动画，只保留必要过渡 */
@media (prefers-reduced-motion: reduce) {
    .modal-mask, .modal-mask.show .modal,
    .modal.auth::before, .modal.auth::after,
    .modal.auth .auth-logo, .modal.auth .auth-tabs,
    .modal.auth .modal-body > .field, .modal.auth .modal-body > .auth-tip { animation: none !important; }
    .btn.auth-ok:hover::after { animation: none !important; }
}

/* ==========================================================
   管理员后台
   ========================================================== */
 .adm-tabs { display: flex; gap: 6px; margin-bottom: 14px; }
 .abt {
     padding: 7px 16px; font-size: 12.5px; font-family: inherit; cursor: pointer;
     color: var(--txt-2); background: rgba(255, 255, 255, .04);
     border: 1px solid var(--line); border-radius: 8px; transition: .15s;
 }
 .abt:hover { color: var(--cyan); }
 .abt.on { color: #fff; background: linear-gradient(135deg, rgba(0, 229, 255, .26), rgba(47, 123, 255, .26)); border-color: rgba(0, 229, 255, .5); }

 .adm-permhead, .adm-row {
     display: grid; grid-template-columns: 1.2fr .9fr 2.2fr .8fr; gap: 8px; align-items: center;
 }
 .adm-permhead { padding: 0 6px 7px; font-size: 10.5px; color: var(--mut); letter-spacing: .5px; }
 .adm-users { max-height: 300px; overflow-y: auto; padding-right: 3px; }
 .adm-row {
     padding: 9px 6px; margin-bottom: 6px; border-radius: 9px;
     background: rgba(0, 0, 0, .26); border: 1px solid rgba(0, 174, 255, .14);
 }
 .adm-row.off { opacity: .5; border-color: rgba(255, 77, 109, .25); }
 .adm-row .c1 b { display: block; font-size: 12.5px; color: #eaf5ff; }
 .adm-row .c1 span { font-size: 10px; color: var(--mut); }
 .adm-row .c2 { display: flex; flex-direction: column; gap: 5px; }
 .adm-row .c2 select, .adm-node select {
     padding: 4px 6px; font-size: 11px; font-family: inherit; border-radius: 6px;
     color: var(--txt); background: rgba(4, 12, 26, .85); border: 1px solid var(--line); outline: none;
 }
 .adm-row .c2 select:focus { border-color: var(--cyan); }
 .adm-row .c3 { display: flex; flex-wrap: wrap; gap: 4px; }
 .pk {
     padding: 3px 7px; font-size: 10.5px; font-family: inherit; cursor: pointer; border-radius: 999px;
     color: var(--mut); background: rgba(255, 255, 255, .04); border: 1px solid var(--line); transition: .15s;
 }
 .pk:hover { color: var(--cyan); border-color: rgba(0, 229, 255, .5); }
 .pk.on { color: #7df3c4; background: rgba(34, 227, 154, .14); border-color: rgba(34, 227, 154, .45); }
 .pk.lock { cursor: not-allowed; opacity: .85; }
 .adm-row .c4 { display: flex; flex-direction: column; gap: 5px; }
 .mini {
     padding: 3px 7px; font-size: 10.5px; font-family: inherit; cursor: pointer; border-radius: 6px;
     color: var(--txt-2); background: rgba(255, 255, 255, .04); border: 1px solid var(--line); transition: .15s;
 }
 .mini:hover { color: var(--cyan); border-color: rgba(0, 229, 255, .5); }
 .mini.on { color: #7df3c4; border-color: rgba(34, 227, 154, .45); background: rgba(34, 227, 154, .12); }
 .mini.danger { color: #ffa8b6; border-color: rgba(255, 77, 109, .35); }
 .mini.danger:hover { background: rgba(255, 77, 109, .2); color: #fff; }

.adm-nodes { max-height: 260px; overflow-y: auto; padding-right: 3px; }
.adm-nodehead, .adm-node {
    display: grid; grid-template-columns: 1.35fr 1.35fr .95fr 1.05fr; gap: 8px; align-items: center;
}
.adm-nodehead {
    padding: 0 8px 6px; font-size: 10px; color: var(--mut); letter-spacing: .5px;
}
.adm-node {
    padding: 6px 8px; margin-bottom: 4px; border-radius: 7px;
    background: rgba(0, 0, 0, .24); border: 1px solid rgba(0, 174, 255, .1);
}
.adm-node.real { border-color: rgba(255, 176, 32, .22); }
.adm-node .nn { font-size: 12px; color: var(--txt); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-node .ng { font-size: 10.5px; color: var(--mut); }
.adm-node .nl { display: flex; align-items: center; gap: 5px; }
.adm-node .nl input, .adm-node .na .mini { min-width: 0; }
.adm-node input.nloc {
    width: 100%; padding: 4px 7px; font-size: 11.5px; border-radius: 6px;
    background: rgba(2, 14, 30, .72); color: #d8ecff;
    border: 1px solid rgba(0, 174, 255, .18); outline: none;
}
.adm-node input.nloc:focus { border-color: var(--cyan); box-shadow: 0 0 0 2px rgba(0, 200, 255, .1); }
.adm-node .na { display: flex; gap: 4px; justify-content: flex-end; }
.adm-node .na .mini:disabled { opacity: .45; cursor: default; }
.rn-sel { flex: 0 0 42%; min-width: 0; }
.rn-arrow { color: var(--mut); font-size: 12px; }
#mkCity, #rnTo { flex: 1 1 auto; min-width: 0; }
.tagm {
    display: inline-block; margin-left: 5px; padding: 0 5px; border-radius: 4px;
    font-style: normal; font-size: 9px; color: #7df3c4;
    background: rgba(34, 227, 154, .14); border: 1px solid rgba(34, 227, 154, .35);
}

 @media (prefers-reduced-motion: reduce) {
     .wx-scene, .wx-scene *, .wx-ico, .aq-bar u, .aq-bar i, .w-halo { animation: none !important; }
 }

/* ==========================================================
   2026-09-23 UI 增强：统一 SVG 图标 + 动感细节
   （追加在末尾，覆盖同优先级的前置规则）
   ========================================================== */

/* ---------- 图标基础 ---------- */
.ic { width: 1em; height: 1em; flex: none; display: inline-block; vertical-align: -.135em; }
.btn .ic { opacity: .92; }
.btn:hover .ic { filter: drop-shadow(0 0 5px currentColor); }
.globe-reset, .wx-ref { display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
.tmenu-i { display: flex; align-items: center; gap: 8px; }
.wx-ref .ic { width: 1.05em; height: 1.05em; }
.chip .ic { opacity: .8; }

/* ---------- 面板：入场 / 悬停 / 标题流光 ---------- */
.panel { animation: panelIn .5s cubic-bezier(.22,.9,.28,1) both; transition: border-color .25s, box-shadow .25s; }
.panel:hover {
    border-color: rgba(0,229,255,.32);
    box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 10px 34px rgba(0,0,0,.42), 0 0 24px rgba(0,229,255,.09);
}
@keyframes panelIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.panel-title { position: relative; }
.panel-title::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -6px; height: 1px;
    background: linear-gradient(90deg, rgba(0,229,255,.6), rgba(168,85,247,.3), transparent 72%);
    transform: scaleX(0); transform-origin: left;
    animation: titleLine .9s .18s cubic-bezier(.2,.9,.3,1) forwards;
}
@keyframes titleLine { to { transform: scaleX(1); } }

/* ---------- 资源条：流光 ---------- */
.res-bar i { position: relative; overflow: hidden; }
.res-bar i::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent);
    transform: translateX(-110%); animation: barFlow 2.8s linear infinite;
}
@keyframes barFlow { to { transform: translateX(110%); } }

/* ---------- KPI 光扫已改为 .k-shine 真实元素（见上方 KPI 段），
   原来挂在 .kpi-row > *::after 上会和 .kpi::after 抢同一个伪元素 ---------- */

/* ---------- 顶栏 ---------- */
/* logo 光晕：box-shadow 动画每帧都要重绘（阴影不是合成属性），改成一层
   静态阴影 + 只动 opacity，交给合成器，视觉几乎一致但几乎不要钱。 */
.logo { position: relative; }
.logo::after {
    content: ''; position: absolute; inset: 0; border-radius: 10px; pointer-events: none;
    box-shadow: 0 0 26px rgba(0,229,255,.45), inset 0 0 18px rgba(0,229,255,.20);
    animation: logoGlow 3.6s ease-in-out infinite;
}
@keyframes logoGlow { 0%, 100% { opacity: .28; } 50% { opacity: 1; } }
.chip b, .kpi-row b, .gk b, .sys-card b { font-variant-numeric: tabular-nums; }
/* 2026-09-24：去掉「每次轮询闪一下」（body.tick + numPulse / cardTick）。
   整屏同时播动画在低配机上就是肉眼可见的闪烁，改成靠数值/进度条自身的
   transition 做平滑过渡，数据依然"在动"，但不会闪。 */

/* ---------- 节点卡片 ---------- */
.srv-card { transition: transform .18s, border-color .35s ease, box-shadow .35s ease, opacity .35s ease; }
.srv-card:hover {
    border-color: rgba(0,229,255,.45);
    box-shadow: 0 6px 20px rgba(0,0,0,.34), 0 0 18px rgba(0,229,255,.10);
}

/* ---------- 画布按钮 ---------- */
.globe-reset { transition: .18s; }
.globe-reset:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,.4), 0 0 12px rgba(0,229,255,.22); }
.globe-btns .ic { opacity: .95; }

/* ---------- 弹层入场 ---------- */
.modal-mask.show .modal { animation: modalIn .34s cubic-bezier(.16,.84,.24,1) both; }
@keyframes modalIn {
    0%   { opacity: 0; transform: translateY(22px) scale(.955); }
    55%  { opacity: 1; }
    100% { opacity: 1; transform: none; }
}

/* ---------- 系统状态弹层 ---------- */
.sys-modal .modal-body { max-height: 66vh; overflow: auto; padding-right: 4px; }
.sys-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 6px; }
.sys-card {
    background: linear-gradient(160deg, rgba(12,32,60,.9), rgba(7,16,34,.9));
    border: 1px solid var(--line); border-radius: 10px; padding: 12px 13px;
    display: flex; flex-direction: column; gap: 3px; position: relative; overflow: hidden;
}
.sys-card::after {
    content: ''; position: absolute; top: 0; bottom: 0; width: 60%;
    background: linear-gradient(100deg, transparent, rgba(0,229,255,.08), transparent);
    transform: translateX(-170%); animation: sweep 6.9s ease-in-out infinite; pointer-events: none;
}
.sys-card .sl { display: flex; align-items: center; gap: 6px; font-size: 11px; letter-spacing: 1px; color: var(--mut); }
.sys-card b { font-size: 20px; color: #bff2ff; font-weight: 600; letter-spacing: 1px; }
.sys-card b em { font-size: 11px; font-style: normal; color: var(--mut); margin-left: 3px; }
.sys-card i { font-size: 11px; font-style: normal; color: var(--txt-2); }
.sys-card.good { border-color: rgba(34,227,154,.45); box-shadow: inset 0 0 22px rgba(34,227,154,.10); }
.sys-card.good b { color: #8ff5c9; }
.sys-card.bad { border-color: rgba(255,77,109,.5); box-shadow: inset 0 0 22px rgba(255,77,109,.12); }
.sys-card.bad b { color: #ffb3c0; }
.sys-sec { display: flex; align-items: center; gap: 8px; font-size: 12px; letter-spacing: 2px; color: #cfe6ff; margin: 15px 0 8px; }
.sys-sec .ic { color: var(--cyan); }
.sys-sec-line { flex: 1; height: 1px; background: linear-gradient(90deg, rgba(0,229,255,.4), transparent); }
.sys-rows { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 14px; }
.sys-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 7px 10px; border-radius: 8px; background: rgba(255,255,255,.028);
    border: 1px solid rgba(0,174,255,.12); transition: border-color .2s, background .2s;
}
.sys-row:hover { border-color: rgba(0,229,255,.35); background: rgba(0,229,255,.05); }
.sys-row .sk { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--txt-2); }
.sys-row .sk .ic { color: rgba(0,229,255,.75); }
.sys-row .sv { font-size: 12px; color: #dceaff; font-weight: 500; text-align: right; }
@media (max-width: 900px) {
    .sys-grid { grid-template-columns: repeat(2, 1fr); }
    .sys-rows { grid-template-columns: 1fr; }
}

/* ---------- 尊重系统减弱动效设置 ---------- */
@media (prefers-reduced-motion: reduce) {
    .panel, .panel-title::after, .res-bar i::after,
    .kpi .k-shine, .kpi .k-flow::after, .kpi .k-ico, .kpi.fresh,
    .dsk-edge, .dsk.fresh, .dsk-temp, .dsk-temp::after, .dsk-warn.on,
    .srv-card.fresh, .srv-name .st::after,
    .logo::after, .sys-card::after, .bg-layer.scan::after { animation: none !important; }
}

/* 系统状态弹层：标题行 + 长值行占满整行（避免"Web 服务"这种换行） */
.sys-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.sys-head h3 { margin: 0 !important; }
.sys-sub { font-size: 10px; letter-spacing: 3px; color: var(--mut); margin-top: 4px; }
.sys-row.wide { grid-column: 1 / -1; }
.sys-row.wide .sv { text-align: right; }

/* 顶栏底部的光带缓慢流动（节点卡片"轮询闪一下"已移除，改由 .srv-card 的 transition 平滑过渡） */
/* 顶栏流光：原来动 background-position，每帧都要重画这条线。改成只动 opacity
   —— 同样是合成属性，零重绘，也不需要给 .topbar 加 overflow:hidden
   （顶栏里有告警菜单，一裁就把菜单切掉了）。 */
.topbar::after { animation: topBarFlow 8s ease-in-out infinite; }
@keyframes topBarFlow { 0%, 100% { opacity: .34; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .topbar::after { animation: none !important; }
}

/* ==========================================================
   节点列表：一行两列（左侧面板已在 .main 里同步加宽到 420px）
   ========================================================== */
.srv-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: max-content;   /* 关键：行高按内容算，容器不够高时滚动而不是把卡片压扁 */
    gap: 10px;
    align-content: start;
}
.srv-list .empty { grid-column: 1 / -1; }

.srv-card { margin-bottom: 0; padding: 10px 11px; }
.srv-card:hover { transform: translateY(-1px); }

/* 卡片内：名称最多两行（完整名放 title）。
   左列 384px 时卡片内容宽只有 ~141px，名称若和状态标签挤在一行就放不下
   「网关-05（布宜诺斯艾利斯·阿根廷）」这类长名，所以标签下移到 IP 那一行，
   名称独占整行宽度。 */
.srv-head { gap: 5px; margin-bottom: 4px; }
.srv-head .srv-name { flex: 1 1 auto; min-width: 0; align-items: flex-start; }
.srv-card .tag { font-size: 9.5px; padding: 1px 6px; border-radius: 9px; }
.srv-meta { display: flex; align-items: center; gap: 5px; }
.srv-meta .srv-ip { flex: 1 1 auto; min-width: 0; }
.srv-meta .tag { flex: 0 0 auto; }
.srv-name { font-size: 11px; line-height: 1.36; letter-spacing: 0; }
.srv-name .st { flex: none; margin-top: 4px; }
.srv-name .nmtxt {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; word-break: break-word;
}
.srv-ip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.srv-metrics { gap: 6px 8px; }
.srv-metrics .m span { font-size: 11.5px; }
.srv-actions { gap: 4px; margin-top: 8px; }
.srv-actions .btn.sm { flex: 1 1 auto; justify-content: center; padding: 3px 6px; font-size: 11px; }

/* 窄屏（面板铺满一行时）两列依旧合适；手机宽度退回单列 */
@media (max-width: 760px) { .srv-list { grid-template-columns: 1fr; } }

/* 后台节点列表：加了「删除」之后操作列要够宽，否则按钮文字会被挤成竖排 */
#adminMask .modal.wide { width: 780px; }
.adm-nodehead, .adm-node { grid-template-columns: 1.22fr 1.22fr .76fr 1.45fr; }
.adm-node .na .mini { white-space: nowrap; padding: 3px 6px; }

/* ==========================================================
   2026-09-23 深夜：画布「其他功能」悬浮菜单 + 底部系统时间
   ========================================================== */

/* ---------- 画布右上角：除全屏外的显示选项收进「其他功能」，悬停/点击向左展开 ---------- */
.gb-more { position: relative; display: flex; flex-direction: column; }
#btnMore { position: relative; width: 100%; justify-content: flex-start; }
.gb-caret { width: 11px; height: 11px; margin-left: auto; opacity: .7; transform: rotate(90deg); transition: transform .22s, opacity .22s; }
.gb-more:hover .gb-caret, .gb-more.open .gb-caret { opacity: 1; transform: rotate(-90deg); }
/* 里面的开关有偏离默认的项时，按钮左上角点一个小圆点，不然收起来就看不见了 */
#btnMore.dot::after {
    content: ''; position: absolute; left: -2px; top: -2px;
    width: 6px; height: 6px; border-radius: 50%;
    background: #ffc45c; box-shadow: 0 0 7px #ffc45c;
}
.gb-pop {
    position: absolute; right: 100%; top: -3px; z-index: 6;
    padding-right: 9px;   /* 按钮与菜单之间的过渡带，鼠标可以平滑走过去不触发收起 */
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity .18s, visibility .18s;
}
/* 位移只做在内容盒上：外层盒子的位置保持锚定在按钮左侧，
   否则过渡期间菜单的命中区域会跟视觉位置错开 7px */
.gb-pop-in {
    transform: translateX(7px);
    transition: transform .18s cubic-bezier(.2,.9,.3,1);
}
.gb-more:hover .gb-pop, .gb-more.open .gb-pop {
    opacity: 1; visibility: visible; pointer-events: auto;
}
.gb-more:hover .gb-pop-in, .gb-more.open .gb-pop-in { transform: none; }
.gb-pop-in {
    display: flex; flex-direction: column; gap: 5px; padding: 9px;
    border-radius: 10px; min-width: 132px;
    background: rgba(4,12,26,.96);
    border: 1px solid rgba(0,229,255,.34);
    box-shadow: 0 14px 34px rgba(0,0,0,.58), 0 0 24px rgba(0,229,255,.10);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
.gb-pop-h {
    display: block; padding: 0 2px 7px;
    border-bottom: 1px solid rgba(0,174,255,.18);
    font-size: 10px; letter-spacing: 2px; color: #cfe6ff; white-space: nowrap;
}
.gb-pop-h span { display: block; margin-top: 2px; font-size: 8px; letter-spacing: 1.5px; color: var(--mut); }
.gb-pop-in .globe-reset { width: 100%; justify-content: flex-start; padding: 4px 8px; }
.gb-pop-sep { height: 1px; margin: 2px 0 1px; background: linear-gradient(90deg, rgba(0,174,255,.28), transparent); }
@media (prefers-reduced-motion: reduce) {
    .gb-pop, .gb-pop-in, .gb-caret { transition: none !important; }
}

/* ---------- 底部状态条：系统时间（原来挂在顶栏中部）+ 运行信息 ---------- */
.bottombar {
    flex: 0 0 auto; position: relative; margin-top: 10px;
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
    padding: 8px 4px 4px;
    border-top: 1px solid rgba(0,174,255,.16);
}
.bottombar::before {
    content: ''; position: absolute; left: 0; right: 0; top: -1px; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(0,229,255,.5), rgba(168,85,247,.32), transparent);
    background-size: 220% 100%;
    animation: topBarFlow 9s linear infinite;
}
.bottombar .foot-note { text-align: left; padding: 0; }
.clock { display: inline-flex; align-items: baseline; justify-content: center; gap: 9px; white-space: nowrap; }
.clock .ic { width: 13px; height: 13px; align-self: center; color: rgba(0,229,255,.75); }
.clock .t {
    font-size: 17px; font-weight: 300; letter-spacing: 2px; color: #e8fbff;
    font-variant-numeric: tabular-nums; text-shadow: 0 0 14px rgba(0,229,255,.4);
}
.clock .d { font-size: 11px; letter-spacing: 1.5px; color: var(--mut); }
@media (max-width: 900px) {
    .bottombar { grid-template-columns: 1fr; justify-items: center; }
    .bottombar .foot-note { text-align: center; }
    .bb-r { display: none; }
}
@media (prefers-reduced-motion: reduce) { .bottombar::before { animation: none !important; } }

/* ==========================================================
   2026-09-23：多时区时钟 / 右键菜单 / 右栏新增板块
   ========================================================== */

/* ---------- 底部：多时区时钟（时间·日期·时区三者字号颜色完全一致） ---------- */
.clocks {
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; row-gap: 2px;
    font-size: 13px; letter-spacing: .5px; color: #9fb6d8;
}
.clk { position: relative; display: inline-flex; align-items: baseline; gap: 8px; padding: 0 16px; white-space: nowrap; }
.clk + .clk::before {
    content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 1px; height: 13px;
    background: linear-gradient(180deg, transparent, rgba(0,174,255,.45), transparent);
}
/* 三段的字号/颜色必须完全一致（用户要求），层次只靠字距、底色和边框体现 */
.clk .t, .clk .d, .clk .z { font-size: 13px; color: #9fb6d8; font-weight: 400; }
.clk .t { font-variant-numeric: tabular-nums; letter-spacing: 1.6px; text-shadow: 0 0 12px rgba(0,229,255,.28); }
.clk .z {
    padding: 1px 7px; border-radius: 7px; letter-spacing: .5px;
    background: rgba(6,18,38,.7); border: 1px solid rgba(0,174,255,.2);
}
.clk.home .z { border-color: rgba(0,229,255,.45); }
.clk-note { font-size: 13px; color: #6b86ab; }

.tz-pick {
    position: absolute; right: 0; bottom: calc(100% + 10px); z-index: 70;
    width: 348px; max-height: 288px; overflow-y: auto;
    padding: 10px; border-radius: 11px;
    background: rgba(4,12,26,.97);
    border: 1px solid rgba(0,229,255,.34);
    box-shadow: 0 16px 40px rgba(0,0,0,.62), 0 0 26px rgba(0,229,255,.10);
    backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}
.tz-pick[hidden] { display: none; }
.tz-pick-h {
    padding: 0 2px 8px; margin-bottom: 6px;
    border-bottom: 1px solid rgba(0,174,255,.18);
    font-size: 11px; letter-spacing: 2px; color: #cfe6ff;
}
.tz-pick-h span { display: block; margin-top: 3px; font-size: 9px; letter-spacing: 1px; color: var(--mut); }
.tz-pick-list { display: flex; flex-direction: column; gap: 2px; }
.tz-i {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
    padding: 6px 8px; border-radius: 7px; cursor: pointer;
    background: none; border: 1px solid transparent; color: var(--txt-2); font-size: 12px;
}
.tz-i:hover { background: rgba(0,229,255,.08); border-color: rgba(0,229,255,.24); }
.tz-i .box {
    flex: none; width: 13px; height: 13px; border-radius: 4px;
    border: 1px solid rgba(120,160,220,.5); display: grid; place-items: center;
    font-size: 9px; color: #04101f; background: transparent;
}
.tz-i.on .box { background: var(--cyan); border-color: var(--cyan); box-shadow: 0 0 8px rgba(0,229,255,.5); }
.tz-i .nm { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tz-i .of { flex: none; font-size: 10px; color: var(--mut); font-variant-numeric: tabular-nums; }
.tz-i .now { flex: none; font-size: 11px; color: #8fdcff; font-variant-numeric: tabular-nums; }
.tz-i.on .nm { color: #eaf5ff; }
.tz-sep { height: 1px; margin: 5px 2px; background: linear-gradient(90deg, rgba(0,174,255,.26), transparent); }

/* ---------- 节点卡片右键菜单 ---------- */
.ctx-menu {
    position: fixed; z-index: 9500; min-width: 156px;
    padding: 5px; border-radius: 10px;
    background: rgba(4,12,26,.97);
    border: 1px solid rgba(0,229,255,.35);
    box-shadow: 0 16px 38px rgba(0,0,0,.62), 0 0 22px rgba(0,229,255,.10);
    backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
    animation: ctxIn .12s ease-out;
}
.ctx-menu[hidden] { display: none; }
@keyframes ctxIn { from { opacity: 0; transform: scale(.96) translateY(-3px); } to { opacity: 1; transform: none; } }
.ctx-head {
    padding: 6px 9px 7px; margin-bottom: 4px;
    border-bottom: 1px solid rgba(0,174,255,.16);
    font-size: 11px; color: #cfe6ff;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px;
}
.ctx-i {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
    padding: 7px 9px; border-radius: 6px; cursor: pointer;
    background: none; border: 0; color: var(--txt-2); font-size: 12px;
}
.ctx-i .ic { width: 13px; height: 13px; opacity: .85; }
.ctx-i:hover { background: rgba(0,229,255,.13); color: #bff2ff; }
.ctx-i.danger { color: #ffb3c0; }
.ctx-i.danger:hover { background: rgba(255,77,109,.18); color: #ffd6dd; }
.ctx-sep { height: 1px; margin: 4px 6px; background: linear-gradient(90deg, rgba(0,174,255,.26), transparent); }
/* 卡片上不再摆按钮，给个极淡的暗示 */
.srv-card .ctx-tip {
    position: absolute; right: 8px; top: 8px; font-size: 9px; color: rgba(107,134,171,.0);
    transition: color .16s; pointer-events: none;
}
.srv-card:hover .ctx-tip { color: rgba(107,134,171,.75); }

/* 右栏：面板变多，整列滚动 */
.right { overflow-y: auto; padding-right: 4px; }
.right::-webkit-scrollbar { width: 5px; }
.right::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
.right > .panel { flex: 0 0 auto; }
.right > .panel:nth-child(1), .right > .panel:nth-child(3) { flex: 0 0 auto; }
#volBox, #infoBox, .right .rank { overflow: visible; max-height: none; }
/* 磁盘健康 / 磁盘分区在"全部汇总"下条目很多，限高内部滚动，
   否则它们会把下面的功耗 / 进程板块顶到一屏之外 */
/* 磁盘监控：磁盘 / 分区两个选项卡共用一块面板 */
.tab-pane { display: none; }
.tab-pane.on { display: block; }
#diskBox, #volBox, #tempBox { max-height: 246px; overflow-y: auto; padding-right: 3px; }
#diskBox::-webkit-scrollbar, #volBox::-webkit-scrollbar, #tempBox::-webkit-scrollbar { width: 5px; }
#diskBox::-webkit-scrollbar-thumb, #volBox::-webkit-scrollbar-thumb, #tempBox::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }

/* 选项卡：颜色统一，选中不高亮，仅文字颜色稍作区分（三个时收紧间距以免挤到右侧英文） */
.tabs { display: inline-flex; align-items: center; gap: 2px; }
.tabs .tab {
    appearance: none; border: 0; cursor: pointer; padding: 2px 0 3px; margin: 0 4px;
    font: inherit; font-size: 11px; letter-spacing: .5px; color: #9fb6d8;
    background: transparent;
    display: inline-flex; align-items: center; gap: 4px;
    transition: color .18s;
}
.tabs .tab svg { width: 12px; height: 12px; opacity: .7; transition: opacity .25s, transform .3s cubic-bezier(.2,.8,.3,1); }
.tabs .tab:hover { color: #cfe6ff; }
.tabs .tab:hover svg { opacity: 1; transform: scale(1.08); }
.tabs .tab.on { color: #eafcff; }
.tabs .tab.on svg { opacity: 1; }

/* 硬件监控标题：标题独占一行、选项卡整排落到第二行左对齐，
   彻底避免窄栏里「硬件监控」与三个选项卡挤成错位的两截 */
.panel-title.hw-title { flex-wrap: wrap; row-gap: 7px; }
.panel-title.hw-title .t { flex: 0 0 100%; }
.panel-title.hw-title .pt-tools { width: 100%; justify-content: flex-start; gap: 6px; flex-wrap: wrap; }
.panel-title.hw-title .tabs .tab { padding: 1px 0 2px; margin: 0 3px; }

.panel-title .pt-tools { display: flex; align-items: center; gap: 7px; }
.panel-title .pt-tools .btn.sm { padding: 2px 7px; font-size: 10px; letter-spacing: 0; }

/* ---------- 磁盘监控（2026-09-24 改版：卡片化 + 图标 + 动效） ---------- */
.dsk {
    --dcol: #7d97b8;
    position: relative; margin-bottom: 7px; padding: 7px 9px 8px 12px; border-radius: 10px;
    background: linear-gradient(135deg, rgba(11,25,48,.72), rgba(7,16,32,.5));
    border: 1px solid rgba(0,174,255,.13);
    transition: transform .3s cubic-bezier(.2,.8,.3,1), border-color .3s, box-shadow .3s, background .3s;
}
.dsk:last-child { margin-bottom: 0; }
.dsk:hover {
    transform: translateX(2px);
    border-color: rgba(0,229,255,.30);
    background: linear-gradient(135deg, rgba(14,32,60,.8), rgba(8,18,36,.6));
    box-shadow: 0 5px 18px rgba(0,0,0,.32), 0 0 16px rgba(0,229,255,.09);
}
.dsk.t-ok   { --dcol: #22e39a; }
.dsk.t-warn { --dcol: #ffb020; }
.dsk.t-crit { --dcol: #ff4d6d; }
.dsk.t-mut  { --dcol: #7d97b8; }

/* 左缘色条：颜色跟着温度档走，低频率呼吸 */
.dsk-edge {
    position: absolute; left: 0; top: 8px; bottom: 8px; width: 2px; border-radius: 0 2px 2px 0;
    background: var(--dcol); box-shadow: 0 0 8px var(--dcol);
    animation: dEdge 3.4s ease-in-out infinite;
}
.dsk:nth-child(2n) .dsk-edge { animation-delay: -1.7s; }
@keyframes dEdge { 0%, 100% { opacity: .42; } 50% { opacity: .95; } }
.dsk.fresh { animation: dskIn .42s cubic-bezier(.2,.9,.3,1) both; }
@keyframes dskIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: none; } }

.dsk-top { display: flex; align-items: flex-start; gap: 8px; }
/* 介质图标（SSD 青色 / HDD 琥珀）+ 右下角序号角标 */
.dsk-ico {
    position: relative; flex: none; width: 28px; height: 28px; border-radius: 9px;
    display: grid; place-items: center; color: #00e5ff;
    background: linear-gradient(140deg, rgba(0,229,255,.15), rgba(6,16,32,.45));
    border: 1px solid rgba(0,229,255,.22);
    transition: transform .35s cubic-bezier(.2,.8,.3,1), box-shadow .35s;
}
.dsk-ico.hdd {
    color: #ffb020;
    background: linear-gradient(140deg, rgba(255,176,32,.15), rgba(6,16,32,.45));
    border-color: rgba(255,176,32,.24);
}
.dsk-ico svg { width: 16px; height: 16px; }
.dsk:hover .dsk-ico { transform: scale(1.06) rotate(-4deg); box-shadow: 0 0 14px rgba(0,229,255,.22); }
.dsk-ico i {
    position: absolute; right: -4px; bottom: -4px; min-width: 13px; height: 13px; padding: 0 2px;
    border-radius: 7px; display: grid; place-items: center;
    font-size: 8.5px; font-style: normal; font-weight: 700; line-height: 1;
    color: #06121f; background: #00e5ff;
}
.dsk-ico.hdd i { background: #ffb020; }

.dsk-id { flex: 1 1 auto; min-width: 0; }
.dsk-name {
    font-size: 11px; color: #e6f2ff; line-height: 1.35; letter-spacing: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dsk-sub { display: flex; align-items: center; gap: 3px; margin-top: 4px; font-size: 9.5px; color: #6b86ab; overflow: hidden; }
.dsk-sub em.hst {
    font-style: normal; margin-right: 0; color: #7f9cbf; max-width: 108px;
    font-size: 9.2px; letter-spacing: -.15px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dsk-sub .cap { display: inline-flex; align-items: center; white-space: nowrap; flex: none; }
.dsk-sub svg { width: 10px; height: 10px; margin-right: 1px; opacity: .7; }
.dsk-sub .tag {
    flex: none; padding: 1px 4px; border-radius: 4px; letter-spacing: .4px;
    background: rgba(6,18,38,.72); border: 1px solid rgba(0,174,255,.18); color: #9fb6d8;
}
.dsk-sub .tag.ssd { border-color: rgba(34,227,154,.3); color: #8ff0c8; }
.dsk-sub .tag.hdd { border-color: rgba(255,196,92,.3); color: #ffd9a0; }

.dsk-temp {
    flex: none; display: inline-flex; align-items: center; gap: 3px;
    font-size: 11px; padding: 2px 7px; border-radius: 8px; margin-top: 1px;
    font-variant-numeric: tabular-nums; white-space: nowrap;
    transition: background .35s, color .35s, border-color .35s;
    position: relative;
}
/* 温度徽章的呼吸光：原来动 box-shadow（每帧重绘），改成一层静态阴影只动 opacity */
.dsk-temp::after {
    content: ''; position: absolute; inset: -1px; border-radius: 8px; pointer-events: none;
    box-shadow: 0 0 12px -2px currentColor;
    animation: tBreath 4s ease-in-out infinite;
}
.dsk:nth-child(2n) .dsk-temp::after { animation-delay: -2s; }
.dsk-temp svg { width: 11px; height: 11px; }
.dsk-temp b { font-weight: 600; }
.dsk-temp.ok   { color: #8ff0c8; background: rgba(34,227,154,.12); border: 1px solid rgba(34,227,154,.3); }
.dsk-temp.warn { color: #ffd28a; background: rgba(255,176,32,.12); border: 1px solid rgba(255,176,32,.34); }
.dsk-temp.crit { color: #ffb3c0; background: rgba(255,77,109,.14); border: 1px solid rgba(255,77,109,.4); }
.dsk-temp.crit::after { animation-duration: 2.2s; }
.dsk-temp.mut  { color: #8ba3c4; background: rgba(120,160,220,.10); border: 1px solid rgba(120,160,220,.24); }
.dsk-temp.mut::after { animation: none; opacity: 0; }
@keyframes tBreath { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }

/* 两个指标格：图标 + 数值 + 说明，格子带 hover 反馈 */
.dsk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin-top: 8px; }
.dsk-cell {
    display: flex; align-items: center; gap: 7px; padding: 5px 8px; border-radius: 8px;
    background: rgba(6,16,32,.6); border: 1px solid rgba(0,174,255,.13);
    transition: border-color .3s, background .3s;
}
.dsk:hover .dsk-cell { border-color: rgba(0,174,255,.24); background: rgba(8,20,40,.72); }
.dsk-cell .ci {
    flex: none; width: 22px; height: 22px; border-radius: 7px; display: grid; place-items: center;
    color: #6fb8e8; background: rgba(0,174,255,.10);
    transition: transform .35s cubic-bezier(.2,.8,.3,1), color .35s, background .35s;
}
.dsk-cell .ci svg { width: 13px; height: 13px; }
.dsk:hover .dsk-cell .ci { color: #8fdcff; background: rgba(0,174,255,.18); transform: scale(1.06); }
.dsk-cell .ct { min-width: 0; }
.dsk-cell b {
    display: block; font-size: 13px; font-weight: 600; color: #cfe6ff;
    font-variant-numeric: tabular-nums; line-height: 1.15;
}
.dsk-cell b i { font-style: normal; font-size: 9.5px; color: #7d97b8; margin-left: 2px; }
.dsk-cell .ct > span { display: block; margin-top: 1px; font-size: 9px; color: #6b86ab; letter-spacing: .5px; }

/* SSD 磨损度（HDD 无此项，整行隐藏；用 class 而不是 hidden，避免被 display 覆盖） */
.dsk-wear { display: none; align-items: center; gap: 7px; margin-top: 8px; }
.dsk-wear.on { display: flex; }
.dsk-wear .wl { flex: none; font-size: 9px; color: #6b86ab; letter-spacing: .5px; }
.dsk-wear .wb { flex: 1 1 auto; height: 3px; border-radius: 2px; overflow: hidden; background: rgba(255,255,255,.07); }
.dsk-wear .wb i {
    display: block; height: 100%; width: 0; border-radius: 2px;
    background: linear-gradient(90deg, rgba(34,227,154,.45), #22e39a);
    box-shadow: 0 0 8px rgba(34,227,154,.45);
    transition: width .8s cubic-bezier(.25,.8,.35,1);
}
.dsk-wear .wv { flex: none; font-size: 10px; color: #9fb6d8; font-variant-numeric: tabular-nums; }
.dsk-wear.crit .wb i { background: linear-gradient(90deg, rgba(255,77,109,.45), #ff4d6d); box-shadow: 0 0 8px rgba(255,77,109,.5); }
.dsk-wear.crit .wv { color: #ffb3c0; }

.dsk-warn {
    display: none; margin-top: 7px; padding: 4px 8px; border-radius: 7px;
    font-size: 9.5px; color: #ffd28a;
    background: rgba(255,176,32,.09); border: 1px solid rgba(255,176,32,.22);
}
.dsk-warn.on { display: block; animation: warnPulse 2.4s ease-in-out infinite; }
@keyframes warnPulse { 0%, 100% { opacity: .8; } 50% { opacity: 1; } }

/* ================= 温度监控：CPU / 显卡（与磁盘卡同一套温度配色） ================= */
.therm {
    position: relative; margin-bottom: 7px; padding: 8px 9px 9px; border-radius: 9px;
    background: linear-gradient(90deg, rgba(8,18,38,.55), rgba(5,12,26,.85));
    border: 1px solid rgba(0,174,255,.12); overflow: hidden;
    transition: border-color .35s ease, transform .28s ease, box-shadow .28s ease;
}
.therm:last-child { margin-bottom: 0; }
.therm:hover { border-color: rgba(0,229,255,.3); transform: translateY(-1px); box-shadow: 0 6px 18px rgba(0,0,0,.35); }
.therm.t-ok   { --dcol: #22e39a; }
.therm.t-warn { --dcol: #ffb020; }
.therm.t-crit { --dcol: #ff4d6d; }
.therm.t-mut  { --dcol: #7d97b8; }
.therm.fresh { animation: dskIn .42s cubic-bezier(.2,.9,.3,1) both; }

.th-top { display: flex; align-items: center; gap: 8px; }
.th-ico {
    flex: none; width: 28px; height: 28px; border-radius: 8px; display: inline-flex;
    align-items: center; justify-content: center; color: var(--dcol, #7d97b8);
    background: rgba(0,174,255,.08); border: 1px solid rgba(0,174,255,.16);
    transition: color .35s ease, box-shadow .35s ease;
}
.th-ico svg { width: 16px; height: 16px; }
.therm:hover .th-ico { box-shadow: 0 0 14px rgba(0,229,255,.2); }
.th-id { flex: 1 1 auto; min-width: 0; }
.th-name {
    font-size: 12px; font-weight: 600; color: #eaf5ff;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.th-sub {
    margin-top: 2px; font-size: 9.5px; color: #6b86ab;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.th-badge {
    flex: none; display: inline-flex; align-items: center; gap: 3px;
    padding: 3px 7px; border-radius: 20px; font-size: 11px; font-variant-numeric: tabular-nums;
    color: #8ba3c4; background: rgba(120,160,220,.10); border: 1px solid rgba(120,160,220,.24);
    transition: color .35s ease, background .35s ease, border-color .35s ease;
}
.th-badge svg { width: 11px; height: 11px; }
.th-badge b { font-weight: 600; }
.th-badge.ok   { color: #8ff0c8; background: rgba(34,227,154,.12); border-color: rgba(34,227,154,.3); }
.th-badge.warn { color: #ffd28a; background: rgba(255,176,32,.12); border-color: rgba(255,176,32,.34); }
.th-badge.crit { color: #ffb3c0; background: rgba(255,77,109,.14); border-color: rgba(255,77,109,.4); }

.th-lines { margin-top: 8px; display: grid; gap: 5px; }
.th-line {
    display: grid; grid-template-columns: 14px minmax(0,1fr) auto;
    align-items: center; gap: 6px;
    padding: 4px 6px; border-radius: 7px;
    background: rgba(0,0,0,.28); border: 1px solid rgba(0,174,255,.1);
    transition: border-color .3s ease, background .3s ease;
}
.th-line:hover { border-color: rgba(0,174,255,.24); background: rgba(8,20,40,.72); }
.th-line .tl-i { color: #6b86ab; display: inline-flex; }
.th-line .tl-i svg { width: 13px; height: 13px; }
.th-line .tl-n {
    font-size: 10.5px; color: #cfe3ff; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.th-line .tl-s { display: none; font-size: 9px; color: #6b86ab; }
.th-line .tl-b {
    display: none; width: 58px; height: 3px; border-radius: 2px;
    background: rgba(255,255,255,.08); overflow: hidden;
}
.th-line.has-bar { grid-template-columns: 14px minmax(0,1fr) 58px auto; }
.th-line.has-bar .tl-b { display: block; }
.th-line .tl-b i {
    display: block; height: 100%; border-radius: 2px; width: 0;
    background: linear-gradient(90deg, rgba(0,229,255,.5), #00e5ff);
    transition: width .6s cubic-bezier(.2,.8,.3,1);
}
.th-line .tl-b.md i { background: linear-gradient(90deg, rgba(255,176,32,.5), #ffb020); }
.th-line .tl-b.hi i { background: linear-gradient(90deg, rgba(255,77,109,.5), #ff4d6d); }
.th-line .tl-v {
    font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
    color: #8ba3c4; text-align: right; transition: color .35s ease;
}
.th-line .tl-v.ok   { color: #8ff0c8; }
.th-line .tl-v.warn { color: #ffd28a; }
.th-line .tl-v.crit { color: #ffb3c0; }

@media (prefers-reduced-motion: reduce) {
    .therm.fresh { animation: none; }
    .therm:hover, .th-line .tl-b i { transition: none; }
}

/* 功耗与电费 */
.pow-main { display: flex; align-items: baseline; gap: 8px; }
.pow-main b {
    font-size: 27px; font-weight: 600; color: var(--amber);
    text-shadow: 0 0 18px rgba(255,176,32,.45); font-variant-numeric: tabular-nums;
}
.pow-main span { font-size: 11px; color: var(--mut); letter-spacing: .5px; }
.pow-bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; margin-top: 10px; background: rgba(6,18,38,.85); }
.pow-bar i { height: 100%; }
.pow-legend { display: flex; flex-wrap: wrap; gap: 4px 9px; margin-top: 7px; }
.pow-legend span { display: inline-flex; align-items: center; gap: 4px; font-size: 9.5px; color: var(--mut); }
.pow-legend i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }
.pow-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 10px; }
.pow-cell {
    text-align: center; padding: 7px 3px; border-radius: 8px;
    background: rgba(6,16,32,.6); border: 1px solid var(--line);
}
.pow-cell b { display: block; font-size: 13px; color: #dceaff; font-variant-numeric: tabular-nums; }
.pow-cell span { display: block; margin-top: 3px; font-size: 9px; color: var(--mut); letter-spacing: .5px; }
.pow-note { margin-top: 8px; font-size: 9.5px; color: var(--mut); line-height: 1.55; }
.pow-edit { display: block; margin-top: 8px; }
.pow-edit input {
    width: 62px; padding: 3px 7px; font-size: 11px; border-radius: 6px;
    background: rgba(4,12,26,.85); border: 1px solid rgba(0,174,255,.3); color: #dceaff;
    font-variant-numeric: tabular-nums;
}
.pe-mode { display: flex; gap: 14px; margin-bottom: 7px; }
.pe-r { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: #cfe6ff; cursor: pointer; }
.pe-body { display: flex; flex-direction: column; gap: 6px; }
.pe-lbl { font-size: 11px; color: #9fb6d8; }
.pe-tier { display: grid; grid-template-columns: 22px 1fr 1fr 26px; gap: 7px; align-items: center; }
.pe-tier.pe-head { font-size: 9.5px; color: var(--mut); letter-spacing: .5px; margin-bottom: 1px; }
.pe-rank { font-size: 11px; color: var(--cyan); text-align: center; }
.pe-tier input { width: 100%; padding: 3px 7px; font-size: 11px; border-radius: 6px; background: rgba(4,12,26,.85); border: 1px solid rgba(0,174,255,.3); color: #dceaff; font-variant-numeric: tabular-nums; }

/* 运行进程 */
#procBox { max-height: 236px; overflow-y: auto; padding-right: 3px; }
#procBox::-webkit-scrollbar { width: 5px; }
#procBox::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
.proc { display: flex; align-items: center; gap: 7px; padding: 4px 0; border-bottom: 1px dashed rgba(120,160,220,.1); }
.proc:last-child { border-bottom: 0; }
.proc .no {
    flex: none; width: 15px; height: 15px; border-radius: 4px; display: grid; place-items: center;
    font-size: 9px; background: rgba(0,229,255,.12); color: #8fdcff;
}
.proc .pn { flex: 1 1 auto; min-width: 0; font-size: 11.5px; color: #dceaff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.proc .pc { flex: none; min-width: 46px; text-align: right; font-size: 10.5px; font-variant-numeric: tabular-nums; color: #8fdcff; }
.proc .pm { flex: none; min-width: 52px; text-align: right; font-size: 10.5px; font-variant-numeric: tabular-nums; color: var(--mut); }
.proc-host { padding: 6px 0 3px; font-size: 10px; letter-spacing: 1px; color: #6b86ab; }
.proc-host:first-child { padding-top: 0; }

@media (prefers-reduced-motion: reduce) { .ctx-menu { animation: none !important; } }
@media (max-width: 900px) {
    .clocks { justify-content: center; }
    .tz-pick { position: fixed; left: 10px; right: 10px; bottom: 56px; width: auto; }
}

/* 明细行里的节点名前缀（全部汇总视图下要能看出这条属于哪台机器） */
.hst { font-style: normal; color: #6b86ab; font-size: 9.5px; margin-right: 4px; }
.empty-min {
    padding: 16px 4px; text-align: center;
    font-size: 11.5px; color: #6b86ab; line-height: 1.7;
}
.empty-min span { font-size: 10px; color: #52698a; }


/* ==========================================================
   2026-09-25 布局修补：栏位溢出 / 弹层对齐
   ----------------------------------------------------------
   1) 中心列在 900~985px 高的视口里内容比可用高度高，原来会被
      html/body{overflow:hidden} 直接裁掉（底部图表行彻底看不到），
      这里把中心列本身做成"最后兜底可滚"，并把地球面板的最小高度
      从 280 收到 240、画布从 240 收到 200，先保证一屏放得下。
   2) 右栏原来 #diskBox 内部滚动、.right 又整体滚动（两层嵌套滚动），
      磁盘列表被截成半行。统一成单层：右栏整体滚，列表不再单独裁。
   3) 访问来源列表的 max-height 写在基础规则之前，被后面的
      max-height:none 覆盖掉了，小屏下会无限增高把地图挤扁 → 补回。
   4) 底栏三栏网格里右侧按钮原来贴在时钟右边，看着偏；改为右对齐。
   ========================================================== */
.center { overflow-y: auto; overflow-x: hidden; padding-right: 2px; }
.center::-webkit-scrollbar { width: 5px; }
.center::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
.globe-panel { min-height: 240px; }
.globe-canvas-box { min-height: 200px; }

/* 磁盘列表：保留自身滚动，但高度给足（约两张卡 + 第三张露头，一眼看出可滚），
   避免 6 块盘把「硬件监控」这一块撑到 1000px 高、把右栏其它面板全顶下去 */
#diskBox { max-height: 320px; overflow-y: auto; padding-right: 4px; }
#diskBox::-webkit-scrollbar { width: 5px; }
#diskBox::-webkit-scrollbar-thumb { background: rgba(0,229,255,.35); border-radius: 4px; }
#volBox, #tempBox { max-height: 320px; overflow-y: auto; padding-right: 4px; }
.right::-webkit-scrollbar { width: 6px; }
.right::-webkit-scrollbar-thumb { background: rgba(0,229,255,.42); border-radius: 4px; }
.right::-webkit-scrollbar-track { background: rgba(255,255,255,.04); border-radius: 4px; }

@media (max-height: 899px), (max-width: 1100px) {
    .visitor-list { max-height: 232px; }
}
.bottombar .bb-r { justify-self: end; }

/* 右栏是"整列一条滚动条"，滚到底之前给一点下沿渐隐，提示下面还有内容
   （sticky 让它始终贴在滚动容器底部；负 margin 让它不占布局高度） */
.right::after {
    content: ''; position: sticky; bottom: 0; flex: 0 0 auto; display: block;
    height: 26px; margin-top: -26px; pointer-events: none;
    background: linear-gradient(180deg, rgba(4,11,24,0), rgba(4,11,24,.92));
}
