/* 自托管字体：JetBrains Mono 与 Space Grotesk 的 latin 子集（SIL OFL 许可，经 @fontsource 的 woff2 落地到 web/vendor/fonts/）。
   原先外链 Google Fonts 在国内网络实测不可达（302 循环 / ERR_TOO_MANY_REDIRECTS），仅剩系统字体回退，故改为本地文件。
   中文仍走系统 CJK 字体回退，与本地上字体策略一致。 */
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url('vendor/fonts/jetbrains-mono-latin-400.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url('vendor/fonts/jetbrains-mono-latin-500.woff2') format('woff2'); }
@font-face { font-family: 'JetBrains Mono'; font-style: normal; font-weight: 700; font-display: swap; src: url('vendor/fonts/jetbrains-mono-latin-700.woff2') format('woff2'); }
@font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 500; font-display: swap; src: url('vendor/fonts/space-grotesk-latin-500.woff2') format('woff2'); }
@font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 700; font-display: swap; src: url('vendor/fonts/space-grotesk-latin-700.woff2') format('woff2'); }

/* ============ 柔和深空 Cyber Neon（护眼版） ============ */
/* 相对初版霓虹主题的调整（目标：长时间阅读不刺眼）：
   1) 背景由近纯黑抬升为中性深灰蓝，降低文字与背景的亮度落差（避免"黑洞+白字"式眩光）；
   2) 电光青 #00e5ff 降饱和降亮度为雾青，全站强调色统一走这一支；
   3) 发光强度大幅收敛（text-shadow/box-shadow 峰值约降 60%），保留赛博身份但不晃眼；
   4) 文字改中性灰阶 + 边框改中性浅边，减少满屏青色描边带来的色疲劳；
   5) 对比度仍满足 WCAG AA：正文约 12:1，次要文字约 7:1，主色文字约 9:1。 */
:root {
  color-scheme: dark;

  /* 背景层级 */
  --bg: #101720;
  --bg-2: #151d29;
  --surface: #1a2331;
  --surface-2: #212c3d;
  --surface-3: #202b3a;
  /* 半透明压暗层（覆盖在卡片底上，供内容区局部下沉用） */
  --bg-3: rgba(0, 0, 0, 0.15);

  /* 边框：--border 仅用于装饰性分隔线（卡片/分区描边，WCAG 1.4.11 不约束纯装饰）；
     交互控件边界一律用 --border-strong——alpha 0.64 叠在最浅表面 --surface-2 上对比度 3.14:1，
     满足 WCAG 1.4.11 非文本对比 ≥3:1（色值每次调整须按此口径实测复核） */
  --border: rgba(148, 163, 184, 0.16);
  --border-strong: rgba(148, 163, 184, 0.64);

  /* 主色 */
  --primary: #7ec8d8;
  --primary-dim: rgba(126, 200, 216, 0.12);
  --primary-glow: rgba(126, 200, 216, 0.18);

  /* 语义色（紫/红已提亮至 WCAG AA：着色小字在 hover 行等最差底上 ≥4.5:1） */
  --purple: #a397e2;
  --green: #6fca8f;
  --danger: #ea8f95;
  --warning: #d6a45f;
  /* 辅助蓝（徽章/标签用，替代组件内写死的 #6eb8f0） */
  --blue: #6eb8f0;

  /* 文字 */
  --text: #cfd8e3;
  --text-dim: #93a3b8;

  /* 间距 */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;
  --space-2xl: 32px;

  /* 圆角 */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 14px;
  --radius-full: 999px;

  /* 字号（正文层级刻度） */
  --font-size-xs: 12px;
  --font-size-sm: 13px;
  --font-size-base: 14px;
  --font-size-lg: 15px;
  --font-size-xl: 16px;
  --font-size-2xl: 18px;
  --font-size-3xl: 22px;
  /* 字号（展示刻度：大屏 KPI 数字等大号数字专用，不参与正文层级） */
  --font-size-4xl: 28px;
  --font-size-5xl: 36px;
  --font-size-6xl: 64px;

  /* 表单控件统一高度（搜索框 / 下拉 / 工具栏按钮等高，见下方「控件等高」） */
  --control-height: 40px;

  /* 星图配色 */
  --cat-1: #7ec8d8;
  --cat-2: #8578c8;
  --cat-3: #6fca8f;
  --cat-4: #d6a45f;
  --cat-none: #93a3b8;
  --cat-asset: #6b7a8f;

  /* 字体 */
  --font-mono: 'JetBrains Mono', 'Cascadia Code', Consolas, 'Courier New', monospace;
  --font-head: 'Space Grotesk', 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body { height: 100%; }

body {
  font-family: var(--font-mono);
  font-size: var(--font-size-base);
  line-height: 1.5;
  color: var(--text);
  background: radial-gradient(1200px 800px at 20% -10%, rgba(133, 120, 200, 0.10), transparent 60%),
              radial-gradient(1000px 700px at 90% 10%, rgba(126, 200, 216, 0.07), transparent 55%),
              linear-gradient(160deg, var(--bg) 0%, var(--bg-2) 100%);
  background-attachment: fixed;
  min-height: 100vh;
}

/* 深空网格背景 */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  background-image:
    linear-gradient(rgba(126, 200, 216, 0.02) 1px, transparent 1px),
    linear-gradient(90deg, rgba(126, 200, 216, 0.02) 1px, transparent 1px);
  background-size: 42px 42px;
  pointer-events: none;
  z-index: 0;
}

.hidden { display: none !important; }
.muted { color: var(--text-dim); }
.small { font-size: var(--font-size-xs); }
.error { color: var(--danger); margin-top: 8px; font-size: var(--font-size-sm); }

h1, h2, h3, h4 { font-family: var(--font-head); letter-spacing: 0.5px; }

/* 控件类文本行高统一：按钮 / 胶囊 / 标签 / 菜单等固定高度控件不随 body 行高（1.5）被撑高 */
.btn, .tab-btn, .chip, .tag, .badge, .menu-item, .params-kind, .legend-chip, .crumb,
.pin-badge, .act-ai, .act-status, .tag-vers, .adr-no, .sev-tag, .raid-tag { line-height: 1.2; }

/* 居中屏幕（登录） */
.center-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  z-index: 1;
  padding: 24px;
}

.card {
  background: rgba(26, 35, 49, 0.88);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 36px 32px;
  /* 卡片默认铺满容器：原为固定 400px（登录卡片的尺寸），被 Gitea 等页面当面板复用时
     会把内容压成 400px 窄条——表单塌成单列、按钮行被迫换行。登录卡片的 400px 见 .login-card */
  width: 100%;
  box-shadow: 0 4px 24px rgba(8, 12, 18, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.03);
  backdrop-filter: blur(10px);
}
.card h1 {
  font-size: var(--font-size-3xl);
  margin-bottom: 6px;
  color: var(--primary);
  text-shadow: 0 0 12px rgba(126, 200, 216, 0.18);
}
.card p { margin-bottom: 20px; color: var(--text-dim); }

/* 表单标签 */
label {
  display: block;
  margin-bottom: var(--space-xs);
  font-size: var(--font-size-sm);
  color: var(--text-dim);
  font-weight: 500;
}

/* 表单输入框 */
input, textarea, select {
  width: 100%;
  padding: 11px 13px;
  margin-bottom: 14px;
  background: var(--bg-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  font-family: inherit;
  color: var(--text);
  transition: border-color .15s ease, box-shadow .15s ease;
}
input::placeholder, textarea::placeholder { color: var(--text-dim); opacity: 0.8; }
input:hover, textarea:hover, select:hover { border-color: var(--primary); }
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary), 0 0 10px var(--primary-glow);
}
/* WCAG 2.4.7：键盘可达焦点统一可见焦点环（outline 不占布局；显式元素列表与上方 input:focus 同 specificity，靠后者位置优先） */
button:focus-visible, a:focus-visible, [tabindex]:focus-visible,
input:focus-visible, textarea:focus-visible, select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
textarea { min-height: 64px; resize: vertical; }
select { cursor: pointer; }
select option { background: var(--surface); color: var(--text); }

/* 控件等高：搜索框与同排的「搜索 / 重置」等按钮必须一样高（各页面的搜索栏统一）。
   根因：输入框继承正文字体、按钮用标题字体，二者默认行高不同——只靠 padding 推算高度必然错位；
   用 flex-end 底部对齐时表现为输入框顶边高出按钮一截，看起来就是不齐。
   故把文本类控件高度钉死，不再依赖行高；按钮侧仅作用于工具栏容器内的按钮，
   不动表格行内的小按钮（.btn-sm）与弹窗按钮，避免连带放大其它位置。 */
input[type="text"], input[type="search"], input[type="password"], input[type="email"],
input[type="number"], input[type="tel"], input:not([type]),
select {
  height: var(--control-height);
  padding-top: 0;
  padding-bottom: 0;
}
.toolbar .btn, .list-toolbar .btn, .starmap-toolbar .btn, .sub-head .btn { height: var(--control-height); }

/* 复选框 / 单选框保持原生尺寸：全局 input 规则里的 width:100% 与 padding/margin
   会把它们拉成整行宽的大方块（实测 Gitea 监控设置页三个勾选框宽 355–371px），
   并把所在行撑歪。需要特定尺寸的地方（如 .check-item 的 16px）用更高优先级覆盖。 */
input[type="checkbox"], input[type="radio"] {
  width: auto;
  height: auto;
  padding: 0;
  margin: 0;
}

/* 表单：验证状态 */
input.invalid, textarea.invalid, select.invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px rgba(234, 143, 149, 0.3);
}
input.invalid:focus, textarea.invalid:focus, select.invalid:focus {
  box-shadow: 0 0 0 1px var(--danger), 0 0 10px rgba(234, 143, 149, 0.2);
}
input.valid, textarea.valid, select.valid {
  border-color: var(--green);
}
.form-error {
  color: var(--danger);
  font-size: var(--font-size-xs);
  margin-top: calc(-14px + var(--space-xs));
  margin-bottom: var(--space-md);
}
.form-hint {
  color: var(--text-dim);
  font-size: var(--font-size-xs);
  margin-top: calc(-14px + var(--space-xs));
  margin-bottom: var(--space-md);
}

