/* ─────────────────────────────────────────────
   文档页：接口条；模型管理页：模型表格 + 映射弹窗；网关 Key 页：Key 表格

   三个页面的样式同住一个文件，是因为它们共用同一套表格与面板骨架
   （.models-table 是模型管理与网关 Key 共用，见下方注释）。
   **接口条（.ep-*）R8 起住在「文档」页**：它原来在「网关 Key」页，
   整块搬走后这些规则一字未改 —— 它们没有按 [data-page] 收窄（CSS 本来就是
   全局加载的），所以换页面不需要动样式。这里只更正文件头的描述。
   ───────────────────────────────────────────── */

/* ─── 接口条：纵向列表，一行一条端点 ───
   （Base URL / 三种对话协议 / 模型列表；替代原「一行三段」布局 ——
   端点从 3 条涨到 5 条后，横排会把每条压到放不下完整 URL）
   每行两排：上排「名字 + 用途」，下排「方法+URL + 复制」。
   URL 独占一行才放得下完整地址，也不与说明抢宽度。 */
.ep-panel .panel-head { padding-bottom: 12px; }

/* 内部横线用 --hairline（比 --border 淡一档）：面板头用的也是它，
   整块看起来才是「一个面板里的分行」而不是几条独立边框叠在一起 */
.ep-list { border-top: 1px solid var(--hairline); }

.ep-row {
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
}
.ep-row:last-child { border-bottom: 0; }
/* 悬停整行淡底：复制按钮在行尾，行够宽时眼球容易跟丢是哪一条 */
.ep-row:hover { background: var(--surface-inset); }

.ep-head { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }

.ep-name { color: var(--text); font-size: 12.5px; font-weight: 600; }

.ep-note { color: var(--text-3); font-size: 11.5px; }

.ep-line { display: flex; align-items: center; gap: 9px; margin-top: 5px; }

.ep-value {
  flex: 0 1 auto;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* 值可选中：想手动挑一段比复制整条更常见（比如只拿路径） */
  user-select: text;
}

/* 分组小标题：把「地址 / 对话协议 / 模型清单」三块视觉上分开，
   否则五条同构的行读起来是一整片 */
.ep-group {
  padding: 7px 16px 6px;
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-inset);
  color: var(--text-3);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
}
/* 工具栏：提供商分段 + 状态分段 + 搜索，排成一行。
   它挂在 .panel-body 上（那条规则是 flex 纵向），这里必须显式改回横向，
   否则三个控件会各占一行并居中 */
/* 状态筛选与搜索框（都住在面板头里，见 index.html 的说明）。
   「搜索框 + 那排按钮」包成一个整体靠右：auto 外边距只给这个整体，不给搜索框自己
   —— 给它自己的话那个 auto 会吃掉全部剩余空间，右边那排按钮被挤到第二行。
   搜索框 240px（模型 ID / 别名一般十几个字符，再宽是白占地方），窄窗口下可压缩到 150px。 */
/* 右侧那排按钮（列设置 + 三颗操作）：不压缩、不内部换行 —— 允许压缩或折行的后果
   是它把自己拆开（按钮分两行），面板头就散了。空间不够时由 .panel-head 自己的
   flex-wrap 把整排挪到下一行。靠右由 .head-actions 自带的 margin-left:auto 完成。 */
.page[data-page="gateway"] .panel-head .head-actions { flex: 0 0 auto; flex-wrap: nowrap; }
/* 搜索框紧跟状态筛选（同为「看这张表」的控件），靠左；240px 是模型 ID / 别名
   能完整显示的宽度，这一页放得下 —— 曾为挤进同一行收到 180，不必了 */
#models-search { flex: 0 0 auto; width: 240px; }

