/* ══════════════════════════════════════════════════════════════
   管理后台样式
   ══════════════════════════════════════════════════════════════
   设计语言：
     · 深色侧栏 + 浅灰纸面，内容用白卡片浮起来
     · 只有一个品牌色（绿），其余靠明度分层
     · 圆角偏大（卡片 16px、按钮胶囊），投影很淡但很长
     · 小标签用「大写 + 字距」区分层级，不用加粗
     · 数字用 tabular-nums 对齐

   ⚠️ 所有颜色都走 CSS 变量。改主题只改 :root 一处。
      不要在组件里写死颜色 —— 那会让换主题变成全局搜索替换。
   ⚠️ 本文件必须是 UTF-8。用 PowerShell 往返读写会把中文注释变成乱码。
   ══════════════════════════════════════════════════════════════ */

:root {
  /* 品牌色：只用这一个色相，深浅靠明度 */
  --brand: #0e9f6e;
  --brand-soft: #e8f7f1;
  --brand-line: #bfe8d8;
  --brand-deep: #0a7d56;

  /* 文字：ink 主 / ink2 次 / muted 弱 */
  --ink: #16211c;
  --ink2: #4a5a53;
  --muted: #8a9a93;

  /* 面：paper 页面底 / card 卡片 / line 分隔 */
  --paper: #f4f6f5;
  --card: #fff;
  --line: #e8edea;

  /* 侧栏（深色） */
  --side: #10201a;
  --side-ink: #d3e0da;
  --side-muted: #7d918a;

  /* 语义色（各有 -soft 浅底 / -line 边线两档：标签、错误块、失败行共用同一套底色） */
  --warn: #e8871e;
  --warn-soft: #fdf0e4;
  --danger: #d9534f;
  --danger-soft: #fdeceb;
  --danger-line: #f5c6c3;
  --ok: #0e9f6e;

  /* ── 设计令牌（组件规范的唯一出处；改这里 = 全站生效）──────────
     字号阶梯：h3 页标题 16 / 卡内分区 14 / 正文 13.5 / 次要 12.5 / 弱说明 12 / 标签 11
     控件高度：按钮-lg 40 / 按钮-sm 32 / 输入 40 / 表格行 44（上下 12px）
     圆角阶梯：卡片 16 / 大块 12 / 控件 10 / 小件（标签/mono）8
     间距阶梯：页面 24 / 卡片 20 / 组件间 16 / 控件间 8
     ⚠️ 写组件时先来这里找值，找不到再用魔法数 —— 同一个"东西"只准有一个尺寸。 */
  --fs-h3: 16px;
  --fs-sec: 14px;
  --fs-body: 13.5px;
  --fs-sub: 12.5px;
  --fs-note: 12px;
  --fs-tag: 11px;
  --ctl-h: 40px;
  --ctl-h-sm: 32px;
  --ctl-r: 10px;
  --ctl-r-sm: 8px;
  --sp-card: 20px;
  --sp-item: 16px;
  --sp-ctl: 8px;

  --r: 16px;
  --r-sm: 10px;
  --shadow: 0 2px 4px rgba(22, 33, 28, 0.03), 0 12px 28px -16px rgba(22, 33, 28, 0.22);

  /* 缓动：统一用这一条，动效才不会各走各的 */
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

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

body {
  font-family: "PingFang SC", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
  background: var(--paper);
  color: var(--ink);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.hidden { display: none !important; }
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: 14px; }
/* 勾选框 / 单选一律用品牌色：浏览器默认的蓝勾和整站的绿放在一起像两个系统 */
input[type="checkbox"], input[type="radio"] { accent-color: var(--brand); }
/*
  链接：**默认不带下划线**，靠颜色区分"可点"。

  ⚠️ 浏览器默认会给 <a> 加下划线，而这张样式表原先只在侧栏（`.nav a` / `.side-foot a`）
     显式去掉了它 —— 于是表格与提示语里的链接（账号明细的账号名、栏目跳转…）
     全是"老实巴交的下划线"，一行行的看着很碎。现在统一在根上关掉。

  可点性怎么表达：静止时是品牌绿；悬停时**变色 + 下划线回归**（位置下移 2px、线更细）——
  有反馈但不会在静止状态下给满屏加线。
*/
a {
  color: var(--brand);
  text-decoration: none;
  transition: color 0.15s var(--ease);
}
a:hover {
  color: var(--brand-deep);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
/* 导航与页脚本来就不要下划线（连悬停也不要） */
.nav a:hover, .side-foot a:hover { text-decoration: none; }

/* ── 登录 ───────────────────────────────────────────────── */
.login-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* 右上角一团品牌色光晕，让空白页不至于太空 */
  background:
    radial-gradient(760px 420px at 70% 0%, rgba(14, 159, 110, 0.12), transparent 65%),
    var(--paper);
}
.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--card);
  border-radius: 22px;
  padding: 36px 30px;
  box-shadow: 0 4px 8px rgba(22, 33, 28, 0.04), 0 30px 60px -30px rgba(22, 33, 28, 0.45);
}
.login-card h1 { font-size: 21px; letter-spacing: -0.03em; margin-bottom: 6px; }
.login-card p { color: var(--muted); font-size: 13px; margin-bottom: 22px; }

/* ── 表单 ───────────────────────────────────────────────── */
.field { margin-bottom: 14px; }
.field label {
  display: block;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.field input, .field select, .field textarea {
  width: 100%;
  padding: 0 14px;
  height: var(--ctl-h);           /* 输入与按钮同一档高度 —— 同一行里不再"一高一矮" */
  border: none;
  border-radius: 12px;
  background: var(--paper);
  color: var(--ink);
  outline: none;
  transition: box-shadow 0.2s var(--ease), background 0.2s;
}
.field textarea { height: auto; min-height: 76px; padding: 11px 14px; line-height: 1.6; }
.field select {
  appearance: none;
  /* 下拉箭头：内联 SVG（灰），替代各浏览器不一致的原生箭头 */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a9a93' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}
.field select::-ms-expand { display: none; }
.field input:focus, .field select:focus, .field textarea:focus {
  background: #fff;
  box-shadow: 0 0 0 2px var(--brand-line);
}
.field textarea { resize: vertical; min-height: 76px; line-height: 1.6; }
/* ⚠️ 文件选择框（新增应用 · 代码来源=文件）的垂直居中：原生 `input[type=file]` 的按钮/文件名
   在固定高度里**默认贴顶**（Chromium 不接受 display:flex 调整它内部排版 —— 实测加了完全不动；
   40px 高时内容占 0..24 行、下方空 15px，截图里看着"没对齐"，2026-11-04 使用者指出）。
   只能给上下补等高的内边距把内容压到中间：8px 时内容行 8..32，上下约 8/7px。 */
.field input[type="file"] { padding-top: 8px; padding-bottom: 8px; }

/* 两列 / 三列表单行 */
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .row2, .row3 { grid-template-columns: 1fr; } }

.checks { display: flex; gap: 18px; flex-wrap: wrap; padding-top: 4px; }
.checks label {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--ink2);
  text-transform: none; letter-spacing: 0;
}