/* 表单：输入框组（输入框 + 图标/按钮） */
.input-group {
  display: flex;
  align-items: stretch;
  gap: 0;
  margin-bottom: 14px;
}
.input-group input {
  margin-bottom: 0;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
}
.input-group .btn {
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  border-left: none;
}
.input-group-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-right: none;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
  color: var(--text-dim);
  font-size: var(--font-size-lg);
  flex-shrink: 0;
}
.input-group-icon + input {
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

/* 表单：必填标记 */
label .required {
  color: var(--danger);
  margin-left: 2px;
}

/* 表单：禁用状态 */
input:disabled, textarea:disabled, select:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: var(--surface);
}

/* 表单：行内布局 */
.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
}
.form-row label { grid-column: 1 / -1; }
.form-row label.half { grid-column: auto; }

/* 按钮 */
.btn {
  padding: 8px 16px;
  border: 1px solid var(--border-strong);
  background: rgba(26, 35, 49, 0.6);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--font-size-base);
  font-family: var(--font-head);
  color: var(--text);
  transition: all .15s ease;
  user-select: none;
  white-space: nowrap;
}
.btn:hover { border-color: var(--primary); background: rgba(126, 200, 216, 0.08); }
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

/* 可点击胶囊（筛选 chip / 星图图例 / 参数组导航）：与按钮同一套交互反馈（按压微缩放、禁用置灰） */
.chip:active, .params-kind:active, .starmap-legend .legend-chip:active { transform: scale(0.97); }
.chip:disabled, .params-kind:disabled, .starmap-legend .legend-chip:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

.btn-primary {
  background: var(--primary-dim);
  border-color: var(--primary);
  color: var(--primary);
  box-shadow: 0 1px 2px rgba(8, 12, 18, 0.45), 0 0 12px rgba(126, 200, 216, 0.16);
  font-weight: 700;
}
.btn-primary:hover { background: var(--primary-glow); box-shadow: 0 0 16px rgba(126, 200, 216, 0.26); }

.btn-danger { border-color: rgba(234, 143, 149, 0.45); color: var(--danger); }
.btn-danger:hover { background: rgba(234, 143, 149, 0.12); box-shadow: 0 0 12px rgba(234, 143, 149, 0.2); }

.btn-warn { border-color: rgba(214, 164, 95, 0.45); color: var(--warning); }
.btn-warn:hover { background: rgba(214, 164, 95, 0.12); box-shadow: 0 0 12px rgba(214, 164, 95, 0.2); }

.btn-block { width: 100%; }
.btn-sm { padding: 5px 10px; font-size: var(--font-size-xs); }

/* 帐号按钮（domain 条目下） */
.btn-accounts { border-color: rgba(126, 200, 216, 0.3); color: var(--primary); font-size: var(--font-size-xs); }
.btn-accounts:hover, .btn-accounts.active { background: rgba(126, 200, 216, 0.12); box-shadow: 0 0 8px rgba(126, 200, 216, 0.15); }

/* 帐号展开区域 */
.accounts-row td { padding: 0 !important; background: rgba(0,0,0,0.12); }
.accounts-container { padding: 12px 16px; }
.da-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.da-table { margin: 0; background: rgba(0,0,0,0.1); }
.da-table th { font-size: var(--font-size-xs); color: var(--text-dim); padding: var(--space-sm) var(--space-md); }
.da-table td { font-size: var(--font-size-sm); padding: var(--space-sm) var(--space-md); }
.btn-add-da { border-color: rgba(126, 200, 216, 0.3); color: var(--primary); }

/* 分配弹窗 checkbox 列表 */
.check-group { margin-bottom: var(--space-lg); }
.check-group-title { font-size: var(--font-size-sm); font-weight: 700; color: var(--primary); margin-bottom: var(--space-sm); padding-bottom: 6px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 6px; }
.check-list { display: flex; flex-direction: column; }
/* 两列勾选行（名称 | 说明/编码）：说明列固定宽度成列，列间加与表格列分割线同规格的 1px 细线（--primary-dim）。
   分割线画在名称列右缘并用负 margin 拉满行高，即使该行没有说明文案，竖线也连续不断。 */
.check-item {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr) 180px;
  align-items: center;
  column-gap: var(--space-md);
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background .15s ease;
  font-size: var(--font-size-sm);
}
.check-item:hover { background: rgba(126, 200, 216, 0.08); }
.check-item input[type="checkbox"] { accent-color: var(--primary); width: 16px; height: 16px; }
.check-item .check-label {
  min-width: 0;
  align-self: stretch;
  display: flex;
  align-items: center;
  margin: -8px 0;
  padding: 8px var(--space-md) 8px 0;
  border-right: 1px solid var(--primary-dim);
}
.check-item .check-desc {
  min-width: 0;
  color: var(--text-dim);
  font-size: var(--font-size-xs);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.check-toolbar { display: flex; gap: var(--space-sm); margin-bottom: 12px; }
/* 小号按钮统一走 .btn-sm 规格（5px 10px / 12px），不再各写一套 */
.check-toolbar .btn { font-size: var(--font-size-xs); padding: 5px 10px; }
.check-box { max-height: 400px; overflow-y: auto; padding: var(--space-sm); background: var(--bg); border-radius: var(--radius-md); border: 1px solid var(--border); }

/* PEM 私钥文件选择 */
.file-input { margin-top: 6px; padding: 4px 0; font-size: var(--font-size-sm); color: var(--text-dim); }
.file-input::file-selector-button {
  background: rgba(126, 200, 216, 0.12);
  border: 1px solid var(--primary);
  color: var(--primary);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  cursor: pointer;
  font-family: inherit;
  margin-right: 10px;
  transition: all .15s ease;
}
.file-input::file-selector-button:hover { background: rgba(126, 200, 216, 0.18); }
.file-input::file-selector-button:active { transform: scale(0.97); }

/* 顶栏 */
.topbar {
  background: rgba(18, 25, 35, 0.92);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.03);
  padding: 14px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  z-index: 1;
  backdrop-filter: blur(10px);
}
.topbar .brand {
  font-family: var(--font-head);
  font-size: var(--font-size-xl);
  font-weight: 700;
  color: var(--primary);
  text-shadow: 0 0 12px rgba(126, 200, 216, 0.18);
  letter-spacing: 1px;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.topbar .brand .brand-icon {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  filter: drop-shadow(0 0 6px rgba(126, 200, 216, 0.3));
}
.topbar-right { display: flex; align-items: center; gap: var(--space-md); }
.topbar-right .muted { color: var(--text-dim); }

.container { max-width: 1280px; margin: 28px auto; padding: 0 28px; position: relative; z-index: 1; }

.section-header {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
  margin-bottom: 18px;
}
.section-header h2 {
  font-size: var(--font-size-2xl);
  flex: 1;
  color: var(--text);
}

/* 管理 tab */
.tabs { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-bottom: 18px; border-bottom: 1px solid var(--border); }
.tab-btn {
  padding: 9px 18px;
  border: none;
  background: none;
  cursor: pointer;
  font-size: var(--font-size-base);
  font-family: var(--font-head);
  color: var(--text-dim);
  border-bottom: 2px solid transparent;
  transition: color .15s ease, border-color .15s ease;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); }
.tab-panel { margin-top: 14px; }

/* 项目详情底部的「上一条 / 下一条」：与顶部同款按钮，滚到页面底部也能连续编辑 */
.detail-nav {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
  margin-top: var(--space-xl);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}

.perm-box { display: flex; gap: 10px; margin-bottom: 18px; }
.perm-box select { margin-bottom: 0; width: auto; flex: 1; }

/* 表格 */
.table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 4px 16px rgba(6, 10, 16, 0.25);
}
.table th, .table td {
  padding: 11px 14px;
  text-align: left;
  border-bottom: 1px solid rgba(126, 200, 216, 0.08);
  vertical-align: middle;
  /* 长内容（URL / IP / 说明）只在放不下时才折行，正常单词不被拦腰截断 */
  overflow-wrap: anywhere;
  transition: background .15s ease;
}
.table th {
  background: var(--surface-2);
  font-family: var(--font-head);
  font-weight: 700;
  text-align: center;
  color: var(--primary);
  white-space: nowrap;
  letter-spacing: 0.5px;
  font-size: var(--font-size-sm);
  text-transform: uppercase;
}
.table tr:last-child td { border-bottom: none; }
.table tr:hover td { background: rgba(126, 200, 216, 0.04); }
.table td { font-size: var(--font-size-base); }

/* 表格：可排序列 */
.table th.sortable { cursor: pointer; user-select: none; position: relative; padding-right: 24px; }
.table th.sortable:hover { background: rgba(126, 200, 216, 0.1); }
.table th.sortable::after {
  content: '⇅';
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  font-size: var(--font-size-xs);
  opacity: 0.7;
}
.table th.sort-asc::after { content: '↑'; opacity: 0.9; color: var(--primary); }
.table th.sort-desc::after { content: '↓'; opacity: 0.9; color: var(--primary); }

/* 表格：选中行 */
.table tr.selected td { background: var(--primary-dim); }
.table tr.selected:hover td { background: rgba(126, 200, 216, 0.15); }

/* 表格：空状态 */
.table-empty {
  text-align: center;
  padding: var(--space-2xl) var(--space-xl);
  color: var(--text-dim);
  font-size: var(--font-size-sm);
}
.table-empty-icon { font-size: 32px; margin-bottom: var(--space-sm); opacity: 0.5; }

/* 表格：行内操作按钮 */
.row-actions { white-space: nowrap; text-align: right; }
.row-actions .btn { margin-left: var(--space-xs); }