/* ── 面板头两行制（2026-10 重设计）────────────────────────
   原来一行塞下「筛选 + 搜索 + 动作按钮」，没有「这是谁家的表」的上下文。
   现在拆两行：第一行家名 + 统计 + 动作（.mm-titlebar），第二行筛选 + 搜索 +
   批量条（.mm-filters）。两行都通栏（width:100%），窄窗口由 .panel-head 的
   flex-wrap 整体下折。.panel-head 自身的 padding / 分隔线在这里清零 ——
   行距交给两行自己给，与表格的分界交给 .models-table-wrap 的 border-top。 */
.page[data-page="gateway"] .panel-head {
  flex-wrap: wrap;
  gap: 0;
  padding: 0;
  border-bottom: 0;
}
.mm-titlebar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 13px 16px 5px;
}
/* 窄窗口下标题与动作按钮允许各占一行 */
@media (max-width: 900px) { .mm-titlebar { flex-wrap: wrap; row-gap: 6px; } }
.mm-title { display: flex; align-items: center; gap: 9px; min-width: 0; }
.mm-title h2 { margin: 0; font-size: 15px; font-weight: 650; line-height: 1.25; white-space: nowrap; }
/* 统计：三个数加重量、文字用灰档 —— 读的是数字，不是句子 */
.mm-meta { color: var(--text-3); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mm-meta b { font-weight: 600; color: var(--text-2); font-variant-numeric: tabular-nums; }

.mm-filters {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 16px 12px;
}

/* 批量条：勾选后出现在筛选行的带计数小药丸（原「批量操作」孤按钮升级）——
   计数本身是这条存在的原因，写进药丸里比按钮 title 更可读 */
.batch-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 28px;
  flex: 0 0 auto;
  padding: 0 4px 0 11px;
  border: 1px solid var(--primary-bd);
  border-radius: var(--r-pill);
  background: var(--primary-soft);
  color: var(--primary-fg);
  font-size: 12px;
  white-space: nowrap;
}
.batch-chip b { font-weight: 700; font-variant-numeric: tabular-nums; }

/* 刷新说明与逐家结果：常驻在表格下方，不随 toast 消失；有失败才标红 */
/* ─── 「获取模型」弹窗的样式已随迁移移除 ───────────────
   弹窗迁到组件库后（ui-islands/src/islands/models-fetch-modal.tsx），这里原先那套
   .fm-tools / .fm-search / .fm-wrap / #fm-table-* .f-* 列宽 / .fm-table .detail /
   .modal-narrow 全部失去消费者，一并删除。搬走的口径（都是逐字照抄的，改之前先读
   岛里对应的注释）：

   · 列宽百分数：从「按 id 写的 CSS」搬到岛里 `<col>` 的 Tailwind 类上（列宽层
     table-columns.js 仍按表 id + col 的 data-col 定位，所以表 id 保留了）；
     六列 15/25/10/7/24/19、三列 8/74/18 —— 「更新日期」要给足 19% 才装得下完整
     时间戳（只给日期会在同一天两次刷新间分不出先后），「模型来源」25% 由邮箱长度
     定（emailAsName 的家以邮箱为主名）。
   · 滚动盒高度上限 min(48vh, 460px)、工具行「汇总在左、动作在右」的分工、
     窄形态 552px / 宽形态 920px：都改由岛里的 Tailwind 类表达。
   · 「模型来源」下拉：旧实现靠原生 select 当宽度锚（.select-shell 撑满单元格），
     新实现直接用组件库的 Select，宽度由单元格自己管。 */

/* 表格（模型管理 / 网关 Key 共用）：贴面板左右边缘，表头粘顶。
   overflow-anchor 同 .page.active：tbody 重建时不做锚定补偿，行内点开关视口不动 */
/* 表格滚动盒（模型管理 / 网关 Key 共用）。max-height 是**内容撑高、超过才收**的
   语义，网关 Key 页就按这个来（那页没有自己的高度骨架）。模型管理页要的是固定
   高度（与账号页一致），由 `.mm-main > .models-table-wrap` 覆盖成 flex 吃满剩余。 */
