/* ─────────────────────────────────────────────
   移动端适配层（真机 < 768px）
   ─────────────────────────────────────────────
   既有 layout.css 的 900px 断点只为「窄桌面 / 平板」设计：侧边栏收为
   68px 图标栏。真正的手机（360-430px）上还会撞上几处破版：

      0. tokens.css 给 body 写死 min-width:720px（桌面 Tauri 窗口的
         最小宽度下限），手机上把整个文档撑到 720px 宽 —— 375px 的视口里
         只露出中间那 375px，左右各裁掉 172px。「页面看不全」的根因。
         这一条已搬到 tokens.css（它必须在媒体查询之外的级联里被压掉）。
      1. 侧边栏恒显 68px，占满 1/5 屏宽，且点图标后没有「收起」入口
         -> 抽屉化：默认隐藏，顶栏汉堡按钮切换
      2. 五张表（账号 / 模型 / 网关 Key / 网络代理）都是 table-layout:fixed，
         JS 把 min-width 写成 ~400-1200px 的 inline style，375px 里只能横滚
         -> 卡片化：逐行拆成 grid 卡片，列头成 label，强制关 JS 写的 min-width
      3. 请求日志 9 列 grid（--req-cols 变量）在窄屏挤成竖条
         -> 同样卡片化：表头藏掉，每行变「label + 值」的纵列卡片
      4. 面板头 / 工具条 / 筛选行 / 表格页脚在窄屏挤成一团
         -> 各控件整行排布
      5. 报表排行卡 4 列（名字 / 条 / 读数 / 百分比）在 246px 里把名字
         压到只剩提供商徽章 -> 改两行：名字 + 百分比一行，条 + 读数一行
      6. 设置页分类导航变顶部横条、开关区竖排
      7. 报表两列卡片网格 -> 单列

   桌面端（>=768px）整份不生效：全部规则锁在 @media (max-width:767px) 内。
   选择器都锚在既有 class 上，不动原 CSS 文件的其它声明。

   注意「不生效」只等于「本文件不覆盖桌面」，它管不到新加的元素本身：
   **移动端专有的元素必须自己保证在桌面端不占位**（见下面的 .mobile-menu-btn
   / .nav-scrim）—— .shell 是「2 列 + 100vh」的 grid，只要多一个在流内的
   子元素，就会多出一个隐式行，align-content 把 100vh 的富余高度在两行之间
   平分，侧栏与主区会提前结束、窗口底部空出一条。 */

/* ═══ 顶栏：加一条汉堡按钮（768px 以下才出现） ═══ */
.topbar { position: relative; }
.mobile-menu-btn {
  display: none;
  position: absolute;
  left: max(var(--pad, 12px), 8px);
  top: 50%;
  transform: translateY(-50%);
  width: 38px; height: 38px;
  padding: 0; margin: 0;
  border: 1px solid var(--border, #ccc);
  border-radius: var(--radius, 8px);
  background: var(--surface, #fff);
  color: var(--text, #333);
  cursor: pointer;
  z-index: 5;
  align-items: center;
  justify-content: center;
}
/* 图标由内联 SVG 提供（index.html 里的 mobile-menu-btn），这里只兜底居中 */
.mobile-menu-btn svg { display: block; }

@media (max-width: 767px) {
  .mobile-menu-btn { display: inline-flex; }
  /* 汉堡按钮占位后，面包屑让出左侧空间 */
  .topbar {
    padding-left: calc(max(var(--pad, 12px), 8px) + 46px);
    padding-right: max(var(--pad, 12px), 8px);
  }
  /* 面包屑的「上级」在手机上没有价值（点侧栏就是切换页面），
     只留当前页名，给右侧状态徽标与汉堡按钮让出空间 */
  .crumb { overflow: hidden; white-space: nowrap; min-width: 0; }
  .crumb > span:not(.cur),
  .crumb .sep { display: none; }
  .crumb .cur { font-size: 14px; }
}

/* ═══ 侧边栏抽屉化（纯 CSS，#nav-toggle 驱动） ═══ */
.nav-toggle-input { position: absolute; opacity: 0; pointer-events: none; }
/* 遮罩在桌面端必须**彻底不存在**，不能只是"看不见"：它是 .shell 的直接子
   元素，而 .shell 是「2 列 + 100vh」的 grid —— 没有样式时它会成为第三个
   网格项，多出的隐式行把 100vh 的富余高度分走一半，表现就是侧栏与主区提前
   结束、窗口底部空出一条白带（内容越矮越明显）。.nav-toggle-input 靠
   position:absolute 脱离了流才没踩到这条；遮罩要盖满整屏只能用 fixed，
   所以这里直接 display:none，767px 内再打开。 */
.nav-scrim { display: none; }

@media (max-width: 767px) {
  .shell { grid-template-columns: 1fr; }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: 256px;
    transform: translateX(-100%);
    transition: transform .22s ease;
    z-index: 40;
    box-shadow: 4px 0 16px rgba(0,0,0,.18);
  }
  /* 纯 CSS 开合：汉堡 label 与遮罩 label 都指向 #nav-toggle，
     :checked 时侧边栏滑入、遮罩淡入 */
  #nav-toggle:checked ~ .sidebar { transform: translateX(0); }

  /* 900px 断点把 label / 分组名全藏了，手机上恢复 */
  .sidebar .nav-item .label,
  .sidebar .nav-item .count,
  .sidebar .nav-group-label,
  .sidebar .brand-text,
  .sidebar .sidebar-status .txt { display: inline; }
  .sidebar .nav-item { justify-content: flex-start; padding: 9px 14px; }
  .sidebar .sidebar-status { padding: 10px 14px; align-items: flex-start; }
  .sidebar .status-line { justify-content: flex-start; }

  /* 遮罩（.nav-scrim 与 #nav-toggle 同为 .shell 子元素，在它之后）
     display:block 是压掉顶层那条 display:none —— 少了它手机上遮罩照样不显示 */
  .nav-scrim {
    display: block;
    position: fixed; inset: 0;
    background: rgba(0,0,0,.35);
    z-index: 30;
    opacity: 0;
    pointer-events: none;
    transition: opacity .18s ease;
  }
  #nav-toggle:checked ~ .nav-scrim { opacity: 1; pointer-events: auto; }

  /* 内容区不再给侧栏留位 */
  .main { margin-left: 0; }
}

/* ═══ 内容区：留白收窄，面板贴住视口边 ═══
   底部留白交给 .page.active 的 padding-bottom（layout.css 的移动端段），
   这里不再给 .content 加下边距 —— 它是滚动容器外的壳，加了下边距会在
   内容区底部留出一条固定空白，滚动时看着像「滚不到底」。 */
@media (max-width: 767px) {
  .content { padding: 10px 12px 0; }
}

/* ═══ 面板：标题 / 副题 / 动作区在窄屏重排 ═══
   桌面上一行放得下的「标题 + 副题 + 右侧动作」，在 360px 的屏里三块互顶。
   这里是**两行结构**而不是纯纵排：
     第一行：标题（+ 问号提示）+ 动作按钮（靠右顶对齐）
     第二行：副题说明（整行，可换行）
   纯 column 纵排会让「添加条目」「立即全部签到」这类按钮孤零零落到第三行、
   离标题很远，看不出它属于哪个面板。 */
@media (max-width: 767px) {
  /* 用 flex-wrap 而不是两列 grid：grid 的 `auto` 列会按动作区的**内容宽度**
     参与分配，动作区一宽（概览页五档时间选择器实测 334px）就把 1fr 列压到
     0px，标题「统计概览」逐字竖排。这在 381-767px 区间（平板 / 手机横屏 /
     网页版窄窗口）稳定复现 —— 原来只在 ≤380px 兜底，中间是空档。
     flex 下 .head-actions 用 margin-left:auto 靠右，放不下时自然折行。 */
  .panel-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 8px;
    row-gap: 6px;
    padding: 12px;
  }
  /* 标题：占据剩余宽度，永不被压到 0 */
  .panel-head > h2 { flex: 1 1 auto; min-width: 0; }
  .panel-head > .tip-q { flex: 0 0 auto; }
  /* 动作区：靠右；放不下时整组折到下一行（flex-wrap 负责） */
  .panel-head > .head-actions {
    flex: 0 0 auto;
    margin-left: auto;
    width: auto;
    min-width: 0;
    justify-content: flex-end;
    flex-wrap: wrap;
  }
  .panel-head > .head-actions > * { flex: 0 0 auto; }
  /* 裸按钮（没有 .head-actions 包裹的，如「添加条目」「立即全部签到」） */
  .panel-head > button { flex: 0 0 auto; margin-left: auto; align-self: center; }
  /* 副题说明：独占一行 */
  .panel-head > .panel-sub {
    flex: 1 1 100%;
    min-width: 0;
    white-space: normal;
  }
  /* 其它零星元素（标签、开关等）自然落到后续行 */
  /* 通栏块：.mm-titlebar / .mm-filters 这类自带 width:100% 的行独占一行 */
  .panel-head > .mm-titlebar,
  .panel-head > .mm-filters { flex: 1 1 100%; }
  .panel-body { padding: 12px; }
  .panel-foot { padding: 10px 12px; }
}

/* ═══ 面板页脚 / 通用表格页脚：控件组独占一行 ═══
   .table-foot-ctl 有五个控件（读数 / 每页 / 条 / 跳页 / 翻页器），桌面上一行
   摆得下。窄屏下 flex:0 0 auto 不让它收缩，399px 的组宽把 298px 的面板顶出
   横向溢出。改成整行铺开 + 内部自己折行，输入框同步收窄。 */
@media (max-width: 767px) {
  .panel-foot .spacer { display: none; }
  .table-foot-ctl {
    flex: 1 1 100%;
    width: 100%;
    margin-left: 0;
    justify-content: flex-start;
    row-gap: 6px;
  }
  .table-foot-size input,
  .table-foot-jump input { width: 54px; }
}

/* ═══ 账号页：筛选收进右侧抽屉（≤767px） ═══
   桌面端工具条平铺四个筛选组（提供商 / 搜索 / 状态 / 限额）是合理的 —— 屏幕宽、
   一屏能容下，平铺让「当前筛选状态」始终可见。手机上这四行要吃掉 190px
   （812px 屏的近四分之一），首屏只剩一半给账号，且每行只有一个小控件。

   改成：工具条只留「筛选」按钮 + 三个常用动作，四个筛选组移进右侧抽屉。
   常用动作留在外面（折叠 / 添加账号 / 查询余额 是高频），不常用筛选收起。

   ★ 桌面端零影响：抽屉标题栏（.acct-filter-head）、遮罩（.acct-filter-scrim）、
   「筛选」按钮（.acct-filter-open）、角标（.acct-filter-badge）全部
   display:none；.acct-filter-group 在桌面是 display:contents（不产生盒子，
   子元素照旧参与 .toolbar 的 flex 排布），与改动前逐像素一致。

   ★★ 层级：抽屉必须**低于**浮层（Select / Popover / Tooltip 都是 z 35），
   否则抽屉里的下拉一展开就被抽屉自己盖住 —— 选项全在、点着没反应，看起来
   就是「下拉没有值」（2026-10 实测：26 个提供商选项齐全，命中测试打到的是
   抽屉的标签，不是选项）。
   现有层级表：30 弹窗 / 35 浮层 / 38 确认框 / 40 toast / 50 抽屉 / 51 标题栏
   / 52 抽屉内浮层兜底。抽屉整体压在 35 之下，抽屉里才会浮出下拉。 */

/* 桌面端：这些元素不存在。
   用 min-width 媒体查询限定「只在桌面隐藏」，而不是全局 display:none ——
   全局写会让下面的小屏规则要靠 !important 才能覆盖回来。
   !important 是必须的：组件库的 Button 自带 inline-flex 工具类
   （Tailwind utilities 层带 !important，且 islands/ui.css 在本文件之后加载）。 */
@media (min-width: 768px) {
  .acct-filter-open,
  .acct-filter-scrim,
  .acct-filter-head { display: none; }
}
/* 桌面端：容器透明化，四个筛选组仍是 .toolbar 的直接 flex 子项 */
.acct-filter-group { display: contents; }

/* 断点写 767.98 而不是 767：某些环境（含测试用的浏览器视口）的实际宽度
   是 767.0000x 这类浮点值，`max-width: 767px` 会在正好 767 时不匹配，
   而 767.98 能覆盖到。真实设备 innerWidth 是整数，两种写法等价。 */
@media (max-width: 767.98px) {
  /* 「筛选」按钮：工具条第一行左侧，带生效条数角标 */
  /* 原生 button：组件库 Button 的样式在这里手工补齐（outline 档的观感） */
  .acct-filter-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    flex: 1 1 0;
    min-width: 0;
    min-height: 34px;
    padding: 0 12px;
    position: relative;
    border: 1px solid var(--control-border, var(--border));
    border-radius: var(--r-md, 8px);
    background: var(--control, var(--surface));
    color: var(--text);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
  }
  .acct-filter-open:active { transform: translateY(.5px); }
  .acct-filter-badge {
    display: inline-grid;
    place-items: center;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--primary);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1;
  }

  /* 筛选组容器：在窄屏变成一个占位的普通盒子，由下面把它固定到抽屉里 */
  .acct-filter-group { display: block; }

  /* 抽屉里的四个筛选组：从工具条「搬」进来 —— position:fixed 相对视口定位，
     靠 right/top 把它们叠到抽屉面板里。用 fixed 而不是挪 DOM 是为了让控件
     保持挂载（受控输入不丢焦点、id 不重复）。 */
  /* 筛选组容器：与标题栏、面板同处右侧，从视口外滑入。
     三者用同一套 right/width/transform，视觉上拼成一张完整抽屉。
     顶部 52px 留给标题栏（它是 fixed，层级更高，会盖在这里上面）。 */
  .acct-filter-group {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 25;
    width: min(320px, 86vw);
    padding: 52px 12px 16px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    overflow-y: auto;
    background: var(--surface);
    border-left: 1px solid var(--border);
    transform: translateX(100%);
    transition: transform .2s ease;
    -webkit-overflow-scrolling: touch;
  }
  .acct-filter-group.open { transform: translateX(0); }

  /* 抽屉内每个筛选组：标签在上、控件整行 */
  .acct-filter-group > .group {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    width: 100%;
  }
  /* 标签：整行左对齐的小标题。
     组件库的 button/通用控件在 .group 里被居中，span 自身 text-align 不生效
     （父级 justify-content 决定），所以这里同时压 .group 的 align-items 与
     label 的 width。 */
  .acct-filter-group > .group {
    align-items: stretch;
    text-align: left;
  }
  .acct-filter-group > .group > .label {
    flex: 0 0 auto;
    width: 100%;
    font-size: 11.5px;
    color: var(--text-3);
    text-align: left;
    font-weight: 600;
    letter-spacing: .02em;
  }
  .acct-filter-group > .group > *:not(.label) {
    flex: 1 1 auto;
    min-width: 0;
    width: 100% !important;
    max-width: none !important;
  }
  .acct-filter-group > .divider { display: none; }
  .acct-filter-group [data-slot="segmented-control"] { width: 100%; }
  .acct-filter-group [data-slot="segmented-control"] > * { flex: 1 1 0; min-width: 0; justify-content: center; }

  /* 抽屉标题栏：钉在顶部，与面板同一层 */
  .acct-filter-head {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 26;
    width: min(320px, 86vw);
    display: flex;
    align-items: center;
    gap: 8px;
    height: 52px;
    padding: 0 8px 0 14px;
    background: var(--surface);
    border-left: 1px solid var(--border);
    border-bottom: 1px solid var(--hairline);
    box-sizing: border-box;
    transform: translateX(100%);
    transition: transform .2s ease;
  }
  .acct-filter-head.open { transform: translateX(0); }
  .acct-filter-title { font-size: 14px; font-weight: 650; }
  .acct-filter-clear { margin-left: auto; }
  .acct-filter-close { min-width: 32px; min-height: 32px; font-size: 14px; }

  /* 遮罩：抽屉打开时盖住内容区，点它收起 */
  .acct-filter-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 24;
    background: rgba(0, 0, 0, .34);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
  }
  .acct-filter-scrim.open { opacity: 1; pointer-events: auto; }
}