/* 服务器管理表格：外层容器负责横向滚动与圆角裁剪。
   表格自身必须去掉 overflow（.table 默认 overflow:hidden），否则表格会成为 sticky 的参照容器，
   操作列就无法随外层滚动固定在右侧 */
.table-scroll {
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 16px rgba(6, 10, 16, 0.25);
}
.table-scroll .table { border: none; border-radius: 0; box-shadow: none; }
#view-servers .table {
  table-layout: fixed; /* 列宽严格按 colgroup 生效（auto 会把设定值当建议值压缩） */
  overflow: visible;
  border: none;
  border-radius: 0;
  box-shadow: none;
}
/* 类别 / 系统：内容必须单行不折行（列宽在 index.html 的 colgroup 按最长内容设定，两者配套） */
#view-servers .table td:nth-child(1), #view-servers .table td:nth-child(2) { white-space: nowrap; }

/* 访问管理表格：列宽严格按 index.html 的 colgroup 生效（说明列不设宽度＝吃掉剩余空间） */
#access-table { table-layout: fixed; }
/* 项目详情分类表格：列宽由 app.js 的 CAT_TABLE_COLS 生成 colgroup 后严格生效（未设宽度的列＝吃掉剩余空间） */
.cat-table-fixed { table-layout: fixed; }
/* 项目详情「云服务器」列表：地址列不换行（宽度已按完整展示 IP 设定）；说明列单行显示、超出省略，全文靠 title 悬停查看 */
.linked-server-ip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.linked-server-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 第三方服务列表：列宽严格按 index.html 的 colgroup 生效；按大类分组的组标题行 */
#view-thirdparty .table { table-layout: fixed; }
.tp-group-row td {
  background: var(--surface-2);
  color: var(--primary);
  font-weight: 700;
  font-size: var(--font-size-sm);
  letter-spacing: 0.5px;
}
.table tr.tp-group-row:hover td { background: var(--surface-2); }
/* 登录网址列：单行显示、超出省略，全文靠 title 悬停查看 */
.tp-login-cell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-login-cell a { color: var(--primary); text-decoration: none; }
.tp-login-cell a:hover { text-decoration: underline; }

/* 操作列固定在右侧：横向滚动时始终可见。需用实色背景，否则滚动内容会从单元格下透出。
   服务器 / 基础设施 / 第三方服务三张宽表共用同一套粘性列规格 */
#view-servers .table th:last-child, #view-servers .table td:last-child,
#view-infra .table th:last-child, #view-infra .table td:last-child,
#view-thirdparty .table th:last-child, #view-thirdparty .table td:last-child {
  position: sticky;
  right: 0;
  z-index: 2;
  box-shadow: -10px 0 12px -10px rgba(6, 10, 16, 0.75);
}
#view-servers .table th:last-child,
#view-infra .table th:last-child,
#view-thirdparty .table th:last-child { background: var(--surface-2); }
#view-servers .table td:last-child,
#view-infra .table td:last-child,
#view-thirdparty .table td:last-child { background: var(--surface); }
#view-servers .table tbody tr:hover td:last-child,
#view-infra .table tbody tr:hover td:last-child,
#view-thirdparty .table tbody tr:hover td:last-child { background: var(--surface-3); }

/* 使用率告警文字色 */
#view-servers .metric-warn { color: var(--warning); }
#view-servers .metric-danger { color: var(--danger); }

.project-link, .url-link { color: var(--primary); text-decoration: none; }
.project-link { font-weight: 700; }
.project-link:hover, .url-link:hover { text-decoration: underline; }
.url-link { word-break: break-all; }

/* 分类区块 */
.cat-section { margin-bottom: 26px; }
.cat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(126, 200, 216, 0.1);
}
.cat-header h3 {
  font-size: var(--font-size-lg);
  color: var(--primary);
  letter-spacing: 0.5px;
}

/* 表格列分割线（全站统一规格，首例见项目详情 #detail-entries，2026-10-11 全局化）：
   列间加极细分隔线（--primary-dim 即 rgba(126,200,216,0.12)），末列去线避免与容器右边框重影。
   border-collapse: collapse 下相邻单元格的 border-right 会合并成 1px 单线。
   范围限定在 .table 内（含弹窗、模态、嵌套 .da-table 等全部 30 张表），不影响卡片/图表等非表格组件。 */
.table th, .table td { border-right: 1px solid var(--primary-dim); }
.table th:last-child, .table td:last-child { border-right: none; }
/* 项目详情表格（#detail-entries）：操作列（末列）内容居中 */
#detail-entries .table th:last-child,
#detail-entries .table td:last-child { text-align: center; }

/* 敏感字段 */
.sensitive-cell .mask {
  font-family: var(--font-mono);
  color: var(--primary);
  cursor: pointer;
  user-select: none;
  text-shadow: 0 0 6px rgba(126, 200, 216, 0.12);
}
.sensitive-cell .mask.revealed {
  color: var(--text);
  cursor: text;
  user-select: text;
  text-shadow: none;
}
/* 密码格「看+复制」按钮：紧跟打码文本，同排不折行 */
.sensitive-cell .mask-copy { margin-left: 8px; white-space: nowrap; vertical-align: middle; }

.secret-box {
  background: rgba(18, 25, 35, 0.8);
  border: 1px solid var(--border);
  padding: var(--space-md);
  border-radius: var(--radius-md);
  font-family: var(--font-mono);
  font-size: var(--font-size-xl);
  word-break: break-all;
  margin: 8px 0;
  color: var(--primary);
}

/* 模态框 */
.modal-mask {
  position: fixed;
  inset: 0;
  background: rgba(8, 12, 18, 0.7);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: var(--space-xl);
}
.modal {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  width: 500px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 20px 48px rgba(6, 10, 16, 0.6), 0 0 1px rgba(126, 200, 216, 0.1);
}
.modal h3 {
  margin-bottom: var(--space-lg);
  font-size: var(--font-size-xl);
  color: var(--primary);
  font-weight: 700;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  padding-top: var(--space-lg);
  border-top: 1px solid var(--border);
}

/* 轻量 toast：替代阻塞式 alert（成功/错误/信息提示，点击可提前关闭）。
   危险删除仍用 confirm——那类需要用户停下来确认，不适合非阻塞提示。 */
#toast-container {
  position: fixed;
  top: var(--space-lg);
  right: var(--space-lg);
  z-index: 3000;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  max-width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-size: var(--font-size-sm);
  line-height: 1.5;
  color: var(--text);
  white-space: pre-line;
  word-break: break-word;
  box-shadow: 0 8px 24px rgba(6, 10, 16, 0.5);
  cursor: pointer;
  animation: toast-in 0.18s ease-out;
}
.toast.success { border-left-color: var(--green); }
.toast.error { border-left-color: var(--danger); }
.toast.leaving { opacity: 0; transition: opacity 0.25s ease-out; }
@keyframes toast-in {
  from { opacity: 0; transform: translateX(16px); }
  to { opacity: 1; transform: none; }
}

/* 分类区块的「＋ 添加」按钮：带主色调提示，避免入口被忽略 */
.btn-add {
  border-color: rgba(126, 200, 216, 0.45);
  color: var(--primary);
  background: rgba(126, 200, 216, 0.1);
}
.btn-add:hover { background: rgba(126, 200, 216, 0.18); border-color: var(--primary); }

/* 服务器表单字段较多：宽版弹窗（约一倍宽）+ 二列布局；其余弹窗保持 500px 单列 */
.modal.modal-wide { width: min(1000px, 94vw); }
.modal.modal-wide #modal-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 18px;
  align-items: start;
}
/* 账号、PEM 密钥（多行文本）、说明占整行，其余字段成对排列 */
.modal.modal-wide #modal-form label:has(input[name='username']),
.modal.modal-wide #modal-form label:has(textarea),
.modal.modal-wide #modal-form label:has(input[name='remark']) { grid-column: 1 / -1; }

/* 项目表单字段偏多：只加宽到默认的一倍（500px → 1000px），保持单列，避免勾选/标签控件被挤成半列 */
.modal.modal-project { width: min(1000px, 94vw); }

/* 服务器详情弹窗：与编辑弹窗同宽，两者视觉保持一致；放宽后指标区用三列更舒展 */
#server-modal-mask .modal { width: min(1000px, 94vw); }
#server-modal-mask .metric-grid { grid-template-columns: repeat(3, 1fr); }

/* 滚动条 */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: rgba(148, 163, 184, 0.6); border-radius: var(--radius-sm); }
::-webkit-scrollbar-thumb:hover { background: rgba(148, 163, 184, 0.85); }

/* ============ 登录页特效 ============ */
/* 说明（2026-09-26 性能降级）：原 @property --angle + 旋转描边动画、3 个 blur(90px) 光晕的无限浮动，
   在无 GPU 加速的 Electron 渲染进程里是持续的主线程重绘来源；现改为静态描边 + 静态光晕 + 更小模糊半径。 */

/* 粒子背景画布 */
#bg-particles {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}

/* 漂浮光晕 */
.orb {
  position: fixed;
  border-radius: 50%;
  filter: blur(28px);
  pointer-events: none;
  z-index: 0;
  opacity: 0.32;
  contain: paint;
}
.orb-1 {
  width: 420px; height: 420px;
  background: radial-gradient(circle, rgba(126, 200, 216, 0.16), transparent 70%);
  top: -120px; left: -100px;
}
.orb-2 {
  width: 380px; height: 380px;
  background: radial-gradient(circle, rgba(133, 120, 200, 0.18), transparent 70%);
  bottom: -120px; right: -80px;
}
.orb-3 {
  width: 260px; height: 260px;
  background: radial-gradient(circle, rgba(111, 202, 143, 0.10), transparent 70%);
  top: 42%; right: 8%;
}