.models-table-wrap { overflow: auto; overflow-anchor: none; max-height: min(62vh, 720px); border-top: 1px solid var(--border); }

.models-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 12.5px;
}

/* 单元格内边距统一 8px（上下左右同值）：列宽本来就紧（七列分 100%），
   14px 会从每列吃掉 12px 的内容宽度。表头与表体用同一档，两者才对得齐；
   列宽拖动（table-columns.js）的可用宽度从 computed style 现读 padding，
   跟着这条生效，不需要另改。 */
.models-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 8px;
  border-bottom: 1px solid var(--border);
  background: var(--table-head-bg);
  color: var(--text-3);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .05em;
  text-align: left;
  white-space: nowrap;
}

.models-table td { padding: 8px; border-bottom: 1px solid var(--hairline); vertical-align: middle; }
.models-table tbody tr:last-child td { border-bottom: 0; }
.models-table tbody tr:hover td { background: var(--row-hover); }
.models-table th.r, .models-table td.r { text-align: right; }
.models-table td.empty { padding: 26px 8px; text-align: center; color: var(--text-3); }

/* ── 模型管理页的密度与表头（2026-10 重设计）──────────────
   行高收紧一档（上下 7px）、左右给到 10px 便于扫读；表头字号提到 11px、
   去掉字距（中文不受益）。只住本页 —— 网关 Key 页共用 .models-table
   骨架与列宽拖动，不跟着这组覆盖走。 */
.page[data-page="gateway"] .models-table thead th { padding: 8px 10px; font-size: 11px; letter-spacing: 0; }
.page[data-page="gateway"] .models-table td { padding: 7px 10px; }

/* 「模型映射」表头的问号提示（完整口径从括号长标题挪进 title，见 models-page） */
.page[data-page="gateway"] .models-table .th-help {
  display: inline-flex;
  vertical-align: -2px;
  margin-left: 3px;
  opacity: .7;
  cursor: help;
}
.page[data-page="gateway"] .models-table .th-help svg { width: 11px; height: 11px; display: block; }

/* 勾选的行：品牌淡底（比 hover 高一档）——批量勾选后滚动了视图，
   「选中的是哪几行」仍可辨。与 hover 叠加时取更深的一档。 */
.page[data-page="gateway"] .models-table tbody tr.sel td {
  background: color-mix(in srgb, var(--primary) 7%, transparent);
}
.page[data-page="gateway"] .models-table tbody tr.sel:hover td {
  background: color-mix(in srgb, var(--primary) 10%, transparent);
}

/* 列宽（合计 100%，table-layout: fixed 下按 colgroup 分配） */
/* 选择列：一整列只有一枚复选框，给 3.5% 的窄档（46px 上下，照账号页勾选列的量级）；
   多出的宽度从映射列（chips 有弹性）与操作列（按钮定宽、无长文案）各挪一点 ——
   合计仍 100%，没有换行风险。 */
.models-table .c-check { width: 3.5%; }
/* 勾选列的表头 / 单元格都居中：一整列只有复选框，居中才与账号页勾选列同一观感
   （数据行走列设置的 ta-center；th 上的 text-align 是全局 left，这里单独补） */
.models-table th[data-col="check"] { text-align: center; }
.models-table .c-model { width: 19%; }
.models-table .c-rate { width: 5.5%; }
.models-table .c-source { width: 6.5%; }
/* 能力位两列：数值一格（上下文 / 最大输出）、四枚图标圆点一格。
   并入映射列的宽度从这里来 —— 五列会把它挤到装不下 chips（见 models-page
   文件头的列数取舍） */
.models-table .c-budget { width: 10%; }
.models-table .c-caps { width: 11.5%; }
.models-table .c-alias { width: 33%; }
.models-table .c-act { width: 11%; }
.keys-table .k-name { width: 22%; }
.keys-table .k-key { width: 44%; }
.keys-table .k-time { width: 16%; }
.keys-table .k-state { width: 8%; }
.keys-table .k-act { width: 10%; }

