:root {
  --bg: #0f1420;
  --bg-panel: #171d2b;
  --bg-hover: #1f2737;
  --border: #2a3346;
  --text: #e6eaf2;
  --text-dim: #8b95a8;
  --accent: #4f8cff;
  --accent-dim: rgba(79, 140, 255, 0.14);
  --ok: #37c98b;
  --err: #e5484d;
  --warn: #e0a53a;
  --radius: 10px;
}
* { box-sizing: border-box; }
/* `hidden` 属性必须说话算数。UA 样式里的 `[hidden]{display:none}` 优先级极低，
   任何带 `display` 的作者规则都会把它顶掉，元素就变成"藏了却还在页面上"
   ——2026-10-08 实测的线上问题正是这一类：手机上点开行内编辑时，旧值所在的
   `.cell-view` 被移动端规则改成 inline-block，于是 `hidden=true` 也不消失，
   和 16px 的输入框同时显示同一段文字（描述列最长，最显眼）。
   在这里统一兜底，凡是 `display` 与 `hidden` 打架都以 `hidden` 为准。
   （此前 `.modal[hidden]` 那条例外规则就是为同一类问题打的补丁，已被本规则覆盖。） */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
/* 关掉手机浏览器的「按块字体放大」（iOS 的 text-size-adjust:auto、各家 Android
   浏览器的字体增强）。它按**容器宽度**算放大倍数，会让页面里宽度不同的文字块
   被放大成不同字号——用户 2026-10-14 实测反馈的「描述列里 batai/any2txt 两行
   字比别行大」就是它干的（详见下面 .cell-view 那条）。写明 100% 即等于「不要动
   我设定的字号」，桌面浏览器本来也不做这件事，属无害声明。 */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  font: 14px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
        "Microsoft YaHei", sans-serif;
  background: var(--bg); color: var(--text);
  min-height: 100vh;
}
a { color: var(--accent); text-decoration: none; }
button {
  font: inherit; border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
  padding: 8px 16px; background: var(--bg-hover); color: var(--text);
}
button:hover { border-color: var(--accent); }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
button.danger { color: var(--err); border-color: rgba(229,72,77,.4); }
button.ghost { background: transparent; color: var(--text-dim); }
button:disabled { opacity: .5; cursor: not-allowed; }
input, select, textarea {
  font: inherit; width: 100%; padding: 9px 11px; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px; outline: none;
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); }
label { font-size: 13px; color: var(--text-dim); }
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 22px; border-bottom: 1px solid var(--border); background: var(--bg-panel);
}
/* 品牌只保留纯文字：顶部不再放图标（emoji/图片），把宽度让给右侧操作按钮 */
.brand { display: flex; align-items: center; gap: 10px; font-size: 16px; font-weight: 700; }
.top-actions { display: flex; align-items: center; gap: 10px; }
.user-chip { color: var(--text-dim); font-size: 13px; }
/* 设置页顶部的「当前账号」：手机端顶栏不显示用户名（见下方 640px 规则），
   这里是查看自己是谁的地方。 */
.whoami { color: var(--text-dim); font-size: 13px; margin: -6px 0 16px; }
.container { max-width: 1000px; margin: 0 auto; padding: 28px 22px; }
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; margin-top: 22px; }
.card {
  display: flex; flex-direction: column; gap: 10px; padding: 20px;
  background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--text); transition: transform .12s, border-color .12s;
}
.card:hover { transform: translateY(-3px); border-color: var(--accent); }
.card .icon { font-size: 30px; }
.card .name { font-size: 17px; font-weight: 700; }
.card .desc { font-size: 13px; color: var(--text-dim); }
.card.locked { opacity: .5; cursor: not-allowed; pointer-events: none; }

.panel { background: var(--bg-panel); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px; }
.field { margin-bottom: 16px; }
.field > label { display: block; margin-bottom: 6px; }
.checkbox { display: flex; align-items: center; gap: 8px; }
.checkbox input { width: auto; }
.hint { font-size: 12px; color: var(--text-dim); margin-top: 4px; }
.msg { font-size: 13px; min-height: 20px; margin-top: 10px; }
.msg.err { color: var(--err); }
.msg.ok { color: var(--ok); }