/* ═══ 账号页工具条：筛选项各自整行、按钮等宽成网格 ═══ */
@media (max-width: 767px) {
  /* 原布局（桌面口径）在手机上是一堆参差的小块：提供商下拉 157px 独占一行、
     搜索框/状态/限额各占半行、三个操作按钮挤在末尾 —— 6 个元素错落 5 行、
     宽度参差不齐，视觉上很乱。
     改成规整结构：① 每个筛选组占整行（标签固定宽度，控件吃满右侧，左边缘
     全部对齐）② 操作按钮排成等宽 2 列网格（同一行 2 个、最后独占行时也
     撑满），视觉是整齐的栅格而不是长短不一的碎片。 */
  .toolbar { flex-flow: column nowrap; align-items: stretch; gap: 8px; }
  .toolbar .group { flex: 0 0 auto; width: 100%; display: flex; align-items: center; gap: 8px; }
  /* 标签固定宽度：三行控件左边缘对齐成一条线 */
  .toolbar .group > .label { flex: 0 0 44px; }
  /* 控件/下拉吃满整行，不再各自参差。组件库给 select-trigger 硬编码了
     `max-w-[200px] min-w-[130px]`（Tailwind 任意值类，且自带 !important），
     它比同排的搜索框窄 14px，右边缘参差。注意 ui.css 在 index.html 里
     后于本文件加载 —— 同为 !important 时后加载者胜，所以这里用 `html`
     前缀把特异性抬到 (0,2,3) 压过 Tailwind 的单类选择器。 */
  html .toolbar .group > *:not(.label) { flex: 1 1 auto !important; max-width: none !important; min-width: 0 !important; width: 100%; }
  html .toolbar .group [data-slot="select-trigger"] { max-width: none !important; min-width: 0 !important; width: 100% !important; }
  html .toolbar .group [data-slot="segmented-control"] { width: 100%; }
  html .toolbar .group [data-slot="segmented-control"] > * { flex: 1 1 0; min-width: 0; justify-content: center; }
  .toolbar .divider { display: none; }
  /* 操作按钮：等宽网格。auto-fit + 78px 下限 —— 375px 容器 266px 排 3 列
     （每个 83px，容得下「折叠全部」的 72px），320px 容器 222px 自动降 2 列，
     不会把文字挤断。比原来固定 2 列省下一整行（68px → 30px）。 */
  .toolbar .actions {
    flex: 0 0 auto;
    width: 100%;
    margin-left: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
    gap: 8px;
  }
  .toolbar .actions > * { width: 100%; min-width: 0; }

  /* 批量操作栏：同样收成栅格 —— 全选/已选/摘要各占整行，按钮 auto-fit 网格 */
  .batch-bar { flex-flow: row wrap; align-items: center; gap: 6px 10px; }
  .batch-bar .provider-summary { flex: 1 1 0; min-width: 0; }
  .batch-actions {
    flex-basis: 100%;
    margin-left: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
    gap: 8px;
  }
  .batch-actions > * { width: 100%; min-width: 0; }
}

/* ═══ 表格卡片化（账号 / 模型 / 网关 Key / 网络代理四张表共用）═══
   .acct-table 是账号页专用，.models-table 是模型管理 / 网关 Key / 网络代理
   三页共用（后两页各挂一个修饰类）。四张表都是 table-layout:fixed，列宽
   公式为桌面设计；375px 里 7 列表只能横滚、第一列滚出去就分不清哪一行。

   改成卡片：table -> block，每行 -> grid 卡片（两列），表头藏掉，列名由
   mobile-columns.js 从 th[data-col] 写进 td 的 data-col，td::before 渲染。
   身份信息 / 长文本 / 按钮组整行占宽，其余两两并排。 */