/* ── 按钮 ─────────────────────────────────────────────────
   规范（见 :root 的令牌注释）：
   · lg：高 40（--ctl-h）· 14px 字 · padding 0 18px（用行高撑，不写死 height）
   · sm：高 32（--ctl-h-sm）· 12.5px 字 · padding 0 14px —— 表格行内、顶栏里都是它
   · 主 = 实心绿 / 次 = 白底描边 ghost / 危险 = ghost + danger（表单提交里的实心红另算）
   · 三种形态的**高度必须一致**（同一行里实心比 ghost 高 1px 是一眼能看出来的歪） */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--ctl-h);
  padding: 0 18px;
  border: none;
  border-radius: 99px;
  font-size: 14px;
  font-weight: 600;
  background: var(--brand);
  color: #fff;
  transition: transform 0.15s var(--ease), filter 0.15s, border-color 0.15s, color 0.15s;
}
.btn:hover { filter: brightness(1.06); }
.btn:active { transform: scale(0.98); }
.btn.full { width: 100%; }
.btn.ghost { background: #fff; color: var(--ink2); border: 1px solid var(--line); }
.btn.ghost:hover { border-color: var(--brand-line); color: var(--brand); }
.btn.danger { background: var(--danger); }
/* ⚠️ `.danger` 与 `.ghost` 同特异性、且在后面声明 —— 不额外钉一条的话，
   `class="btn ghost sm danger"` 会渲染成**实心红块**（行内「删除」全是红药丸）。
   行内动作按全站规范必须是 ghost：白底 + 红字 + 红描边，hover 才铺浅红。 */
.btn.ghost.danger { background: #fff; color: var(--danger); border-color: var(--danger-line); }
.btn.ghost.danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.btn.sm { height: var(--ctl-h-sm); padding: 0 14px; font-size: var(--fs-sub); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── 布局 ───────────────────────────────────────────────── */
/*
 * 🔴 第 463 轮（用户要求）：左菜单不跟随内容滚动
 *
 * 原实现是 `.layout { min-height: 100vh }` —— **整页滚动**：
 * 右侧内容一长，左边菜单跟着滚出视口，翻到列表底部时菜单已经看不见了，
 * 想换个页面得先滚回顶部。
 *
 * 现在把滚动**关进 `.content` 里**（三层都固定）：
 *   .layout  高度锁死 100vh + overflow:hidden  → 页面本身不滚
 *   .sidebar 自己的 overflow-y:auto            → 菜单超高时菜单内部滚，不带走页面
 *   .main    仍是 flex 列，但 overflow:hidden  → 顶栏永远贴顶
 *   .content flex:1 + overflow-y:auto          → **只有这里滚**
 *
 * ⚠️ 手机端（≤860px）必须还原成 `display:block` + 整页滚动：
 *    那边菜单是**抽屉**（position:fixed），锁 100vh 会让抽屉高度算错。
 */
.layout { display: flex; height: 100vh; overflow: hidden; }

.sidebar {
  width: 216px;
  flex: none;
  background: var(--side);
  color: var(--side-ink);
  display: flex;
  flex-direction: column;
  padding: 22px 14px;
  /* 菜单项多到超出视口时，滚的是菜单自己，不是整页 */
  overflow-y: auto;
}
.brand {
  display: flex; align-items: center; gap: 10px;
  padding: 0 8px 20px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
  margin-bottom: 16px;
}
.brand .dot {
  width: 26px; height: 26px;
  border-radius: 8px;
  background: var(--brand);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 800; color: #fff;
}
.brand b { font-size: 14px; color: #fff; letter-spacing: -0.01em; }
.brand span { display: block; font-size: 10.5px; color: var(--side-muted); font-weight: 400; }

.nav { display: flex; flex-direction: column; gap: 3px; }
.nav a {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 12px;
  border-radius: 9px;
  color: var(--side-ink);
  text-decoration: none;
  font-size: 13.5px;
  transition: background 0.2s, color 0.2s;
}
.nav a:hover { background: rgba(255, 255, 255, 0.06); }
.nav a.on { background: var(--brand); color: #fff; font-weight: 600; }
.nav a i { font-style: normal; width: 16px; text-align: center; opacity: 0.9; }

.side-foot {
  margin-top: auto;
  padding: 12px 8px 0;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.side-foot a { color: var(--side-muted); font-size: 12px; text-decoration: none; display: block; }
.side-foot a:hover { color: var(--side-ink); }
.side-foot a + a { margin-top: 6px; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
  height: 58px; flex: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 26px;
  background: #fff;
  border-bottom: 1px solid var(--line);
}
.topbar h2 { font-size: 16px; letter-spacing: -0.02em; }
.topbar .who { font-size: 12px; color: var(--muted); }
/* 滚动容器（第 463 轮）：顶栏与左菜单固定，只有这一层滚 */
.content { flex: 1; overflow-y: auto; padding: 24px 26px 60px; }

/* 手机端才出现的汉堡按钮 / 抽屉遮罩（桌面端不占位） */
.nav-toggle {
  display: none;
  align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  border: 0; border-radius: 10px;
  background: var(--paper); color: var(--ink);
  font-size: 17px; line-height: 1; cursor: pointer;
}
.nav-toggle:active { background: var(--brand-soft); }
.nav-mask { display: none; }

/* ── 手机/窄屏：侧栏变抽屉 + 内容单列 ───────────────────── */
@media (max-width: 860px) {
  /* 手机端还原成整页滚动（第 463 轮）：
     桌面端锁了 `height:100vh; overflow:hidden` 让内容区自己滚，
     而这里的菜单是**抽屉**（position:fixed），锁死高度会让抽屉算错、也滚不动内容。 */
  .layout { display: block; height: auto; overflow: visible; }
  .main { overflow: visible; }
  .content { flex: none; overflow: visible; }
  .sidebar {
    position: fixed; inset: 0 auto 0 0;
    width: 264px; max-width: 82vw;
    z-index: 60;
    padding: 18px 12px;
    transform: translateX(-105%);
    transition: transform 0.22s var(--ease);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.28);
  }
  .sidebar.open { transform: none; }
  .nav-mask {
    display: block;
    position: fixed; inset: 0;
    background: rgba(16, 32, 26, 0.42);
    z-index: 55;
  }
  .nav-toggle { display: inline-flex; }
  .topbar { height: 52px; padding: 0 14px; gap: 10px; }
  .topbar h2 { font-size: 15px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar .who { display: none; }
  .content { padding: 14px 14px 44px; }
  .card { padding: 14px; }
  .grid { gap: 10px; }
  .grid.c4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid.c2, .grid.c3, .grid.split { grid-template-columns: 1fr; }
  .kpi-cell .v { font-size: 22px; }
  .panel-head { gap: 8px; }
  .panel-head h3 { font-size: 14px; }
  .pager { flex-wrap: wrap; }
  table { font-size: 12px; }
  th, td { padding-right: 8px; }
  .login-card { width: min(420px, 92vw); }
}

@media (max-width: 420px) {
  .grid.c4 { grid-template-columns: 1fr; }
  .content { padding: 12px 10px 40px; }
  .card { padding: 12px; }
}

/* ── 卡片与统计 ─────────────────────────────────────────── */
.grid {
  display: grid;
  gap: 14px;
  /* ⚠️ 裸 `.grid` 必须自带列定义：原先只写了 `display:grid`，
     忘了加 `.c4`/`.c3` 修饰类的页面就变成**单列竖排**（总览与审计页实测就是这么错的）。
     这里给一个 auto-fit 兜底 —— 任何数量的卡片都能自己排开，漏写修饰类最多是列宽不理想。 */
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.grid.c4 { grid-template-columns: repeat(4, 1fr); }
.grid.c2 { grid-template-columns: repeat(2, 1fr); }
/* 六宫格（总览的资源计数）：宽屏一行六格，中屏三格，窄屏两格，手机一列 */
.grid.c6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
@media (max-width: 1280px) { .grid.c6 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 1080px) { .grid.c4 { grid-template-columns: repeat(2, 1fr); } }
/* c2 原来没有窄屏兜底：1080 以下两列会被挤成"标题换行 + 数字竖排" */
@media (max-width: 780px) { .grid.c2 { grid-template-columns: 1fr; } }
@media (max-width: 780px) { .grid.c6 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .grid, .grid.c2, .grid.c4, .grid.c6 { grid-template-columns: 1fr; } }

/* 凭据列：明文 token 比较长，允许折行（等宽字体自带 word-break） */
.cred-cell { display: flex; align-items: baseline; gap: 8px; flex-wrap: nowrap; white-space: nowrap; min-width: 0; }
/* ⚠️ 格内的小字（`+1`、`生产分支 main`）不要带 `margin-top`：
   flex 换行本来就有 8px 的 row-gap，再加一层 margin 会让第二行"飘"在下面（2026-11-03 使用者指出）。
   `.cred-cell` 里的字都是同一格里的并列信息，行距只由 gap 决定。 */
/* 部署时间 + 成功/失败：必须同一行（列宽 190px 时它会折成两行） */
.deploy-time { flex-wrap: nowrap; white-space: nowrap; }

/* ── 全局请求进度（删除 / 保存 / 部署 都靠它给反馈）─────────────
   顶部细条：立刻出现（任何请求在飞）—— 不闪、不挡点击；
   居中胶囊：只在**超过 700ms** 时出现（快请求看不到，慢请求才有"在干活"的实感）。
   z-index 高于弹层（50）：删除确认关掉后、以及部署弹层里的长请求都看得见。 */
.net-status { position: fixed; inset: 0; z-index: 90; pointer-events: none; }
.net-bar { position: absolute; top: 0; left: 0; right: 0; height: 3px; overflow: hidden; }
.net-bar > i {
  display: block; height: 100%; width: 40%;
  background: linear-gradient(90deg, transparent, var(--brand), transparent);
  animation: net-slide 1.1s ease-in-out infinite;
}
@keyframes net-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(260%); }
}
.net-box {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 10px;
  padding: 10px 18px; border-radius: 999px;
  background: rgba(16, 40, 32, 0.92); color: #e9f5ef;
  font-size: 13px; letter-spacing: 0.02em;
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.22);
}
.net-spin {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35); border-top-color: #fff;
  animation: net-spin 0.8s linear infinite;
}
@keyframes net-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .net-bar > i { animation-duration: 2.6s; }
  .net-spin { animation-duration: 2.2s; }
}

.card {
  background: var(--card);
  border-radius: var(--r);
  padding: 20px;
  box-shadow: var(--shadow);
}
/* 卡片之间留白：用相邻兄弟选择器，避免每个卡片各自写 margin */
.card + .card, .grid + .panel { margin-top: 18px; }

/* ⚠️ 网格里的卡片要豁免上面那条：`.card + .card` 是给**纵向堆叠**的卡片用的，
   加在网格行里会变成"第二张起比第一张低 18px"—— 一行四张卡看起来是斜的
   （`.grid` 的 `align-items` 默认 stretch，于是那 18px 全落在卡片自己的上边距里）。 */
.grid > .card { margin-top: 0; }

/* ── 总览的「合计带」 ─────────────────────────────────────
   ⚠️ 六个数字**不各自装卡片**了。原先用 `.stat`（组件里就是 `card stat`）：
      卡片里再摆六张卡片 = 一屏"卡套卡"，像把仪表盘塞进文件柜（被指出过）。
      现在是一条**共底的带**：容器铺分隔线的颜色，`gap:1px` 把缝露成 1px 细线。
      这样**换行也成立** —— 窄屏排到第二排时线自动接上，不用写
      "行首那格去掉左边框"之类的 `nth-child` 补丁（那种补丁在列数变化时必错）。 */
.kpi-band {
  display: grid;
  /* 第一格（账号）宽一点：它是这屏的主语，后面五格是它的展开 */
  grid-template-columns: 1.3fr repeat(5, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.kpi-cell { background: var(--card); padding: 13px 16px; }
.kpi-cell .k {
  font-size: 11px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap;
}
.kpi-cell .v {
  font-size: 26px; font-weight: 800; letter-spacing: -0.03em;
  margin-top: 2px; line-height: 1.15; font-variant-numeric: tabular-nums;
}
/* 只有"账号"这格上色：其余五格读起来是它的展开 */
.kpi-cell.hl { background: var(--brand-soft); }
.kpi-cell.hl .v { color: var(--brand); }
@media (max-width: 1280px) { .kpi-band { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 780px)  { .kpi-band { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px)  { .kpi-band { grid-template-columns: 1fr; } }

/* 合计带下面那行：左边账号健康标签，右边本次开销（meta） */
.band-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-top: 10px;
}
.band-foot .cost { font-size: 11.5px; color: var(--muted); }

/* 卡片内部的**分区标题**（总览的「各账号明细」）：一条细线 + 一行标题，右侧放跳转。
   卡片里再放一张卡片才是"卡套卡"；同层里用线分区不是。 */
.sec-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--line);
}
.sec-head h3 { font-size: 15px; letter-spacing: -0.02em; }

/* ── AI 页「可用模型」的列表区 ────────────────────────────────
   ⚠️ 模型目录可以有几十上百条（跨账号合并后更多）—— 让它**自己滚**，整页就不再跟着变长，
      上面的「资源列表」才能**始终全量看得见**（使用者的要求："不需要上下拉"）。
      高度给 42vh 而不是固定 px：小窗口里也留住"上面那张表 + 这张表的表头"。
   ⚠️ 滚动区里的表头要 `sticky` 并且**给底色** —— 表头默认是透明的，滚动时字会与行叠在一起。 */
.model-list .tw { max-height: min(34vh, 320px); overflow: auto; }
.model-list thead th { position: sticky; top: 0; background: var(--card); z-index: 1; }
/* ⚠️ 模型 ID 是长串（@cf/pipecat-ai/smart-turn-v2）。`.mono` 的 break-all 会把这一格的最窄宽度算成
      "一个字符"，自动布局于是把它压成一字一行。这里换回"只在放不下时断词"，并给首列一个下限。 */
.model-list .mono { word-break: normal; overflow-wrap: break-word; }
/* ⚠️ 表格里的模型 ID 仍然要**一行截断**（`word-break: break-all` 会折成两行、把行高顶起来）：
   上面那条只负责"不要在斜杠处稀烂地断"，这里把 td 里的 mono 收成单行 + ellipsis。 */
.model-list td .mono { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.model-list th:first-child, .model-list td:first-child { min-width: 196px; }
/* 说明是上游的营销文案（几句英文），不折会一行撑到 100px 高、一屏只放得下 3 个模型。
   限两行 + hover 看全文：列表要的是"能扫"，不是"读说明"。 */
.model-list .model-note {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
  overflow: hidden; max-width: 360px; line-height: 1.5;
}

/* 读取失败的账号：整行淡红底（"谁坏了"要在一屏里看得见），错误原文在脚注里 */
.tw tr.row-fail td { background: var(--danger-soft); }
/* 脚注里的失败原文：折行留在页面上（不要藏进 title 或 toast） */
.foot-note .fail-list { margin-top: 6px; color: var(--danger); }

.panel { margin-top: 18px; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 12px; flex-wrap: wrap;
}
.panel-head h3 { font-size: 15px; letter-spacing: -0.02em; }
.tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* 第 455 轮：apps 页「筛选 / 全选本页 / 上传 APK」按钮组要与筛选**输入框**在同一条水平线上。
   原来 `.tools` 用 `align-items: center`，对齐的是整块 filters（含上方 label），
   于是按钮比输入框高一截 —— 用户报「btn-group 未居中」。 */
.panel-head .tools { align-items: flex-end; }
.panel-head .tools > .btn-group { margin-bottom: 2px; }
.tools input, .tools select {
  height: var(--ctl-h);
  padding: 0 14px;
  background: var(--paper);
  border: 1px solid transparent;
  border-radius: 11px;
  outline: none;
  transition: box-shadow 0.2s var(--ease), background 0.2s;
}
.tools input:focus, .tools select:focus {
  background: #fff;
  box-shadow: 0 0 0 2px var(--brand-line);
}

/* ── 表格 ─────────────────────────────────────────────────
   规范：表头 11px 大写 + 9px 下距；行高 44（10px 上下）；数字 tabular-nums；
   首列加粗主色、其余次要色；悬停整行浅绿 —— 各页共用这一套，不再各写各的。 */
.tw { overflow-x: auto; }
/* ⚠️ 自动布局（默认）下写死的 width 只是"最小值"：域名格内容 260px 也会被撑到 275。
   auto 布局的列宽由内容决定 —— 恰好符合"域名列按内容收缩"的要求（2026-11-04），
   但要配上 td 的 max-width 上限，否则长域名还是把列顶宽。 */
/* ⚠️ auto 布局里 max-width 不参与列宽计算（列宽 = 内容宽），"按内容收缩 + 上限"做不到；
   fixed 布局 + 显式列宽才是可控的 —— 各页面的 th 都写了宽度，正好配套。 */
table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: var(--fs-body); }
/* 长内容列的收缩上限（配 td 内的 ellipsis 用）：.cell-content / .dom-cell 的容器 */
td .dom-cell { max-width: 260px; }
th {
  text-align: left;
  font-size: var(--fs-tag); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
  padding: 0 12px 9px 0;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
td {
  padding: 11px 12px 11px 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink2);
  vertical-align: middle;
  line-height: 1.5;
}
td:first-child { color: var(--ink); font-weight: 600; }
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--brand-soft); }

.num { font-variant-numeric: tabular-nums; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.acts { display: flex; gap: 6px; white-space: nowrap; }
/* 行内操作按钮用浅底，避免表格里出现一排实心按钮 */
.acts .btn.ghost { background: var(--paper); border-color: transparent; }
.acts .btn.ghost:hover { background: var(--brand-soft); color: var(--brand-deep); }

/* ── 标签 ─────────────────────────────────────────────────
   规范：11px、2px 8px、圆角 8（--ctl-r-sm）；o=橙描边系（中性强调）、g=绿、w=橙、d=红。
   弹层/表格里共用；文字只放 1~4 个字，长说明一律挪到 title 或 hint。 */
.tag {
  display: inline-block;
  font-size: var(--fs-tag); padding: 2px 8px;
  border-radius: var(--ctl-r-sm);
  background: var(--paper); color: var(--muted);
  margin-right: 4px;
  line-height: 1.6;
  white-space: nowrap;
}
.tag.o { background: var(--brand-soft); color: var(--brand); }
.tag.g { background: var(--brand-soft); color: var(--brand-deep); }
.tag.w { background: var(--warn-soft); color: var(--warn); }
.tag.d { background: var(--danger-soft); color: var(--danger); }

/* ── 趋势条 ─────────────────────────────────────────────── */
.bars {
  display: flex; align-items: flex-end; gap: 10px;
  height: 120px; padding-top: 10px;
}
.bars .b {
  flex: 1;
  display: flex; flex-direction: column; align-items: center;
  gap: 6px; height: 100%; justify-content: flex-end;
}
.bars .b i {
  display: block; width: 100%; max-width: 42px;
  border-radius: 6px 6px 3px 3px;
  background: linear-gradient(180deg, #2ec38f, var(--brand));
  min-height: 3px;
  transition: height 0.6s var(--ease);
}
.bars .b span { font-size: 10.5px; color: var(--muted); }
.bars .b b { font-size: 11px; color: var(--ink2); }

/* ── 分布条 ─────────────────────────────────────────────── */
.dist { display: flex; flex-direction: column; gap: 9px; margin-top: 4px; }
.dist .r {
  display: grid; grid-template-columns: 78px 1fr 42px;
  gap: 10px; align-items: center; font-size: 12.5px;
}
.dist .track { height: 8px; border-radius: 99px; background: var(--paper); overflow: hidden; }
.dist .track i { display: block; height: 100%; border-radius: 99px; background: var(--brand); }

/* ── 分页 ───────────────────────────────────────────────── */
.pager {
  display: flex; align-items: center; gap: 10px;
  margin-top: 14px; font-size: 13px; color: var(--muted);
}
.pager .btn.ghost { background: var(--paper); border-color: transparent; }
.pager .btn.ghost:hover { background: var(--brand-soft); color: var(--brand-deep); }

/* ── 弹层 ─────────────────────────────────────────────────
   ⚠️ 弹层**必须一屏之内**（使用者的反馈："弹窗高度太高不能一次显示完整"）：
      卡片限高 `calc(100vh - 48px)` + 纵向 flex，头/尾 `flex: none`，
      **只有正文 `.modal-body` 滚动**。这样标题与操作按钮永远看得见 ——
      原来是"整个浮层一起滚"（`.modal { overflow-y: auto }` + `align-items: flex-start`），
      长表单一旦超过一屏，标题和底部按钮一起滚出视野，要滚到底才知道能不能提交。 */
.modal {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(16, 32, 26, 0.55);
  display: flex; align-items: center; justify-content: center;
  padding: 24px 20px;
  overflow: hidden;
}
.modal-card {
  width: 100%; max-width: 640px;
  max-height: calc(100vh - 48px);
  display: flex; flex-direction: column;
  background: #fff; border-radius: var(--r);
  box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}
.modal-head {
  flex: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 22px; border-bottom: 1px solid var(--line);
}
.modal-head h3 { font-size: var(--fs-h3); letter-spacing: -0.01em; }
/* 危险弹层（确认删除一类）标题前加一个红点：与"确定"按钮的红色呼应，误点前多看一眼 */
.modal.danger .modal-head h3::before,
.modal-card.danger .modal-head h3::before {
  content: ""; display: inline-block;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger); margin-right: 8px; vertical-align: 2px;
}
.modal-head .x {
  border: none; background: none;
  width: 30px; height: 30px; border-radius: 50%;
  font-size: 20px; color: var(--muted); line-height: 1;
  transition: background 0.15s var(--ease), color 0.15s;
}
.modal-head .x:hover { color: var(--ink); background: var(--paper); }
.modal-body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 20px 22px;
}
.modal-foot {
  flex: none;
  display: flex; justify-content: flex-end; gap: 10px;
  padding: 16px 22px; border-top: 1px solid var(--line);
}

/* ── 开关 ───────────────────────────────────────────────── */
.switch {
  position: relative; display: inline-block;
  width: 46px; height: 26px; vertical-align: middle;
}
.switch input { opacity: 0; width: 0; height: 0; }
.switch .slider {
  position: absolute; inset: 0;
  background: #cfd8d4; border-radius: 999px;
  transition: 0.18s var(--ease); cursor: pointer;
}
.switch .slider::before {
  content: ""; position: absolute;
  width: 20px; height: 20px; left: 3px; top: 3px;
  background: #fff; border-radius: 50%;
  transition: 0.18s var(--ease);
}
.switch input:checked + .slider { background: var(--brand); }
.switch input:checked + .slider::before { transform: translateX(20px); }
.switch input:focus-visible + .slider { box-shadow: 0 0 0 2px var(--brand-line); }

/* ── 缩略图网格 ─────────────────────────────────────────── */
.thumb-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px; margin-top: 4px;
}
.thumb-cell {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px;
  overflow: hidden;
}
.thumb-img {
  display: block; width: 100%; height: 110px;
  object-fit: cover; border-radius: 7px; background: var(--paper);
}
.thumb-key {
  font-size: 11px; color: var(--muted); margin-top: 6px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.thumb-meta {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 6px; font-size: 11.5px; color: var(--muted);
}

/* ── 状态提示 ───────────────────────────────────────────── */
.toast {
  position: fixed; left: 50%; bottom: 34px;
  transform: translateX(-50%) translateY(16px);
  background: var(--ink); color: #fff;
  padding: 11px 20px; border-radius: 99px;
  font-size: 13.5px; opacity: 0; pointer-events: none; z-index: 80;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.err { background: var(--danger); }

/* 空态：一块浅底圆角区，居中标题 + 说明 —— 各页共用（原来裸文本飘在表格里，看着像坏了） */
.empty {
  padding: 40px 20px; text-align: center;
  color: var(--muted); font-size: var(--fs-body);
}
.loading { padding: 40px; text-align: center; color: var(--muted); font-size: var(--fs-body); }

/* 错误框：登录页与弹层内共用 */
.err-box {
  background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  border-radius: var(--r-sm);
  padding: 12px 14px;
  font-size: 13px;
  margin-bottom: 14px;
}

/* 键值说明块（设置页用） */
.kv { display: flex; flex-direction: column; gap: 0; }
.kv .r {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--line);
}
.kv .r:last-child { border-bottom: none; }
.kv .k { font-size: 13px; color: var(--ink2); }
.kv .v { font-size: 13px; color: var(--ink); font-variant-numeric: tabular-nums; }

/* ── 头像 ───────────────────────────────────────────────── */
.av {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
  background: var(--paper);
}
.av-txt {
  background: var(--brand-soft);
  color: var(--brand-deep);
  font-weight: 700;
}

/* ── 侧栏角标（待处理数量） ─────────────────────────────── */
.nav a { position: relative; }
.badge {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 99px;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}

/* ── 表格里的两行堆叠（主标题 + 次要说明） ───────────────── */
.stack { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stack > b { font-weight: 600; }
.stack > span { font-size: 12px; color: var(--ink2); }
.stack .muted { color: var(--muted); }

/* ── 表格内联输入（设置页） ─────────────────────────────── */
.cell-in {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--ctl-r-sm);
  background: #fff;
  outline: none;
  font-size: 13px;
}
/* 表格行内下拉：自绘箭头（原生箭头各浏览器不一样，且会把 "Global API Key" 挤成 "Global A…"） */
.cell-inselect, select.cell-in {
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238a9a93' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 28px;
}
.cell-in:focus { border-color: var(--brand-line); box-shadow: 0 0 0 2px var(--brand-line); }

/* ── 只读文本块（日记详情等） ───────────────────────────── */
.pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--ink);
  max-height: 420px;
  overflow: auto;
}

