/* ─────────────────────────────────────────────
   请求日志页：转发明细表（列形态参考 OmniProxy 的请求日志）
   列表容器与筛选行（.log-list / .log-empty / .log-filters）沿用 page-logs.css
   的同一套声明，本文件只写表格本身、两个维度筛选的宽度上限与窄窗口重排。
   页脚分页栏是通用件（islands/table-shell.tsx），样式在 layout.css。
   颜色一律取 tokens.css 的语义变量，浅色深色共用同一组声明。
   ───────────────────────────────────────────── */

/* ── 提供商 / 模型两个维度筛选（本次新增）─────────────────────
   宽度由 page-logs.css 的 `.log-filters select { width: auto }

` 定成「按内容」，
   这里只加上限：模型名可以很长（claude-sonnet-4-5-20250929 之类），
   不设上限时它会按最长选项把触发器撑得很宽、把时间档位挤到换行。
   截断只发生在**触发器**上（显示省略号）；选项完整文案在浮层里
   （select.js 的浮层宽度独立算：至少与触发器同宽、最长 320px）。
   两个上限按各自的内容量级取：提供商名最长是「AutoClaw 国内版」（约 110px），
   模型名普遍再长一档。 */
.log-filters #req-provider { max-width: 168px; }
.log-filters #req-model { max-width: 200px; }

/* 九列：时间 / 提供商·账号 / 重试 / 状态 / 模型 / 用时 / 用量 / 错误 / 详情。
   时间与用时定宽（等宽数字 + tabular-nums，竖着比对才齐），
   其余占弹性宽度（都是不定长内容，给固定宽度会互相挤）：
   模型 1.3fr（claude-sonnet-4-5 这类名字要放得下），
   用量 1.6fr（两行读数里 in/out/all 最长），超出由省略号收住、完整值进 title。
   状态 96px：进行中的行有两行（阶段徽章 + 阶段计时），而徽章最长是
   「等待响应」四个字（约 76px）—— 68px 那一版会把它压成省略号。
   详情列定宽（只有一个「详情」按钮或一个「-」）。

   --req-cols 是用户拖过的列宽（table-columns.js 写在 #req-list 上，靠继承
   作用到每一行 —— 表格是整体重绘的，变量不需要在渲染后再写一遍）：
   拖过哪一列，那一条轨道就从 minmax(…fr) 换成固定像素，没拖过的仍是弹性权重。
   拖动只改被拖的那一列，其余列保持各自宽度；总宽超出容器就由下面的横向滚动
   接手（列宽是绝对量，不做「按比例分完容器」那套）。变量没设时走下面的默认值，
   改默认列宽时与 table-columns.js 里对应列的 track 字段要同步。 */
.req-row,
.req-head {
  display: grid;
  grid-template-columns: var(--req-cols,
    92px minmax(0, 1.1fr) 52px 96px minmax(0, 1.3fr) 96px minmax(0, 1.6fr) minmax(0, 1.2fr) 60px);
  gap: 12px;
  align-items: center;
  padding: 10px 14px;
  /* 列表是 flex 列容器：条目不许被压缩，超出的一律交给列表滚动 */
  flex: none;
}

/* 表头：粘在列表顶部。列表自身滚动，滚动条是它自己的（不是页面），
   sticky 的参照物就是 .log-list，因此顶部偏移写 0。 */
.req-head {
  position: sticky;
  top: 0;
  z-index: 1;
  padding-top: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  background: var(--table-head-bg);
  color: var(--text-3);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
}

/* 表头格子是列宽把手的定位锚（.col-grip 绝对定位挂在它内側右缘，见 components.css） */
.req-head > span { position: relative; }

/* ── 横向滚动 ──────────────────────────────────────────────
   拖动把列拖宽、或窗口窄到装不下所有列时，让列表横向滚动，而不是把列压没：
   不会伸缩的列（时间 / 重试 / 详情，以及被拖过的那些）压不动时只能滚。

   行宽下限由 table-columns.js 按「不会伸缩的列之和 + 列间距 + 左右内边距」算好，
   写在 #req-list 的 --req-row-min 上：固定列把容器占满时，行与表头跟着撑到内容
   宽，溢出那截才有背景与下边框（否则横向滚到右边，表头会缺一块、行底色只铺
   半截）；不溢出时它正好等于容器宽，与 min-width: 100% 等效。

   **不要改成 CSS 的 max-content**：那是按内容的固有宽度算的，同一列在表头
   （「用量」两个字）和数据行（两行读数）里会算出两个宽度，表头与数据直接错位，
   而且列被内容撑开就不再触发省略号。 */