@media (max-width: 767px) {
  .acct-scroll, .models-table-wrap { overflow-x: hidden; }
  .acct-table, .models-table {
    display: block;
    table-layout: auto;
    width: 100%;
    min-width: 0 !important;   /* 压住 JS 写进 inline style 的 ~400-1200px */
  }
  .acct-table thead, .models-table thead { display: none; }
  .acct-table tbody, .models-table tbody { display: block; }
  .acct-table tr, .models-table tr {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 10px;
    border: 1px solid var(--border, #ddd);
    border-radius: var(--r-md, 8px);
    padding: 10px;
    margin: 0 0 10px;
    background: var(--surface, #fff);
  }
  /* 每格自带列名：表头文字由 mobile-columns.js 注入到 td 的 data-col 属性 */
  .acct-table td, .models-table td {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    width: auto;
    white-space: normal;
  }
  .acct-table td::before, .models-table td::before {
    content: attr(data-col);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-3, #999);
  }

  /* 整行占宽：身份信息、长文本（Key / 地址 / 别名）、按钮组 */
  .acct-table td.cell-account,
  .acct-table td.cell-proxy,
  .acct-table td.cell-actions,
  .models-table td.cell-model,
  .models-table td.cell-alias,
  .models-table td.cell-act,
  .models-table td.cell-key,
  .models-table td.cell-name,
  .models-table td.cell-time { grid-column: 1 / -1; }
  /* 含按钮的格一律整行（覆盖四张表所有「操作」列，不管类名叫什么） */
  .acct-table td:has(button),
  .models-table td:has(button) { grid-column: 1 / -1; }

  /* 单一 td 的行：分组标题（模型表按提供商分组）与空态行。
     不显示列名标签，按行类型给视觉区分 */
  .acct-table tr > td:only-child,
  .models-table tr > td:only-child { grid-column: 1 / -1; }
  .acct-table tr > td:only-child::before,
  .models-table tr > td:only-child::before { content: none; }

  /* 2.9.6 模型表新增「选择」列（复选框）：桌面在最左；卡片里让它和
     模型名同排，不独占一行 —— 否则复选框孤零零占第一行、卡片被撑高 */
  .models-table tr > td.cell-check { grid-column: 1; grid-row: 1; justify-self: start; }
  .models-table tr > td.cell-check::before { content: none; }
  .models-table tr > td.cell-check + td.cell-model { grid-column: 2 / -1; grid-row: 1; }

  /* 分组标题行：去边框、小一号、大写字距，读起来像 section heading */
  .models-table tr.tr-group {
    border: 0;
    border-radius: 0;
    margin: 14px 0 6px;
    padding: 4px 2px;
    background: transparent;
  }
  .models-table tr.tr-group td {
    display: block;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: .04em;
  }

  /* 操作按钮纵向堆 */
  .cell-actions { flex-direction: column; align-items: stretch; }
  .cell-actions .btn, .cell-act .btn { text-align: center; }
  .cell-act .row-actions, .cell-actions .row-actions { flex-wrap: wrap; }

  /* 代理列的地址 / Key 列的长串：允许折行，不被省略号吃掉 */
  .cell-proxy code, .cell-key code, .cell-key .mono {
    word-break: break-all;
    white-space: normal;
  }
}

/* ═══ 账号卡片：按内容分区重排（通用两列网格不够，这里单独设计） ═══
   账号表是四张表里唯一「每行都是一个富控件组」的：优先级步进器、提供商徽章、
   账号名、代理选择器、启停开关、限流徽章、有效期、余额、四颗操作按钮。
   桌面 11 列里只有「账号」是弹性列，其余全是定宽 —— 通用两列网格会把绝大多数
   格子推成整行（实测每张卡 9 个网格行、581px 高，8 个账号撑出 4749px 滚动区，
   一屏只看得到半张卡，这就是「乱」）。

   按内容分区重排成 6 行：头部（勾选 / 账号名 / 启停）、身份（提供商 / 优先级）、
   代理整行、读数两两并排（限流 + 有效期、余额 + 连接数）、操作整行。 */
@media (max-width: 767px) {
  .acct-table tr.acct-row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 5px 10px;
    padding: 10px;
  }
  /* 头部：勾选 | 账号名 | 启停开关 */
  .acct-table tr.acct-row .cell-pick     { grid-area: 1 / 1; justify-self: start; align-self: center; }
  .acct-table tr.acct-row .cell-account  { grid-area: 1 / 2; }
  .acct-table tr.acct-row .cell-status   { grid-area: 1 / 3; justify-self: end; align-self: center; }
  /* 身份：提供商徽章 | 优先级 */
  .acct-table tr.acct-row .cell-provider { grid-area: 2 / 1 / 3 / 3; }
  .acct-table tr.acct-row .cell-priority { grid-area: 2 / 3; justify-self: end; }
  /* 代理整行 */
  .acct-table tr.acct-row .cell-proxy    { grid-area: 3 / 1 / 4 / 4; }
  /* 读数两两并排 */
  .acct-table tr.acct-row .cell-limits      { grid-area: 4 / 1 / 5 / 3; }
  .acct-table tr.acct-row .cell-expiry      { grid-area: 4 / 3; }
  .acct-table tr.acct-row .cell-usage       { grid-area: 5 / 1 / 6 / 3; }
  .acct-table tr.acct-row .cell-connections { grid-area: 5 / 3; }
  /* 操作整行 */
  .acct-table tr.acct-row .cell-actions  { grid-area: 6 / 1 / 7 / 4; }

  /* 单元格内边距收窄：桌面 7px 上下是给表格行高用的，卡片里每格都带一份，
     6 行就是 84px 白边 */
  .acct-table tr.acct-row td { padding: 2px 0; }

  /* 「标签 + 值」改横排：竖排时每格至少占「标签 + 值」两行（约 40px），
     横排一行就够（约 24px）。只给短值的格子用，代理那格的值是可伸缩控件，
     横排能让标签与选择器同一基线 */
  .acct-table tr.acct-row .cell-priority,
  .acct-table tr.acct-row .cell-limits,
  .acct-table tr.acct-row .cell-expiry,
  .acct-table tr.acct-row .cell-usage,
  .acct-table tr.acct-row .cell-connections,
  .acct-table tr.acct-row .cell-proxy {
    flex-direction: row;
    align-items: center;
    gap: 8px;
  }
  .acct-table tr.acct-row td::before { flex: 0 0 auto; }
  /* 横排后值一侧要能收缩，否则 Select 触发器把卡片顶出去 */
  .acct-table tr.acct-row .cell-proxy > * { min-width: 0; }

  /* 操作按钮：桌面是四颗 24px 高的小按钮挤在 200px 的列里。卡片里让它们撑满
     整行均分 —— 触控目标从 32×24 提到约 60×34，也不再是一小撮堆在左边 */
  .acct-table tr.acct-row .cell-actions .row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    width: 100%;
  }
  .acct-table tr.acct-row .cell-actions button {
    /* basis 给 0 而不是 auto：这些按钮带 Tailwind 的 shrink-0（那条工具类是
       !important），按内容宽排会 4×64 + 间距 > 卡片内容宽，第四颗被切掉。
       basis 0 时行内没有溢出，grow 均分剩余宽度 */
    flex: 1 1 0;
    min-width: 0;
    min-height: 34px;
    justify-content: center;
  }

  /* 自明的格子不挂列名：勾选框、账号名（本身就是标题）、提供商徽章、
     启停开关、操作按钮 */
  .acct-table tr.acct-row .cell-pick::before,
  .acct-table tr.acct-row .cell-account::before,
  .acct-table tr.acct-row .cell-provider::before,
  .acct-table tr.acct-row .cell-status::before,
  .acct-table tr.acct-row .cell-actions::before { content: none; }

  /* 空值格子整格收起：连接数为 0 时后端渲染空 td，留着只剩一个
     「连接数」标签配一片空白 */
  .acct-table tbody td:empty { display: none; }
  /* 连接数收起后，余额那一格补到整行，不留半截空位 */
  .acct-table tr.acct-row:has(.cell-connections:empty) .cell-usage { grid-column: 1 / 4; }
}

/* ═══ 请求日志：多列 grid -> 卡片 ═══
   桌面 9 列（时间 / 提供商·账号 / 重试 / 状态 / 模型 / 用时 / 用量 / 错误 /
   详情）走 --req-cols 变量，行宽下限由 JS 算成 ~520px 写在 #req-list 上。
   375px 里横滚要滑两次、第一列滚出去就分不清在讲哪条请求。

   改成卡片：表头藏掉，每行变成纵列的「label + 值」列表，label 由
   mobile-columns.js 从 .req-head > span[data-col] 写进 .req-row > span。
   值为空（重试 / 错误 / 详情没内容）的格整格隐藏，不留空标签。 */
@media (max-width: 767px) {
  .log-list { overflow-x: hidden; }
  #req-list { overflow-x: hidden; }
  .req-head { display: none; }
  .req-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
    padding: 10px 12px;
    min-width: 0 !important;   /* 压住 JS 写在 #req-list 上的 --req-row-min */
  }
  .req-row > span {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    flex-wrap: wrap;
  }
  .req-row > span::before {
    content: attr(data-col);
    flex: 0 0 40px;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .03em;
    color: var(--text-3);
  }
  /* 空值整格隐藏：req-none 是「无」占位，:empty 是错误 / 详情无内容 */
  .req-row > .req-none,
  .req-row > span:empty { display: none; }

  /* 重置 page-requests.css 的 @media(max-width:1200px) 逐格定位：它把这几列
     重排成 4 列 × 4 行的「时间+模型 / 提供商+用时 / 用量 / 客户端+详情」表格。
     改成单列卡片后这些 grid-row/grid-column 会让浏览器隐式生成多余列轨道，
     单列变成多列。逐格重置为 auto 后回归自然流。
     ⚠️ 这份清单必须与 page-requests.css 那个媒体查询里的逐格定位**逐格对应**：
     那边新增一列（例如后来的「客户端」）而这边漏了它，漏掉的那一格就会带着
     grid-column 落进单列卡片里，整块破版。 */
  .req-row > .req-time,
  .req-row > .req-model,
  .req-row > .req-status,
  .req-row > .req-retry,
  .req-row > .req-target,
  .req-row > .req-dur,
  .req-row > .req-usage,
  .req-row > .req-client,
  .req-row > .req-error-cell,
  .req-row > .req-detail {
    grid-row: auto;
    grid-column: auto;
    justify-self: stretch;
  }
  .req-row > .req-detail { justify-self: stretch; justify-content: flex-end; }
  /* 用量两行读数（in/out/all + 缓存命中率）竖排，标签在左上 */
  .req-row > .req-usage { flex-direction: column; align-items: flex-start; gap: 2px; }
  .req-row > .req-usage::before { flex: 0 0 auto; }
  .req-row > .req-usage > * { flex: 0 0 auto; width: 100%; }
  /* 错误信息要能读全：不换行会被压成半行截断 */
  .req-row > .req-error-cell { word-break: break-word; }
  /* 详情按钮右对齐到行尾 */
  .req-row > .req-detail { justify-content: flex-end; }
}

/* ═══ 运行日志行：消息列吃满剩余宽度 ═══
   桌面三列 grid（10px 轨道 / 117px 消息 / 93px 时间）在 272px 的容器里，
   消息只有 117px —— 日志正文里混着账号 ID、路径、端点，117px 一行放不
   下一个中文字符组。中间列改弹性、时间列 max-content，消息多拿一倍宽度。
   .log-line 本来就是 flex-wrap:wrap，cat/lvl 与 msg 自然分两行，不用额外处理。 */
@media (max-width: 767px) {
  .log-row {
    grid-template-columns: 10px minmax(0, 1fr) max-content;
    column-gap: 8px;
    padding: 8px 10px;
  }
}

/* ═══ 模型管理页：提供商栏 ═══
   218px 的固定栏在 375px 屏里吃掉一半多。900px 断点已经把 .mm 收成单列
   （提供商栏在上、表格在下），这里不要再改 grid-template-columns ——
   改回两列会让表格只剩 ~140px，7 列表完全没法看。这里只收窄栏内留白。 */
@media (max-width: 767px) {
  .prov-rail { padding: 6px 6px 10px; }
}

/* ═══ 模型管理页：标题栏与筛选区排整齐 ═══
   .mm-titlebar（标题 + 统计 + 动作）与 .mm-filters（状态筛选 + 倍率排序 +
   搜索框 + 批量药丸）桌面是两行通栏，各自 flex-wrap。375px 下：
   · .mm-titlebar 里「标题/统计」被动作按钮挤成 32px 宽多行堆叠
   · .mm-filters 三个控件各自一行、宽度 171/142/240px 参差，右边缘全不齐
   改成与账号页工具条同款：横向每一组占整行（控件吃满、左边缘对齐），
   动作按钮排 auto-fit 等宽网格。 */