/* 禁用 / 已删除的行整体压淡，开关与操作列保持可点 */
.models-table tr.off td { opacity: .5; }
.models-table tr.off td.state, .models-table tr.off td.r { opacity: 1; }

/* 提供商分组行：表内的一条分组带。取 --surface-2，只比表体差一档 ——
   它仍是「表里的一行」，不该用卡内凹槽那档深色（那会把表切成几块）。 */
.models-table .tr-group td {
  padding: 5px 8px;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 11.5px;
  font-weight: 600;
}
.models-table .tr-group td:hover { background: var(--surface-2); }
.models-table .prov-tag {
  display: inline-block;
  margin-right: 8px;
  padding: 0 8px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  font-weight: 700;
}
.models-table .tr-more td { padding: 4px 8px; text-align: center; }

/* 模型 id：等宽加粗。名字本身就是复制入口（上游模型列与映射别名 chip 的
   `.t` 都带 data-copy，点击走 clipboard.js 的全局委托）—— 曾经另挂一颗
   悬浮才显形的 ⧉ 按钮（.mid .cp），已去掉：点名字更省事，也少一个控件。
   可点样式只给带 data-copy 的名字：`.mid .t` 是模型管理 / 网关 Key 两页
   共用的，Key 页的名字不可复制，别把指针样式串过去。 */