#req-list { overflow-x: auto; }
#req-list > .req-row,
#req-list > .req-head {
  min-width: var(--req-row-min, 100%);
}

.req-row { border-bottom: 1px solid var(--hairline); transition: background .12s ease; }
.req-row:last-child { border-bottom: none; }
.req-row:hover { background: var(--row-hover); }

/* 表头里用时一格跟数值同侧对齐（数值列是右对齐的） */
.req-head .req-dur { text-align: right; }

/* 失败行轻微着色，与事件日志 error 行同一手法（不干扰阅读的淡底） */
.req-row.failed { background: color-mix(in srgb, var(--danger-soft) 45%, transparent); }
.req-row.failed:hover { background: var(--danger-soft); }

/* 无值占位（重试 / 错误列的「-」）：比正文淡一档，空着会被当成渲染缺失 */
.req-none { color: var(--text-3); }

/* ─── 进行中的行（status=0，转发还没收尾）─────────────────────
   后端契约：转发一开始就插一条 status=0 的行，收尾后由终态记账覆盖
   （见 stats_api::stats_requests）。行本身不加底色 —— 它还没落定，
   红底（.failed）是终态的颜色，进行中的信号交给状态徽章那一格。 */

/* 进行中行**分阶段**显示（连接中 / 等待响应 / 响应中 / 重试中，本次改造）：
   文案与类名由 request-phase.js 给（那边是唯一的文案来源），这里只管配色。
   色相对标 OmniProxy 的 Tag color（cyan / green / orange / processing），
   落到本项目的语义令牌上：连接中 = info 蓝（与改造前那枚「进行中」同色，
   语义也是同一档）、等待响应 = 青（就地定色，理由见下）、响应中 = ok 绿、
   重试中 = warn 琥珀。四个色相在浅深两套主题下都取自 tokens.css 的
   --x / --x-soft / --x-bd 三件套，所以这里不需要再写主题分支。

   为什么「等待响应」要就地定一个青色而不是复用现有四组之一：它与另外三个
   语义都不同（不是失败、不是完成、也不是「刚开始」），而等待时长恰恰是用户
   最常盯着看的一格 —— 借 --ok 会让「等模型出字」（可能很慢）看着像已经成功，
   借 --info 又与「连接中」分不开。OmniProxy 那边同样是就地给了个 cyan。 */

/* 没有阶段可读的在途行（旧数据、更早版本写入的行，见 request-phase.js 的
   FALLBACK_CLS）回落成改造前那枚「进行中」：info 蓝，与下面「连接中」同色
   —— 它们本来就是同一档语义，只是这枚说不出「卡在哪一步」。 */
.badge.tag.running {
  background: var(--info-soft);
  border: 1px solid var(--info-bd);
  color: var(--info);
}

.badge.tag.phase-connecting {
  background: var(--info-soft);
  border-color: var(--info-bd);
  color: var(--info);
}

.badge.tag.phase-waiting {
  background: oklch(0.62 0.12 200 / 12%);
  border-color: oklch(0.62 0.12 200 / 32%);
  color: oklch(0.5 0.13 205);
}

.badge.tag.phase-streaming {
  background: var(--ok-soft);
  border-color: var(--ok-bd);
  color: var(--ok);
}

.badge.tag.phase-retrying {
  background: var(--warn-soft);
  border-color: var(--warn-bd);
  color: var(--warn);
}

/* 排队中：既不是失败（不取 warn）也不是正常推进（不取 ok），
   用「信息」档的紫蓝，与「等待响应」的青蓝区分开 */
.badge.tag.phase-queued {
  background: oklch(0.62 0.14 285 / 12%);
  border-color: oklch(0.62 0.14 285 / 32%);
  color: oklch(0.5 0.16 290);
}

@media (prefers-color-scheme: dark) {
  .badge.tag.phase-waiting {
    background: oklch(0.72 0.11 200 / 15%);
    border-color: oklch(0.72 0.11 200 / 34%);
    color: oklch(0.8 0.1 200);
  }

  .badge.tag.phase-queued {
    background: oklch(0.72 0.13 285 / 15%);
    border-color: oklch(0.72 0.13 285 / 34%);
    color: oklch(0.82 0.12 288);
  }
}