@media (max-width: 767px) {
  /* 标题栏：标题+统计一行（整行），动作按钮整行成网格。
     注意 flex-basis:100% 必须配 min-width:0，否则在 flex 容器里被压成
     10px 宽（文字全竖排）—— 实测过这个坑。 */
  .mm-titlebar {
    flex-wrap: wrap;
    row-gap: 8px;
    padding: 12px 12px 6px;
  }
  .mm-titlebar .mm-title { flex: 1 1 100%; width: 100%; min-width: 0; flex-wrap: wrap; }
  .mm-titlebar .mm-title > * { min-width: 0; }
  .mm-titlebar .mm-title h2 { white-space: normal; }
  .mm-titlebar .mm-meta { white-space: normal; overflow: visible; text-overflow: clip; flex: 1 1 100%; min-width: 0; }
  /* 动作区：整行 + 等宽网格（获取模型 / 添加模型 / 列设置） */
  .mm-titlebar > .head-actions {
    flex: 1 1 100%;
    width: 100%;
    min-width: 0;
    margin-left: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 8px;
  }
  .mm-titlebar .head-actions > * { width: 100%; min-width: 0; min-height: 30px; }
  /* 按钮统一等高：组件库给了 h-[26px] 一档，网格里与 30px 档混排会参差 */
  .mm-titlebar .head-actions button { min-height: 30px; }

  /* 筛选区：每个控件占整行、吃满宽度，右边缘对齐成一条直线 */
  .mm-filters {
    flex-flow: column nowrap;
    align-items: stretch;
    gap: 8px;
    padding: 6px 12px 12px;
  }
  .mm-filters > * { flex: 0 0 auto; width: 100%; min-width: 0; max-width: none; }
  /* 分段筛选（状态 / 倍率排序）：自身铺满、内部三档等分 */
  .mm-filters [data-slot="segmented-control"] { width: 100%; }
  .mm-filters [data-slot="segmented-control"] > * { flex: 1 1 0; min-width: 0; justify-content: center; }
  /* 搜索框（input-group）：吃满整行。组件库给的是 w-[240px] 固定值，
     与上面两条 274px 的线段右边缘不齐 —— 覆盖成整行宽。 */
  .mm-filters [data-slot="input-group"],
  .mm-filters [data-slot="input"] { width: 100% !important; max-width: none !important; min-width: 0 !important; }
  /* 批量药丸排成一行可横向滚（数量多时不挤成多行） */
  .mm-filters .batch-chip { width: auto; }
}

/* ═══ 设置页：手机改成「一级菜单 -> 二级设置页」 ═══
   之前是「顶部横条 + 下方内容」：11 个分类横向滚动才能找全，找到后内容
   又直接铺在下面，两个层级混在一屏里。
   现在按手机原生设置页的习惯分两层（只在 ≤767px 生效）：
     · 一级：只有分类列表，一屏看全 11 项
     · 二级：点进某一类，显示该类的面板，顶部一条「返回 + 分类名」
   层级由 .settings-layout.is-detail 切换，该 class 由 React 的
   mobileDetail 状态控制（settings-page.tsx）。

   ★ 桌面端：.settings-back-bar 必须**彻底不占位**（display:none 而不是
   视觉隐藏）—— .settings-layout 是 flex 容器，多一个子元素就会改变
   导航与内容区的排布。 */

/* 返回栏：桌面端不存在 */
.settings-back-bar { display: none; }

/* 断点用 900px 而不是 767：page-settings.css 在 ≤900px 就把左栏折成顶部横条，
   767-900px（平板 / 窄窗口）落在那套「横条 + 内容」里 —— 11 个分类要横滚才
   找得全。两级模式在 900px 以下全部接管，与导航折叠的断点对齐。 */
@media (max-width: 900px) {
  .settings-layout { flex-direction: column; gap: 0; }

  /* ── 一级：分类列表 ──
     纵向列表而不是横条：一屏 11 项、每项 44px 触控高度、右侧留箭头暗示可进入。 */
  .settings-nav {
    width: auto;
    flex-direction: column;
    gap: 0;
    padding: 0;
    border-right: 0;
    border-bottom: 0;
    overflow-x: visible;
    overflow-y: auto;
  }
  .settings-nav-item {
    width: 100%;
    flex: 0 0 auto;
    min-height: 46px;
    padding: 0 12px;
    border-radius: 0;
    border-bottom: 1px solid var(--hairline);
    font-size: 13.5px;
  }
  /* 列表项右侧的进入箭头（纯装饰，用 ::after 画，不额外加 DOM） */
  .settings-nav-item::after {
    content: "";
    width: 7px;
    height: 7px;
    margin-left: auto;
    border-top: 1.5px solid var(--text-3);
    border-right: 1.5px solid var(--text-3);
    transform: rotate(45deg);
    flex: 0 0 auto;
  }
  /* 一级里不强调「选中」：列表是入口不是状态，选中色留给二级 */
  .settings-nav-item.active { background: transparent; color: var(--text); }
  .settings-nav-item.active::after { border-color: var(--text-3); }

  /* ── 层级切换 ──
     一级：显示导航、藏内容区；二级：反过来。 */
  .settings-layout:not(.is-detail) .settings-panes { display: none; }
  .settings-layout.is-detail .settings-nav { display: none; }

  /* ── 二级：返回栏 + 内容 ── */
  .settings-layout.is-detail .settings-back-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
    padding: 8px 12px;
    border-bottom: 1px solid var(--hairline);
  }
  .settings-back-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-height: 32px;
    padding: 0 8px 0 2px;
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--primary);
    font-size: 13px;
    cursor: pointer;
  }
  .settings-back-btn .ico { display: grid; place-items: center; transform: rotate(180deg); }
  .settings-back-btn .ico svg { display: block; }
  .settings-back-title {
    margin-left: auto;
    font-size: 12px;
    color: var(--text-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* 二级页内容区占满剩余高度、自己滚 */
  .settings-layout.is-detail .settings-panes {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 0 20px;
  }

  .settings-switches { flex-direction: column; }
  /* 保留天数 / 提示词阈值一类「标签 + 输入」的两列表，窄屏改成标签在上 */
  .retention-row { grid-template-columns: max-content 1fr; row-gap: 4px; }
}

/* ═══ 报表卡片单列 ═══ */
@media (max-width: 767px) {
  .report-rank-grid,
  .report-donut-grid { grid-template-columns: minmax(0, 1fr) !important; }
}

/* ═══ 报表排行卡：名字 / 条 / 读数 / 百分比四列 -> 两行 ═══
   桌面 grid-template-columns: minmax(0,max-content) minmax(56px,1fr) 68px 48px，
   靠 subgrid 让 .rank-row 继承四列。卡片单列铺满后容器约 246px，减去
   padding 14×2 剩 218px：68 + 48 + 3×10(gap) = 146px 固定占位，只剩 72px
   给 minmax(0,max-content) 的名字列 —— 提供商徽章一枚就 140px，名字被压
   到只剩徽章（截断成半个字符）。

   改成两行：第一行名字 + 百分比，第二行进度条 + 读数。名字拿到整行宽度，
   长邮箱也能展示完整。 */
@media (max-width: 767px) {
  .rank-shares {
    grid-template-columns: minmax(0, 1fr);
    padding: 12px;
    gap: 10px;
  }
  .rank-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    gap: 4px 8px;
  }
  .rank-row .name {
    grid-column: 1;
    grid-row: 1;
    max-width: none;
    white-space: normal;
    word-break: break-word;
    text-overflow: clip;
  }
  .rank-row .pct { grid-column: 2; grid-row: 1; }
  .rank-row .track { grid-column: 1 / -1; grid-row: 2; }
  .rank-row .num { grid-column: 2; grid-row: 2; }
}

/* ═══ 文档页：接口地址行 ═══
   .ep-value 是 code 元素，桌面一行放得下。窄屏下 code + 复制按钮挤在一行，
   长地址（含路径参数）被省略号吃掉后半段 —— 而这半段正是用户要复制的部分。

   但「值独占整行 + 按钮折到下一行」也不好：按钮孤零零占一行、每行多出
   30px 空白，六个接口地址就是 180px 的浪费。改成：值占满左侧并允许折行，
   按钮钉在右上角（align-self: flex-start），两者同行。 */
@media (max-width: 767px) {
  .ep-line {
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 8px;
  }
  .ep-value {
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-all;
    white-space: normal;
    line-height: 1.5;
    /* 给按钮让出一点呼吸空间，长地址折行时不贴着按钮 */
    padding-right: 2px;
  }
  .ep-line button { flex: 0 0 auto; align-self: flex-start; }
}

/* ═══ 账号分组带：尾部「⋯ / 折叠」按钮被挤出带外裁掉 ═══
   .grp-band-inner 是 flex 且 nowrap，中部 .grp-name/.grp-stat 都 nowrap，
   长组名把 ⋯ 按钮推出 td 右缘（实测 ⋯ 按钮出界 14-63px）。
   修法：让名字可截断，按钮钉死在行尾。 */
