/* ============ 主题令牌(深色主题)============
   一套变量,四套值。基准 :root=浅色;auto(无 data-theme)时靠 prefers-color-scheme
   切深;手动选 dark/oled/vscode 用属性块压过媒体查询。每套必带 color-scheme,让原生
   控件(input/select/button/dialog/滚动条)跟着变。契约:主题由服务端 baseData.SystemTheme
   渲染;data-theme ∈ {未设(=auto), light, dark, oled, vscode}。 */
:root {
  color-scheme: light;
  --bg: #ffffff; --fg: #1a1a1a; --fg-muted: #57606a; --fg-subtle: #8b949e;
  --border: #d0d7de; --border-strong: #888888; --surface: #f6f8fa; --surface-2: #f0f0f0;
  --accent: #0366d6; --overlay: rgba(0,0,0,.35); --shadow: rgba(0,0,0,.15);
  --zebra: rgba(0,0,0,.04); --highlight-bg: #fff7cc;
  --success-fg: #1a7f37; --success-bg: #def7e0;
  --danger-fg: #cf222e; --danger-bg: #ffebe9;
  --warn-fg: #9a6700; --warn-bg: #fff1cc;
  --attn-fg: #bc4c00; --attn-bg: #fff1e5;
  --badge-r: #f0ad4e; --badge-d: #5bc0de; --badge-b: #d9534f;
  --cell-degrade-fg: #8a6d00; --cell-degrade-bg: #fdf3cf;
  --cell-warming-fg: #54657a; --cell-warming-bg: #eef2f7;
  --cell-unverified-fg: #00695c; --cell-unverified-bg: #e0f2f1;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;
    --bg: #0d1117; --fg: #c9d1d9; --fg-muted: #8b949e; --fg-subtle: #6e7681;
    --border: #30363d; --border-strong: #484f58; --surface: #161b22; --surface-2: #21262d;
    --accent: #58a6ff; --overlay: rgba(1,4,9,.8); --shadow: rgba(0,0,0,.4);
    --zebra: rgba(255,255,255,.04); --highlight-bg: #26210a;
    --success-fg: #3fb950; --success-bg: #12261e;
    --danger-fg: #f85149; --danger-bg: #2d1414;
    --warn-fg: #d29922; --warn-bg: #272115;
    --attn-fg: #db6d28; --attn-bg: #2b1c12;
    --badge-r: #9e6a03; --badge-d: #1f6feb; --badge-b: #da3633;
    --cell-degrade-fg: #cbb130; --cell-degrade-bg: #2b2609;
    --cell-warming-fg: #79c0ff; --cell-warming-bg: #12233a;
    --cell-unverified-fg: #39c5cf; --cell-unverified-bg: #0c2626;
  }
}
/* 注意:本块令牌须与上方 @media(prefers-color-scheme:dark) 内的 :root:not([data-theme]) 逐字一致——auto-dark 与 manual-dark 外观必须相同,改一处须同步另一处。 */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0d1117; --fg: #c9d1d9; --fg-muted: #8b949e; --fg-subtle: #6e7681;
  --border: #30363d; --border-strong: #484f58; --surface: #161b22; --surface-2: #21262d;
  --accent: #58a6ff; --overlay: rgba(1,4,9,.8); --shadow: rgba(0,0,0,.4);
  --zebra: rgba(255,255,255,.04); --highlight-bg: #26210a;
  --success-fg: #3fb950; --success-bg: #12261e;
  --danger-fg: #f85149; --danger-bg: #2d1414;
  --warn-fg: #d29922; --warn-bg: #272115;
  --attn-fg: #db6d28; --attn-bg: #2b1c12;
  --badge-r: #9e6a03; --badge-d: #1f6feb; --badge-b: #da3633;
  --cell-degrade-fg: #cbb130; --cell-degrade-bg: #2b2609;
  --cell-warming-fg: #79c0ff; --cell-warming-bg: #12233a;
  --cell-unverified-fg: #39c5cf; --cell-unverified-bg: #0c2626;
}
:root[data-theme="oled"] {
  color-scheme: dark;
  --bg: #000000; --fg: #c9d1d9; --fg-muted: #8b949e; --fg-subtle: #6e7681;
  --border: #21262d; --border-strong: #3d444d; --surface: #0d0d0d; --surface-2: #161616;
  --accent: #58a6ff; --overlay: rgba(0,0,0,.85); --shadow: rgba(0,0,0,.6);
  --zebra: rgba(255,255,255,.03); --highlight-bg: #1c1804;
  --success-fg: #3fb950; --success-bg: #0a1a10;
  --danger-fg: #f85149; --danger-bg: #1f0d0d;
  --warn-fg: #d29922; --warn-bg: #1a160c;
  --attn-fg: #db6d28; --attn-bg: #1c1109;
  --badge-r: #9e6a03; --badge-d: #1f6feb; --badge-b: #da3633;
  --cell-degrade-fg: #cbb130; --cell-degrade-bg: #211d06;
  --cell-warming-fg: #79c0ff; --cell-warming-bg: #0a1826;
  --cell-unverified-fg: #39c5cf; --cell-unverified-bg: #061a1a;
}
:root[data-theme="vscode"] {
  color-scheme: dark;
  --bg: #1e1e1e; --fg: #d4d4d4; --fg-muted: #9d9d9d; --fg-subtle: #858585;
  --border: #3c3c3c; --border-strong: #555555; --surface: #252526; --surface-2: #2d2d2d;
  --accent: #3794ff; --overlay: rgba(0,0,0,.6); --shadow: rgba(0,0,0,.4);
  --zebra: rgba(255,255,255,.035); --highlight-bg: #3a3a1c;
  --success-fg: #89d185; --success-bg: #1e2d1e;
  --danger-fg: #f48771; --danger-bg: #2d1e1e;
  --warn-fg: #cca700; --warn-bg: #2d2a1e;
  --attn-fg: #d18616; --attn-bg: #2d2416;
  --badge-r: #cca700; --badge-d: #3794ff; --badge-b: #f48771;
  --cell-degrade-fg: #d7c250; --cell-degrade-bg: #332e17;
  --cell-warming-fg: #9cdcfe; --cell-warming-bg: #1e2a35;
  --cell-unverified-fg: #4ec9b0; --cell-unverified-bg: #14292a;
}
:root { font-family: system-ui, sans-serif; line-height: 1.5; }
body { padding: 1rem; background: var(--bg); color: var(--fg); }
nav { display: flex; gap: 1rem; align-items: center; border-bottom: 1px solid var(--border); padding-bottom: .5rem; margin: 0 auto 1rem; max-width: 1100px; }
nav a { text-decoration: none; color: var(--accent); }
nav a.active { font-weight: 600; text-decoration: underline; }
nav .spacer { flex: 1; }
/* 布局容器各自限宽居中(而非把 max-width 加在 body 上),这样拓扑页只放开 main 全宽,
   顶部导航栏在所有页面保持 1100px 居中、跨页切换不抖动。 */