/* 弹层里「确定」按钮被隐藏时，底部只剩取消 —— 让它靠右就好 */
#modalOk.hidden + * { margin-left: 0; }

/* ── 页签 ───────────────────────────────────────────────── */
.tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 14px;
  padding: 4px;
  background: var(--paper);
  border-radius: 12px;
  width: fit-content;
}
.tabs button {
  padding: 7px 18px;
  border: none;
  border-radius: 9px;
  background: transparent;
  color: var(--ink2);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.tabs button:hover { color: var(--ink); }
.tabs button.on {
  background: #fff;
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(16, 40, 32, .1);
}

/* ── 筛选条 ─────────────────────────────────────────────── */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  padding: 4px 0 2px;
}
.filters label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.filters input,
.filters select {
  height: var(--ctl-h);
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  outline: none;
  min-width: 120px;
}
.filters input:focus,
.filters select:focus {
  border-color: var(--brand-line);
  box-shadow: 0 0 0 2px var(--brand-line);
}

/* ── 三列网格 ───────────────────────────────────────────── */
.grid.c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1180px) { .grid.c3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 780px)  { .grid.c3 { grid-template-columns: 1fr; } }

/* ── 排行 ───────────────────────────────────────────────── */
.rank { display: flex; flex-direction: column; }
.rank-row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(40px, 1fr) 48px auto;
  align-items: center;
  gap: 10px;
  padding: 8px 6px;
  border: none;
  border-bottom: 1px solid var(--line);
  background: transparent;
  text-align: left;
  cursor: pointer;
  border-radius: 6px;
}
.rank-row:last-child { border-bottom: none; }
.rank-row:hover { background: var(--paper); }
.rank-key {
  font-size: 12px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rank-bar {
  height: 6px;
  border-radius: 99px;
  background: var(--paper);
  overflow: hidden;
}
.rank-bar i {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--brand);
}
.rank-n {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  text-align: right;
}
.rank-f {
  font-size: 11px;
  color: var(--danger);
  white-space: nowrap;
}