@media (max-width: 767px) {
  /* 分组带是一条信息密度很高的横条：把手 + 顺位 + 徽标 + 家名 + 统计 + 余额
     + 两个按钮。所有元素都写 `flex: 0 1 auto` 时它们会平均收缩，
     实测「小浣熊」被压到 16px（只剩一个字的宽度）、余额被压到 43px
     塞进「可用合计345计入 1/4⚠」。
     这里按重要性重新分配：
       · 固定不动：把手、徽标、两个按钮（触控目标，不能被压）
       · 保证宽度：家名（至少要显示 2-3 个字）
       · 优先压缩：统计（「4 个账号 · 启用 4 · 禁用 0」→ 省略）、余额
     放不下时整体允许折行，而不是把每个元素都挤成一条。 */
  /* 分组带在 335px 里要塞 9 个元素（把手 / 顺位 / 徽标 / 家名 / 统计 /
     余额 / 两个按钮），内容需求 363px —— 物理上装不下。之前靠「全部可
     压缩」硬塞，结果是每个都被压成半截（「4 个...」「可用」）。
     这里改成：允许整条横向滚动，各元素保持完整宽度。
     按钮固定在行尾不动（position: sticky），滚到哪都能点到。 */
  /* ── 分组带：一条横条塞 9 个元素，335px 里装不下（内容需求 363px）──
     之前靠「所有元素都可压缩」硬塞，结果每个都被压成半截：
     「4 个...」「可用」。改为按重要性分配：
       · 固定不压：把手、徽标、家名、两个按钮（触控目标）
       · 完整显示：统计、余额（允许整条横向滚动看全）
       · 收掉：spacer（横滚时它只会把内容推得更远）
     按钮 position:sticky 钉在右缘，滚到哪都能点到。 */
  .grp-band-inner {
    min-width: 0;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .grp-band-inner::-webkit-scrollbar { display: none; }
  .grp-band-inner > * { flex: 0 0 auto; }
  .grp-band-inner .grp-spacer { display: none; }

  .grp-band-inner .grp-name {
    min-width: 3.2em;
    max-width: 7em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* 「第 1 顺位」收成小号灰字：顺位靠拖动把手感知，不值得占 45px */
  .grp-band-inner .grp-rank {
    white-space: nowrap;
    font-size: 10.5px;
    color: var(--text-3);
  }
  .grp-band-inner .grp-stat { white-space: nowrap; }
  /* 余额：「计入 x/y」是脚注，窄屏丢掉；读数与 ⚠ 保留 */
  .grp-band-inner .grp-balance { white-space: nowrap; }
  .grp-band-inner .grp-balance .n { display: none; }

  /* 按钮钉右缘 */
  .grp-band-inner .grp-iconbtn {
    position: sticky;
    right: 0;
    background: var(--rail-bg, var(--surface));
    z-index: 1;
  }
  .grp-band-inner .grp-iconbtn + .grp-iconbtn { right: 30px; }

  /* 触控目标放大（原 18-22px，手指难点） */
  .grp-band-inner .queue-grip,
  .grp-band-inner .grp-iconbtn { width: 30px; height: 30px; min-width: 30px; }
}

/* ═══ 全局触控与滚动 ═══
   移动端最小触控目标：图标/小按钮不小于 30px（Apple HIG 44px 是上限，
   面板密度优先取 30，够手指点中又不毁布局）。覆盖各页 icon 按钮：
   模型页提供商栏删除 ×、文档页复制 ⧉、账号分组带把手等。 */
@media (max-width: 767px) {
  /* 文档级横向溢出彻底锁死。body 的 min-width:720px 已在 tokens.css 里
     用媒体查询去掉，这里再加一道 overflow-x 保险。 */
  html { -webkit-text-size-adjust: 100%; }
  body { overflow-x: hidden; }
  /* 按钮 / 可点行 44px 最小触控高 */
  .nav-item, .btn { min-height: 44px; }
  .nav-item { padding-top: 6px; padding-bottom: 6px; }

  /* ── CJK 断行口径 ──
     中文按字换行是正常行为，真正的"竖排"来自容器被挤到 ~1 个字宽
     （例如 flex 行里某个弹性格被固定宽的兄弟挤成 width:0），
     已在各页就地修容器宽度。这里只保证长串不撑破布局：默认按字断，
     超长英文/URL 允许断词。 */
  .page.active, .modal, [role="dialog"] { word-break: normal; overflow-wrap: break-word; }

  /* ── 图标 / 小按钮触控目标放大（原 16-22px，手指点不中）── */
  /* 状态开关（账号表 / 签到条目）：原 36×21，抬到 30px 高 */
  [role="switch"], [data-slot="switch"] { min-height: 30px; }
  /* 模型管理页：提供商栏删除 × */
  .prov-rail button, .prov-rail [role="button"] { min-width: 30px; min-height: 30px; }
  /* 文档页：复制按钮 ⧉ */
  .ep-line button { min-width: 30px; min-height: 30px; }
  /* 请求日志：详情列按钮、重试标签可点区 */
  .req-row button { min-height: 30px; }
  /* 模型表：＋等级 小按钮 */
  .models-table td button.sm, .models-table td [class*="button"] { min-height: 28px; }
  /* 账号表：组内拖动手柄（order-grip / queue-grip） */
  .acct-table .order-grip, .acct-table .queue-grip { min-width: 28px; min-height: 28px; }
}

/* ═══════════════════════════════════════════════════════════════
   以下为 3.0.9 之后新增两页（Agent / AI 助手）的窄屏补丁。
   上一版适配层写于这两页存在之前，通用规则会与它们的专用布局打架，
   这里逐条压掉 —— 仍全部锁在 767px 内，桌面端不受影响。
   ═══════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  /* ─── Agent 页：工具栏是纵向 flex，搜索框的 flex-basis 会变成高度 ───
     page-agent.css 给 .agent-search 写了 `flex: 0 1 240px`（桌面是宽度），
     而本文件上方 `.toolbar { flex-flow: column nowrap }` 把主轴转成了竖的，
     同一条 flex-basis 于是被当成**高度**，搜索框被拉成 240px 高的方块。
     窄屏下工具栏本就该竖排，这里把搜索框的高度交回内容。 */
  .agent-toolbar .agent-search {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    height: auto;
  }
  /* 工具栏纵向排列后，标签行与计数行各自成行即可，不要再让它们互挤 */
  .agent-toolbar .agent-count { align-self: flex-start; }

  /* Agent 页两栏：980px 断点已改成单列（左栏限高 200px），手机上再收一点，
     让左栏只占一屏的 1/4 左右，别把详情区推到屏幕外 */
  .agent-body { grid-template-rows: minmax(0, auto) minmax(0, 1fr); }
  .agent-rail { max-height: 168px; }

  /* 客户端条目在窄屏：头像 + 名称 + 版本挤一行会压扁名称 */
  .agent-client { grid-template-columns: 28px minmax(0, 1fr); }
  .agent-client-ver { display: none; }

  /* 表单 / 卡片：标签在窄屏统一折到控件上方（980px 断点只处理了 .agent-field） */
  .agent-card-body { padding: 10px; }
  .agent-dhead { flex-wrap: wrap; }
  .agent-meta { flex-wrap: wrap; }
  /* 写入预览的 <pre> 里每行是 flex 子项，必须能折行，否则长行撑出卡片 */
  .agent-preview pre, .agent-card pre {
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: anywhere;
  }

  /* ─── AI 助手页：900px 断点已把左栏做成抽屉，这里只补手机细节 ───
     抽屉宽 240px 在 320px 视口里占 3/4，收窄到 min(240px, 78vw)。 */
  .asst-side { width: min(240px, 78vw); }

  /* 顶栏那一排（新对话 / 标题 / 助手设置）在窄屏换行，别让标题被压成 0 宽 */
  .asst-top { flex-wrap: wrap; row-gap: 6px; }
  .asst-title { flex: 1 1 100%; order: -1; }

  /* 助手消息里的表格（markdown 渲染）在窄屏允许横滚，不撑破气泡 */
  .asst-msg .md-table, .asst-msg .asst-mini-table {
    display: block;
    overflow-x: auto;
    max-width: 100%;
  }
  .asst-msg.user .bubble { max-width: 100%; }

  /* 代码块：长行折行，避免整页被一条命令撑宽 */
  .asst-msg.bot .md pre {
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: anywhere;
  }
}

@media (max-width: 767px) {
  /* Agent 页筛选 segmented control：四个选项一行排不下（320px 下第 4 个右边缘
     到 321px，「未安装」被裁）。改成两行两列，与账号页工具条的栅格口径一致。 */
  .agent-toolbar [data-slot="segmented-control"],
  .agent-toolbar .group\/seg {
    display: grid;
    grid-template-columns: 1fr 1fr;
    width: 100%;
  }
  .agent-toolbar [data-slot="segmented-control"] > *,
  .agent-toolbar .group\/seg > * {
    width: 100%;
    justify-content: center;
    min-width: 0;
  }
}

@media (max-width: 767px) {
}

@media (max-width: 767px) {
  /* 账号分组带的余额格：上一版只藏了数字（.grp-balance .n { display:none }），
     但「余额查询失败」的 ⚠ 徽标仍占位 —— 320px 下 .grp-band-inner 的内容
     被顶到 324px（视口 320），右边缘裁掉一角。
     .grp-balance 是 flex 且 flex:0 0 auto，子项按内容撑宽，光靠它自己的
     overflow:hidden 收不住；这里把它改成可压缩，并让内部文本省略。
     余额读数本身保留（它是组级合计，窄屏也该看得到）。 */
}

/* ═══════════════════════════════════════════════════════════════
   观感优化层（2026-10 手机端实测后补）
   ───────────────────────────────────────────────────────────────
   前一轮只保证「不溢出、能操作」，但逐页截图后暴露出另一类问题：
   首屏被面板头 / 筛选区吃光，真正的内容要滑一屏才看得到。
   实测 375×812：

       页面          chrome 高   内容可见比
       模型管理      456px       44%
       报表          636px       22%
       账号          205px       75%

   这一节专门压这些「非内容」的垂直占用，全部锁在 767px 内。
   ═══════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  /* ─── 报表页：六张统计卡从单列改两列 ───
     .field-grid 的桌面口径是 repeat(auto-fit, minmax(178px, 1fr)) ——
     手机上容器只有 275px，178×2 + gap 放不下，auto-fit 退化成单列，
     六张卡竖排占 565px（一屏 70%）。窄屏把下限降到 130px 就排得下两列。
     卡片本身内容很少（标题 + 读数 + 一行说明），两列不会挤。 */
  .field-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .field { padding: 10px 11px; }

  /* ─── 模型管理页：标题栏与筛选区压缩 ───
     mm-titlebar 148px（标题 54 + 三个按钮 68 竖排），mm-filters 160px
     （三组筛选各一行 29px + 搜索 30px）。两处合计 308px，首屏只剩 44%。
     目标：标题与按钮同行（标题可省略号），三组筛选横向排列可横滚。 */
  /* ─── 账号页：工具条 205px -> 压缩 ───
     现状：筛选组四行（提供商 / 搜索 / 状态 / 限额）各占整行，
     再加两行按钮。压缩点：标签列从 44px 收到 38px、行间距收紧。 */
  .toolbar { padding: 10px 12px; gap: 7px; }
  .toolbar .group > .label { flex: 0 0 38px; font-size: 11px; }

  /* ─── 日志页：单条日志的正文限行 ───
     正文里常有一长串模型映射（「mimo-v2.6-pro → cline-pass/...」），
     折行后单条能到 400px+，一屏只放得下一条。
     注意 .log-line 是 flex 容器（-webkit-line-clamp 对它无效），正文在
     .log-main 里，所以要作用在 .log-main 上；同时给整行一个高度上限，
     避免 flex 子项撑开。桌面端保持原样。 */
  /* 正文在 .log-line > .msg 里（.log-line 是 flex，钳制对它无效）。
     .msg 自身钳到 4 行，整行高度自然收住。 */
  .log-main .msg {
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
    overflow-wrap: anywhere;
  }
  .log-row { align-items: flex-start; }
}