/* 登录页 */
.auth-wrap { max-width: 380px; margin: 10vh auto; padding: 0 20px; }
.auth-wrap h1 { text-align: center; font-size: 22px; }
.auth-wrap .sub { text-align: center; color: var(--text-dim); margin: -8px 0 22px; }

/* 管理面板 */
.tabs { display: flex; gap: 6px; margin-bottom: 18px; border-bottom: 1px solid var(--border); }
.tabs button {
  background: transparent; border: none; border-bottom: 2px solid transparent;
  border-radius: 0; padding: 10px 16px; color: var(--text-dim);
}
.tabs button.active { color: var(--text); border-bottom-color: var(--accent); }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border); }
th { color: var(--text-dim); font-weight: 600; }
.row-actions { display: flex; gap: 6px; }
.tool-check { display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 8px; margin-bottom: 8px; }
.tool-check input { width: auto; }
.tool-check .t-name { font-weight: 600; }
.tool-check .t-url { color: var(--text-dim); font-size: 12px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.form-grid .full { grid-column: 1 / -1; }

/* ── 行内编辑（工作区通用要求 §8：点条目即编辑、编辑即自动保存）────────
   单元格结构：<td class="edit-cell" data-field data-kind data-value>
                 <span class="cell-view">显示值</span>
                 <input|select class="cell-input" hidden>
               </td>
   未编辑时只显示 .cell-view（观感与普通表格一致，靠虚线下划线提示可点），
   点开后显示 .cell-input，失焦/回车即写盘。 */
.edit-cell { cursor: pointer; }
.edit-cell .cell-view { border-bottom: 1px dashed transparent; }
.edit-cell:hover .cell-view { border-bottom-color: var(--border); }
.edit-cell .cell-input { min-width: 0; }
.edit-cell[data-field="icon"] .cell-input { min-width: 3.2em; max-width: 6em; }
.edit-cell[data-field="is_admin"] select.cell-input,
.edit-cell[data-field="is_active"] select.cell-input { min-width: 6.5em; }
.edit-cell[data-field="default_remember_days"] select.cell-input { min-width: 6em; }
.edit-cell[data-field="api_key"] .cell-input { min-width: 12em; }
.edit-cell[data-field="password"] .cell-input { min-width: 11em; }
.edit-cell[data-field="slug"] .cell-input { min-width: 8em; }
/* 模型清单：多行文本，一行一个模型名（编辑态给足高度，长清单不用横向拖） */
.edit-cell[data-field="models"] .cell-input {
  min-width: 16em; min-height: 5.6em; resize: vertical;
  font: inherit; line-height: 1.5;
}
.edit-cell[data-field="models"] .cell-view { color: var(--text-dim); }
.edit-cell[data-field="models"] .cell-view code { color: var(--text); }
/* 服务端明确拒绝（如重名）时闪一下红色内边框，配合区域提示说明原因 */
.edit-cell.cell-rejected .cell-input { border-color: var(--err); }
.dim { color: var(--text-dim); }
.err-text { color: var(--err); }
.sub-h { margin: 26px 0 10px; }
.badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; background: var(--accent-dim); color: var(--accent); }
.badge.admin { background: rgba(224,165,58,.15); color: var(--warn); }
.site-footer { text-align: center; padding: 18px 0 26px; font-size: 12px; color: var(--text-dim); }

/* ── LLM 供应商列表 ─────────────────────────────────────────────
   只有 5 列（名称 / Base URL / API Key / 状态 / 操作），模型的编辑收进弹窗，
   列表只显示条数。名称列是编辑入口：悬停给虚线下划线（同行内编辑一致的可点提示）。 */
td.p-name { cursor: pointer; }
td.p-name .p-name-txt { font-weight: 600; border-bottom: 1px dashed transparent; }
td.p-name:hover .p-name-txt { border-bottom-color: var(--accent); }
td.p-name .hint { margin-top: 2px; }
code.p-url { color: var(--text); font-size: 12px; word-break: break-all; }

/* 启用 / 停用开关：改完立即生效，不用确认 */
.switch { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; color: var(--text); }