/* 状态列两行（徽章 + 阶段计时）——只在进行中的行上出现。
   与用时列的两行结构同一手法：主读数是徽章，次读数是「这个阶段已经持续了
   多久」（淡一档、小半号）。这一列原先定宽 68px，而徽章最长是「等待响应」
   四个字放不下，所以本次加宽到 96px（见下方 grid-template-columns 与
   table-columns.js 的登记，两处必须同改）。 */
.req-row .req-status {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  min-width: 0;
}

.req-row .req-status-badge {
  display: flex;
  min-width: 0;
}

/* 阶段计时：等宽 + tabular-nums，相邻行的秒数竖着能直接比大小 */
.req-row .req-status-line {
  overflow: hidden;
  /* 与 .req-usage-line 同一条道理：flex 子项不限宽就会被内容顶开、压到隔壁列 */
  max-width: 100%;
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 徽章里的呼吸圆点：1.2s 一拍的不透明度脉动 —— 不用进度条，
   状态列那点宽度塞不下，圆点的「活着」感刚好。currentColor 跟随
   徽章文字色，深浅主题都由 --info 一处决定。 */
.req-live-dot {
  flex: 0 0 auto;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  animation: req-live-pulse 1.2s ease-in-out infinite;
}

@keyframes req-live-pulse {
  0%, 100% { opacity: .3; transform: scale(.75); }
  50% { opacity: 1; transform: scale(1); }
}

/* 系统开了「减少动态效果」就停在常亮：信息还在，动效退场 */
@media (prefers-reduced-motion: reduce) {
  .req-live-dot { animation: none; opacity: .8; }
}

/* ─── 导航徽标的「进行中」态 ──────────────────────────────────
   #nav-count-requests 两种数字共用一颗：进行中走 info 蓝（.is-running），
   未读失败维持 layout.css 的既有红。用 ID 选择器是为了稳定压过
   .nav-item .count:not(.muted) 的红色（与 update-notes.css 同一手法）。

   类名**刻意不叫 live**：layout.css 的 .live 是侧栏状态灯（7px 圆点、
   绿底、光晕），同一个类名会把角标压成小圆点（数字挤在 7px 高度里）。
   两颗组件都表达「活着」，但一个是灯、一个是数字，样式不能共用。 */
#nav-count-requests.is-running {
  border-color: var(--info-bd);
  background: var(--info-soft);
  color: var(--info);
}

/* 选中态导航项底色更深，保持同样的分离度（对齐 layout.css 的 active 覆盖） */
.nav-item.active #nav-count-requests.is-running { background: var(--surface); }

/* ─── 清理请求日志弹窗 ───────────────────────────────
   已迁到组件库（见 ui-islands/src/islands/request-clear-modal.tsx）：两种清理方式
   的单选卡片改走 ui-kit 的 RadioGroup + Tailwind 类，下面那套 .clear-mode 一族
   随之移除。保留这条说明是为了让「原先为什么是卡片而不是 seg」的口径可追 ——
   两种方式的差别（删不删统计行、动不动日报）必须写在选择的当下，
   seg 只放得下名字，放不下后果说明。 */

/* 时间列：日期 + 时刻两行，等宽避免每一行的冒号错位。
   数据格与表头格共用同一个类名（窄窗口要按列重排，见文件末尾的媒体查询），
   所以每一条排版声明都带 .req-row 前缀限定在数据行上 —— 否则表头会被一并
   套上等宽字体，一行小标题看着像数据。 */