/* 登录卡片：静态渐变描边（原为旋转动画，已按性能降级） */
.login-card {
  position: relative;
  border: 1px solid transparent;
  width: 400px; /* 登录框保持窄卡片（.card 现在默认铺满容器） */
}
.login-card::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(from var(--angle), transparent 0%, var(--primary) 12%, var(--purple) 30%, transparent 50%, transparent 100%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

/* HUD 四角括号（弱化：细边框 + 轻微发光） */
.hud-corner {
  position: absolute;
  width: 16px;
  height: 16px;
  pointer-events: none;
  z-index: 3;
  opacity: 0.7;
}
.hud-corner.tl { top: -1px; left: -1px; border-top: 1px solid rgba(126, 200, 216, 0.35); border-left: 1px solid rgba(126, 200, 216, 0.35); border-top-left-radius: 6px; box-shadow: -2px -2px 5px rgba(126, 200, 216, 0.12); }
.hud-corner.tr { top: -1px; right: -1px; border-top: 1px solid rgba(126, 200, 216, 0.35); border-right: 1px solid rgba(126, 200, 216, 0.35); border-top-right-radius: 6px; box-shadow: 2px -2px 5px rgba(126, 200, 216, 0.12); }
.hud-corner.bl { bottom: -1px; left: -1px; border-bottom: 1px solid rgba(126, 200, 216, 0.35); border-left: 1px solid rgba(126, 200, 216, 0.35); border-bottom-left-radius: 6px; box-shadow: -2px 2px 5px rgba(126, 200, 216, 0.12); }
.hud-corner.br { bottom: -1px; right: -1px; border-bottom: 1px solid rgba(126, 200, 216, 0.35); border-right: 1px solid rgba(126, 200, 216, 0.35); border-bottom-right-radius: 6px; box-shadow: 2px 2px 5px rgba(126, 200, 216, 0.12); }

/* 品牌标题：呼吸发光 + 故障抖动 */
.login-brand { margin-bottom: 6px; }
.login-brand h1 {
  animation: title-glow 2.6s ease-in-out infinite;
}
@keyframes title-glow {
  0%, 100% { text-shadow: 0 0 14px rgba(126, 200, 216, 0.22); }
  50% { text-shadow: 0 0 26px rgba(126, 200, 216, 0.38), 0 0 52px rgba(126, 200, 216, 0.16); }
}
/* 定位说明：中文长句，不能沿用原来给英文短标语设的等宽字体与 3px 字距（否则会横向溢出登录卡） */
.login-sub {
  font-family: inherit;
  font-size: var(--font-size-sm);
  letter-spacing: 0;
  line-height: 1.85;
  color: var(--text-dim);
  margin-top: 8px;
}
.cursor {
  display: inline-block;
  margin-left: 3px;
  color: var(--primary);
  text-shadow: 0 0 8px rgba(126, 200, 216, 0.3);
  animation: blink 1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }
.login-hint { margin-bottom: 20px; }

/* 登录输入框：聚焦下划线扫光 */
#login-form input {
  caret-color: var(--primary);
}
#login-form input:hover {
  border-color: var(--primary);
}
#login-form input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary), 0 0 12px rgba(126, 200, 216, 0.22);
}

/* 登录按钮：流动高光 + 脉冲光晕（弱化） */
.btn-login {
  position: relative;
  overflow: hidden;
  animation: btn-pulse 3.6s ease-in-out infinite;
}
.btn-login::after {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.14), transparent);
  animation: btn-shine 4.5s ease-in-out infinite;
}
@keyframes btn-shine {
  0% { left: -100%; }
  55%, 100% { left: 100%; }
}
@keyframes btn-pulse {
  0%, 100% { box-shadow: 0 0 8px rgba(126, 200, 216, 0.10); }
  50% { box-shadow: 0 0 14px rgba(126, 200, 216, 0.18); }
}

/* ============ 主界面布局（顶栏 + 左侧菜单 + 内容） ============ */
/* 应用外壳固定为视口高度：顶栏与左侧导航固定不动，仅内容区 .content 内部滚动
   （此前是整页 body 滚动，顶栏/侧栏会随内容滚走） */
#main-view { display: flex; flex-direction: column; height: 100vh; }

.layout { display: flex; flex: 1; min-height: 0; position: relative; z-index: 1; }

.sidebar {
  width: 220px;
  flex-shrink: 0;
  background: rgba(18, 25, 35, 0.92);
  border-right: 1px solid var(--border);
  padding: 18px 12px;
  display: flex;
  flex-direction: column;
  transition: width .2s ease;
  backdrop-filter: blur(10px);
  /* 外壳固定高度后，菜单过高时在侧栏内部滚动，避免菜单项被裁掉 */
  overflow-y: auto;
}
.sidebar.collapsed { width: 64px; }
.sidebar.collapsed .menu-text { display: none; }
.sidebar.collapsed .menu-item { justify-content: center; }

/* 菜单按内容高度排布（不设 flex:1），使下方「折叠/展开」按钮紧贴最后一个菜单项，
   而不是被撑到侧栏最底部 */
.menu { display: flex; flex-direction: column; gap: 6px; }

.menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: 11px 14px;
  border-radius: var(--radius-md);
  color: var(--text-dim);
  text-decoration: none;
  font-family: var(--font-head);
  font-size: var(--font-size-base);
  cursor: pointer;
  border: 1px solid transparent;
  transition: all .15s ease;
  white-space: nowrap;
}
.menu-item:hover { background: rgba(126, 200, 216, 0.06); color: var(--text); }
.menu-item.active {
  background: var(--primary-dim);
  color: var(--primary);
  border-color: rgba(126, 200, 216, 0.3);
  box-shadow: 0 0 12px rgba(126, 200, 216, 0.1);
}
.menu-icon { font-size: var(--font-size-xl); display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; }
.menu-icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; flex-shrink: 0; }

/* 侧栏「折叠 / 展开」：做成安静的页脚控件——去掉按钮边框与底色、改用一条极细分隔线收口，
   默认低对比（仅淡色箭头），悬停才提亮，避免整块按钮压在菜单下方显得突兀 */
.collapse-btn {
  margin-top: 14px;
  align-self: stretch;
  background: transparent;
  border: none;
  border-top: 1px solid rgba(126, 200, 216, 0.12);
  border-radius: 0;
  padding: 10px 0 4px;
  color: var(--text-dim);
  font-size: var(--font-size-sm);
  letter-spacing: 2px;
  line-height: 1;
  text-align: center;
}
.collapse-btn:hover { background: transparent; color: var(--primary); }
.collapse-btn:active { transform: none; }

.content { flex: 1; min-width: 0; padding: 26px 30px; overflow: auto; }

/* ============ 数据大屏 ============ */
.stat-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg); margin-bottom: var(--space-xl); }
.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: 0 4px 16px rgba(6, 10, 16, 0.25);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.stat-card:hover {
  border-color: rgba(126, 200, 216, 0.2);
  box-shadow: 0 4px 20px rgba(6, 10, 16, 0.3), 0 0 12px rgba(126, 200, 216, 0.06);
}
.stat-num {
  font-family: var(--font-head);
  font-size: var(--font-size-5xl);
  font-weight: 700;
  color: var(--primary);
  line-height: 1;
}
.stat-label { margin-top: var(--space-sm); color: var(--text-dim); font-size: var(--font-size-sm); }

.chart-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-lg); }
.chart-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-xl);
  box-shadow: 0 4px 16px rgba(6, 10, 16, 0.25);
}
.chart-card h3 { font-size: var(--font-size-base); color: var(--text); margin-bottom: var(--space-md); font-weight: 700; }
.chart-card.chart-wide { grid-column: 1 / -1; }
.chart { width: 100%; height: 280px; }

/* ============ 工具栏 / 分页 ============ */
.toolbar { display: flex; gap: var(--space-md); margin-bottom: 16px; }
.toolbar input { margin-bottom: 0; flex: 1; }
.toolbar select { margin-bottom: 0; width: 200px; }

.pagination { display: flex; align-items: center; gap: 14px; margin-top: 16px; justify-content: flex-end; }

/* ============ 服务器管理 ============ */
.badge { padding: 3px 10px; border-radius: var(--radius-full); font-size: var(--font-size-xs); font-family: var(--font-mono); }
.badge.online { color: var(--green); border: 1px solid rgba(111, 202, 143, 0.4); background: rgba(111, 202, 143, 0.08); }
.badge.offline { color: var(--danger); border: 1px solid rgba(234, 143, 149, 0.4); background: rgba(234, 143, 149, 0.08); }
.badge.warn { color: var(--warning); border: 1px solid rgba(214, 164, 95, 0.4); background: rgba(214, 164, 95, 0.08); }
.badge.agent { color: var(--blue); border: 1px solid rgba(110, 184, 240, 0.4); background: rgba(110, 184, 240, 0.08); }
.badge.unknown { color: var(--text-dim); border: 1px solid var(--border); }