/* 24 格的小时柱状图要比 7 格窄一些 */
.bars.hours .b span { font-size: 9px; }
.bars.hours .b b { font-size: 9px; }

/* 路径列可能很长，超出截断（完整值在 title 里） */
.path-cell {
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}

/* ══════════════════════════════════════════════════════════════
   本项目扩展组件（在模板设计系统之上）
   ⚠️ 只用 :root 的令牌，不写死颜色 —— 换主题仍然只改一处。
   ⚠️ 模板的 `.stack` 是"表格单元格两行"，页面纵向堆叠用 `.page-stack`。
   ══════════════════════════════════════════════════════════════ */

/* 页面纵向堆叠 */
.page-stack { display: flex; flex-direction: column; gap: 18px; }

/* 左右分栏：主内容 + 侧栏信息 */
.grid.split { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 1080px) { .grid.split { grid-template-columns: 1fr; } }

/* 说明性文字 */
.page-note { font-size: 12.5px; color: var(--muted); line-height: 1.7; margin: -4px 0 0; }
.foot-note {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 12px; color: var(--muted);
}
/* 次要数据（表格里的账号名、占位 `—`、变量值、部署来源…）用这一档灰色小字。
   ⚠️ 2026-11-04 全站去掉"hint"（说明/提醒文字）之后，这里只剩**数据** ——
      所以叫 `.dim`（只是压暗），不再承担"提示"的语义，也不带 `margin-top`。 */
.dim { font-size: 12px; color: var(--muted); line-height: 1.7; }
.muted { color: var(--muted); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; background: var(--paper);
  border-radius: 5px; padding: 1px 5px;
}