.mid { display: flex; align-items: center; gap: 6px; min-width: 0; }
.mid .t { font-family: var(--font-mono); font-size: 12px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.keys-table .mid .t { font-family: var(--font-sans); }
.models-table .mid .t[data-copy],
.models-table .alias .t[data-copy] { cursor: pointer; }
.models-table .mid .t[data-copy]:hover,
.models-table .alias .t[data-copy]:hover {
  color: var(--text);
  text-decoration: underline dotted;
  text-underline-offset: 2px;
}
/* 点击瞬间压淡一下：复制没有可见的落点（toast 之外），这一下是「点到了」的凭据 */
.models-table .mid .t[data-copy]:active,
.models-table .alias .t[data-copy]:active { opacity: .55; }
.mname { color: var(--text-3); font-size: 11.5px; }
.rate { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }

/* ── 能力位两列（「上下文 / 输出」与「能力」）─────────────────────────
   cell 内容是一个按钮：点开「模型能力」弹窗。两列的入口语义相同（能力是一个
   整体，没必要各开一个弹窗），所以共用 .caps-open 外壳 —— 无样式按钮、hover
   给一层浅底、focus 有圈（键盘可达）。数值与徽章的三态样式只定义在这里；
   弹窗里用的是组件库控件，不复用这一套。

   负外边距 4px：hover 底比内容外扩一圈，又不会顶到列边界上（td 左右 8px 的
   内边距减掉 4px 外扩，还剩 4px 呼吸）。 */
.caps-open {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  margin: -3px -4px;
  padding: 3px 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  box-shadow: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.caps-open:hover { background: var(--surface-2); }
.caps-open:focus-visible { outline: 2px solid var(--primary); outline-offset: -1px; }

/* 数值格：上下文 / 最大输出（等宽、按位数对齐，两行的两个数能上下比） */
.cap-num {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
}
/* 未声明：压淡而不是换成文字 —— 与「明确声明了 0 / 不支持」一眼可分 */
.cap-num.unset { color: var(--text-3); opacity: .6; }
.cap-sep { color: var(--text-3); font-size: 11px; }

/* ── 「来源」列：色点 + 小字（2026-10 重设计）──────────────
   原描边徽标在这一列是满屏的小框框（每行都在重复同一个词），换成色点后
   扫表时只剩「点的颜色」一个信号：远程 = 品牌蓝（正常态）、内置 = 灰
   （还没拉到目录）、手动 = 琥珀（人工登记，可移除）。 */
.src { display: inline-flex; align-items: center; gap: 5px; color: var(--text-3); font-size: 11.5px; white-space: nowrap; }
.src i { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--text-3); opacity: .55; }
.src.remote { color: var(--text-2); }
.src.remote i { background: var(--primary); opacity: 1; }
.src.manual i { background: var(--warn); opacity: 1; }

/* ── 「能力」列：四枚图标圆点（2026-10 重设计）──────────────
   文字徽章 → 图标圆点：四个键在每一行都出现，扫表时读的是「几个亮圆」，
   具体语义由悬停提示兜底（capabilityTip）。三态：
   支持 = 品牌淡底圆、明确不支持 = 灰实底圆、未声明 = 虚线圈。 */
.caps-open.caps-badges { gap: 4px; flex-wrap: wrap; }
.cap-dot {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1px solid transparent;
}
.cap-dot svg { width: 11px; height: 11px; display: block; }
.cap-dot.on { background: var(--primary-soft); color: var(--primary-fg); }
.cap-dot.off { background: var(--surface-3); color: var(--text-3); }
.cap-dot.unset { border: 1px dashed var(--border-strong); color: var(--text-3); opacity: .75; }

/* 覆盖标记：被用户改过的那一项挂一枚小点（title 说明「已覆盖上游值」）。
   基础 .cap-mark 是行内小点（「上下文 / 输出」格用）；圆点里的那枚绝对定位
   到右上角，外圈描一层表面色把它从底色上托出来。 */
.cap-mark { flex: 0 0 auto; display: block; width: 4px; height: 4px; border-radius: 50%; background: var(--primary); }
.cap-dot .cap-mark { position: absolute; top: -1px; right: -1px; box-shadow: 0 0 0 2px var(--surface); }

/* 映射名药丸（2026-10 重设计）：**中性表面底** —— 原品牌色淡底把整列刷成蓝色按钮墙，
   品牌色在该列只属于两个真正的交互件：chip 上的开关与「＋ 映射」入口。
   chip 本体降级为「一条绑定的容器」：surface-3 底 + 细描边（比行 hover 底
   深一档，行悬浮时也分得开）；行禁用时随行压淡并加删除线。
   纵向排列（每条绑定独占一行，参照账号页模型管理弹窗）——映射一多条，
   横向 wrap 会挤成变宽的迷宫，竖着读每行一条才对得上「绑定列表」的心智。
   第一行是例外：默认那条与「＋ 映射」并排（.alias-row）—— 入口按钮不是绑定，
   让它独占一行会把每个模型行都撑高三行。 */
.aliases { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
/* 默认 chip + 「＋ 映射」。max-width: 100% 让这一行在最宽处收住，放不下时
   按钮自己折到第二行，而不是把 chip 挤扁或把列顶宽（.mm 是 minmax(0,1fr)，
   列一被顶宽，横向滚动条就挂到面板上了）。 */
.alias-row { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; max-width: 100%; }
.alias {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: auto;
  min-height: 26px;
  max-width: 100%;
  padding: 3px 5px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface-3);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
}
.alias.off { border-color: var(--border); background: var(--surface-inset); color: var(--text-3); text-decoration: line-through; }
/* 关闭的映射（chip 上自己的开关关着）：虚线弱化 + 删除线，但仍可点
   （开关就在 chip 里）。与行级压淡的 .off 互相独立 —— 「行开着、某条映射
   关着」的状态要一眼可辨，所以另起一个类名。 */
.alias.map-off { border-style: dashed; background: transparent; color: var(--text-3); }
.alias.map-off .t { text-decoration: line-through; font-weight: 500; }
.alias .t { min-width: 0; overflow-wrap: anywhere; }
.binding-default { flex: 0 0 auto; padding: 0 4px; border: 1px solid var(--border); border-radius: var(--r-xs); background: var(--surface); color: var(--text-3); font-family: var(--font-sans); font-size: 10px; font-weight: 500; }
.alias .switch { flex: 0 0 auto; }
.alias .alias-level, .alias .x { flex: 0 0 auto; }

/* chip 内的小号开关：复用 .switch 的结构与交互（components.css），只在这里
   收尺寸 —— 药丸高 22px，滑块压到 24×14 才放得下；滑块位移同步缩放 */
.alias .switch { gap: 0; margin-right: 2px; }
.alias .switch .track { width: 24px; height: 14px; }
.alias .switch .track::after { top: 2px; left: 2px; width: 10px; height: 10px; }
.alias .switch input:checked + .track::after { transform: translateX(10px); }
.alias .x {
  height: 16px;
  width: 16px;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  box-shadow: none;
  color: inherit;
  opacity: .6;
  font-size: 12px;
  line-height: 1;
}
.alias .x:hover:not(:disabled) { opacity: 1; background: color-mix(in srgb, var(--text) 10%, transparent); color: inherit; border-color: transparent; }
/* 行内操作：默认略淡，悬浮行时恢复 */
.row-actions { display: flex; gap: 4px; justify-content: flex-end; }
.row-actions button { opacity: .75; }
.models-table tr:hover .row-actions button { opacity: 1; }

/* 网关 Key 页：key 单元格 */
.keycell { display: flex; align-items: center; gap: 6px; min-width: 0; }
.keycell .kv { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono); font-size: 12px; }