/* ── 表格里的控件一律单行（2026-09-30 修「控件文字折行」）─────────────
   768px 这类中间宽度下（既没到 ≤640px 的卡片布局，容器又只有 ~680px），
   列会被挤窄，于是：「测试」被折成「测/试」两行、开关的「启用」掉到复选框
   下一行、「重新生成」也被折行。实测证据：开关高 42px、按钮高 60px（单行只需 ~33px）。
   这里统一三条：
     1. 控件文字不换行；
     2. flex 子项（按钮 / 复选框）不许被压缩（flex: none），否则宽度不足时
        浏览器宁可折行也不溢出；
     3. 挤不下时让整张表在 .table-scroll 里横向滚动（§2 允许），
        而不是把控件揉成一团——所以允许表格超出容器，别用固定列宽硬塞。 */
.row-actions { flex-wrap: nowrap; }
.row-actions > button { flex: none; white-space: nowrap; }
.switch { white-space: nowrap; }
td.p-name .p-name-txt, td.p-name .badge { white-space: nowrap; }
/* 操作列 / 令牌列的小按钮：紧凑但仍是可点区域（手机端由 ≤640px 规则抬到 40px） */
td.row-actions button,
td.la-token button { flex: none; white-space: nowrap; padding: 5px 10px; font-size: 12px; }
.switch input { width: auto; margin: 0; flex: none; accent-color: var(--accent); }

/* 应用接入表：「令牌」格显示前缀 + 两个显式按钮（重新生成 / 撤销）。
   不用"点格子即编辑"——凭据变更是显式动作，且挨着「撤销」容易点错。 */
td.la-token code { color: var(--text); }
td.la-token .row-actions { gap: 4px; }

/* ── 弹窗（新增 / 编辑供应商、生成接入令牌）──────────────────────
   不用 <dialog>：老一些的移动端浏览器（iOS < 15.4）不支持 showModal()。
   遮罩 + 卡片自己实现，行为哪儿都一样。 */
.modal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(8, 11, 18, .68);
}
/* 关闭态由 `hidden` 属性控制，上面那条全局 `[hidden]{display:none!important}` 已经兜住
   （这里原先有一条 `.modal[hidden]{display:none}`，是为同一类"display 顶掉 hidden"打的
   局部补丁，全局规则生效后已多余，2026-10-08 删掉）。 */