/* 小统计块 */
.mini-stat { background: var(--paper); border-radius: var(--r-sm); padding: 12px 14px; }
.mini-stat span { display: block; font-size: 11.5px; color: var(--muted); }
.mini-stat b {
  display: block; margin-top: 6px;
  font-size: 22px; font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
/* 数据健康：坏消息标红（0 不标）；`.sub` 是比 mini-stat 原样多出来的一行说明 */
.mini-stat .sub {
  display: block; margin-top: 4px; font-style: normal;
  font-size: 11px; color: var(--muted); line-height: 1.5;
}
.mini-stat.warn b { color: var(--danger); }

/* 网盘副本分布：一行一个后端，列固定为 名 / 条 / 副本数 / 主副本·覆盖版本 */
.stor-list { margin-top: 6px; display: flex; flex-direction: column; gap: 8px; }
.stor-list .head,
.stor-list .row {
  display: grid; align-items: center; gap: 8px;
  grid-template-columns: 52px minmax(24px, 1fr) 56px minmax(96px, 132px);
}
.stor-list .head { font-size: 10.5px; color: var(--muted); }
.stor-list .head span:nth-child(3) { text-align: right; }
.stor-list .row { font-size: 12px; }
.stor-list .row .nm { color: var(--ink2); }
.stor-list .row .bar {
  display: block; height: 6px; border-radius: 99px;
  background: var(--paper); overflow: hidden;
}
.stor-list .row .bar i {
  display: block; height: 100%; border-radius: 99px;
  background: var(--brand); transition: width 0.25s var(--ease);
}
.stor-list .row b { text-align: right; font-variant-numeric: tabular-nums; }
.stor-list .row .meta {
  font-size: 11px; color: var(--muted); text-align: right;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ══ 总览顶部两宫格（品类分布含合计 / 下载+浏览含今日）═════════════════
   左边是 5 列小表、右边是 2×2 数字，形状差别大 —— 按 1.35 : 1 分列，窄屏退成上下两格。
   两格同在 `.grid` 里，高度相等（`.kpi` 用 flex 把脚注推到底 → 说明行落在同一条水平线上）。 */
.grid.kpi2 { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
@media (max-width: 1080px) { .grid.kpi2 { grid-template-columns: 1fr; } }

.kpi { display: flex; flex-direction: column; }
.kpi .s { margin-top: auto; }

/* 品类分布：一行一个品类，列固定为 名 / 占比条 / 占比 / 版本 / 应用（表头共用同一套列） */
.cat-list { margin-top: 10px; display: flex; flex-direction: column; gap: 5px; }
.cat-list .head,
.cat-list .row {
  display: grid; align-items: center; gap: 6px;
  grid-template-columns: 32px minmax(24px, 1fr) 30px 44px 32px;
}
.cat-list .head { font-size: 10.5px; color: var(--muted); }
.cat-list .head span:nth-child(3),
.cat-list .head span:nth-child(4),
.cat-list .head span:nth-child(5) { text-align: right; }
.cat-list .row { font-size: 12px; }
.cat-list .row .nm { color: var(--ink2); }
.cat-list .row .bar {
  display: block; height: 6px; border-radius: 99px;
  background: var(--paper); overflow: hidden;
}
.cat-list .row .bar i {
  display: block; height: 100%; border-radius: 99px;
  background: var(--brand); transition: width 0.25s var(--ease);
}
.cat-list .row .pc,
.cat-list .row .ap {
  font-size: 11px; color: var(--muted); text-align: right;
  font-variant-numeric: tabular-nums;
}
.cat-list .row b { text-align: right; font-variant-numeric: tabular-nums; }
/* 合计行 = 原来的「应用」「版本」两张卡（四个品类之和），用一条分线隔开 */
.cat-list .row.total {
  margin-top: 3px; padding-top: 6px;
  border-top: 1px solid var(--line);
  font-size: 12.5px;
}
.cat-list .row.total .nm { color: var(--ink); font-weight: 600; }
.cat-list .row.total b,
.cat-list .row.total .ap { color: var(--ink); font-weight: 700; }
.cat-list .row.total .ap { font-size: 12.5px; }

/* 下载 / 浏览合并成一格：两个数字并排，各自带"累计 · 今日" */
.kpi .duo { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.kpi .duo .it span { display: block; font-size: 11.5px; color: var(--muted); }
.kpi .duo .it b {
  display: block; margin-top: 4px;
  font-size: 22px; font-weight: 800; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.kpi .duo .it em {
  display: block; margin-top: 4px;
  font-style: normal; font-size: 11.5px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* 按钮组 */
.btn-group { display: inline-flex; gap: 6px; flex-wrap: wrap; }

/* 行列表（最近操作 / 摘要） */
.rows { list-style: none; }
.rows li {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
  font-size: 13px; flex-wrap: wrap;
}
.rows li:last-child { border-bottom: none; }
.rows li > b { font-weight: 600; }
.rows .grow { flex: 1; min-width: 0; }

/* 确认框正文 */
.confirm-text { font-size: 13.5px; line-height: 1.7; color: var(--ink2); }

/* 上传进度 */
/* 进度条：文本（百分比）**漂浮在条的中间**（使用者要求）
   ⚠️ 条要够高才放得下字（11px 字 → 条 18px），否则字会被挤出去；
   ⚠️ 字压在**填充色**上时要看得清：深色字 + 一圈浅色光晕，条上/条下都清楚
      （纯白字在浅色轨道上看不见，纯深字在深绿上又发糊 —— 光晕是唯一两边都成立的解）。 */
.progress {
  margin-top: 0; height: 18px; border-radius: 99px;
  background: var(--paper); position: relative; overflow: hidden;
}
.progress i {
  display: block; height: 100%; width: 0;
  border-radius: 99px; background: var(--brand);
  transition: width 0.25s var(--ease);
}
.progress span {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  text-shadow: 0 0 3px #fff, 0 0 3px #fff, 0 0 6px #fff;
  pointer-events: none;
}

/* 文件选择框 */
/* 部署日志：命令行观感（深底等宽）。部署时逐行追加，滚动到底 */
.deploy-log {
  max-height: 240px; overflow: auto; margin: 0;
  padding: 12px 14px; border-radius: var(--r-sm);
  background: #0e1713; color: #cfe3d9;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px; line-height: 1.7;
  white-space: pre-wrap; word-break: break-word;
}
.deploy-log .acc { color: #6ee7b7; }
.deploy-log .err { color: #fca5a5; }

.upload-box {
  display: flex; align-items: center; gap: 12px;
  padding: 14px;
  border: 1px dashed var(--brand-line);
  border-radius: var(--r-sm);
  background: var(--brand-soft);
}
.upload-box input[type="file"] { font-size: 12.5px; }
/* 选文件的按钮与文件名同一行（原生 input 已隐藏 —— 它那句"未选择文件"会和我们的文案打架） */
.upload-box > label.btn { flex: none; white-space: nowrap; }
/* ⚠️ 这个 label 在 `.field` 里，会被 `.field label` 的 11px / uppercase / display:block 命中 ——
   结果按钮文字变成「选择 .ZIP」且贴着左边。这里把这几个属性明确改回来。 */
.field .upload-box > label.btn {
  display: inline-flex; margin: 0; font-size: var(--fs-sub);
  letter-spacing: normal; text-transform: none; color: var(--ink2);
}
/* 「清空」这类紧跟其后的按钮也不能被 flex 压扁（压扁时文字会竖排成两个字一行） */
.upload-box > .btn { flex: none; white-space: nowrap; }
.upload-box > .dim { flex: 1 1 auto; min-width: 0; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 表单底部按钮 */
.form-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }

/* 空态支持两行（标题 + 说明） */
.empty b { display: block; color: var(--ink); font-size: var(--fs-sec); font-weight: 600; margin-bottom: 6px; }
.empty span { display: block; font-size: var(--fs-sub); line-height: 1.7; max-width: 46ch; margin: 0 auto; }

/* 宽弹层（应用详情 / 版本列表） */
.modal-card.wide { max-width: 960px; }

/* 表格里的次要说明 */
td .sub { display: block; font-size: 11.5px; color: var(--muted); font-weight: 400; }
td .tag { white-space: nowrap; }

/* 复选框列 */
td.check, th.check { width: 34px; text-align: center; }
td.check input, th.check input { width: 15px; height: 15px; accent-color: var(--brand); }

/* 只读 JSON / 长文本 */
.pre.code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  background: var(--paper);
  border-radius: var(--r-sm);
  padding: 12px;
}

/* 行内计数 */
.count {
  display: inline-block; min-width: 18px; padding: 0 6px;
  border-radius: 99px; background: var(--paper);
  color: var(--ink2); font-size: 11.5px; text-align: center;
}
.count.gap { margin-left: 8px; }

/* ── 页面实现过程中沉淀下来的小工具类 ───────────────────── */

/* 弹层里紧跟说明块的表单项（第一项与上方内容拉开距离） */
.field.gap-top { margin-top: 16px; }

/* 表格单元格：图标/头像 + 文字 */
.cell-main { display: flex; align-items: center; gap: 10px; }

/* 头像/图标的尺寸修饰（模板 `.av` 只有圆形，没有尺寸档） */
.av-34 { width: 34px; height: 34px; }

/* 筛选条里的按钮组贴底（`.filters label` 是竖排的，按钮没有 label 的高度） */
.filters .btn-group { align-self: flex-end; }

/* 表格里的长文本列 */
.stack.wide { max-width: 460px; }
.truncate.wide { display: inline-block; max-width: 520px; }

/* 标题旁的小标签（tag 自带 margin-right，这里是"跟在标题后面"） */
.tag.tight { margin: 0 0 0 8px; font-weight: 400; }

/* 开关 + 文字横排 */
.switch-cell { display: inline-flex; align-items: center; gap: 8px; }

/* 12px 的次要说明 */
.small { font-size: 12px; }

/* 警告色说明 */
.dim.warn { color: var(--warn); }

/* 配置行：标题列 / 值列 / 操作列（窄屏堆叠） */
.cfg-row {
  display: grid;
  grid-template-columns: minmax(200px, 1fr) minmax(0, 1.7fr) auto;
  gap: 16px; align-items: start;
}
@media (max-width: 900px) { .cfg-row { grid-template-columns: 1fr; } }
.cfg-key { margin-top: 2px; }
.cfg-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.group-sub { font-weight: 600; color: var(--ink2); margin: 6px 0 -6px; }



/* ── 危险操作区（第 470 轮）──────────────────────────────
 * 删除应用是不可逆动作，原先和「保存」「上下架」并排在同一行按钮里 ——
 * 长得一样、位置相邻，误点代价极高。这里用一条红线和独立标题把它隔开：
 * 视觉上"要跨过一道线才能点到"，操作前多一次迟疑，就够避免误点。
 */
.danger-zone { margin-top: 18px; padding-top: 14px; border-top: 1px dashed #e6b8b3; }
.danger-title { margin: 0 0 6px; font-size: 12.5px; font-weight: 700; color: #b4342a; letter-spacing: 0.02em; }
.danger-zone .form-actions { margin-top: 8px; }

/* ── 应用详情的「概要 + 图标」宫格（第 472 轮）──────────────────
 * 目标：把这整块的高度压下去。5 项只读信息原来是竖排 Kv（每项一行，约 5×28px），
 * 现在 3 列 × 2 行；图标从"地址字符串"变成真正的缩略图，点它就能换。
 */
.app-head { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 12px; }
.app-icon-wrap { position: relative; flex: none; cursor: pointer; }
.app-icon {
  width: 58px; height: 58px; border-radius: 14px; object-fit: cover;
  background: #f3f0ea; border: 1px solid var(--line); display: block;
}
.app-icon-empty { display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--muted); }
.app-icon-tip {
  position: absolute; left: 0; right: 0; bottom: 0; text-align: center;
  font-size: 10px; line-height: 15px; color: #fff; background: rgba(16, 32, 26, 0.62);
  border-radius: 0 0 14px 14px; opacity: 0; transition: opacity 0.15s;
}
.app-icon-wrap:hover .app-icon-tip { opacity: 1; }
.app-grid { flex: 1; min-width: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 16px; }
.app-grid > div { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.app-grid .k { font-size: 11px; color: var(--muted); }
.app-grid .v { font-size: 13px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-grid .vsub { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 620px) { .app-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── 详情里的「勾选项 + 按钮」同一行（第 473 轮）────────────────
 * 用户要求：勾选项在左、按钮在右，一行完成"选删除方式 → 执行"。
 * 窄屏时自动换行（否则 4 个控件会挤成一团）。
 */
.app-actions-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.app-actions-row .checks { padding-top: 0; gap: 14px; }
.app-actions-row .form-actions { margin: 0; }

/* ── 版本来源筛选条（第 474 轮）── */
.ver-filter { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }

/* ── 二级浮层（第 475 轮）────────────────────────────────
 * 打开编辑版本时叠在「应用详情」**内部**，而不是替换它 ——
 * 详情弹窗保持挂载，版本列表与滚动位置原样保留，关掉就回到原处。
 */
.submodal { position: fixed; inset: 0; z-index: 70; background: rgba(16, 32, 26, 0.5);
  display: flex; align-items: flex-start; justify-content: center; padding: 48px 20px; overflow-y: auto; }
.submodal-box { width: 100%; max-width: 720px; }
.submodal-box > .card { box-shadow: 0 18px 60px rgba(0, 0, 0, 0.28); }

/* 编辑版本里的「事实」区：采集解析出来的、不可改的字段 */
.ver-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px 16px;
  background: #f7f5f1; border: 1px solid var(--line); border-radius: 12px; padding: 12px 14px; margin-bottom: 14px; }
.ver-facts > div { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ver-facts .wide { grid-column: 1 / -1; }
.ver-facts .k { font-size: 11px; color: var(--muted); }
.ver-facts .v { font-size: 12.5px; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 620px) { .ver-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* 替换上传入口（第 476 轮） */
.replace-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px; }
.replace-row .dim { margin: 0; }

/* form-actions 里的文件选择按钮（第 477 轮：与「保存」同一行） */
.form-actions { flex-wrap: wrap; }
.form-actions label.btn { cursor: pointer; }

/* 版本副本列表（第 478 轮）：一个网盘一行 */
.copy-list { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
.copy-item { display: flex; align-items: center; gap: 8px; min-width: 0; }
.copy-item .v { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 版本列表里的网盘胶囊（第 479 轮） */
.pills { display: inline-flex; gap: 4px; flex-wrap: wrap; }

/* ══════════════════════════════════════════════════════════════
   CFManager 扩展组件
   ══════════════════════════════════════════════════════════════ */

/* 等宽：库名、键名、zone id、token 掩码 —— 靠字体区分"这是标识符" */
/* 等宽：库名、键名、zone id、token 掩码 —— 靠字体区分"这是标识符"
   ⚠️ 长标识符在**窄列**里要截断而不是折行（折行把行高顶起来、一行里出现"半截 ID"）；
      默认 break-all 是给"表格自由宽度列"用的，给 `.mono` 加 max-width + 截断的两条通用形态：
      `.mono.cut`（块级截断）与 td 里的默认（依赖列宽，title 看全文）。 */
.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace;
  font-size: 12px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--ctl-r-sm);
  padding: 2px 8px;
  word-break: break-all;
  line-height: 1.6;
}
td > .mono, td .cred-cell > .mono {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
/* ⚠️ flex 容器（.cred-cell）里 inline-block 的 max-width:100% 参照的是**自身内容宽**，
   等于没截。正确做法是直接把它变成**受控的 flex 子项**：
   `display:block` + `min-width:0` 解除 flex 的 min-content 下限，
   `flex:1 1 0` 让它吃掉剩余空间（放不下就 ellipsis），后面的 .tag/.dim `flex:none` 永不折行。
   ⚠️ mono 的高度（等宽字 + 内边距 = 25px）比 tag（22px）高，`align-items:center` 下基线不齐
      看着像两行 —— 统一 `align-items: baseline`，文字底边对齐才是"同一行"。 */
.cred-cell > .mono { display: block; flex: 1 1 0; min-width: 0; }
.cred-cell > .tag, .cred-cell > .dim { flex: none; }
.cred-cell { align-items: baseline; }
/* ── 应用页域名格的例外（2026-11-04 使用者要求）────────────────
   域名是"数据"，**按内容收缩**（多长就多宽、上限 230px），不吃剩余空间 ——
   "吃满"会把「部署时间」一路顶到最右。域名本身是链接（新窗口打开），
   `+N` 也是按钮（弹层列出全部域名）。 */
.dom-cell { flex-wrap: nowrap; }
.dom-cell .dom-link { display: block; flex: 0 1 auto; min-width: 0; max-width: 230px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dom-cell .dom-link .mono { display: block; }
.dom-cell .dom-more {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--brand);
  border-bottom: 1px dashed var(--brand-line);
  white-space: nowrap; line-height: 1.7;
}
.dom-cell .dom-more:hover { color: var(--brand-deep); border-bottom-color: var(--brand-deep); }

/* 代码块（Workers 源码 / KV 值 / SQL）：等宽 + 可滚动，不自撑破布局 */
.pre.code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace;
  font-size: 12.5px;
  line-height: 1.55;
  background: #0f1b16;
  color: #d7e6df;
  border-radius: var(--r-sm);
  padding: 12px 14px;
  overflow: auto;
  white-space: pre;
  tab-size: 2;
}

/* 类型标签（DNS 记录类型、账号状态、部署状态） */
.tag.type { text-transform: uppercase; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; }

/* 记录内容列：长 TXT / 长 CNAME 不要撑破表格 */
/* 表格里"一行放不下、悬停看全文"的通用容器（价格 / 说明 / 任务名…）。
   同样是 inline-block：行内元素吃不到 max-width/overflow。 */
.cell-clip { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* ⚠️ 必须是 inline-block：`max-width` / `overflow` 对**行内**元素不生效 ——
   之前它是个裸 `<span>`，长 TXT / CNAME 会直接溢出到"代理"列上（2026-11-04 实测）。 */
.cell-content {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* 面板"缓存"页：键名过长时折行但不撑破卡片 */
.cache-key { word-break: break-all; }

/* 危险操作区（删库 / 删脚本 / 清缓存）—— 与普通表单拉开距离 */
.danger-note {
  border-left: 3px solid var(--danger);
  background: #fdf4f4;
  padding: 10px 12px;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  margin: 12px 0;
  color: #8a3a38;
  line-height: 1.7;
}

/* SQL 控制台：结果表格可以横向滚动 */
.sql-result { margin-top: 12px; }
.sql-meta { display: flex; gap: 18px; flex-wrap: wrap; margin: 8px 0; color: var(--muted); font-size: 12.5px; }

/* 工具条：一行放筛选器与按钮（视图里用得很多，缺它就只能靠内联样式） */
.toolbar {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin: 12px 0;
}
.toolbar .field { margin: 0; }

/* 账号列表里的额度条：四条一组，紧凑但要能看清是哪一条 */
.quota-bars { display: flex; flex-direction: column; gap: 4px; min-width: 190px; }
.quota-bars .qrow { display: grid; grid-template-columns: 92px 1fr; align-items: center; gap: 6px; }
.quota-bars .qk {
  font-size: 11px; letter-spacing: 0.06em; color: var(--muted);
  text-transform: uppercase; white-space: nowrap;
}

/* 从别处跳过来时的"定位高亮"（账号页按 ?id= 定位到具体账号） */
.tw tr.row-hl td { background: var(--brand-soft); }

/* AI 绘图结果：图是随响应一次性返回的 base64，给个上限免得把弹层撑爆 */
.ai-image {
  display: block;
  max-width: 100%;
  max-height: 420px;
  border-radius: var(--r);
  border: 1px solid var(--line);
}

/* 账号列表的密钥格：点一下复制明文（不再放「显示 / 复制」两个按钮） */
.cred-copy {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-bottom: 1px dashed var(--line);
}
.cred-copy:hover { color: var(--brand); border-bottom-color: var(--brand); }

/* 账号表：九列都是"内容宽度就够"的窄列 ——
   ⚠️ 每个 <th> 都写了宽度，浏览器才会按比例分配剩余空间；只要有一列不写宽度，
   它就会把剩余空间**全部**吸走（邮箱 / 密钥 被撑到 200+px，右侧的额度与操作被挤在一起）。
   ⚠️ 操作列左侧留一口气：否则按钮紧贴着 AI 那条进度条，"操作"看起来像挂在额度上。 */
.tw.accounts th:last-child,
.tw.accounts td:last-child { padding-left: 22px; }
/* 超宽屏上给整张表封个顶：比例分配是"一起变宽"，不封顶的话邮箱/密钥在 2K 屏上又会变成 300+px */
.tw.accounts table { max-width: 1440px; }

/* ── 页面顶栏（PanelBar）：标题 · 元信息 · 筛选器+按钮，**一行** ──────────────
   ⚠️ 只有窄屏才允许换行（flex-wrap），正常宽度下它必须是一行 ——
      这就是"把 toolbar 与 tools 合并"的意思。
   ⚠️ 里面的 Field 一律 inline（label 与控件同排）。 */
.panel-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  /* ⚠️ 顶栏**就是一行**（这是使用者的硬要求）：不换行，放不下就横向滚动。
     只有窄屏（手机）才退回换行 —— 那时候横向滚动更难受。 */
  flex-wrap: nowrap;
  overflow-x: auto;
  margin-bottom: 12px;
}
@media (max-width: 720px) {
  .panel-bar { flex-wrap: wrap; overflow-x: visible; }
}
.panel-bar h3 { font-size: 15px; letter-spacing: -0.02em; white-space: nowrap; }
.panel-bar .meta {
  font-size: 12px;
  color: var(--muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.panel-bar .bar-tools {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: nowrap;   /* 与顶栏一致：不换行（否则顶栏会因为它塌成两行） */
  margin-left: auto;   /* 贴右：左侧留给标题与元信息 */
  padding-bottom: 2px; /* 横向滚动条出现时不要贴着控件 */
}
/* ⚠️ 顶栏里的东西**不许被压缩**：`flex-wrap: nowrap` 之后 flex 默认会让子项收缩 ——
   按钮被压窄就会把中文标签折成两行（一行顶栏里出现"两行的按钮"，比换行更难看）。
   所以固定不缩、不折；真放不下时由顶栏横向滚动。 */
.panel-bar > *,
.panel-bar .bar-tools > * { flex: none; white-space: nowrap; }
.panel-bar .bar-tools .field { margin: 0; }
/* 顶栏里的控件小一号：一行放得下更多（输入框默认偏大） */
/* 顶栏里的输入框矮一档（32px）：一行里与 sm 按钮等高，紧凑但不拥挤 */
.panel-bar input, .panel-bar select { height: var(--ctl-h-sm); padding: 0 10px; }
.panel-bar input, .panel-bar select, .toolbar input, .toolbar select { width: auto; }

/* ── 顶栏 / 工具栏里的字段：label 与控件**同排** ───────────────────────────
   ⚠️ 原来是 label 独占一行（控件在下一行）—— 一行工具栏因此变成两行高，
      和旁边的按钮对不齐。这里统一改成同排。
   ⚠️ 三个选择器都要覆盖：`Field inline` 生成的 `.field.inline`、
      顶栏里手写的 `.field`、以及工具栏里的 `.field`。 */
.panel-bar .field,
.toolbar .field,
.field.inline {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
}
.panel-bar .field > label,
.toolbar .field > label,
.field.inline > label {
  margin: 0;
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}

/* 弹层里的页签正文：**全部挂载**、非当前页只隐藏（见 apps.js 里为什么这么做）
   ⚠️ 用 display:none 而不是"条件渲染"：切页签不重建组件，正在编辑的内容不丢。 */
.tab-hide { display: none; }
/* ══════════════════════════════════════════════════════════════
   弹层里的表单排版（新增 / 编辑 Workers·Pages，2026-11-03 重排）
   ══════════════════════════════════════════════════════════════
   ⚠️ 这一段的类名**早就写在 apps.js 里了**，但样式表里一直**没有对应的规则**
      （deploy-grid / chips-field / chip / chip-x / binding-row）——
      所以目标账户的 chips、绑定行、多列表单行全是"裸奔"的：
      chips 没有底色、绑定行只是一堆上下叠着的默认控件、
      .deploy-grid 根本没分列（每个字段都独自占满一行）。
      使用者的原话是"页面也太粗糙不美观，有些控件可以一行显示多个"。
   ──────────────────────────────────────────────────────────────
   约定：
   1. **短字段一行放多个**：用 .deploy-grid（自适应）或它的 .c2 / .c3 修饰类明确列数；
      格里的 .field 不再自带下边距（行距交给 grid 的 gap）；
   2. **弹层里的原生控件统一外观**：.chips-field / .binding-row 里的 input / select
      没有 .field 包着，以前是浏览器默认长相 —— 现在按同一套底色、圆角、聚焦环来；
   3. **正文里的主操作钉在底部**：.modal-body .form-actions 用 position: sticky，
      滚动时始终可见（配合外壳的"只有正文滚"，长表单也一眼看得到能不能提交）；
   4. **次要设置默认收起**：用原生 details.disclose，别把"一年改一次"的字段摆在第一屏。
   ══════════════════════════════════════════════════════════════ */

/* 一行多个字段（自适应列宽；放不下自动折行） */
.deploy-grid { display: grid; gap: 12px 14px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); align-items: start; }
.deploy-grid.c2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.deploy-grid.c3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.deploy-grid .field { margin-bottom: 0; }
@media (max-width: 620px) {
  .deploy-grid, .deploy-grid.c2, .deploy-grid.c3 { grid-template-columns: 1fr; }
}

/* 目标账户：chips + 「+ 添加」触发下拉（全选收在下拉里）+ 清空 */
.chips-field { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
/* 空态也用一个 chip 占位：看着像"待填的槽位"，而不是一小段灰色说明贴在控件左边 */
.chip.empty { background: var(--paper); color: var(--muted); padding-right: 10px; box-shadow: inset 0 0 0 1px var(--line); }
.chips-field select { width: auto; min-width: 148px; }
/* 「+ 添加」触发也做成 chip（与「还没选账号」同款虚线边，2026-11-04 使用者要求） */
.chip.add {
  padding: 3px 10px; border: none; background: #fff; color: var(--brand-deep);
  box-shadow: inset 0 0 0 1px var(--brand-line); cursor: pointer;
  font: inherit; font-size: 12px; line-height: 1.5;
}
.chip.add:hover { background: var(--brand-soft); }
.chip.add:disabled { opacity: 0.5; cursor: not-allowed; }
.chip {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 3px 4px 3px 10px; border-radius: 999px;
  background: var(--brand-soft); color: var(--brand-deep);
  font-size: 12px; line-height: 1.5;
}
.chip-x {
  border: none; background: none; color: inherit; cursor: pointer;
  font-size: 13px; line-height: 1; padding: 0 5px; border-radius: 50%;
}
.chip-x:hover { color: var(--danger); }
.chip-x:disabled { opacity: 0.4; cursor: not-allowed; }
/* 「+ 添加」触发下拉（2026-11-04）：触发按钮在 Field 标签行（field-head）或 chips 行里；
   菜单是绝对定位的浮层，盖在 chips 上方，点外面 / Esc 关。 */
.field-head { display: flex; align-items: center; gap: 8px; }
.field-head > label { margin-bottom: 0; } /* 独占一行时 label 自带 6px 下边距，同排时收掉 */
.add-select { position: relative; display: inline-flex; }
.add-select > .btn { flex: none; }
.add-menu {
  position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 168px; max-height: 240px; overflow-y: auto;
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.25);
  padding: 4px; z-index: 5;
}
.add-menu-item {
  display: block; width: 100%; text-align: left;
  border: none; background: none; cursor: pointer;
  padding: 7px 10px; border-radius: 7px;
  font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.add-menu-item:hover { background: var(--brand-soft); color: var(--brand-deep); }
.add-menu-item.all { color: var(--brand); font-weight: 600; border-bottom: 1px solid var(--line); border-radius: 7px 7px 0 0; margin-bottom: 2px; padding-bottom: 8px; }
.add-menu-item.all:hover { color: var(--brand-deep); }

/* 绑定行 / cron 行：一行放「名字 + 值 + 删除」（原来是上下叠着的裸控件）。
   ⚠️ 三处都用 .binding-row，但子元素个数不同（绑定行 3~4 个、cron 3 个、Pages 绑定 3 个）——
      所以这里用 flex + "首个固定宽度、其余分掉剩余空间"，不做 grid 模板。
   ⚠️ 2026-11-04：类型标签不再占固定槽位（类型由分组标题表达），
      只在真有问题时挂一个小标签（值不可读 / 填不全），按内容宽度排。 */
.binding-row { display: flex; align-items: center; gap: 8px; }
.binding-row > :first-child { flex: 0 0 190px; }
.binding-row > :not(:first-child) { flex: 1 1 0; min-width: 0; }
/* 环境变量行的类型下拉：定宽，别跟着值输入框一起被拉长 */
.binding-row > .bind-type { flex: 0 0 112px; }
/* 行尾动作（删除 / 保存+取消）：不收缩、不占剩余宽度 */
.binding-row > .bind-actions { flex: none; display: flex; align-items: center; gap: 8px; }
.binding-row > .btn, .binding-row > .tag, .binding-row > code, .binding-row > b { flex: none; }
.binding-row input, .binding-row select { width: 100%; }
.binding-row .cred-cell { flex-wrap: nowrap; }
.binding-row + .binding-row { margin-top: 8px; }
@media (max-width: 620px) {
  .binding-row { flex-wrap: wrap; }
  .binding-row > :first-child { flex: 1 1 100%; }
}

/* 弹层里"没套 .field"的原生控件：统一按表单控件的样子来 */
.chips-field select, .chips-field input,
.binding-row input, .binding-row select {
  height: 36px; padding: 0 10px;
  border: 1px solid var(--line); border-radius: var(--ctl-r);
  background: #fff; color: var(--ink); outline: none; font-size: 13px;
  transition: box-shadow 0.2s var(--ease), border-color 0.2s;
}
.chips-field select:focus, .chips-field input:focus,
.binding-row input:focus, .binding-row select:focus {
  border-color: var(--brand-line); box-shadow: 0 0 0 2px var(--brand-line);
}

/* 可折叠的次要设置（原生 details：不涉及"更新期间挂载组件"那类坑） */
.disclose { border: 1px solid var(--line); border-radius: var(--r-sm); margin-bottom: 14px; }
.disclose > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 8px;
  padding: 9px 14px; font-size: 12.5px; color: var(--ink2);
}
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary::before { content: "▸"; color: var(--muted); font-size: 11px; }
.disclose[open] > summary::before { content: "▾"; }
.disclose > summary:hover { color: var(--brand-deep); }
.disclose[open] > summary { border-bottom: 1px solid var(--line); }
.disclose-body { padding: 12px 14px 2px; }
.disclose-body .field:last-child { margin-bottom: 0; }

/* 正文里的主操作：钉在弹层底部（配合外壳的"只有正文滚"）。
   ⚠️ 只认 `.sticky-actions` 这个类，**不是所有** `.form-actions` ——
      正文里还有别的 form-actions（"+ 添加绑定"、"仍然保存"这类内联行），
      全都 sticky 会在底部叠成一摞互相盖住（实测一眼就能看出来）。
   ⚠️ bottom 取负的正文内边距值（-20px），让它正好贴在卡片底边、不留一道白缝。
   ⚠️ **不加阴影**（2026-11-03 使用者要求去掉）：那颗 `box-shadow` 在浅底上看着像一条脏边。
      白色背景保留 —— 它是"滚上去的内容不要从按钮底下透出来"的唯一保证。 */
.modal-body .form-actions.sticky-actions {
  position: sticky; bottom: -20px;
  background: #fff; padding: 12px 0 20px; margin-top: 14px;
}

/* 代码编辑器：给个上限，长文件在框里自己滚（原来 rows=14 直接把弹层顶出一屏） */
.code-edit { max-height: 250px; }
/* ── 表单行里的短控件（2026-11-03 第一轮实测后补）────────────────
   ⚠️ 实测发现：`.deploy-grid` 里的单选组（部署类型 / 代码来源）在 960 宽的弹层里
      会被 flex 压缩，中文标签"文件"被压成"文\n件"、"直接写 / 粘贴"折成两行 ——
      一行里放多个控件的前提是**标签不折行**，所以这里给网格内的 `.checks label`
      加 `nowrap` + `flex: none`（只作用于网格里；别处的 `.checks` 还有长标签需要折行，
      全局 nowrap 会让长标签顶破容器）。 */
.deploy-grid .checks { gap: 14px; }
.deploy-grid .checks label { flex: none; white-space: nowrap; }
/* 网格里同排的左邻是输入框（40px 高）：单选/多选/下拉没有边框盒子，不撑到同样高度就会显得"飘在上面" */
.deploy-grid .checks, .deploy-grid .chips-field { min-height: 40px; align-items: center; }
/* ── 折叠区的观感（2026-11-03 第一轮实测后调）─────────────────
   ⚠️ 实测发现：`.disclose` 原来只画了一根边框，收起来时看着**像一个空输入框**
      （截图里被描述成"一个空的 text input"）。折叠区得一眼看出是"一节可展开的设置"：
      收起时给一层浅底（paper），展开后底色变白、标题下加分隔线。 */
.disclose { background: var(--paper); border-color: transparent; }
.disclose[open] { background: #fff; border-color: var(--line); }
.disclose[open] > summary { border-bottom: 1px solid var(--line); }
/* 顶栏里的「返回」槽（2026-11-03 使用者要求：返回按钮统一在标题左侧）
   ⚠️ 返回是**页面级**的定位动作，不是这一页的工具 —— 所以它不进 `.bar-tools`，
      而是由 `PanelBar` 的 `back` 槽渲染在标题左边（`.panel-bar > *` 已经保证了
      "不缩、不折行"，这里只是把这一层的对齐写明确）。 */
.panel-bar .bar-back { display: flex; align-items: center; flex: none; }
/* ── 单选组也要防折行（2026-11-03 实测补）─────────────────────
   ⚠️ 实测：`.deploy-grid` 里我给 `.checks label` 加了 nowrap，但**不在网格里**的
      单选组（比如新建弹层的「代码来源」）照样被压折行 ——「文件」两个字被压成
      「文 / 件」两行。给这一类加个显式的 `.checks.nowrap`：短标签不折行、不被压。
   ⚠️ 不能全局给 `.checks label` 加 nowrap：`.checks` 里还有很长的说明式标签
      （「保留已有绑定（取消 = 清空这个脚本的全部绑定）」），nowrap 会让它顶破容器。 */
.checks.nowrap label { flex: none; white-space: nowrap; }
/* ── 总览的统计面板（资源构成 / 各账号负载）────────────────────
   2026-11-03：使用者要去掉「去账号页看额度」那个按钮，改成"多加入一些统计面板"。
   这两个面板**只用这次总览已经拿到的数**（不再打上游），回答两个新问题：
   "资源主要在哪一类"（构成）与"在哪个账号"（负载）。
   ⚠️ 条的颜色仍只用品牌绿 —— 全站只有一个色相，长度本身就表达了大小。 */
.ov-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
@media (max-width: 900px) { .ov-stats { grid-template-columns: 1fr; } }
.ov-panel { min-width: 0; }
.ov-panel h4 {
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 10px;
}
.ov-rows { display: flex; flex-direction: column; gap: 9px; }
.ov-rows .r {
  display: grid; grid-template-columns: minmax(0, 104px) minmax(0, 1fr) 46px 38px;
  gap: 10px; align-items: center; font-size: 12.5px;
}
.ov-rows .k {
  color: var(--ink2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-rows .track { height: 8px; border-radius: 99px; background: var(--paper); overflow: hidden; }
.ov-rows .track i {
  display: block; height: 100%; border-radius: 99px; background: var(--brand);
  transition: width 0.3s var(--ease);
}
.ov-rows .v { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
.ov-rows .p { text-align: right; font-variant-numeric: tabular-nums; font-size: 11.5px; color: var(--muted); }
.ov-panel .foot { margin-top: 10px; font-size: 11.5px; color: var(--muted); line-height: 1.6; }
/* ── 新增应用的第一行：名称 / 部署类型 / 目标账户 并排（2026-11-03 使用者要求）──
   ⚠️ 顺序就是这一行的顺序（名称 → 类型 → 目标账户），不许再调回"类型在最前"。
   ⚠️ 三格的宽度是**手动配的**（auto-fit 会把目标账户那格挤窄、chips 一行只放得下一个）：
      名称 1.1fr · 类型 0.75fr（两个单选，够了）· 目标账户 1.5fr（chips + 清空；「+ 添加」在标签行，全选收进下拉）。
      窄屏（≤760）退回一列，否则三样挤在一行会各自折成两行。 */
.deploy-grid.head-row {
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.75fr) minmax(0, 1.5fr);
}
@media (max-width: 760px) {
  .deploy-grid.head-row { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════
   编辑弹层（Worker / Pages）的内部版式
   ══════════════════════════════════════════════════════════
   2026-11-04 使用者反馈"编辑弹窗布局很乱"。根因是**弹层里又摆了整张卡片**：
   「自定义域」「路由」「同步」「设置」「环境变量」「域名」「部署」「JS 混淆工具」
   每个都是一张带底色/圆角的 Card，和弹层本身那张大卡套在一起（卡片套卡片）；
   再加上两层同样式的页签（编辑器页签 + 绑定类型页签），层级完全读不出来。

   现在的版式：
   · `.edit-body`：纵向堆叠，间距统一；
   · `.edit-meta`：顶部一行灰字（账号 / 最后修改 / 默认域）；
   · `.edit-sec`：**一条上分隔线 + 小标题**，没有卡片底色 —— 弹层里只有一层"卡"；
   · `.bind-groups`：绑定 / 环境变量按组堆叠（组标题 + 右侧「+ 添加」，组内一行一条）；
   · `.edit-foot`：删除压在左下、保存在右下（钉底）。 */
.edit-body { display: flex; flex-direction: column; gap: 16px; }
/* 一个页签里有多节时的堆叠（域名 = 子域 + 自定义域 + 路由；项目信息 = 设置 + 混淆工具）。
   ⚠️ 不能用 `.stack`（那是表格里"两行文字"的 2px 间距），否则两节几乎贴在一起。 */
.edit-sections { display: flex; flex-direction: column; gap: 16px; }
/* ── 绑定 / 环境变量分组（2026-11-04 使用者要求：与参考图同款版式）──────
   一组 = 组标题（左）+「+ 添加」（右）+ 组内一行一条（变量名 / 值 / 删除）。
   ⚠️ 组间 16px、组内行间 8px：组与组要一眼分开，同一组的行要看起来是一件事。
   ⚠️ 组标题比正文小标题（.edit-sec-head h4）低一档，别和页签正文的节标题抢层级。 */
.bind-groups { display: flex; flex-direction: column; gap: 16px; }
.bind-group { display: flex; flex-direction: column; gap: 6px; }
.bind-group-head { display: flex; align-items: center; gap: 10px; min-height: 30px; }
.bind-group-head > b { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.bind-group-head > .btn { margin-left: auto; }
.edit-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.edit-meta .mono { font-size: 11.5px; }
.edit-sec { border-top: 1px solid var(--line); padding-top: 14px; }
/* 页签正文里第一节不画上分隔线：它上面紧跟着页签，划一刀会多出一条空带 */
.edit-body .tabs { margin-bottom: 0; }
.edit-body .tab-hide > .edit-sec:first-child, .edit-body > div > .edit-sec:first-child { border-top: none; padding-top: 0; }
.edit-body .tab-hide > .edit-sections > .edit-sec:first-child { border-top: none; padding-top: 0; }
.edit-body > .edit-sec:first-of-type { border-top: none; padding-top: 0; }
.edit-sec-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; min-height: 28px; }
.edit-sec-head > h4 { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.edit-sec-tools { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* 弹层里的只读键值表（账号子域 / 脚本域名 / 生产分支…）不要拉满整宽：
   标签靠左、值甩到 1000px 外，读起来像两个不相干的字段。 */
.edit-sec .kv { max-width: 660px; }

/* 弹层页脚：删除在左下、保存在右下（2026-11-04 使用者要求）。
   之前是「删除这个脚本」红字 + 「删除脚本」红按钮并排，看起来像两个删除按钮。
   现在左侧只有一颗 ghost-danger 的删除（点两次才执行，第二次时浮出代价说明），
   右侧是当前页签登记的「保存」；没有登记保存的页签就只留删除。
   ⚠️ 钉在弹层底部（与 .sticky-actions 同一套做法），正文再长也不会看不到保存。 */
.edit-foot {
  position: sticky; bottom: -20px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: #fff; border-top: 1px solid var(--line);
  padding: 12px 0 20px; margin-top: 4px;
}
.edit-foot > .cred-cell { margin: 0; }
.edit-foot > .btn { margin-left: auto; }
/* ── 弹层里的资源行（域名 / 路由 / 自定义域）──────────────────────
   2026-11-04：这几行原来是 `.cred-cell`，里面的 `.mono` 是 `flex:1 1 0` —— 域名被拉成
   一整条"灰色输入框"，一行里只剩名字和右端的按钮，看着像表单而不是列表。
   现在：名字按内容收缩（超长才截断）、`zone：…` 吃剩余并截断、动作贴右，行间一条细线。 */
.list-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.list-row:last-of-type { border-bottom: none; }
.list-row > .mono { flex: 0 1 auto; max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row > .dim { flex: 1 1 0; min-width: 0; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row > .cred-cell { flex: none; margin: 0 0 0 auto; }
/* 状态条：一行里把「域名 + 状态 + 账号子域」说完（原来是三行 label/value，
   标签贴左、值甩到最右，中间一大片空白） */
.kv-strip { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.kv-strip > .mono { max-width: 380px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kv-strip > .dim { margin: 0; }

/* 「加一条」行：输入吃满、按钮定宽；裸 input 在这里要有和控件一致的外观 */
.row-add { display: flex; align-items: center; gap: 8px; margin-top: 10px; max-width: 560px; }
.row-add > input, .row-add > select {
  flex: 1 1 0; min-width: 0; height: var(--ctl-h-sm); padding: 0 12px;
  border: 1px solid var(--line); border-radius: var(--ctl-r-sm); background: #fff;
  outline: none; font-size: 13px;
}
.row-add > input:focus, .row-add > select:focus { border-color: var(--brand-line); box-shadow: 0 0 0 2px var(--brand-line); }
.row-add > .btn { flex: none; }

/* 定宽的小字段（生产分支这种短值） */
.field.w320 { max-width: 320px; }

/* 弹层里的折叠区：一条细线 + 灰字，不再是一整块灰底"空输入框" */
.edit-body .disclose {
  background: transparent; border: none; border-bottom: 1px solid var(--line);
  border-radius: 0; margin-bottom: 0;
}
.edit-body .disclose > summary { padding: 10px 0; color: var(--muted); }
.edit-body .disclose[open] > summary { border-bottom: none; color: var(--ink2); }
.edit-body .disclose-body { padding: 4px 0 14px; }