.metric-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-md); margin: 8px 0 16px; }
.metric-item {
  background: rgba(18, 25, 35, 0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
}
.metric-item .metric-label { color: var(--text-dim); font-size: var(--font-size-xs); margin-bottom: 6px; }
.metric-item .metric-value { font-size: var(--font-size-2xl); font-family: var(--font-head); color: var(--text); }
.metric-bar { height: 6px; background: rgba(126, 200, 216, 0.12); border-radius: 4px; margin-top: 8px; overflow: hidden; }
.metric-bar > span { display: block; height: 100%; background: var(--primary); box-shadow: 0 0 6px rgba(126, 200, 216, 0.22); }
.metric-bar > span.warn { background: var(--warning); }
.metric-bar > span.danger { background: var(--danger); }

.code-block { background: var(--surface); border: 1px solid var(--border, #253040); color: var(--text, #cfd8e3); font-family: var(--font-mono, monospace); }
.alert-box { padding: 10px 14px; border-radius: var(--radius-md); margin-bottom: 8px; font-size: var(--font-size-sm); }
.alert-box.danger { color: var(--danger); border: 1px solid rgba(234, 143, 149, 0.45); background: rgba(234, 143, 149, 0.1); }
.alert-box.warning { color: var(--warning); border: 1px solid rgba(214, 164, 95, 0.45); background: rgba(214, 164, 95, 0.1); }
.alert-box.neutral { color: var(--text-dim); border: 1px solid var(--border); background: transparent; }

.server-loading { color: var(--text-dim); padding: 16px; text-align: center; }

/* 响应式：≤1200px */
@media (max-width: 1200px) {
  .stat-cards { grid-template-columns: repeat(2, 1fr); }
  .form-row { grid-template-columns: 1fr; }
}

/* 响应式：≤900px */
@media (max-width: 900px) {
  .stat-cards { grid-template-columns: 1fr; }
  .chart-grid { grid-template-columns: 1fr; }
  .chart-card.chart-wide { grid-column: auto; }
  .metric-grid { grid-template-columns: 1fr; }
  #server-modal-mask .metric-grid { grid-template-columns: 1fr; }
  .content { padding: var(--space-lg); }
  .topbar { padding: var(--space-md) var(--space-lg); }
  .section-header { flex-wrap: wrap; }
  .section-header h2 { font-size: var(--font-size-xl); }
}

/* 响应式：≤768px - 平板/移动端适配 */
@media (max-width: 768px) {
  .sidebar { display: none; }
  .content { padding: var(--space-md); }
  .modal { width: calc(100% - var(--space-xl)); margin: var(--space-md); }
  .modal.modal-wide { width: calc(100% - var(--space-xl)); }
  .modal.modal-project { width: calc(100% - var(--space-xl)); }
  .toolbar { flex-direction: column; }
  .toolbar input, .toolbar select { width: 100%; }
  /* 搜索栏分组在窄屏改为整行铺满、按组纵向堆叠 */
  .list-toolbar .lf, .list-toolbar .lf input[type="search"], .list-toolbar .lf select { width: 100%; }
  .list-toolbar .lf-actions { width: auto; }
  .pagination { flex-wrap: wrap; }
  .topbar { padding: var(--space-sm) var(--space-md); }
  .topbar .brand { font-size: var(--font-size-lg); }
  .topbar-right { gap: var(--space-sm); }
  .list-filters { flex-direction: column; align-items: stretch; }
  .chips { justify-content: flex-start; }
  .batch-bar { margin-left: 0; }
  .cat-header { flex-direction: column; gap: var(--space-sm); align-items: flex-start; }
  /* 宽版弹窗的两列表单在窄屏改单列，避免字段被压成半列 */
  .modal.modal-wide #modal-form { grid-template-columns: 1fr; }
  /* 服务器弹窗的 SSH 凭据行同理 */
  .ssh-row { grid-template-columns: 1fr; }
  /* 自定义字段编辑行（名称/值/类型/加密/删除）窄屏改为两行排布 */
  .cf-row { grid-template-columns: 1fr 1fr; }
  .cf-row .cf-label { grid-column: 1 / -1; }
  /* 星图节点详情浮层：窄屏收窄为贴近全宽，避免盖住整张图 */
  .starmap-side { width: calc(100% - 24px); max-height: 45%; }
}

/* 响应式：≤480px - 小屏手机 */
@media (max-width: 480px) {
  body { font-size: var(--font-size-sm); }
  .content { padding: var(--space-sm); }
  .stat-card { padding: var(--space-lg); }
  .stat-num { font-size: var(--font-size-4xl); }
  .table th, .table td { padding: 8px 10px; font-size: var(--font-size-xs); }
  .btn { padding: 6px 12px; font-size: var(--font-size-sm); }
  .btn-sm { padding: 4px 8px; font-size: var(--font-size-xs); }
  /* 顶栏按钮组在小屏允许换行，避免整页出现横向滚动条 */
  .topbar { flex-wrap: wrap; row-gap: var(--space-sm); }
  .topbar-right { flex-wrap: wrap; justify-content: flex-start; }
  /* 登录卡固定 400px，小屏改为贴合视口（含 24px 安全边距） */
  .login-card { width: 100%; max-width: 400px; }
  /* 两列勾选行的说明列在小屏收窄，保住名称列可读宽度 */
  .check-item { grid-template-columns: 16px minmax(0, 1fr) 120px; }
  .modal { padding: var(--space-lg); }
  .modal h3 { font-size: var(--font-size-lg); }
}

/* 密码框「眼睛」切换按钮（登录框与各弹窗的 input[type=password] 通用） */
/* 注意：input 自带 margin-bottom:14px，必须把外边距挪到包装层，否则包装层比输入框高 14px，按钮会偏下 */
.pwd-wrap { position: relative; display: block; width: 100%; margin-bottom: 14px; }
.pwd-wrap input { margin-bottom: 0; padding-right: 42px; }
.pwd-eye {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 4px;
  margin: auto 0; /* 配合 top/bottom:0 实现真正垂直居中，不受输入框高度影响 */
  height: 26px;
  width: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 0;
  font-size: var(--font-size-sm);
  line-height: 1;
  cursor: pointer;
  border-radius: var(--radius-sm);
  filter: grayscale(1); /* emoji 转中性灰，贴合深色主题 */
  opacity: 0.6;
  transition: opacity .15s ease, background .15s ease;
}
.pwd-eye:hover { opacity: 1; background: rgba(148, 163, 184, 0.14); }
.pwd-eye:active { transform: scale(0.97); }
.pwd-eye:focus-visible { outline: 1px solid var(--primary); outline-offset: 1px; }

/* 服务器弹窗里的 SSH 凭据区块（可多条，每条含账号/密码/私钥/备注） */
.ssh-block { margin-top: 4px; padding-top: 14px; border-top: 1px solid var(--border); }
.ssh-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.ssh-head strong { color: var(--text); }
.ssh-head .btn { margin-left: auto; }
.ssh-row {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 18px;
  align-items: start;
  margin-bottom: 10px;
  padding: 14px 14px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: rgba(18, 25, 35, 0.5);
}
.ssh-row label:has(textarea),
.ssh-row label:has(input.ssh-remark) { grid-column: 1 / -1; }
.ssh-row .ssh-del { position: absolute; top: 10px; right: 10px; }

/* ============ 基础设施管理（MySQL / Redis / 存储） ============ */
/* 与服务器表同一套列宽策略：外层 .table-scroll 负责横向滚动与圆角，表格自身去掉 overflow 才能让操作列 sticky */
#view-infra .table {
  table-layout: fixed;
  overflow: visible;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* 实例详情弹窗内的子表与表清单 */
.sub-title { margin: 18px 0 8px; font-size: var(--font-size-lg); }
.table-scroll.inner { max-height: 260px; }
.table-scroll.inner .table {
  table-layout: auto;
  overflow: visible;
  border: none;
  border-radius: 0;
  box-shadow: none;
}
.table-list { max-height: 200px; overflow: auto; margin-top: 6px; font-size: var(--font-size-xs); line-height: 1.7; }
details > summary { cursor: pointer; color: var(--primary); }

/* 项目「管理关联」勾选弹窗 */
.link-group { margin-bottom: 16px; }
.link-group-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  padding-bottom: 6px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
  font-weight: 700;
}
.link-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.link-row input[type="checkbox"] { width: auto; margin: 0; }

/* ============ 第三方服务凭据 ============ */
/* 与服务器/基础设施表同一套列宽策略 */
#view-thirdparty .table {
  table-layout: fixed;
  overflow: visible;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* 自定义字段编辑器（弹窗内）：字段名 / 值 / 类型 / 加密 / 删除。第三方服务与服务器共用 */
.cf-block { margin-top: 6px; }
.cf-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}
.cf-head strong { font-size: var(--font-size-base); }
.cf-head .muted { flex: 1 1 220px; }
.cf-head select { width: auto; min-width: 150px; }
.cf-row {
  display: grid;
  grid-template-columns: 150px 1fr 92px 64px 34px;
  gap: var(--space-sm);
  align-items: start;
  margin-bottom: 8px;
}
.cf-row .cf-label,
.cf-row .cf-value-cell,
.cf-row .cf-type { min-width: 0; }
.cf-row .cf-value-cell { display: flex; flex-direction: column; gap: var(--space-xs); }
.cf-row textarea.cf-value { min-height: 42px; }
.cf-row .cf-file { font-size: var(--font-size-xs); }
.cf-row .cf-secret {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding-top: 9px;
  font-size: var(--font-size-xs);
  white-space: nowrap;
}
.cf-row .cf-secret input { width: auto; margin: 0; }
.cf-row .pwd-wrap { min-width: 0; }

/* 服务器详情：自定义字段的只读展示 */
.sv-fields { display: flex; flex-wrap: wrap; gap: var(--space-sm) 18px; margin: 6px 0 2px; }
.sv-field { font-size: var(--font-size-sm); }
.sv-field b { color: var(--text-dim); font-weight: 500; margin-right: 6px; }

/* 实例详情的小节头：标题 + 搜索 + 添加按钮；行内操作列 */
.sub-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 18px 0 8px; }
.sub-head .sub-title { margin: 0; }
.sub-head .muted { margin-left: auto; }
.sub-head input[type="search"] { width: auto; min-width: 180px; max-width: 260px; margin-left: auto; }
.sub-head input[type="search"] ~ .muted { margin-left: 0; }

/* 列表页通用搜索条（由 app.js 的 mountListControls 注入）
   排版：label 在控件上方成一组，关键词框宽、下拉窄，行内自动换行；关键词回车/点搜索生效，下拉选中即筛 */
.list-toolbar { margin-bottom: 14px; flex-wrap: wrap; align-items: flex-end; }
.list-toolbar input[type="search"] { min-width: 220px; }
.list-toolbar .lf { display: flex; flex-direction: column; gap: var(--space-xs); margin-bottom: 0; }
.list-toolbar .lf-label { font-size: var(--font-size-xs); color: var(--text-dim); }
.list-toolbar .lf input[type="search"] { flex: none; width: 240px; }
.list-toolbar .lf select { flex: none; width: auto; min-width: 132px; }
.list-toolbar .lf-actions { display: flex; align-items: flex-end; gap: var(--space-sm); }