main { max-width: 1100px; margin: 0 auto; }
body > footer { max-width: 1100px; margin: 0 auto; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid var(--border); padding: .35rem .5rem; text-align: left; font-size: .9rem; vertical-align: top; }
th { background: var(--surface); }
.badge { padding: .1rem .4rem; border-radius: .5rem; font-size: .8rem; }
.cell-pos { position: relative; }
.badge-R, .badge-D, .badge-B { position: absolute; bottom: 2px; right: 2px; padding: 0 4px; font-size: 0.7em; border-radius: 3px; color: white; }
.badge-R { background: var(--badge-r); }
.badge-D { background: var(--badge-d); opacity: 0.7; }
.badge-B { background: var(--badge-b); }
.online { background: var(--success-bg); color: var(--success-fg); }
.offline { background: var(--danger-bg); color: var(--danger-fg); }
.err { color: var(--danger-fg); }
.ok { color: var(--success-fg); }
form.inline { display: inline; }
button { cursor: pointer; }
code { background: var(--surface); padding: .1rem .3rem; border-radius: .3rem; }
table.matrix td, table.matrix th { text-align: center; min-width: 4rem; }
.cell-self { background: var(--surface); color: var(--fg-subtle); }
.cell-direct { background: var(--success-bg); color: var(--success-fg); }
.cell-relay { background: var(--warn-bg); color: var(--warn-fg); }
.cell-none, .cell-stale { background: var(--surface-2); color: var(--fg-subtle); }
.cell-direct-degrade { background: var(--cell-degrade-bg); color: var(--cell-degrade-fg); } /* 黄:直连降级 */
.cell-fake-direct    { background: var(--danger-bg); color: var(--danger-fg); font-weight:600; } /* 红:假直连 */
.cell-relay-lost     { background: var(--warn-bg); color: var(--warn-fg); } /* 橙:直连断已切中继 */
.cell-warming        { background: var(--cell-warming-bg); color: var(--cell-warming-fg); } /* 灰蓝:预热中 */
.cell-direct-unverified { background: var(--cell-unverified-bg); color: var(--cell-unverified-fg); } /* 青:直连·待确认(非健康断言) */
.row-stale { color: var(--fg-subtle); }
.suspended-row { background: var(--surface-2); opacity: 0.65; }
.suspended-row td { color: var(--fg-muted); }
/* v1.17.1: dropdown 改 click 触发(去 hover),弹在 cell **上方**居中,
   避免遮挡下一行 cell 让用户误以为是下个节点的菜单。
   .cell-pos[data-admin] 才显示可点指针(non-admin 不挂 menu)。 */