@media (max-width: 767px) {
  /* ─── 模型管理页：提供商栏从纵向列表改成横向横滚条 ───
     桌面是左侧 210px 高的纵向导航（内置提供商 / 自定义 / 新建）。手机上
     它横跨面板顶部占 210px，与标题栏 142 + 筛选 150 合计 616px ——
     812px 视口里首屏只剩 24%，一个模型都看不到。
     改成一行横向可滚的胶囊：高度降到 ~44px，省下约 165px。
     「新建自定义提供商」作为最后一个胶囊留在行尾。 */
  .prov-rail {
    flex-direction: row;
    align-items: center;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 6px 10px;
    border-right: 0;
    border-bottom: 1px solid var(--sidebar-border);
    gap: 6px;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .prov-rail::-webkit-scrollbar { display: none; }
  .prov-rail > * { flex: 0 0 auto; }
  /* 条目区不再自己滚（父级已横滚），分组标题在横排里没有意义 */
  .prov-rail .rail-scroll { display: contents; }
  .prov-rail .rail-label { display: none; }
  .prov-rail .rail-foot { display: contents; }
  /* 横排后条目不能再是 w-full（那会让每个条目都撑满整条），
     改成按内容自适应：图标 + 家名 + 数量。家名过长时截断到 72px。 */
  .prov-rail [data-slot='nav-item'],
  .prov-rail > .rail-scroll > * {
    width: auto !important;
    max-width: 128px;
    min-width: 0;
    flex: 0 0 auto;
    padding-left: 8px;
    padding-right: 8px;
    gap: 6px;
  }
  .prov-rail [data-slot='nav-item'] .truncate,
  .prov-rail [data-slot='nav-item'] .min-w-0 {
    flex: 0 1 auto;
    max-width: 62px;
    min-width: 0;
  }
  /* 空态占位在横排里会把整条撑高（实测 168px），直接收掉 */
  .prov-rail .rail-empty { display: none; }
}

@media (max-width: 767px) {
  /* ─── 账号卡片：顺序列并进头部行 ───
     上面那套 6 行分区没有给 .cell-order 定位置，它按自动放置落到第 2 行
     第 1 列，独自占 52×51px 的一格 —— 而它内容只是拖动把手 + 「#1」。
     这里把它挪到头部行、紧贴账号名左侧：卡片少一整行，高度从 252 降到 ~215。 */
  .acct-table tr.acct-row {
    /* 头部四列：勾选 / 顺序 / 账号名 / 启停。顺序列不需要 52px ——
       它只有把手 + 「#1」，24px 够；省下的宽度给账号名。 */
    grid-template-columns: auto 26px minmax(0, 1fr) auto;
  }
  .acct-table tr.acct-row .cell-order { width: 26px; overflow: hidden; }
  .acct-table tr.acct-row .cell-order .order-grip { width: 14px; min-width: 14px; }
  .acct-table tr.acct-row .cell-order .order-num,
  .acct-table tr.acct-row .cell-order .order-cell > *:not(.order-grip) {
    font-size: 11px;
  }
  .acct-table tr.acct-row .cell-pick    { grid-area: 1 / 1; }
  .acct-table tr.acct-row .cell-order   { grid-area: 1 / 2; justify-self: start; align-self: center; }
  .acct-table tr.acct-row .cell-account { grid-area: 1 / 3; }
  .acct-table tr.acct-row .cell-status  { grid-area: 1 / 4; }
  /* 其余行整体下移一行（原来从第 2 行开始的分区，现在从第 2 行起仍然成立，
     但列数从 3 变 4，跨列范围要同步） */
  .acct-table tr.acct-row .cell-provider { grid-area: 2 / 1 / 3 / 4; }
  .acct-table tr.acct-row .cell-priority { grid-area: 2 / 4; }
  .acct-table tr.acct-row .cell-proxy    { grid-area: 3 / 1 / 4 / 5; }
  .acct-table tr.acct-row .cell-limits   { grid-area: 4 / 1 / 5 / 4; }
  .acct-table tr.acct-row .cell-expiry   { grid-area: 4 / 4; }
  .acct-table tr.acct-row .cell-usage    { grid-area: 5 / 1 / 6 / 4; }
  .acct-table tr.acct-row .cell-connections { grid-area: 5 / 4; }
  .acct-table tr.acct-row .cell-actions  { grid-area: 6 / 1 / 7 / 5; }
  /* 顺序格横排：把手 + #1 并排，不再上下叠 */
  .acct-table tr.acct-row .cell-order { flex-direction: row; align-items: center; gap: 2px; }
  .acct-table tr.acct-row .cell-order::before { content: none; }
  .acct-table tr.acct-row .cell-order .order-cell { display: flex; align-items: center; gap: 2px; }
  /* 连接数为空时余额补满整行（列数已变 4） */
  .acct-table tr.acct-row:has(.cell-connections:empty) .cell-usage { grid-column: 1 / 5; }
}

@media (max-width: 767px) {
  /* ─── 账号页工具条：只收紧留白，不改「每行一组」的结构 ───
     结构（提供商 / 搜索 / 状态 / 限额 / 按钮各一行）是刻意设计：
     标签固定 44px、控件左边缘对齐成一条线，比强行并排更整齐。
     能省的是留白：上下内边距 12→9、行间距 8→6、标签列 44→38。
     合计约省 25px，且不改变任何一行的排布语义。 */
  .toolbar { padding: 9px 12px; gap: 6px; }
  .toolbar .group > .label { flex: 0 0 38px; }
  /* 批量操作栏同样收紧（它在工具条下方，也是整行） */
  .batch-bar { padding-top: 6px; padding-bottom: 6px; }
}

@media (max-width: 767px) {
  /* ─── 账号页批量栏：未选中时收成一行紧凑条 ───
     现状 90px：全选 + 已选/共 N 个 + 三颗禁用按钮，分两行。
     未选中时那三颗按钮全是 disabled（列设置 / 批量操作 / 取消选择），
     占一整行却不可用。窄屏把它们收进一行，仅在有选中时展开成两行。
     省约 45px。 */
  .batch-bar {
    padding: 7px 12px;
    gap: 6px 8px;
  }
  /* 第一行三个元素（全选 157 + 已选 92 + 摘要 77 = 326px）在 275px 容器里
     必然折行。把「已选 / 共 N 个」与摘要合并压缩到一行：全选固定，
     后两个各让一半并允许省略。 */
  .batch-bar .batch-select-all { flex: 0 0 auto; }
  .batch-bar .batch-count,
  .batch-bar .batch-summary {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .batch-bar .batch-summary { text-align: right; }
  /* 未选中（没有 .active 且 .batch-actions 全是 disabled）：按钮组压成一行小字 */
  .batch-bar:not(.active) .batch-actions {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px;
  }
  .batch-bar:not(.active) .batch-actions > * {
    min-height: 28px;
    font-size: 11.5px;
  }
  /* 有选中时保持原来的 auto-fit 网格（按钮都可用，需要足够触控面积） */
  .batch-bar.active .batch-actions {
    grid-template-columns: repeat(auto-fit, minmax(78px, 1fr));
  }
}

@media (max-width: 767px) {
  /* ─── 账号卡片：读数行的间距与对齐 ───
     现状问题（460px 实测）：
       · 「限流 正常」与「有效期 已过期」两格紧贴，标签「有效期」看起来
         像是接在「正常」后面的文字，读不出是两列
       · 「余额 查询失败」占整行，值贴左，右侧大片空白
     修法：给两列读数之间加一道细分隔，余额行的值靠右对齐到卡片边缘。 */
  /* 分隔线跟着「限流」的值走，而不是画在整格右缘 ——
     限流格跨了 3 列（226px），内容只有一个小徽章，线画在格右缘会悬在
     180px 空白中间，看起来像把有效期划到了另一栏。改成给值加右边框。 */
  .acct-table tr.acct-row .cell-limits { padding-right: 0; border-right: 0; }
  .acct-table tr.acct-row .cell-limits > :last-child {
    padding-right: 10px;
    border-right: 1px solid var(--hairline);
  }
  /* 限流格在卡片里改回竖排：它的内容现在是「整格可点的读数条 + 一行小字」，
     而上面那条通用规则把它设成了横排（当年只有一枚小徽章时才成立）。横排时
     读数条按 calc(100% + 16px) 的出血宽度参与 flex 计算，会把下面那行「最早 …
     恢复」挤成零宽。竖排后两者各占一行，读数条仍然是整格宽、好点。 */
  .acct-table tr.acct-row .cell-limits {
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
  }
  .acct-table tr.acct-row .cell-limits > .lim-sub { padding-right: 10px; }
  .acct-table tr.acct-row .cell-expiry { padding-left: 0; }
  /* 有效期格本身推到右缘，与卡片右边缘对齐 */
  .acct-table tr.acct-row .cell-expiry { justify-content: flex-end; }
  .acct-table tr.acct-row .cell-expiry::before { margin-right: auto; }
  /* 余额：标签与值贴在一起（之前用 space-between 把值推到最右，
     中间空出一大片，看起来像丢了内容）。整行留白由 flex 自然分配。 */
  .acct-table tr.acct-row .cell-usage { justify-content: flex-start; }

  /* 卡片头部：账号名与启停开关之间的空白让账号名更长，别让长邮箱被截断 */
  .acct-table tr.acct-row .cell-account { min-width: 0; }
  .acct-table tr.acct-row .cell-account .acct-name,
  .acct-table tr.acct-row .cell-account .name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ═══════════════════════════════════════════════════════════════
   滚动归属：单面板页改成「头固定 + 列表滚」
   ───────────────────────────────────────────────────────────────
   手机端把滚动交回 .page.active 之后（见 tokens.css 的说明），多面板页
   （报表 / 签到 / 文档）整页滚是正常的 —— 它们本来就是若干张卡片顺序阅读。

   但「一张面板 + 一个长列表」的页面不该整页滚：面板头（标题 / 筛选 / 操作）
   会跟着列表一起滚出视野，用户翻到第 30 个账号时想改个筛选还得滑回顶部。
   账号 / 模型 / 日志 / 任务 / 设置已经是这套骨架（layout.css 与各页 CSS），
   这里补上**网络代理页**，并把 .models-table-wrap 的固定 max-height 改成吃满。

   桌面端零影响：这些规则全锁在 ≤767px 内。 */
@media (max-width: 767px) {
  .page.active[data-page="proxies"] {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding-bottom: var(--pad);
  }
  .page[data-page="proxies"] .panel {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    margin-bottom: 0;
  }
  .page[data-page="proxies"] .panel-head,
  .page[data-page="proxies"] .panel-foot { flex: none; }
  /* 表格区吃满剩余高度并自己滚 —— 取代桌面的 min(62vh, 720px)。
     62vh 在手机上算出来比可视区还高，于是「页面滚 + 表格滚」双重滚动：
     手指落在表格上滚的是表格，滑到边缘才滚页面，非常难受。 */
  .page[data-page="proxies"] .models-table-wrap {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
  }
  /* 模型管理页同理：它的 .mm-main 已经 flex 吃满，但表格 wrap 的 max-height
     仍是桌面口径，一并放开 */
  .page[data-page="gateway"] .models-table-wrap {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
  }
}

/* ═══ 网络代理页：筛选收进右侧抽屉（与账号页同款） ═══
   代理页的筛选行有四个控件（搜索 / 提供方 / 来源 / 状态），加上面板头
   与内核提示条，812px 屏上占掉 408px —— 表格只剩 211px，一屏看不到两个节点。
   收起后只留「筛选」按钮，四个控件进右侧抽屉。

   ★ 桌面端零影响：按钮 / 遮罩 / 标题栏 display:none，.px-filter-group 是
   display:contents（不产生盒子，四个控件照旧参与原 flex 行）。 */
/* 桌面端：抽屉壳不存在、筛选行保持原 flex 布局。
   .px-filter-group 的 JSX 里带了 Tailwind 的 `flex flex-wrap items-center`
   （utilities 在 @layer 里，优先级高于普通声明），所以 `display: contents`
   必须配 !important 才压得住 —— 否则桌面端那四个筛选控件会变成一个
   独立的块，与「搜索 / 提供方 / 来源 / 状态」同一行的原布局被破坏。 */
.px-filter-open,
.px-filter-scrim,
.px-filter-head { display: none; }
.px-filter-group { display: contents !important; }

@media (max-width: 767.98px) {
  /* 「筛选」按钮：与账号页同一观感（原生 button，不用组件库 Button ——
     它的 inline-flex 工具类在 @layer 里，CSS 压不住，桌面端会漏出来） */
  .px-filter-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 30px;
    padding: 0 12px;
    position: relative;
    border: 1px solid var(--control-border, var(--border));
    border-radius: var(--r-md, 8px);
    background: var(--control, var(--surface));
    color: var(--text);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
  }
  .px-filter-badge {
    display: inline-grid;
    place-items: center;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--primary);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1;
  }

  /* 筛选组：从筛选行搬进右侧抽屉 */
  .px-filter-group {
    display: flex !important;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 25;
    width: min(320px, 86vw);
    padding: 52px 12px 16px;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    overflow-y: auto;
    background: var(--surface);
    border-left: 1px solid var(--border);
    border-bottom: 0;
    transform: translateX(100%);
    transition: transform .2s ease;
    -webkit-overflow-scrolling: touch;
  }
  .px-filter-group.open { transform: translateX(0); }
  /* 抽屉里每个控件整行铺开 */
  .px-filter-group > * {
    width: 100% !important;
    max-width: none !important;
    min-width: 0 !important;
    flex: 0 0 auto;
  }
  .px-filter-group [data-slot="segmented-control"] { width: 100%; }
  .px-filter-group [data-slot="segmented-control"] > * { flex: 1 1 0; min-width: 0; justify-content: center; }
  /* 末尾的「显示 N / 共 M 个出口」读数在抽屉里置顶一行 */
  .px-filter-group > .ml-auto { margin-left: 0; text-align: left; }

  /* 抽屉标题栏 */
  .px-filter-head {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 26;
    width: min(320px, 86vw);
    display: flex;
    align-items: center;
    gap: 8px;
    height: 52px;
    padding: 0 8px 0 14px;
    box-sizing: border-box;
    background: var(--surface);
    border-left: 1px solid var(--border);
    border-bottom: 1px solid var(--hairline);
    transform: translateX(100%);
    transition: transform .2s ease;
  }
  .px-filter-head.open { transform: translateX(0); }
  .px-filter-title { font-size: 14px; font-weight: 650; }
  .px-filter-clear { margin-left: auto; }
  .px-filter-close { min-width: 32px; min-height: 32px; font-size: 14px; }

  /* 遮罩 */
  .px-filter-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 24;
    background: rgba(0, 0, 0, .34);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
  }
  .px-filter-scrim.open { opacity: 1; pointer-events: auto; }
}