/* 详情弹窗（#server-modal-mask）之上再弹通用编辑框时，通用弹窗必须盖在它上面 */
#modal-mask { z-index: 110; }

/* 项目详情：查看密钥后的内联展开行 */
.tp-secret-row td { background: var(--surface-2); }
.tp-kv { display: block; padding: 2px 0; font-size: var(--font-size-sm); word-break: break-all; }
.tp-kv b { display: inline-block; min-width: 110px; margin-right: 8px; color: var(--text-dim); font-weight: 500; }

/* ============ 微交互动画 ============ */

/* 加载状态：脉冲呼吸 */
.loading-pulse {
  animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

/* 加载骨架屏 */
.skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* 淡入动画 */
.fade-in {
  animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

/* 滑入动画 */
.slide-in {
  animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
  from { opacity: 0; transform: translateX(-12px); }
  to { opacity: 1; transform: none; }
}

/* 缩放弹出 */
.scale-in {
  animation: scaleIn 0.2s ease-out;
}
@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: none; }
}

/* 成功提示闪烁 */
.flash-success {
  animation: flashSuccess 0.6s ease-out;
}
@keyframes flashSuccess {
  0% { background: rgba(111, 202, 143, 0.2); }
  100% { background: transparent; }
}

/* 错误提示抖动 */
.shake {
  animation: shake 0.4s ease-out;
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* 旋转加载 */
.spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* 按钮加载态 */
.btn.loading {
  pointer-events: none;
  opacity: 0.7;
}
.btn.loading::before {
  content: '';
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  margin-right: 6px;
  vertical-align: middle;
}

/* 工具提示 */
.tooltip {
  position: relative;
}
.tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
  padding: 4px 8px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs);
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity .15s ease;
}
.tooltip:hover::after { opacity: 1; }

/* 减少动效偏好 */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ============ 列表快捷筛选 / 批量操作（项目列表） ============ */
.list-filters { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; margin-bottom: 14px; }
.chips { display: flex; gap: var(--space-sm); flex-wrap: wrap; }
.chip {
  padding: 4px 12px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--font-size-xs);
  cursor: pointer;
  transition: color .15s, border-color .15s, background .15s;
}
.chip:hover { color: var(--text); border-color: var(--primary); }
.chip.active { color: var(--primary); border-color: var(--primary); background: var(--primary-dim); }
.area-filter { width: auto; min-width: 140px; margin-bottom: 0; }
.batch-bar { display: flex; align-items: center; gap: var(--space-sm); margin-left: auto; flex-wrap: wrap; }

/* 项目列表：勾选列与长文本截断 */
.pick-col { width: 40px; }
.pick-cell { width: 40px; text-align: center; }
.pick-cell input { width: auto; margin: 0; }
.remark-cell { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 项目列表：固定列宽 */
/* min-width = 固定列合计（约 1020px）+ 项目说明 / 项目跟进两个自适应列各留约 200px 保底：窗口更窄时表格不再被压扁，
   自适应列也不会被挤成 0 宽（.table-scroll 出横向滚动）；窗口更宽时多出的空间由两列平分，即自适应 */
#project-table { table-layout: fixed; min-width: 1420px; }
#project-table th:first-child { width: 40px; }
/* 编号（第 2 列）、项目名（第 3 列）与项目说明（第 9 列）、项目跟进（第 10 列）靠左，其余列（含类别）居中 */
#project-table td:nth-child(2), #project-table td:nth-child(3), #project-table td:nth-child(9), #project-table td:nth-child(10) { text-align: left; }
/* 行高最多 2 行：单元格内容超过 2 行即省略号截断（省略号由 line-clamp 给出）；
   超出部分靠单元格 title 悬停查看全文（各字段的 title 由 app.js 附上） */
#project-table td {
  text-align: center;
  overflow: hidden;
}
/* 多行钳制容器：line-clamp 不能直接作用于 td，行内各单元格内容统一包一层 .cell-clamp */
#project-table .cell-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 标签组用 inline 排布（flex 排布会限制换行方式），可在 2 行额度内自然换行后被钳制 */
#project-table .tags { display: inline; }
#project-table .tags .tag { margin-right: 6px; vertical-align: middle; }
/* 胶囊（徽标 / 标签）改 inline-block：inline 元素的上下内边距与边框不计入行盒高度，
   会被钳制容器的 overflow:hidden 削掉上下边缘（即「标签显示不全」的根因）；
   inline-block 同时让标签成为原子盒，换行只在标签之间发生，不会拦腰截断 */
#project-table .tag, #project-table .badge {
  display: inline-block;
  vertical-align: middle;
  white-space: nowrap;
}
/* 项目说明列：列宽不设固定值（fixed 布局下自动吃掉剩余空间），钳制规则同上、放宽 .remark-cell 的 280px 上限 */
#project-table .remark-cell { max-width: unset; }
/* 可排序表头（「服务器」列）：点击三态循环，箭头指示方向；热区整格可点 */
#project-table .th-sort { cursor: pointer; user-select: none; }
#project-table .th-sort:hover { color: var(--primary); }
#project-table .th-sort.sorted { color: var(--primary); }
#project-table .sort-arrow { margin-left: 6px; font-size: var(--font-size-xs); color: var(--text-dim); }
#project-table .th-sort:hover .sort-arrow, #project-table .th-sort.sorted .sort-arrow { color: var(--primary); }
/* 行操作下拉（操作列）：浮层用 fixed 定位（JS 贴按钮展开、下方空间不足自动上翻）——
   不能用 absolute：.table-scroll 的 overflow-x:auto 会连带裁掉纵向溢出，单行 / 末行的下拉伸出容器底边就被裁 */
.row-menu-wrap { position: relative; display: inline-block; }
.row-menu {
  position: fixed; z-index: 100;
  min-width: 110px; padding: 4px;
  display: flex; flex-direction: column; gap: 2px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
}
.row-menu[hidden] { display: none; }
.row-menu-item {
  padding: 6px 10px; border: 0; border-radius: var(--radius-sm); background: none;
  color: var(--text); font-size: var(--font-size-sm); cursor: pointer; text-align: left; text-decoration: none;
}
.row-menu-item:hover { background: rgba(255, 255, 255, 0.08); color: var(--primary); }
.row-menu-item.danger:hover { color: var(--danger); }

/* 标签（项目类别 / 项目领域） */
.tags { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.tag {
  padding: 2px 9px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: var(--font-size-xs);
  font-family: var(--font-mono);
  white-space: nowrap;
}
.tag-cat { color: var(--primary); border-color: var(--primary); background: var(--primary-dim); }
.tag-industry { color: var(--green); border-color: rgba(111, 202, 143, 0.4); background: rgba(111, 202, 143, 0.1); }
.tag-no { color: var(--warning); border-color: rgba(214, 164, 95, 0.4); background: rgba(214, 164, 95, 0.08); }

/* 置顶徽标：跟在项目名后，标识置顶状态（置顶序号在编辑项目弹窗里调） */
.pin-badge {
  display: inline-block; margin-left: 6px; padding: 1px 8px;
  border-radius: var(--radius-full); border: 1px solid rgba(214, 164, 95, 0.4);
  background: rgba(214, 164, 95, 0.08); color: var(--warning);
  font-size: var(--font-size-xs); font-family: var(--font-mono); white-space: nowrap;
  vertical-align: middle;
}

/* 项目列表：唯一编号与「已归档」置灰 */
.project-no { font-family: var(--font-mono); color: var(--text-dim); }
tr.row-archived > td { opacity: 0.8; }

/* 项目详情三档说明（简要 / 概述 / 详情） */
.desc-block { display: flex; gap: 10px; margin: 4px 0; }
.desc-label { flex: 0 0 2.5em; color: var(--text-dim); font-size: var(--font-size-xs); font-family: var(--font-mono); padding-top: 2px; }
.desc-text { white-space: pre-wrap; word-break: break-word; }

/* 行业标签（两级受控多选）：已选标签 + 搜索框 + 一级分组/二级多选面板 */
.industry-block { margin-bottom: 14px; }
.industry-block .tag-input-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 24px;
  margin-bottom: 8px;
}
.tag-input-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 2px 8px;
  background: rgba(111, 202, 143, 0.15);
  border: 1px solid rgba(111, 202, 143, 0.3);
  border-radius: var(--radius-full);
  font-size: var(--font-size-xs);
  color: var(--green);
}
.tag-input-remove {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-size: var(--font-size-base);
  line-height: 1;
  padding: 0;
  margin-left: 2px;
  /* WCAG 2.5.8：触控目标最小 24×24 */
  min-width: 24px;
  min-height: 24px;
}
.tag-input-remove:hover { color: var(--danger); }
.industry-search { width: 100%; margin-bottom: 8px; }
.industry-picker {
  max-height: 220px;
  overflow-y: auto;
  padding: 10px 12px;
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.industry-group + .industry-group {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed rgba(126, 200, 216, 0.14);
}
.industry-group-name {
  font-family: var(--font-head);
  font-size: var(--font-size-sm);
  font-weight: 700;
  color: var(--primary);
  letter-spacing: 0.5px;
  margin-bottom: 6px;
}

/* 多选网格（项目领域）：勾选比 <select multiple> 直观，且上限好控 */
.chk-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 6px 12px;
  margin: 4px 0 12px;
}
.chk { display: flex; align-items: center; gap: 6px; margin: 0; font-size: var(--font-size-sm); }
.chk input { width: auto; margin: 0; }
.chk:has(input:disabled) { opacity: .45; }