.cell-menu { display: none; position: absolute; bottom: 100%; left: 50%;
             transform: translateX(-50%); z-index: 10; background: var(--bg);
             border: 1px solid var(--border-strong); border-radius: 4px; padding: 6px 8px;
             margin-bottom: 4px; box-shadow: 0 2px 8px var(--shadow);
             white-space: nowrap; }
.cell-menu.open { display: block; }
.cell-menu select { font-size: 0.85em; }
/* cursor + 边框提示只在含 cell-menu(admin)的 cell 出现。:has() 在
   Safari 15.4+ / Chrome 105+ / Firefox 121+ 都支持,新 Mac 浏览器无问题。 */
.cell-pos:has(.cell-menu) { cursor: pointer; }
.cell-pos:has(.cell-menu):hover { outline: 1px dashed var(--border-strong); outline-offset: -2px; }
/* 改 IP 入口:VIP 单元格里的小按钮 + 共用弹窗 */
.vip-edit { font-size: .75em; padding: 0 .3rem; margin-left: .25rem; line-height: 1.4; color: var(--accent); background: none; border: 1px solid var(--border); border-radius: 3px; }
.vip-edit:hover { background: var(--surface); }
dialog#vip-dialog { border: 1px solid var(--border-strong); border-radius: 6px; padding: 1rem 1.25rem; min-width: 18rem; }
dialog#vip-dialog::backdrop { background: var(--overlay); }
dialog#vip-dialog h3 { margin: 0 0 .5rem; font-size: 1rem; }
dialog#vip-dialog input { width: 100%; padding: .35rem; box-sizing: border-box; }
dialog#vip-dialog .hint { color: var(--fg-subtle); font-size: .8rem; margin: .35rem 0; }
dialog#vip-dialog menu { display: flex; gap: .5rem; justify-content: flex-end; margin: .75rem 0 0; padding: 0; }
dialog#splitdns-dialog { border: 1px solid var(--border-strong); border-radius: 6px; padding: 1rem 1.25rem; min-width: 34rem; max-width: 90vw; }
dialog#splitdns-dialog::backdrop { background: var(--overlay); }
dialog#splitdns-dialog h3 { margin: 0 0 .5rem; font-size: 1rem; }
dialog#splitdns-dialog .hint { color: var(--fg-subtle); font-size: .8rem; margin: .35rem 0; }
dialog#splitdns-dialog #splitdns-rows { max-height: 16rem; overflow-y: auto; margin: .5rem 0; }
dialog#splitdns-dialog .splitdns-row { display: flex; gap: .5rem; margin: .3rem 0; align-items: center; }
dialog#splitdns-dialog .splitdns-domain { flex: 2; padding: .35rem; box-sizing: border-box; }
dialog#splitdns-dialog .splitdns-ip { flex: 1; padding: .35rem; box-sizing: border-box; }
dialog#splitdns-dialog .splitdns-del { flex: 0 0 auto; }
dialog#splitdns-dialog menu { display: flex; gap: .5rem; justify-content: flex-end; margin: .75rem 0 0; padding: 0; }
dialog#route-dialog { border: 1px solid var(--border-strong); border-radius: 6px; padding: 1rem 1.25rem; min-width: 28rem; }
dialog#route-dialog::backdrop { background: var(--overlay); }
dialog#route-dialog h3 { margin: 0 0 .5rem; font-size: 1rem; }
dialog#route-dialog textarea { width: 100%; min-height: 8rem; padding: .35rem; box-sizing: border-box; font-family: monospace; resize: vertical; }
dialog#route-dialog .hint { color: var(--fg-subtle); font-size: .8rem; margin: .35rem 0; }
dialog#route-dialog menu { display: flex; gap: .5rem; justify-content: flex-end; margin: .75rem 0 0; padding: 0; }
.subname { color: var(--fg-subtle); font-size: 0.85em; }
.note-edit { font-size: 0.8em; padding: 0 .3em; }
/* 设备操作页:平铺设置区(每项一行)+ 精简设备表 */
.topo-toolbar { display: flex; align-items: center; gap: .6em; margin: .4em 0; }
.ops-toolbar { margin-bottom: 1rem; }
.ops-toolbar fieldset { margin: 0 0 .6rem; border: 1px solid var(--border); border-radius: 6px; padding: .5rem .75rem; }
.ops-toolbar legend { font-size: .8rem; color: var(--fg-muted); padding: 0 .3rem; }
.ops-shared { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }
.ops-selectall { font-size: .85rem; }
.ops-item { display: flex; align-items: center; gap: .5rem; padding: .2rem 0; font-size: .9rem; }
.ops-item select, .ops-item input[type=text] { font-size: .85rem; }
.ops-item .ops-apply { margin-left: auto; }
.ops-refresh { font-size: .75em; padding: 0 .3rem; }
.ops-mark { text-align: center; }
#operations-table th, #operations-table td { font-size: .82rem; padding: .25rem .4rem; }
#operations-table td .muted { color: var(--fg-subtle); font-size: .8rem; }
.route-edit { display: inline-flex; gap: .25rem; margin-top: .2rem; }
.route-edit input { font-size: .8rem; width: 11rem; }
.dev-ops { display: flex; flex-wrap: wrap; gap: 4px; }
small.acct { display: inline-block; }
/* 操作页迭代4:设置项斑马纹(T6);顶部汇总(T7) */
fieldset .ops-item:nth-of-type(odd) { background: var(--zebra); }
#ops-summary { margin-left: 1rem; font-weight: 600; }
#ops-summary-toggle { margin-left: .5rem; font-size: .8rem; }
.ops-detail { margin-top: .4rem; font-size: .85em; color: var(--fg-muted); }
.ops-detail div { padding: 1px 0; }
#devices-table button { white-space: nowrap; flex-shrink: 0; }
/* 操作页:生命周期颜色 + 状态刷新按钮(合并后的单元格样式见下方 .cell-status) */
.lc-queued { color: var(--fg-muted); }
.lc-dispatched { color: var(--accent); }
.lc-running { color: var(--warn-fg); }
.lc-succeeded { color: var(--success-fg); font-weight: 600; }
.lc-failed { color: var(--danger-fg); font-weight: 600; }
.lc-timeout { color: var(--attn-fg); }
.lc-cancelled { color: var(--fg-muted); }
#ops-refresh-all { margin-left: 1rem; }
#ops-refresh-progress { margin-left: .5rem; font-size: .85em; color: var(--fg-muted); }
/* 操作页:进行中"省略号"——外盒固定 1.5em(永远预留,在流内不变形)+ 内层
   width 动画被外盒 overflow:hidden 裁剪 ⇒ 只揭示点、零布局位移。content 静态
   "···" 作 fallback(Safari 不动画 content)。按钮忙态只变灰+禁用,绝不加 ::after
   或改宽度(旧 .btn-busy::after 会让按钮抖 1.5em,已删)。 */