.modal-card {
  width: min(620px, 100%); max-height: 88vh; overflow-y: auto;
  padding: 20px; background: var(--bg-panel);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.modal-card h4 { margin: 0 0 4px; font-size: 16px; }
.modal-actions {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 10px; margin-top: 14px;
}
.modal-actions .spacer { flex: 1 1 auto; }
/* 一行里放输入框 + 若干小按钮（如 API Key 的「替换 / 清除」） */
.field-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.field-row input { flex: 1 1 12em; }
.field-row button { flex: none; }
@media (max-width: 640px) {
  .form-grid { grid-template-columns: 1fr; }
  .topbar { flex-wrap: wrap; gap: 8px; }
}

/* ── 表格横向滚动容器 ───────────────────────────────────────────
   移动端表格天然比视口宽（如用户表 6 列约 428px），直接放在 .panel 内会被
   裁剪并撑破页面。这里给每个表格加一层可横向滚动的容器：表格保留完整列宽
   （表格在窄屏压成 320px 反而不可读、单元格挤成竖排），由容器滑动查看。
   电脑端容器宽度足够，表格自适应填满，外观与改动前完全一致。 */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
.table-scroll > table { margin-bottom: 0; }

/* ── 移动端增强 ─────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* 顶栏：手机上只留「品牌 + 操作按钮」一行。
     用户名（.user-chip）不再显示——它原来占满一整行白费空间；
     自己的账号在「设置」页顶部（.whoami）与「管理后台 → 用户管理」里都能看到。 */
  .topbar { padding: 12px 14px; }
  .brand { font-size: 15px; }
  .top-actions { flex-wrap: wrap; gap: 8px; }
  .user-chip { display: none; }
  /* 三个按钮（设置 / 管理后台 / 退出）要在 320px 屏上与品牌同排不换行 */
  .top-actions button { font-size: 13px; white-space: nowrap; }

  /* 容器与面板内边距收紧，把宽度让给内容 */
  .container { padding: 18px 14px; }
  .panel { padding: 16px 14px; }

  /* 标签栏：窄屏横向滑动，不换行不挤压 */
  .tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    flex-wrap: nowrap;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs button { flex: 0 0 auto; padding: 10px 12px; font-size: 13px; }

  /* 表格：保证最小可读列宽，超出部分由 .table-scroll 滑动 */
  table { min-width: 520px; }
  th, td { white-space: nowrap; }

  /* LLM 供应商列表：手机端转成卡片（每行一块），不横向滚动。
     只有 5 列且操作按钮有 3 个，硬塞进 520px 会把「删除」推到屏幕外。 */
  #lp-table { min-width: 0; }
  #lp-table thead { display: none; }
  #lp-table tr { display: block; padding: 12px 2px; border-bottom: 1px solid var(--border); }
  #lp-table td { display: block; border: none; padding: 2px 0; white-space: normal; }
  #lp-table td.p-name { font-size: 15px; padding-bottom: 4px; }
  #lp-table .row-actions { margin-top: 8px; flex-wrap: wrap; }
  /* 状态开关：整行可点，命中区抬到 40px（§2 触控目标） */
  #lp-table .switch { min-height: 40px; padding: 8px 0; }
  #lp-table .switch input { width: 20px; height: 20px; }

  /* 应用接入表：同样转卡片（与上面一致，窄屏不横向滚动） */
  #la-table { min-width: 0; }
  #la-table thead { display: none; }
  #la-table tr { display: block; padding: 12px 2px; border-bottom: 1px solid var(--border); }
  #la-table td { display: block; border: none; padding: 2px 0; white-space: normal; }
  #la-table td:first-child { font-size: 15px; }
  #la-table td.la-token { padding-top: 6px; }
  #la-table td.la-token .row-actions { flex-wrap: wrap; }
  #la-table td.la-token button { min-height: 40px; }

  /* 接入令牌弹窗里的令牌串较长：允许换行，别把弹窗撑破 */
  #la-token-value { font-size: 13px; }

  /* 登录表单不要贴边，且避免被移动端键盘遮挡 */
  .auth-wrap { margin: 6vh auto; }

  /* 模型顺序行：窄屏纵向铺开，选择框与输入框占满整行 */
  #la-order > div > select,
  #la-order > div > input { max-width: 100% !important; flex: 1 1 100%; }

  /* 「设置 / 管理后台 / 退出」等按钮的可点区域更贴合触控；
     左右内边距收到 12px，保证 320px 窄屏上三个按钮仍与品牌同一行（不另起一行） */
  .top-actions button { padding: 9px 12px; }

  /* 复选类字段：整行都可点，命中区从 13px 提升到整行高度 */
  .field.checkbox > label,
  .tool-check > label { display: flex; align-items: center; gap: 10px; padding: 6px 0; }
  .field.checkbox input,
  .tool-check input { width: auto; flex: none; }

  /* 触控友好：复选框本体放大（视觉与命中区同步提升） */
  input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); }

  /* 行内编辑：把可点区域从「文字行高」抬到 ≥40px，手机上好点中（§2）。
     这里必须是 `block`，**不能**用 `inline-block`：inline-block 是按内容收缩的块，
     同一列每行的块宽都不一样（实测 185 / 315 / 381 / 406 / 437px），而手机浏览器的
     「按块字体放大」正是按容器宽度算倍率的 —— 于是描述最长、超过屏宽的那两行
     （batai 406px、any2txt 437px）被单独放大，表现为「有些行的字明显更大，
     中英文一起变大」，桌面端该元素是 inline（没有块盒）所以从不复现。
     改成 block 后每行的块宽 = 列宽，各行完全一致，放大即使发生也是全表一致。
     2026-10-14 用户实测反馈，判据见 .responsive-audit/check_admin_mobile.py。 */
  .edit-cell .cell-view { display: block; padding: 8px 0; }

  /* 输入框字号 ≥16px（§2）：14px 会让 iOS 聚焦时自动放大页面，
     行内编辑尤其明显（点一下整页被缩放）。 */
  input, select, textarea { font-size: 16px; }

  /* 触控目标 ≥40px（§2）：默认内边距只有约 38px 高，手机上偏小 */
  button { min-height: 40px; }
}