/* ============ 参数管理 ============ */
.params-layout { display: grid; grid-template-columns: 220px 1fr; gap: 20px; align-items: start; }
/* min-width:0 是关键：不加这行，表格的 min-content 宽度会把栅格列撑破，
   导致 main.content 出现横向滚动条、右侧「操作」列被挤出可视区 */
.params-items { min-width: 0; }
.params-nav { display: flex; flex-direction: column; gap: var(--space-xs); }
.params-kind {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-sm);
  padding: 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: var(--font-size-sm);
  text-align: left;
  cursor: pointer;
}
.params-kind:hover { color: var(--text); border-color: var(--primary); }
.params-kind.active { color: var(--primary); border-color: var(--primary); background: var(--primary-dim); }
.params-head { display: flex; align-items: flex-start; gap: var(--space-md); margin-bottom: 12px; }
.params-head h3 { font-size: var(--font-size-lg); color: var(--text); margin-bottom: 4px; }
.params-head .btn { margin-left: auto; }
.params-items .num { text-align: right; width: 64px; }

/* 参数管理：视口不够宽时把左侧参数组堆到上方，把整宽让给表格（否则「操作」列即便可滚动也不便点） */
@media (max-width: 1100px) {
  .params-layout { grid-template-columns: 1fr; }
  .params-nav { flex-direction: row; flex-wrap: wrap; }
  .batch-bar { margin-left: 0; }
}

/* ============ 追光星图 ============ */
.starmap-toolbar { display: flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; margin-bottom: 12px; }
.starmap-toolbar input[type="search"] { width: auto; min-width: 240px; margin-bottom: 0; }
.starmap-toolbar select { width: auto; min-width: 140px; margin-bottom: 0; }
/* 面包屑：把「领域 / 布局 / 聚焦（几跳）」显式写出来，避免忘了自己正处在子图里 */
.starmap-crumbs { display: flex; align-items: center; gap: var(--space-sm); flex-wrap: wrap; margin: 0 0 10px; min-height: 26px; font-size: var(--font-size-xs); color: var(--text-dim); }
.starmap-crumbs .crumb { background: rgba(126, 200, 216, 0.08); border: 1px solid var(--border); border-radius: var(--radius-full); padding: 2px 10px; }
.starmap-crumbs .crumb-sep { opacity: 0.7; }
/* 画布外图例：必须放在画布外，graph series 才能不指定 rect —— 指定了 rect 会被 ECharts 按 x/y 拉伸变形 */
.starmap-legend { display: flex; flex-direction: column; gap: 6px; margin: 0 0 10px; }
.starmap-legend .legend-row { display: flex; flex-wrap: wrap; gap: 6px; }
.starmap-legend .legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  font-size: var(--font-size-xs);
  font-family: inherit;
  color: var(--text);
  background: rgba(126, 200, 216, 0.08);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: opacity 0.15s ease, border-color 0.15s ease;
}
.starmap-legend .legend-chip:hover { border-color: var(--primary); }
.starmap-legend .legend-chip.off { opacity: 0.65; }
.starmap-legend .legend-dot { width: 9px; height: 9px; flex: none; border-radius: 50%; }
.starmap-legend .legend-dot.is-rect { border-radius: 1px; }
.starmap-legend .legend-dot.is-diamond { border-radius: 1px; transform: rotate(45deg); }
.starmap-legend .legend-dot.is-round { border-radius: 3px; }
/* 必须写在 .table-scroll 之后覆盖它的高度：星图画布需要有确定高度才能初始化 ECharts
   高度里多减 60px 是给上面新增的图例胶囊让位（原来图例在画布内，靠 series rect 让位——那正是变形根因） */
.starmap-wrap {
  position: relative;
  height: calc(100vh - 360px);
  min-height: 440px;
  background: rgba(16, 23, 32, 0.55);
  overflow: hidden;
}
/* 星图内部层级：0 星野背景 → 1 ECharts 画布 → 2 覆盖层(流光/簇边界) → 3 详情面板 → 4 演示层 */
.starmap-chart { position: relative; z-index: 1; width: 100%; height: 100%; }
.starmap-fx-canvas { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
/* 深空星野背景：多层径向渐变星点 + 两团极淡星云，整体缓慢漂移。
   只在 body.fx-on 分支里存在（4.5.6），并尊重系统「减少动效」偏好。 */
body.fx-on .starmap-wrap::before {
  content: '';
  position: absolute;
  inset: -25%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.6;
  background-repeat: repeat, repeat, repeat, repeat, no-repeat, no-repeat;
  background-size: 360px 360px, 300px 300px, 240px 240px, 420px 420px, 100% 100%, 100% 100%;
  background-image:
    radial-gradient(1.4px 1.4px at 22% 28%, rgba(222, 240, 255, 0.9), transparent 65%),
    radial-gradient(1.1px 1.1px at 71% 14%, rgba(178, 214, 255, 0.75), transparent 65%),
    radial-gradient(1.6px 1.6px at 44% 68%, rgba(206, 244, 252, 0.8), transparent 65%),
    radial-gradient(1px 1px at 86% 76%, rgba(190, 210, 240, 0.6), transparent 65%),
    radial-gradient(circle at 24% 80%, rgba(126, 200, 216, 0.1), transparent 44%),
    radial-gradient(circle at 80% 20%, rgba(133, 120, 200, 0.1), transparent 46%);
  animation: starmap-drift 160s linear infinite;
}
@keyframes starmap-drift {
  from { transform: translate3d(0, 0, 0); }
  to { transform: translate3d(-70px, -46px, 0); }
}
@media (prefers-reduced-motion: reduce) {
  body.fx-on .starmap-wrap::before { animation: none; }
}
.starmap-empty {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
  padding: 24px;
  text-align: center;
}
/* 节点详情：浮在画布右上角，不挤压图区域 */
.starmap-side {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 3;
  width: 300px;
  max-height: calc(100% - 24px);
  overflow-y: auto;
  padding: 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  background: rgba(26, 35, 49, 0.96);
  box-shadow: 0 12px 32px rgba(6, 10, 16, 0.5);
}
.starmap-side h4 { font-size: var(--font-size-base); color: var(--primary); margin-bottom: 4px; word-break: break-all; }
.starmap-side .side-row { margin-top: 10px; font-size: var(--font-size-sm); line-height: 1.6; word-break: break-all; }
.starmap-side .side-row b { color: var(--text-dim); font-weight: 500; margin-right: 6px; }
.starmap-side .side-actions { display: flex; gap: var(--space-sm); margin-top: 14px; flex-wrap: wrap; }
.starmap-side .side-close { position: absolute; top: 10px; right: 12px; }
.starmap-side .warn-text { color: var(--warning); }
.starmap-chart code { font-family: var(--font-mono); background: rgba(126, 200, 216, 0.12); padding: 1px 5px; border-radius: 4px; }

/* 星图内的「智能分析」tab */
.starmap-tabs { margin-bottom: 16px; }
.insights-cards { margin-bottom: 8px; }
.starmap-tabs + div .insights-h,
#starmap-tab-insights .insights-h { font-size: var(--font-size-lg); color: var(--text); margin: 22px 0 6px; }
.warn-text { color: var(--warning); }
.path-chain { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; }
.path-chain .via { color: var(--text-dim); font-size: var(--font-size-xs); margin: 0 2px; }
.path-chain .path-node { border-color: var(--primary); color: var(--primary); }
.insights-path { min-height: 24px; margin-bottom: 4px; }

/* 全屏演示星图时去掉高度约束，靠容器撑满（F 键切换） */
.starmap-wrap:fullscreen { height: 100vh; min-height: 0; border: none; border-radius: 0; }
.starmap-wrap:fullscreen .starmap-side { width: 340px; }

/* 演示模式：KPI 轮播层 + 退出按钮。静态样式放这里，动画一律留在文件末尾的 body.fx-on 分支（4.5.6） */
.starmap-demo-kpi {
  position: absolute;
  left: 50%;
  bottom: 7%;
  transform: translateX(-50%);
  text-align: center;
  pointer-events: none;
  z-index: 4;
}
.starmap-demo-kpi .kpi-num {
  font-family: var(--font-head);
  font-size: var(--font-size-6xl);
  line-height: 1;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.starmap-demo-kpi .kpi-label { margin-top: 10px; font-size: var(--font-size-lg); letter-spacing: 3px; color: var(--text-dim); }
.starmap-demo-exit { position: absolute; top: 12px; right: 12px; z-index: 5; }
body.starmap-demo .starmap-side { display: none; }
/* 演示时先把画布铺满整个视口：这样即使浏览器/内嵌环境拒绝了 requestFullscreen（曾实测被拒），
   演示仍然是「满屏」的观感；全屏成功时这条规则也无害（固定定位本就铺满屏幕）。 */
body.starmap-demo .starmap-wrap {
  position: fixed;
  inset: 0;
  z-index: 200;
  height: 100vh;
  min-height: 0;
  border: none;
  border-radius: 0;
}

/* ============ 装饰特效开关（默认关闭 = 轻量模式，性能优先） ============ */
/* 默认：不渲染光晕 / 粒子画布 / HUD 角标 / 旋转描边，并停用装饰性动画。
   开启方式见 web/app.js 顶部 FX 配置：顶栏「特效」按钮（仅管理员）、地址栏 ?fx=1、或 localStorage 的 pv_fx=on。 */
.orb,
#bg-particles,
.hud-corner,
.login-card::before { display: none; }
.login-card { border-color: var(--border); } /* 关掉渐变描边后给卡片保留一层常规边框 */
.login-brand h1 { animation: none; text-shadow: none; }
.btn-login { animation: none; }
.btn-login::after { display: none; }
.cursor { animation: none; }
.tab-btn.active { text-shadow: none; }

/* 开启特效（body.fx-on）：恢复原炫酷强度（大面积模糊与无限动画只允许存在于这个分支里） */
body.fx-on .orb,
body.fx-on #bg-particles,
body.fx-on .hud-corner,
body.fx-on .login-card::before { display: block; }
body.fx-on .login-card { border-color: transparent; }
body.fx-on .orb { filter: blur(90px); }
body.fx-on .orb-1 { animation: orb-float 14s ease-in-out infinite; }
body.fx-on .orb-2 { animation: orb-float 18s ease-in-out infinite reverse; }
body.fx-on .orb-3 { animation: orb-float 12s ease-in-out infinite; }
body.fx-on .login-brand h1 { animation: title-glow 2.6s ease-in-out infinite; text-shadow: 0 0 14px rgba(126, 200, 216, 0.22); }
body.fx-on .btn-login { animation: btn-pulse 3.6s ease-in-out infinite; }
body.fx-on .btn-login::after { display: block; }
body.fx-on .cursor { animation: blink 1s step-end infinite; }
body.fx-on .tab-btn.active { text-shadow: 0 0 10px rgba(126, 200, 216, 0.2); }
body.fx-on .login-card::before { animation: rotate-angle 5s linear infinite; }

/* 演示模式的 KPI 轮播动效（同样只在这个分支里 —— 演示模式会临时打开特效，退出时还原） */
body.fx-on .starmap-demo-kpi .kpi-num { text-shadow: 0 0 26px rgba(126, 200, 216, 0.45); }
body.fx-on .starmap-demo-kpi.kpi-in .kpi-num { animation: kpi-rise 0.6s ease-out; }
body.fx-on .starmap-demo-kpi.kpi-in .kpi-label { animation: kpi-fade 0.9s ease-out; }
@keyframes kpi-rise {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: none; }
}
@keyframes kpi-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes orb-float {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(34px, -34px) scale(1.12); }
}
@keyframes rotate-angle { to { --angle: 360deg; } }
@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* ============ 项目活动：跟进 / 开发日志 / 里程碑（时间线） ============ */
/* 三块共用一套时间线样式，里程碑额外用 .tag-vers（版本号）与 .act-status（状态色）区分 */
.act-head-actions { display: flex; gap: var(--space-sm); align-items: center; }
.act-hint { margin: 6px 0 14px; }
.act-timeline {
  position: relative;
  padding-left: 22px;
  /* 竖向时间轴：一条淡线把同一天/多天的记录串起来 */
  border-left: 1px solid var(--border);
  margin: 4px 0 8px 6px;
}
.act-item { position: relative; padding: 0 0 18px 0; }
.act-item:last-child { padding-bottom: 4px; }
.act-dot {
  position: absolute;
  left: -27px;
  top: 5px;
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--primary);
  box-shadow: 0 0 0 3px var(--bg), 0 0 10px var(--primary-glow);
}
.act-body {
  background: rgba(26, 35, 49, 0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-md) var(--space-lg);
}
.act-top { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; margin-bottom: 6px; }
.act-date { font-family: var(--font-mono); font-size: var(--font-size-xs); color: var(--text-dim); }
.act-meta { font-size: var(--font-size-xs); color: var(--text-dim); }
.act-title { font-family: var(--font-head); font-size: var(--font-size-lg); color: var(--text); margin-bottom: 4px; }
.act-content { font-size: var(--font-size-base); color: var(--text); white-space: pre-wrap; word-break: break-word; line-height: 1.6; }
.act-actions { display: flex; gap: var(--space-sm); margin-top: 10px; }
.tag-vers {
  color: var(--purple);
  border-color: rgba(163, 151, 226, 0.5);
  background: rgba(163, 151, 226, 0.12);
  font-weight: 700;
}
.act-ai {
  font-size: var(--font-size-xs);
  padding: 1px 8px;
  border-radius: var(--radius-full);
  border: 1px solid rgba(111, 202, 143, 0.4);
  background: rgba(111, 202, 143, 0.1);
  color: var(--green);
}
.act-status { font-size: var(--font-size-xs); padding: 1px 8px; border-radius: var(--radius-full); border: 1px solid var(--border); }
.act-planned { color: var(--text-dim); }
.act-in_progress { color: var(--warning); border-color: rgba(214, 164, 95, 0.45); background: rgba(214, 164, 95, 0.1); }
.act-done { color: var(--green); border-color: rgba(111, 202, 143, 0.4); background: rgba(111, 202, 143, 0.1); }