/* 新建 / 编辑 Key 弹窗里的两个多选（可用提供商 / 可用模型）。
   原生 select 留在壳里当宽高锚，给它一个「够放下两个中文名」的下限、上界收在
   260px —— 勾多了由触发器里的文案自己省略号收住，这一行保持等高。`.field-row`
   是 flex 行，不设 flex:1 就会按内容宽度塌成很窄的一条。 */
#key-allowed-providers, #key-allowed-models {
  flex: 1 1 auto;
  min-width: 180px;
  max-width: 260px;
}
/* 弹窗里这两行之间的间距已经由行内 style 给了，这里只补「标签与控件不同基线」
   带来的视觉偏移（.field-row 是 align-items:center，多选外壳比 input 略高） */
#key-modal .field-row > label { align-self: center; }
/* 摘要行：与上面的说明同款小字，首行前留一点空隙（它是「当前选择」而不是
   「使用说明」，视觉上要分得开） */
#key-restrict-summary { margin-top: 2px; }

/* ─────────────────────────────────────────────
   模型管理页：占满剩余高度，滚动只发生在列表内部

   与账号页（layout.css 的 .page.active[data-page="accounts"]）同一套骨架：
   页面本身不滚，面板吃满剩余高度，面板里只有「列表」那一块滚 —— 表格高度因此
   与窗口绑定，而不是随模型条数伸缩（模型少时表格矮成一条、多时把整页顶长，
   两种都不该发生）。同一页里的左栏是列表，所以它也自己滚。
   ───────────────────────────────────────────── */
.page.active[data-page="gateway"] {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding-bottom: var(--pad);
}
.page[data-page="gateway"] .panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}

/* ─────────────────────────────────────────────
   左栏（提供商导航）+ 右栏（选中那家的模型表）

   左栏是**页面内的一段侧边栏**：分组标题、导航项、选中态全部沿用全局侧边栏
   （layout.css 的 .nav-group-label / .nav-item）那一套变量与形态 —— 同一个应用里
   只该有一种「列表导航」的语言，用户在左侧栏认得出的行高、字号、选中底色，
   在这里应当原样成立。差别只有两处：这里的项多一个计数（模型数），
   以及「＋ 新建自定义提供商」是动作而不是选项（虚线框）。

   两栏用 grid 而不是 flex：右栏必须能收缩到 0 宽以下（minmax(0, 1fr)），
   否则「模型映射」那一列的 chips 会把整行顶宽，横向滚动条挂到 panel 上。
   ───────────────────────────────────────────── */