@media (min-width: 768px) {
  .px-filter-open,
  .px-filter-scrim,
  .px-filter-head { display: none; }
}

@media (max-width: 767.98px) {
  /* 代理页面板头的动作区：五颗按钮（筛选 / 设置 / 订阅管理 / 刷新 / 新增代理）
     在 375px 里排成一行会溢出 —— 实测「刷新」「新增代理」被裁到屏幕外。
     改成 auto-fit 等宽网格，一行 3 颗、末行不足时自动撑满。 */
  .page[data-page="proxies"] .panel-head {
    display: flex;
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .page[data-page="proxies"] .panel-head > h2 { flex: 1 1 auto; }
  .page[data-page="proxies"] .panel-head > .panel-sub,
  .page[data-page="proxies"] .panel-head > .text-\[11px\] { flex: 1 1 100%; }
  .page[data-page="proxies"] .panel-head > .head-actions {
    flex: 1 1 100%;
    margin-left: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
    gap: 6px;
    width: 100%;
  }
  .page[data-page="proxies"] .panel-head > .head-actions > * {
    width: 100%;
    min-width: 0;
  }
}

/* ═══════════════════════════════════════════════════════════════
   完美适配层（三）：粘性导航与关键读数
   ───────────────────────────────────────────────────────────────
   上一轮解决了「顶栏固定、内容区滚」。但页面内部还有一层导航与读数：
   签到页的 Tab 条、文档页的协议分组标题 —— 它们滚出视野后，用户就失去了
   「我在哪 / 怎么切」的参照。这里给它们加 position:sticky，贴在内容区顶部。

   ★ 桌面端零影响：全部锁在 ≤767px。
   ═══════════════════════════════════════════════════════════════ */

@media (max-width: 767.98px) {
  /* 签到中心的 Tab 条粘性不再在这里处理：那一页已搬进插件包（在 iframe 里，
     跨源），插件页自己把标签条做成 sticky（见包内 `ui/checkin/page.css`）。 */

  /* ─── 文档页：面板头与协议分组标题粘住 ───
     文档页是「一张面板 + 一列接口地址」，往下翻到「模型清单」时已经看不到
     面板头（不知道这页在讲什么）。面板头粘住；分组标题（对话协议 / 模型清单）
     也粘住，形成两级参照。 */
  .ep-panel { display: flex; flex-direction: column; min-height: 0; }
  .ep-panel > .panel-head {
    position: sticky;
    top: 0;
    z-index: 8;
    background: var(--surface);
    border-bottom: 1px solid var(--hairline);
  }
  .ep-group {
    position: sticky;
    top: 0;
    z-index: 7;
    background: var(--surface);
  }
}

@media (max-width: 767.98px) {
  /* ─── 报表页：时间范围选择器粘住 ───
     报表是七张面板顺序阅读（统计概览 / Top 账号 / Top 提供商 / 趋势图 …），
     整页滚是合理的。但「今天 / 7 天 / 30 天 / 本月 / 全部」是**全局**筛选，
     翻到下面的 Top 账号时想换个时间段，得滑回最顶部 —— 这是最常被抱怨的一类。
     把第一个面板头粘住：它带着时间选择器，滚动时始终可点。

     只粘第一个面板头（:first-child），其余面板头照常跟随滚动 ——
     全部粘住会同时堆叠七条，反而挡内容。 */
  .page.active[data-page="overview"] > .panel:first-child > .panel-head {
    position: sticky;
    top: 0;
    z-index: 8;
    background: var(--surface);
    border-bottom: 1px solid var(--hairline);
  }
}

@media (max-width: 767.98px) {
  /* ─── AI 助手：输入区在手机上必须始终可及 ───
     结构本身是对的（.asst-main 是 flex 列，.asst-scroll 滚、.asst-composer
     flex:none）。手机上要补的是三件小事：

     ① 输入框字号在窄屏由 page-assistant.css 的 900px 断点提到 16px（见那里
        的说明：iOS Safari 在字号 < 16px 时聚焦会自动放大整页）。
     ② 输入区底部让出安全区（iPhone 手势条），否则发送按钮会被压住。
     ③ 消息区滚到底部时留一点余量，最后一条不被输入区贴住。 */
  .asst-composer {
    padding-bottom: max(8px, env(safe-area-inset-bottom));
  }
  .asst-scroll { padding-bottom: 8px; }
  /* 输入区不该吃掉三分之一屏：手机上把文本域收到 4 行、底部说明行收掉。 */
  .asst-composer textarea { max-height: 96px; }
  .asst-footline { display: none; }

  /* 顶栏在窄屏已经换行（.asst-top 由 page-assistant.css 的 900px 断点处理），
     这里补一句：提示文案在手机上没信息量，占一行高度，收掉 */
  .asst-top .asst-top-hint { display: none; }
  /* 建议问句（空态的四条）：整行铺开，点击区域够大 */
  .asst-suggests { width: 100%; }
  .asst-suggests > * { width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════
   模型管理页：卡片重排（信息密度）
   ───────────────────────────────────────────────────────────────
   上一版把模型行卡片化后，每张卡 523px 高 —— 一屏只放得下一张，
   123 个模型要滑 60 多屏。问题不在「有没有溢出」，在于布局浪费：

     · 每个格子都是「标签在上、值在下」的竖排（51-57px）
     · cell-check（一个复选框）独占一列 73px 高
     · 九个格子纵向堆叠 = 523px

   重排成「头部一行 + 读数两列 + 详情整行」：
     ① 勾选 + 模型名同一行（模型名可折行）
     ② 倍率 / 可见性 / 来源 / 上下文 —— 四个短读数两两并排，
        且改成「标签在左、值在右」的横排（每格 26px 而不是 51px）
     ③ 能力 / 模型映射 / 操作 —— 整行（内容长，需要宽度）

   实测目标：523px → 约 200px，一屏能看 3-4 个模型。
   ★ 桌面端零影响：全部锁在 ≤767.98px。 */
@media (max-width: 767.98px) {
  .models-table tr:not(.tr-group) {
    grid-template-columns: auto minmax(0, 1fr) minmax(0, 1fr);
    gap: 6px 10px;
    padding: 9px 10px;
  }
  /* 每个格子仍是「标签 + 值」两行，但标签字号再收一档 */
  .models-table tr:not(.tr-group) td::before { font-size: 9.5px; }

  /* ── 头部行：勾选 | 模型名 ── */
  .models-table tr:not(.tr-group) > td.cell-check {
    grid-column: 1; grid-row: 1;
    align-self: start;
    justify-self: start;
    padding-top: 2px;
  }
  .models-table tr:not(.tr-group) > td.cell-model {
    grid-column: 2 / -1; grid-row: 1;
    min-width: 0;
  }
  /* 模型名：主名与副名（供应商显示名）在同一行，主名过长时省略 */
  .models-table tr:not(.tr-group) .cell-model .mid,
  .models-table tr:not(.tr-group) .cell-model .mname {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
  }

  /* ── 短读数：两两并排，标签在左、值在右 ──
     竖排「标签/值」每格 51px，横排后 26px。四个读数占两行、省 50px。 */
  .models-table tr:not(.tr-group) > td.cell-rate,
  .models-table tr:not(.tr-group) > td.cell-visibility,
  .models-table tr:not(.tr-group) > td.cell-source,
  .models-table tr:not(.tr-group) > td.cell-budget {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    min-height: 26px;
  }
  .models-table tr:not(.tr-group) > td.cell-rate::before,
  .models-table tr:not(.tr-group) > td.cell-visibility::before,
  .models-table tr:not(.tr-group) > td.cell-source::before,
  .models-table tr:not(.tr-group) > td.cell-budget::before {
    flex: 0 0 auto;
    margin: 0;
  }
  .models-table tr:not(.tr-group) > td.cell-rate,
  .models-table tr:not(.tr-group) > td.cell-visibility { grid-row: 2; }
  .models-table tr:not(.tr-group) > td.cell-rate { grid-column: 1; }
  .models-table tr:not(.tr-group) > td.cell-visibility { grid-column: 2 / -1; }
  .models-table tr:not(.tr-group) > td.cell-source,
  .models-table tr:not(.tr-group) > td.cell-budget { grid-row: 3; }
  .models-table tr:not(.tr-group) > td.cell-source { grid-column: 1; }
  .models-table tr:not(.tr-group) > td.cell-budget { grid-column: 2 / -1; }

  /* ── 详情整行 ── */
  .models-table tr:not(.tr-group) > td.cell-caps,
  .models-table tr:not(.tr-group) > td.cell-alias,
  .models-table tr:not(.tr-group) > td.cell-act {
    grid-column: 1 / -1;
  }
  .models-table tr:not(.tr-group) > td.cell-caps { grid-row: 4; }
  .models-table tr:not(.tr-group) > td.cell-alias { grid-row: 5; }
  .models-table tr:not(.tr-group) > td.cell-act { grid-row: 6; }

  /* 能力：四个小图标横排，标签同排，不再单独占两行 */
  .models-table tr:not(.tr-group) > td.cell-caps {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-height: 26px;
  }
  /* 模型映射：值是按钮组，横向可滚，别把卡片撑高 */
  .models-table tr:not(.tr-group) > td.cell-alias {
    min-height: 26px;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .models-table tr:not(.tr-group) > td.cell-alias::-webkit-scrollbar { display: none; }
  .models-table tr:not(.tr-group) > td.cell-alias > * { flex: 0 0 auto; }

  /* 操作：按钮横排、等分整行 */
  .models-table tr:not(.tr-group) > td.cell-act {
    flex-direction: row;
    align-items: center;
    gap: 8px;
  }
  .models-table tr:not(.tr-group) > td.cell-act .row-actions {
    display: flex;
    gap: 8px;
    width: 100%;
  }
  .models-table tr:not(.tr-group) > td.cell-act button { flex: 1 1 0; min-width: 0; }

  /* 空值格收掉：没有映射、没有能力时不留一行空标签 */
  .models-table tr:not(.tr-group) > td:empty { display: none; }
}

/* ═══ 模型管理页：筛选收进右侧抽屉（与账号 / 代理同款） ═══
   三个分段筛选 + 搜索框在窄屏各占一行，共 160px；加上标题栏 148px，
   812px 屏里首屏只剩 44%。收起后只留「筛选」按钮。

   ★ 桌面端零影响：按钮 / 遮罩 / 标题栏 display:none；.mm-filters 保持
   原 flex 布局（它是 .panel-head 的直接子项，不能改成 contents —— 那会
   把里面的控件散进面板头的网格里）。 */
.mm-filter-open,
.mm-filter-scrim,
.mm-filter-head { display: none; }

@media (max-width: 767.98px) {
  /* 「筛选」按钮：与另外两页同一观感（原生 button，理由见账号页那段） */
  .mm-filter-open {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 30px;
    padding: 0 12px;
    position: relative;
    border: 1px solid var(--control-border, var(--border));
    border-radius: var(--r-md, 8px);
    background: var(--control, var(--surface));
    color: var(--text);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
  }
  .mm-filter-badge {
    display: inline-grid;
    place-items: center;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--primary);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    line-height: 1;
  }

  /* 筛选区：从面板头搬进右侧抽屉。
     注意 .mm-filters 是 .panel-head 的子项，用 fixed 脱离布局即可 ——
     不能 display:none（那样抽屉里也没了），只能靠 transform 移出视口。 */
  .mm-filters {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 25;
    width: min(320px, 86vw);
    padding: 52px 12px 16px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    overflow-y: auto;
    background: var(--surface);
    border-left: 1px solid var(--border);
    border-bottom: 0;
    transform: translateX(100%);
    transition: transform .2s ease;
    -webkit-overflow-scrolling: touch;
  }
  .mm-filters.open { transform: translateX(0); }
  .mm-filters > * {
    width: 100% !important;
    max-width: none !important;
    min-width: 0 !important;
    flex: 0 0 auto;
  }
  .mm-filters [data-slot="segmented-control"] { width: 100%; }
  .mm-filters [data-slot="segmented-control"] > * { flex: 1 1 0; min-width: 0; justify-content: center; }
  /* 抽屉里给每个控件上方补一个标签（桌面靠位置识别，抽屉里没有上下文） */
  .mm-filters > * + * { margin-top: 2px; }

  /* 抽屉标题栏 */
  .mm-filter-head {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 26;
    width: min(320px, 86vw);
    display: flex;
    align-items: center;
    gap: 8px;
    height: 52px;
    padding: 0 8px 0 14px;
    box-sizing: border-box;
    background: var(--surface);
    border-left: 1px solid var(--border);
    border-bottom: 1px solid var(--hairline);
    transform: translateX(100%);
    transition: transform .2s ease;
  }
  .mm-filter-head.open { transform: translateX(0); }
  .mm-filter-title { font-size: 14px; font-weight: 650; }
  .mm-filter-clear { margin-left: auto; }
  .mm-filter-close { min-width: 32px; min-height: 32px; font-size: 14px; }

  /* 遮罩 */
  .mm-filter-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 24;
    background: rgba(0, 0, 0, .34);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
  }
  .mm-filter-scrim.open { opacity: 1; pointer-events: auto; }
}

@media (min-width: 768px) {
  .mm-filter-open,
  .mm-filter-scrim,
  .mm-filter-head { display: none; }
}

@media (max-width: 767.98px) {
  /* ─── 模型卡片：头部与映射再压一档 ───
     实测卡片 378px，两处最占地方：
       · 头部行 71.8px —— 「上游模型」标签 + 主名 + 副名三行竖排
       · 模型映射 102.7px —— chip 在 268px 里换行

     头部：标签与主名同排（标签是小灰字，不占一整行），副名跟在主名后面。
     映射：chip 横向可滚，不换行。 */
  .models-table tr:not(.tr-group) > td.cell-model {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 8px;
    min-height: 26px;
  }
  .models-table tr:not(.tr-group) > td.cell-model::before {
    flex: 0 0 auto;
    margin: 0;
  }
  /* 主名与副名同一行：主名优先、副名让位并省略。
     实测主名 158px + 副名 103px = 261px > 可用 178px，两者会折成两行
     （头部因此 70px 高）。给副名一个可收缩的 basis，主名吃剩余。 */
  .models-table tr:not(.tr-group) > td.cell-model { flex-wrap: nowrap; }
  .models-table tr:not(.tr-group) > td.cell-model .mid {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .models-table tr:not(.tr-group) > td.cell-model .mname {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 45%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* 模型映射：chip 组不换行，横向滚 */
  .models-table tr:not(.tr-group) > td.cell-alias {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 6px;
    min-height: 26px;
    overflow-x: auto;
    overflow-y: hidden;
  }
  .models-table tr:not(.tr-group) > td.cell-alias::before { flex: 0 0 auto; }
  .models-table tr:not(.tr-group) > td.cell-alias > * { flex: 0 0 auto; }

  /* 操作列：标签与按钮同排，按钮不换行 */
  .models-table tr:not(.tr-group) > td.cell-act {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-height: 30px;
  }
  .models-table tr:not(.tr-group) > td.cell-act::before { flex: 0 0 auto; white-space: nowrap; }
  .models-table tr:not(.tr-group) > td.cell-act .row-actions {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    gap: 8px;
  }
  .models-table tr:not(.tr-group) > td.cell-act button { flex: 1 1 0; min-width: 0; white-space: nowrap; }

  /* 能力：标签与图标同排 */
  .models-table tr:not(.tr-group) > td.cell-caps {
    flex-direction: row;
    align-items: center;
    gap: 8px;
    min-height: 26px;
  }
  .models-table tr:not(.tr-group) > td.cell-caps::before { flex: 0 0 auto; }
}

@media (max-width: 767.98px) {
  /* ─── 模型卡片：最后两行合并 ───
     卡片 288px 的构成：头部 34 + 读数 32+39+39 + 映射 52 + 操作 43。
     「能力」那行只有一个标签 + 四个小图标（26px 就够），与「模型映射」
     可以并排 —— 但两者都是长内容，并排会挤。
     改为：能力并进「操作」行（都是低信息量的一行），省下 39px。 */
  .models-table tr:not(.tr-group) > td.cell-caps {
    grid-row: 4;
    grid-column: 1 / 2;
  }
  .models-table tr:not(.tr-group) > td.cell-act {
    grid-row: 4;
    grid-column: 2 / -1;
  }
  /* 能力格在左半列：标签与图标挤在一行，图标可以换行 */
  .models-table tr:not(.tr-group) > td.cell-caps {
    flex-wrap: wrap;
    align-content: center;
  }
  /* 操作格在右半列：按钮铺满这一列 */
  .models-table tr:not(.tr-group) > td.cell-act .row-actions { width: 100%; }
  .models-table tr:not(.tr-group) > td.cell-act button { width: 100%; }
  /* 映射从第 5 行提到第 4 行之后的第 5 行不变 */
  .models-table tr:not(.tr-group) > td.cell-alias { grid-row: 5; }

  /* 头部：模型名是这张卡的身份信息，必须拿满宽度。
     之前「上游模型」这个标签占掉一半，「raccoon-chat-ml-5-5」只剩 50px
     显示成「raccoo...」。标签在这里没有信息量（卡片里唯一的长 ID 就是模型名），
     窄屏收掉标签，模型名占满整行。
     副名（供应商显示名）也一并收掉 —— 模型 ID 本身已经带了家名前缀。 */
  .models-table tr:not(.tr-group) > td.cell-model::before { content: none; }
  .models-table tr:not(.tr-group) > td.cell-model .mname { display: none; }
  .models-table tr:not(.tr-group) > td.cell-model .mid { flex: 1 1 100%; }
  .models-table tr:not(.tr-group) > td.cell-model .t {
    font-size: 13px;
    font-weight: 600;
  }
}

@media (max-width: 767.98px) {
  /* ─── 模型卡片：三行读数并成两行 ───
     现状五行读数：倍率|可见性 / 来源|上下文 / 能力|操作 / 映射。
     「来源」只有一个色点 + 两三个字（内置 / 远程），单独占半行很浪费。
     把它挪到「倍率」那一行（倍率也是短值），于是：
       第 2 行：倍率 | 来源
       第 3 行：可见性 | 上下文
     省一整行 39px。 */
  .models-table tr:not(.tr-group) > td.cell-rate { grid-row: 2; grid-column: 1; }
  .models-table tr:not(.tr-group) > td.cell-source { grid-row: 2; grid-column: 2 / -1; }
  .models-table tr:not(.tr-group) > td.cell-visibility { grid-row: 3; grid-column: 1; }
  .models-table tr:not(.tr-group) > td.cell-budget { grid-row: 3; grid-column: 2 / -1; }
  /* 能力与操作保持第 4 行，映射第 5 行 */
  .models-table tr:not(.tr-group) > td.cell-caps { grid-row: 4; }
  .models-table tr:not(.tr-group) > td.cell-act { grid-row: 4; }
  .models-table tr:not(.tr-group) > td.cell-alias { grid-row: 5; }
}

@media (max-width: 767.98px) {
  /* ─── 模型卡片：去掉单元格的表格内边距 ───
     每个 td 都带 `padding: 7px 10px`（桌面表格行高需要）。卡片化后
     9 个格子 × 上下各 7px = 126px 的纯留白，占卡片一半高度。
     卡片自己的 padding 已经是 9px，格子内部只留 1px 呼吸。 */
  .models-table tr:not(.tr-group) > td { padding: 1px 0; }
  /* 头部与映射这两处需要一点上下空间，单独给 */
  .models-table tr:not(.tr-group) > td.cell-model { padding: 2px 0 3px; }
  .models-table tr:not(.tr-group) > td.cell-alias { padding: 2px 0; }
}

@media (max-width: 767.98px) {
  /* 读数值被右缘裁掉（「180K / 80」后面的 0K 看不见）：
     这些格是 flex 行，值那侧没给 min-width:0 也不会省略，
     而是溢出到卡片外被 overflow 裁掉。让值可收缩并省略。 */
  .models-table tr:not(.tr-group) > td.cell-rate > *,
  .models-table tr:not(.tr-group) > td.cell-visibility > *,
  .models-table tr:not(.tr-group) > td.cell-source > *,
  .models-table tr:not(.tr-group) > td.cell-budget > * {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* 模型名同理：给足宽度后仍可能超，省略号兜底 */
  .models-table tr:not(.tr-group) > td.cell-model .t {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