/* RAID 台账：类型标签（风险/假设/问题/依赖）与闭环状态色 */
.raid-tag { font-family: var(--font-head); }
.raid-risk { color: var(--danger); border-color: rgba(234, 143, 149, 0.45); background: rgba(234, 143, 149, 0.12); }
.raid-assumption { color: var(--purple); border-color: rgba(163, 151, 226, 0.5); background: rgba(163, 151, 226, 0.12); }
.raid-issue { color: var(--warning); border-color: rgba(214, 164, 95, 0.45); background: rgba(214, 164, 95, 0.12); }
.raid-dependency { color: var(--primary); border-color: var(--primary-glow); background: rgba(126, 200, 216, 0.12); }
.act-raid-open { color: var(--text-dim); }
.act-raid-mitigating { color: var(--warning); border-color: rgba(214, 164, 95, 0.45); background: rgba(214, 164, 95, 0.1); }
.act-raid-closed { color: var(--green); border-color: rgba(111, 202, 143, 0.4); background: rgba(111, 202, 143, 0.1); }
/* 风险严重级别：P0 阻断（红）/ P1 高（橙）/ P2 一般（灰） */
.sev-tag { font-family: var(--font-head); }
.sev-P0 { color: var(--danger); border-color: rgba(234, 143, 149, 0.5); background: rgba(234, 143, 149, 0.16); }
.sev-P1 { color: var(--warning); border-color: rgba(214, 164, 95, 0.5); background: rgba(214, 164, 95, 0.14); }
.sev-P2 { color: var(--text-dim); }

/* ADR 架构决策记录：卡片式列表（编号 + 标题 + 状态 + 日期，下分背景/决策/后果三节） */
.adr-list { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; }
.adr-item {
  background: rgba(26, 35, 49, 0.6);
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
}
.adr-head { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; margin-bottom: 8px; }
.adr-no {
  font-family: var(--font-mono);
  color: var(--purple);
  border-color: rgba(163, 151, 226, 0.5);
  background: rgba(163, 151, 226, 0.12);
  font-weight: 700;
}
.adr-title { font-family: var(--font-head); font-size: var(--font-size-lg); color: var(--text); flex: 1; min-width: 160px; }
.adr-sec { margin-top: 8px; }
.adr-sec-label {
  font-family: var(--font-head);
  font-size: var(--font-size-xs);
  color: var(--text-dim);
  border-left: 2px solid var(--border-strong);
  padding-left: 8px;
  margin-bottom: 3px;
}
.act-adr-proposed { color: var(--warning); border-color: rgba(214, 164, 95, 0.45); background: rgba(214, 164, 95, 0.1); }
.act-adr-accepted { color: var(--green); border-color: rgba(111, 202, 143, 0.4); background: rgba(111, 202, 143, 0.1); }
.act-adr-deprecated { color: var(--text-dim); }
.act-adr-superseded { color: var(--danger); border-color: rgba(234, 143, 149, 0.45); background: rgba(234, 143, 149, 0.1); }

@media (max-width: 768px) {
  .act-head-actions { width: 100%; justify-content: flex-end; }
}

/* 迭代看板：按周聚合的横向泳道（每列一个记录类型），窄屏改纵向堆叠 */
.board-week-nav { display: flex; align-items: center; gap: var(--space-sm); margin-left: auto; }
.board-lanes { display: flex; gap: 14px; align-items: flex-start; margin-top: 12px; overflow-x: auto; padding-bottom: 6px; }
.board-lane {
  /* 可收缩（1 1 176px）：5 列在 1366 等窄屏也能同屏排下，不再出现横向滚动条 */
  flex: 1 1 190px;
  min-width: 176px;
  display: flex;
  flex-direction: column;
  /* 限高并让卡片区各自独立滚动：避免单条泳道卡片过多（如风险列上百条）把整页撑到几十屏高 */
  max-height: calc(100vh - 210px);
  background: rgba(26, 35, 49, 0.4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-md);
}
.board-lane-head {
  display: flex; justify-content: space-between; align-items: center;
  font-family: var(--font-head); color: var(--text); font-size: var(--font-size-sm);
  padding: 0 2px 8px; border-bottom: 1px solid var(--border); margin-bottom: 8px;
  flex-shrink: 0;
}
.board-count { color: var(--text-dim); font-family: var(--font-mono); font-size: var(--font-size-xs); }
.board-lane-body { display: flex; flex-direction: column; gap: var(--space-sm); flex: 1; min-height: 0; overflow-y: auto; }
.board-empty { padding: 6px 2px; }
.board-card {
  background: rgba(16, 23, 32, 0.7);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-sm);
  padding: var(--space-sm) var(--space-md);
  cursor: pointer;
  transition: border-color 0.15s, transform 0.15s;
  /* 卡片不参与压缩：否则泳道限高后卡片会被压扁而不是出现纵向滚动 */
  flex-shrink: 0;
}
.board-card:hover { border-color: var(--primary); transform: translateY(-1px); }
.board-card-top { display: flex; justify-content: space-between; gap: var(--space-sm); align-items: baseline; }
.board-proj { font-family: var(--font-head); font-size: var(--font-size-xs); color: var(--primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-card-title { font-size: var(--font-size-sm); color: var(--text); margin-top: 4px; overflow-wrap: anywhere; }
.board-card-content {
  font-size: var(--font-size-xs); color: var(--text-dim); margin-top: 3px; line-height: 1.5;
  max-height: 72px; overflow: hidden; overflow-wrap: anywhere;
}
.board-card-meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; align-items: center; }

@media (max-width: 768px) {
  .board-week-nav { margin-left: 0; width: 100%; }
  .board-lanes { flex-direction: column; }
  .board-lane { min-width: 0; width: 100%; flex: 0 0 auto; }
}