.mm {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 218px minmax(0, 1fr);
  /* 行高必须显式吃满容器：grid 的行高默认按内容算，不写这条时左栏与右栏
     都只有内容高，下面的「表格区 flex:1」就没有可分配的剩余空间 */
  grid-template-rows: minmax(0, 1fr);
}
.mm-main {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* 面板头 / 表尾：都是固定高度的两块，不参与压缩 */
.mm-main > .panel-head,
.mm-main > .panel-foot { flex: 0 0 auto; }
/* 表格区：吃掉剩余高度。内容少时它照旧占满（表格下方是空白，不是滚动条），
   内容多时自己滚 —— 「固定高度」的判据是**它不随内容变**，而不是永远出滚动条 */
.mm-main > .models-table-wrap {
  flex: 1 1 auto;
  min-height: 0;
  /* 清掉共用的内容上限：这一页的高度由上面那条 flex 链给，不由内容给 */
  max-height: none;
}

.prov-rail {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: 8px 8px 10px;
  border-right: 1px solid var(--sidebar-border);
  /* 比全局侧栏浅一档（--rail-bg，见 tokens.css）：它是面板内的一段内嵌导航，
     用侧栏同款底色会被读成侧栏的延伸，和右边的模型表也不成一块 */
  background: var(--rail-bg);
  /* 高度由 grid 行高给（= 面板高度）。滚动不再发生在 aside 上（2026-10 重设计）：
     条目区 .rail-scroll 自己滚，「＋ 新建」在 .rail-foot 沉底 —— 它是动作不是选项，
     家多了也不该被推出视野。这里**不能**加 scrollbar-gutter: stable：预留的槽仍在
     padding box 内，分组标题的负外边距一旦铺进去就是 11px 的横向溢出。 */
  overflow: hidden;
}

/* 条目区： aside 里唯一滚动的块（家数多了自己滚） */
.rail-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* 栏底的动作区：与条目区隔一条发丝线，说明下面是「动作」不是「选项」
   （里面的「＋ 新建」是 NavItem 的 add 变体，宽度组件自带 w-full） */
.rail-foot { flex: none; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--hairline); }

/* 分组标题：一行小字 + 右侧细线（2026-10 重设计）。原通栏色带把左栏切成几块、
   又与条目的选中底色抢重量；「小字 + 细线」同样分得开两组，但整栏是一块的。 */
.rail-label {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 4px 5px;
  color: var(--text-3);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .08em;
  white-space: nowrap;
}
.rail-label:first-child { margin-top: 0; }
.rail-label::after { content: ""; flex: 1; height: 1px; background: var(--hairline); }
.rail-empty { padding: 2px 10px 6px; color: var(--sidebar-muted); font-size: 11.5px; }

/* ── 提供商头像（2026-10 重设计）──────────────────────────
   收录过图标的家用**真实图标**（内置家按 id 查 PROVIDER_ICONS，自定义家按名字
   match预置目录，见 models-page.tsx 的 providerAvatar）：品牌图自带颜色，底换
   中性控件色、图 contain 缩进一圈（.img 形态），不与色相 tint 混用。
   没图标可用的家回落色相 monogram：底 = 色相淡混、字 = 色相亮档，色相由内联
   --av-h 给（providerHue）。深浅主题各配一档文字亮度：深色下提亮、浅色下压暗，
   同一份 --av-h 两边都成立。「全部」不是一家（.mute）：中性灰，不参与色环。 */