@keyframes ops-ellipsis { from { width: 0; } to { width: 1.5em; } }
/* 操作页:状态列固定 ~12 中文字宽,超出换行(行高随内容增高),
   不再随徽章文案长度左右跳动。只 pin 本列,不用 table-layout:fixed
   以免影响其它 17 列。th 也带 cell-status → 表头与单元格同宽。 */
.cell-status {
  width: 12em; min-width: 12em; max-width: 12em;
  text-align: center; font-size: .85em;
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
}
.lc-reason { color: var(--fg-muted); font-weight:400; }
.lc-dots { display:inline-block; width:1.5em; overflow:hidden; vertical-align:bottom; white-space:nowrap; }
.lc-dots::after { content:"···"; display:inline-block; width:0; overflow:hidden; vertical-align:bottom; white-space:nowrap; animation: ops-ellipsis 1.1s steps(4) infinite; }
.btn-busy { opacity:.6; pointer-events:none; }
/* 操作页:中继健康列(独立于 lifecycle 的 中继 数据面健康 / needs-attention 标) */
.cell-relayhealth { text-align:center; font-size:.85em; white-space:nowrap; }
.relay-ok { color: var(--success-fg); }
.relay-wedged { color: var(--attn-fg); font-weight:600; }
.relay-attn { color: var(--danger-fg); font-weight:600; }
/* 拓扑页:数据密集,占满视口宽度;其余页仍保持 1100px 居中阅读宽度 */
body.page-topology main { max-width: none; }
body.page-topology .matrix-scroll { overflow-x: auto; }
.link-filter { margin: 0.5rem 0; }
.link-filter label { margin-right: 1rem; }
.row-focus { background: var(--highlight-bg); }
.ro-banner { background: #4d3800; color: #ffd666; text-align: center; padding: 0.5rem; font-size: 0.85em; }
/* 服务器页:coord 服务器(故障切换)地址行——默认输入框太窄,https://host:port 显示不全。
   改为 flex 行,输入框撑满剩余宽度(至少 24rem)完整显示地址;删除按钮保持原宽。 */
.cs-row { display: flex; align-items: center; gap: .5rem; margin: .3rem 0; }
.cs-row input[type=url] { flex: 1 1 auto; min-width: min(24rem, 100%); max-width: 100%; padding: .3rem .4rem; }
/* 服务器页:版本备份区 */
.backups { margin-top: 1.2rem; }
.backups .backup-group { margin: .35rem 0; font-size: .9rem; }
.backups details { margin: .2rem 0 .6rem; }
.backups summary { cursor: pointer; color: var(--accent); font-size: .85rem; }
.ops-pause-banner { padding: .5rem .75rem; margin: .5rem 0; border: 1px solid var(--border); border-radius: .5rem; background: var(--highlight-bg); font-weight: 600; }
.ops-pause-banner.lc-timeout { background: var(--attn-bg); }
.ops-pause-banner.lc-failed { background: var(--danger-bg); }
.ops-pause-hint { display:block; font-weight: 400; font-size: .85em; color: var(--fg-muted); margin-top: .25rem; }
.ops-upgrade-controls { margin: .25rem 0; }
.ops-upgrade-running { color: var(--warn-fg); margin-right: .5rem; }
.ops-upgrade-blocked { color: var(--fg-muted); }