.req-row .req-time {
  display: flex;
  flex-direction: column;
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 提供商 / 账号：同格两行，提供商在上（主读数）、账号在下（次）。
   两者都是短名词，省略号收住长名字，完整值在各自的 title 里。 */
.req-row .req-target {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.req-row .req-provider,
.req-row .req-acct {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.req-row .req-provider { color: var(--text-2); font-size: 12px; }
.req-row .req-acct { color: var(--text-3); font-size: 11.5px; }

/* 无提供商 / 无账号（旧数据或请求在任何账号接手前就失败）：给破折号 */
.req-provider.is-empty,
.req-acct.is-empty { color: var(--text-3); }

/* 模型名：标识符用等宽，超长从尾部省略（完整值在 title 里） */
.req-row .req-model {
  min-width: 0;
  overflow: hidden;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 上下名分行的形态（转发名 ≠ 请求名时，如映射 / 备援改写）：
   ⬆️ 上游实际收到的名字（主读数）在上，⬇️ 下游请求的名字（淡一档）在下 ——
   与用时列的两行结构同一手法；单行请求不受影响（类不出现）。 */
.req-row .req-model-split {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.req-row .req-model-line {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
}

.req-row .req-model-line.sub {
  color: var(--text-3);
  font-size: 11px;
}

/* 用时：等宽 + 右对齐，相邻行的数字能直接竖着比大小。
   两行：总用时（主读数）+ 首响（次读数，淡一档，与用量列的 sub 同一手法）——
   OmniProxy 的用时列就是这个形态。只有数据行是两行，表头仍是单行文本
   （所有排版声明都带 .req-row 前缀限定在数据行上，见上方 .req-time 的说明）。 */
.req-row .req-dur {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.req-row .req-dur-line {
  overflow: hidden;
  max-width: 100%;
  text-overflow: ellipsis;
}

/* 首响是次读数：淡一档、小半号，不抢总用时 */
.req-row .req-dur-line.sub {
  color: var(--text-3);
  font-size: 11px;
}

/* 用量两行：in / out / all 与 缓存读取 / 命中率。
   等宽 + tabular-nums，超出省略（完整两行读数在 title 里）。 */
.req-row .req-usage {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.req-usage-line {
  overflow: hidden;
  /* 必须限住宽度：它是 .req-usage（flex 列）的子项，只写 overflow: hidden 时
     宽度仍按内容算（读数一行 nowrap，两百多像素），顶出格子压到右边列上 ——
     列再窄也不会出现省略号。这里跟 .req-model-line / .req-dur-line 用同一个
     写法（max-width: 100%）；单写 min-width: 0 实测压不住。 */
  max-width: 100%;
  color: var(--text-2);
  font-family: var(--font-mono);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 第二行（缓存读取 / 命中率）是附加说明：淡一档，不抢主读数 */
.req-usage-line.sub { color: var(--text-3); }
/* 窄窗口：拆成多行，免得八列都缩成省略号。
   断点取 1200px：默认窗口 1060px 时内容区约 772px，八列必然挤，
   以下面的四列重排为准；拉宽到 1200 以上才换八列横排。

   ── 为什么每个格子都显式写 grid-row / grid-column ──
   只给部分格子写列位置时，剩余格子由自动放置算法补空，落点取决于「哪个格子
   已被占」这个隐式状态：以后往行里插一个新字段，落点会整体错一格且不报错。
   写死行列值后，增删字段只影响它自己那一格。 */
@media (max-width: 1200px) {
  .req-row,
  .req-head {
    /* 第三格（状态）与宽窗口同一口径取 96px：窄窗口下它同样要放得下
       「等待响应」四个字（比默认窗口更需要 —— 这里的弹性列只剩模型那一格） */
    grid-template-columns: 92px minmax(0, 1fr) 96px 52px;
    row-gap: 4px;
  }
  /* 重排成四行之后轨道已经不是那八条了：上面这条声明直接覆盖 --req-cols，
     拖动没有意义，把手一并收起，免得点了没反应 */
  .req-head .col-grip { display: none; }

  /* 表头只保留第一行的四格，其余列头隐藏（列位置已重排，留着会冲突） */
  .req-head .req-time { grid-row: 1; grid-column: 1 / 2; }
  .req-head .req-model { grid-row: 1; grid-column: 2 / 3; }
  .req-head .req-status { grid-row: 1; grid-column: 3 / 4; }
  .req-head .req-retry { grid-row: 1; grid-column: 4 / 5; }
  .req-head .req-target,
  .req-head .req-dur,
  .req-head .req-usage,
  .req-head .req-error-cell { display: none; }

  /* 第一行：时间 + 模型 + 状态 + 重试 */
  .req-row .req-time { grid-row: 1; grid-column: 1 / 2; }
  .req-row .req-model { grid-row: 1; grid-column: 2 / 3; }
  .req-row .req-status { grid-row: 1; grid-column: 3 / 4; }
  .req-row .req-retry { grid-row: 1; grid-column: 4 / 5; }
  /* 第二行：提供商 / 账号 + 用时（用时贴右） */
  .req-row .req-target { grid-row: 2; grid-column: 1 / 3; }
  .req-row .req-dur { grid-row: 2; grid-column: 3 / 5; }
  /* 第三行：用量整行铺开（两行读数不再被列宽挤省略） */
  .req-row .req-usage { grid-row: 3; grid-column: 1 / -1; }  .req-row .req-detail { grid-row: 4; grid-column: 4 / 5; justify-self: end; }
  /* 「-」占位不再独占一行：窄窗口下空行比省略号更难解释 */
  .req-row .req-error-cell { display: none; }
  /* 详情列头在窄窗口下没有稳定的列位（位置随有没有错误而变），一并隐藏 */
  .req-head .req-detail { display: none; }
}

/* 窄窗口：页头的自动刷新开关整行铺开，与日志页、报表页的时间范围同款处理。
   不折的话开关会压在标题文案上换行，标题与 intro 各自占一行，读起来像断了半句。 */
@media (max-width: 900px) {
  .page[data-page="requests"] .panel-head .head-actions {
    flex-basis: 100%;
    margin-left: 0;
    justify-content: flex-start;
  }
}

/* ─── 详情列与详情弹窗（调试模式的上游原始报文）───────────────
   报文里是上游原文（SSE 文本、JSON 体），一律等宽字体 + 保留换行；
   弹窗比常规确认框宽（原文行很长），高度吃满可用空间后内部滚动。 */

.req-row .req-detail { justify-self: end; }

/* ─── 请求详情弹窗的样式已随迁移移除 ───────────────────
   弹窗迁到组件库后（ui-islands/src/islands/request-detail.tsx），下面这些老类名
   失去了消费者，一并删除：.modal-wide（宽度）、#req-detail-terminate[hidden]、
   .req-detail-meta、.req-detail-segs、.seg-dot、.req-detail-pane 一族、
   .req-detail-empty、.req-detail-grid 一族、.req-detail-sub / -error / -model-split。
   宽度改由 DialogContent 的 className 表达；三标签改走组件库的 SegmentedControl；
   详情网格改走 Tailwind 类。保留的只有下面两条 —— 它们服务的是**别的地方**：
   · .req-detail-btn  列表行的「详情」按钮（requests-panel.js）
   · .req-detail-pre  「预览对话」的原文折叠块（conversation-preview.js） */

/* 详情按钮：小号、无边框感（一列里最轻的控件，不与状态徽章抢注意力） */
.req-detail-btn {
  padding: 2px 8px;
  font-size: 11.5px;
}

/* 「终止请求」按钮与详情网格的样式已随迁移移除（见上）。
   原先这里有一条 #req-detail-terminate[hidden] 的规则，是为了压过
   components.css 给 button 设的 display: inline-flex —— 新实现里「终止请求」
   由 React 条件渲染（不渲染即不存在），不再需要 hidden 这条通路。 */

/* 空段标记与面板切换的样式已随迁移移除（.seg-dot / .req-detail-pane 一族）：
   新实现的三个 pane 常驻、用 hidden 切显示，标签由组件库的 SegmentedControl 渲染。 */

.req-detail-pre {
  max-height: 320px;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2, var(--surface));
  color: var(--text-2);
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  font-size: 11.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
  overflow: auto;
}

/* 重试列的标签与悬停面板（request-hover.js）───────────────
   这一列现在承载两件事：重试链（橙标签）与敏感词命中（紫标签），
   悬停弹出富文本面板。形态照 OmniProxy 的 FailoverTip / SensitiveMaskedTag，
   配色换算到本项目自己的变量（那边用 antd 的 Tag 色板）。
   面板本体是 fixed 定位挂在 body 上 —— 与 .tip-bubble 同因同法
   （.panel 是 overflow:hidden，absolute 会被卡片裁掉）。 */

/* 标签排布：一列里可能有两枚（重试 + 敏感词）。横向排列、装不下才换行 ——
   默认 52px 轨道装不下「重试 N」+「敏」（约 70px），两枚仍落成两行，
   与改造前的竖排观感一致；用户把这一列拖宽之后两枚自动并排，
   不再白占一行高度。
   左右贴边由列设置的 ta-* 走 justify-content：主轴变成水平之后，
   align-items 管的是竖向，够不到「靠哪边」（见 table-col-settings.css）。 */
.req-row .req-retry {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 3px 4px;
  align-items: flex-start;
  min-width: 0;
}

/* 标签自身：沿用 .badge / .tag 的既有形制（尺寸与状态列那两枚一致），
   只补一个「可以悬停看详情」的指针语义与交互反馈 ——
   它是一颗真按钮（见 requests-panel.js 的 retryCell），所以要清掉按钮的
   默认底色与内边距，让它看起来仍是徽章而不是一颗小按钮。 */
.req-hover-tag {
  padding: 0 6px;
  border: 0;
  cursor: help;
}

.req-hover-tag:focus-visible { box-shadow: var(--ring); }

/* 敏感词命中：紫色（OmniProxy 的 Tag color="purple"）。
   tokens.css 没有紫色语义变量，这里就地取一个色相的固定值而不是新加一个
   token —— 全站只有这一处需要紫色，进 tokens.css 会让人以为它是通用语义色。
   浅深两套各写一份：同一组紫在浅底与深底上需要的明度不同。
   .badge.tag.sensitive 是详情弹窗「请求详情」网格里的展开形态（词 × 次数），
   与列表的悬停标签共用同一套紫（见 islands/request-detail.tsx 的 sensitiveHtml）。 */
.req-hover-tag.sensitive,
.badge.tag.sensitive {
  background: oklch(0.55 0.18 305 / 14%);
  border: 1px solid oklch(0.55 0.18 305 / 32%);
  color: oklch(0.45 0.18 305);
}

@media (prefers-color-scheme: dark) {
  .req-hover-tag.sensitive,
  .badge.tag.sensitive {
    background: oklch(0.7 0.16 305 / 16%);
    border-color: oklch(0.7 0.16 305 / 34%);
    color: oklch(0.82 0.12 305);
  }
}
/* 切换路径行：面板顶部一条，与下面的尝试列表用分隔线隔开（OmniProxy 同法） */
.rh-chain {
  padding-bottom: 6px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--border);
  word-break: break-word;
}

.rh-chain-k { color: var(--text-3); font-weight: 600; }
.rh-chain-v { color: var(--text); font-weight: 600; }

/* 面板小标题（敏感词面板用） */
.rh-title {
  margin-bottom: 4px;
  color: var(--text-3);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .02em;
}

/* 一次尝试的一行：头部各段（尝试 N · 提供商 账号 →）与结局（错误摘要 / 成功 /
   进行中）**按文本流连排**。这里刻意不用 flex：flex-wrap 的换行决策发生在
   收缩之前，面板宽度只要比「头部 + 错误摘要」的自然宽度小一点点，错误就整块
   跳到第二行（箭头孤零零结尾）；文本流则让换行发生在句子内部，怎么都不会
   整块跳行。段间间距由下面各段的 margin-right 给（原来是 flex 的 gap）。 */
.rh-row {
  min-width: 0;
  word-break: break-word;
}

/* 头部各段的右间距：紧接下一段文字，不能没有间隔 */
.rh-no,
.rh-who,
.rh-account,
.rh-arrow { margin-right: 4px; }

/* 尝试序号 / 箭头 / 说明是次要标记：淡一档 */
.rh-no,
.rh-arrow,
.rh-dim { color: var(--text-3); }

.rh-who { color: var(--text); font-weight: 600; }

/* 账号名：挂在提供商名后面，淡一档与家名分开。
   账号名可能是很长的邮箱：允许在任意位置断行，否则会把面板顶宽。 */
.rh-account {
  color: var(--text-3);
  overflow-wrap: anywhere;
}

/* 失败结局（红色）：行内文本流的一部分，紧接着箭头排。超长无空格英文靠
   anywhere 在必要处断行，避免把面板顶宽（OmniProxy 的 overflowWrap: anywhere 同法） */
.rh-bad {
  overflow-wrap: anywhere;
  color: var(--danger);
  font-weight: 500;
}

.rh-ok { color: var(--ok); font-weight: 500; }

/* ── 一轮内部的重试链（本次改造）─────────────────────────────
   缩进一格并加左侧竖线：它是**所属那一轮的下级事件**（同账号重发），
   不是并列的尝试 —— 视觉上必须能看出这个层级，否则会被读成又换了一家。 */
.rh-retry {
  margin: 2px 0 4px 10px;
  padding-left: 8px;
  border-left: 2px solid var(--border-strong);
}

.rh-retry-head {
  color: var(--warn);
  font-size: 11.5px;
  font-weight: 600;
}

/* 重试子行同 .rh-row：文本流连排（原因 + HTTP 码 + 延迟），
   折行发生在原因句子内部而不是把状态码整块挤下去 */
.rh-retry-row {
  min-width: 0;
  word-break: break-word;
}

.rh-retry-why,
.rh-retry-status { margin-right: 4px; }

/* 重试原因可能是一句带「（11128）」的中文提示：允许断行，别顶宽面板 */
.rh-retry-why {
  overflow-wrap: anywhere;
  color: var(--text-2);
}

.rh-retry-status {
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 提示行（代理回退直连等）：非失败、但值得记一笔 —— 用警示色而不用红 */
.rh-notice {
  margin: 2px 0 4px 10px;
  padding-left: 8px;
  border-left: 2px solid var(--border-strong);
  color: var(--warn);
  overflow-wrap: anywhere;
}

/* 敏感词列表：词与次数分列，次数贴右竖着能比大小。
   这一行仍用 flex（不是 .rh-row 的默认文本流了，display 要自己声明）。 */
.rh-hit {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.rh-hit-w { color: var(--text); word-break: break-word; }
.rh-hit-c {
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* ② 预览对话：气泡流（形态照 OmniProxy 的 ConversationPreview，
   配色换算到本项目的 tokens）。滚动收在 .cv 内，弹窗自身不被长对话撑破。 */
.cv {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-height: 480px;
  padding: 2px;
  overflow-y: auto;
}

/* 一行的落位：user 靠右、assistant 靠左、system 居中 */
.cv-row { display: flex; justify-content: flex-start; }
.cv-row.user { justify-content: flex-end; }
.cv-row.sys { justify-content: center; }

/* 气泡本体：最大宽度 75%（system 90%），长单词强制断行 */
.cv-msg {
  max-width: 75%;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 12.5px;
  line-height: 1.65;
  overflow-wrap: break-word;
}

/* user 气泡：主色底白字，输入是纯文本（不做 markdown，见 conversation-preview
   的 userInner 说明），保留原有换行与空白 */
.cv-msg.user { background: var(--primary); color: #fff; }
.cv-plain { white-space: pre-wrap; }

/* assistant 气泡：抬升表面色 + 细描边 */
.cv-msg.assistant { background: var(--surface-2); border: 1px solid var(--hairline); }

/* system（提示词）/ 错误条：全宽灰条，淡一档 */
.cv-msg.sys {
  max-width: 90%;
  background: var(--surface-inset);
  color: var(--text-2);
  font-size: 12px;
  border-radius: 8px;
}

.cv-msg.cv-error { background: var(--danger-soft); color: var(--danger); }

/* markdown 正文（.md-body 排版来自 update-notes.css）：收紧首尾与段间距，
   不让弹窗里留出面板级的大边距 */
.cv-msg.md-body > :first-child { margin-top: 0; }
.cv-msg.md-body > :last-child { margin-bottom: 0; }
.cv-msg.md-body p { margin: 6px 0; }
.cv-msg.md-body .md-pre { max-height: 240px; overflow: auto; }

/* 气泡内的图片（请求里的多模态段 / 工具结果） */
.cv-img {
  display: block;
  max-width: min(280px, 100%);
  max-height: 220px;
  margin: 6px 0 0;
  border: 1px solid var(--border);
  border-radius: 8px;
}

/* 折叠块：思考过程 / 工具结果 / 解析降级的原文 */
.cv-details { margin-top: 6px; min-width: 0; }
.cv-details > summary {
  cursor: pointer;
  color: var(--text-3);
  font-size: 11.5px;
  user-select: none;
}
.cv-details[open] > summary { margin-bottom: 4px; }
.cv-reasoning { color: var(--text-2); font-size: 12px; }

/* 工具结果：全宽虚线框，与对话气泡在视觉上区分开 */
details.cv-tool {
  max-width: 88%;
  padding: 6px 10px;
  border: 1px dashed var(--border-strong);
  border-radius: 10px;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: 12px;
}

details.cv-tool > summary { cursor: pointer; color: var(--text-3); }

/* 工具调用块（助手气泡内）：标题 + 参数原文 */
.cv-toolcall { margin-top: 8px; }
.cv-toolcall-name { margin-bottom: 3px; color: var(--text-3); font-size: 11.5px; }
.cv-pre { max-height: 220px; font-size: 11px; }

/* 解析失败的说明行（原文折叠跟在它后面）/ 读取侧的截断提示 */
.cv-parse-empty { color: var(--text-3); font-size: 12px; }