.pv-ico {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  border-radius: 6px;
  background: oklch(0.68 0.13 var(--av-h, 260) / 18%);
  color: oklch(0.70 0.14 var(--av-h, 260));
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1;
  overflow: hidden;
}
:root[data-theme='light'] .pv-ico {
  background: oklch(0.68 0.13 var(--av-h, 260) / 14%);
  color: oklch(0.52 0.15 var(--av-h, 260));
}
.pv-ico svg { width: 12px; height: 12px; display: block; }
.pv-ico.mute {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--text-3);
}
/* 真图标形态：中性底（品牌图自带颜色，色相 tint 会打架），四周留 2px 呼吸 */
.pv-ico.img,
:root[data-theme='light'] .pv-ico.img { background: var(--control-bg); color: initial; padding: 2px; }
.pv-ico.img img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* 面板头标题上的那枚：大一档（它是这页的「页签」） */
.mm-title .pv-ico { width: 26px; height: 26px; border-radius: 8px; font-size: 12px; }
.mm-title .pv-ico svg { width: 14px; height: 14px; }

/* 计数改小药丸（2026-10 重设计）：NavItem 渲染的计数 span 加底色与圆角。
   只作用于本栏 —— 计数药丸是「导航列表」的语义，别处（侧栏等）不跟。 */
.prov-rail [data-slot='nav-item-count'] {
  min-width: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--text) 8%, transparent);
  line-height: 16px;
  text-align: center;
}
/* 选中态下药丸跟随选中文字色（NavItem 已把计数 color 设为 inherit，
   这里只换底色，让它在选中底上仍是可读的一小块） */
.prov-rail [data-slot='nav-item'][data-active] [data-slot='nav-item-count'] {
  background: color-mix(in srgb, currentColor 18%, transparent);
}

/* 条目本体（一家一行）已换成组件库 NavItem（models-page.tsx 的 rail()）：
   行高、圆角、字号、字重、选中态配色、过渡曲线都在组件里取同一套令牌，
   旧的 .pv / .pv.on / .pv .n 一组规则随之删除 —— 左栏剩下的自定义样式只有
   头像（.pv-ico）、计数药丸（上面两条）与下面的删除 ×。 */

/* 自定义家条目外层的定位容器（条目本体是组件库 NavItem 的 button，删除按钮不能嵌在
   它里面 —— button 套 button 会被解析器拆开，绝对定位也跟着失去参照）。
   宽高都交给里面的条目，这一层只负责给 × 当坐标原点。 */
.pv-row { position: relative; flex: none; }

/* 条目上的删除按钮：与右侧那个计数**占同一个位置**，hover / 键盘聚焦时互换
   —— 计数在「要删它」这一刻没有用，让位给按钮既不用加宽左栏，也不会让
   12.5px 的名字被挤掉一截。默认隐藏（opacity 而非 display：显隐不动布局，
   鼠标扫过一排条目时不会有跳动）。（条目换成 NavItem 后计数由 JS 侧的
   RAIL_HIDE_COUNT_ON_HOVER 工具类藏，这里那条保留是为了与该行为同处一地。） */
.pv-del {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  box-shadow: none;
  color: var(--sidebar-muted);
  font-size: 15px;
  line-height: 1;
  opacity: 0;
  cursor: pointer;
  transition: opacity .12s ease, color .12s ease, background .12s ease;
}
.pv-row:hover .pv-del,
.pv-del:focus-visible { opacity: 1; }
/* hover 时把计数让出来（两者都在右缘，不藏计数就会叠字） */
.pv-row:hover [data-slot='nav-item-count'] { visibility: hidden; }
/* :not(:disabled) 是为了压过 components.css 的 `button:hover:not(:disabled)`
   —— 那条的特异性是 (0,2,1)，比单类 + :hover 高一级，不写就抢不回来 */
.pv-del:hover:not(:disabled) {
  color: var(--danger);
  background: var(--nav-hover);
  border-color: transparent;
}
/* 窄窗口（约 900px 以下）改成上下堆叠：左栏在上一行，横向铺满、自己滚 */
@media (max-width: 900px) {
  /* 窄窗口改成上下两行：左栏一行（自己的高度上限），表格区吃掉剩下的 */
  .mm {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  .prov-rail {
    max-height: 210px;
    border-right: 0;
    border-bottom: 1px solid var(--sidebar-border);
  }
}
