/* ============================================================
   MaxMind 控制台 · 对话页外壳与组件（chat.css）
   Codex / ChatGPT 风格的对话界面 + iOS 外壳语言。

   规则（与后台 css/admin.css 同一套铁律，见 CHAT-UI.md）：
     1) 只消费 admin.css 里定义在 :root 的 --adm-* token，不重复定义颜色/字号/圆角；
        所以加载顺序必须是 styles.css → admin.css → chat.css。
     2) 全部 class 用 cx- 前缀：styles.css 里的旧对话样式原样留着不动，新旧互不干扰，
        也不会误伤 API/用量/设置等仍在用旧类的页面。
     3) 玻璃只给「导航栏 / Tab 栏 / composer / 弹层」——对话内容一律不透明材质。
     4) 稳定钩子（#chat-input / #send-btn / #chat-messages …）由 JS 保留，这里只管长相。
   ============================================================ */

/* ⚠️ 必须放在最前面：作者样式里的 display 会盖掉浏览器默认的 [hidden] { display: none }。
   之前就栽在这上面 —— .cx-sheet-overlay 写了 display:flex，于是那个
   position:fixed; inset:0; z-index:60 的历史抽屉遮罩【一直存在】，把整屏的点击全吞了，
   表现就是「任何按钮都按不了」。凡是我自己给了 display 的构件，都要在这里兜住 hidden。 */
.cx-shell [hidden],
.cx-tabbar[hidden],
.cx-sheet-overlay[hidden],
.cx-menu-overlay[hidden],
.cx-menu[hidden],
.cx-largetitle[hidden] { display: none !important; }

/* ============ 1. 外壳：玻璃导航栏 + 大标题 + Tab 栏/侧栏 ============ */
:root {
  /* 手机底部那条浮动胶囊 Tab 占掉的高度（60px 本体 + 10px 下边距 + 安全区）。
     所有贴底的内容都拿它让位，别再各处写死数字。 */
  --cx-tabbar-space: calc(70px + env(safe-area-inset-bottom));
}
.cx-shell { display: flex; width: 100%; height: 100%; min-height: 0; }

/* 桌面左侧栏（iPadOS 分栏）：手机隐藏，改用底部 Tab */
.cx-rail {
  display: none;
  flex: 0 0 auto;
  width: 264px;
  flex-direction: column;
  min-height: 0;
  background: var(--adm-bg);
  border-right: 0.5px solid var(--adm-sep-soft);
}
.cx-rail-head { padding: 14px 14px 8px; }
.cx-rail-title {
  font-size: var(--adm-t-caption1);
  font-weight: 600;
  letter-spacing: 0.004rem;
  color: var(--adm-label-2);
}

/* 侧栏内部：标题 + 搜索 + 列表 + 底部新建 */
.cx-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cx-rail-search { padding: 0 12px 8px; }
.cx-rail-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 8px 8px; }
.cx-rail-foot { flex: 0 0 auto; padding: 8px 12px 14px; }
.cx-search-input {
  width: 100%;
  min-height: 36px;
  padding: 7px 12px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: var(--adm-fill-3);
  color: var(--adm-label);
  font: inherit;
  font-size: max(16px, 0.9375rem);
  outline: none;
}
.cx-search-input::placeholder { color: var(--adm-label-3); }
.cx-search-input:focus-visible { box-shadow: 0 0 0 3px var(--adm-accent-soft); }
/* 只要图标不要文字的圆按钮 */
.cx-icon-only { width: 44px; padding: 0; justify-content: center; }
.cx-nav-btn.cx-block, .cx-block { width: 100%; justify-content: center; }
.cx-nav-btn .cx-tab-label { font-size: inherit; }
#sidebar.hidden { display: none; }

/* 主区域 */
.cx-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* ── 对话页的侧边距（消息区 + 输入区必须用同一条数值）──────────────
   用户 2026-09-14：「这个位置（模型名 / 思考过程）似乎有一点点问题，就是太靠近
   屏幕边缘了…… 最终的回答也太靠近屏幕边缘了，起码留 26 个像素吧。」

   原来消息区两侧只靠 .page-content 的内边距：窄屏 10px、平板 14px、桌面 24px。
   正文是没有气泡的，加上「正式回答」外面那个小方框有自己的边框，
   贴到 10px 时那个框几乎压着屏幕边。

   26px 的来源：.page-content 最少给 10px，所以这里再补 16px → 最窄处也有 26px。
   为什么把 .page-content 的侧内边距提成变量 --cx-page-pad：
   输入框（.cx-composer）不在 .page-content 里面，它的内边距【就是】离屏幕的距离，
   所以只有把两边的来源统一，输入框才能和上面的正文对齐 —— 否则输入框比正文更宽，
   一眼就看出来。下面三个断点与 styles.css 的 .page-content 一一对应，
   改那边记得同步这里。 */
:root {
  --cx-page-pad: 24px;      /* 桌面：styles.css 的 padding: 36px 24px 48px */
  --cx-chat-extra: 16px;    /* 消息区在页面内边距之上再补的余量 */
}
@media (max-width: 768px) { :root { --cx-page-pad: 14px; } }
@media (max-width: 400px) { :root { --cx-page-pad: 10px; } }

/* 消息区：模型名 / 思考过程 / 正文 / 正式回答方框 一起获得这段留白 */
.chat-messages {
  padding-left: var(--cx-chat-extra);
  padding-right: var(--cx-chat-extra);
}

/* 滚动容器里的内容居中（旧的 .content-inner 只给了 max-width，没给居中） */
.cx-scroll > .content-inner { margin: 0 auto; }
/* 尚未换皮的板块（用量 / 设置 / API / 升级）也渲染在 #content-inner 里，
   同样要给浮动 Tab 栏让位；后台是全屏外壳、没有 Tab 栏，用 :not(.adm-mode) 排除 */
body:not(.adm-mode) .cx-scroll > .content-inner { padding-bottom: var(--cx-tabbar-space); }

/* 玻璃导航栏（大标题滚动收起，内容从下方穿过） */
.cx-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  flex: 0 0 auto;
  background: transparent;
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  transition: background-color 200ms var(--adm-ease-out), box-shadow 200ms var(--adm-ease-out);
}
.cx-nav.is-scrolled {
  background: var(--adm-glass);
  box-shadow: 0 0.5px 0 var(--adm-sep-soft) inset, 0 6px 18px -14px rgba(0, 0, 0, 0.5);
}
.cx-nav-inner {
  display: flex;
  align-items: center;
  gap: 4px;
  height: var(--adm-nav-h);
  padding: 0 8px 0 4px;
}
.cx-nav-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--adm-t-headline);
  font-weight: 600;
  letter-spacing: var(--adm-ls-body);
  text-align: center;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 180ms var(--adm-ease-out), transform 180ms var(--adm-ease-out);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
}
.cx-nav.is-scrolled .cx-nav-title { opacity: 1; transform: none; }
.cx-nav-actions { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.cx-nav-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 44px;
  padding: 0 13px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: transparent;
  color: var(--adm-label);
  font: inherit;
  font-size: var(--adm-t-subhead);
  letter-spacing: var(--adm-ls-sub);
  cursor: pointer;
  transition: background-color 160ms var(--adm-ease-out), transform 160ms var(--adm-ease-out);
}
.cx-nav-btn svg { width: 18px; height: 18px; flex: 0 0 auto; }
.cx-nav-btn:active { transform: scale(0.97); background: var(--adm-fill-3); }
.cx-nav-btn.is-primary { background: var(--adm-accent); color: var(--adm-on-accent); font-weight: 600; }

/* 大标题区（滚走） */
.cx-largetitle { padding: 6px 16px 12px; }
.cx-largetitle h1 {
  margin: 0;
  font-size: var(--adm-t-title1);
  font-weight: 700;
  letter-spacing: var(--adm-ls-title1);
  line-height: 1.2;
  overflow-wrap: anywhere;
}
.cx-largetitle p {
  margin: 4px 0 0;
  font-size: var(--adm-t-footnote);
  color: var(--adm-label-2);
}

/* 内容滚动区 */
.cx-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
}

/* 手机底部胶囊 Tab */
.cx-tabbar {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(10px + env(safe-area-inset-bottom));
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 2px;
  height: 60px;
  padding: 0 6px;
  border-radius: var(--adm-r-full);
  background: var(--adm-glass-strong);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  box-shadow: 0 10px 30px -12px rgba(0, 0, 0, 0.6), 0 0 0 0.5px var(--adm-sep-soft) inset;
}
.cx-tab {
  flex: 1 1 0;
  min-width: 0;
  height: 46px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: transparent;
  color: var(--adm-label-2);
  font: inherit;
  font-size: var(--adm-t-caption2);
  letter-spacing: 0.0044rem;
  cursor: pointer;
  transition: color 160ms var(--adm-ease-out), background-color 160ms var(--adm-ease-out),
    transform 160ms var(--adm-ease-out);
}
.cx-tab:active { transform: scale(0.96); }
.cx-tab svg { width: 24px; height: 24px; display: block; }
.cx-tab.is-active { color: var(--adm-label); background: var(--adm-accent-soft); font-weight: 600; }
.cx-tab-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 对话页：收起底部胶囊 Tab。
   理由（用户原话）：它离输入框太近、很挤，导致展示正文的地方太小。
   这是 iOS 的常见做法（进入会话/编辑器时收起 Tab 栏），导航改由侧栏 / 抽屉承担。 */
body.cx-no-tabbar { --cx-tabbar-space: 0px; }
body.cx-no-tabbar .cx-tabbar { display: none; }

/* 侧栏 / 抽屉里的导航（对话页没有底部 Tab 时靠它们切换板块） */
.cx-rail-nav { display: flex; flex-direction: column; gap: 2px; padding: 0 8px 10px; }
.cx-rail-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: var(--adm-r-md);
  background: transparent;
  color: var(--adm-label-2);
  font: inherit;
  font-size: var(--adm-t-subhead);
  letter-spacing: var(--adm-ls-sub);
  text-align: left;
  cursor: pointer;
  transition: background-color 160ms var(--adm-ease-out), color 160ms var(--adm-ease-out),
    transform 160ms var(--adm-ease-out);
}
.cx-rail-link svg { width: 20px; height: 20px; flex: 0 0 auto; }
.cx-rail-link:active { transform: scale(0.98); }
.cx-rail-link.is-active { background: var(--adm-fill-2); color: var(--adm-label); font-weight: 600; }
.cx-rail-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
  color: var(--adm-label);
  font-size: var(--adm-t-subhead);
  font-weight: 600;
  text-decoration: none;
}
.cx-rail-brand svg { width: 22px; height: 22px; }
/* 抽屉里的导航：四个并排的小格子 */
.cx-sheet-nav { display: flex; gap: 4px; padding: 2px 12px 6px; }
.cx-sheet-nav .cx-rail-link {
  flex: 1 1 0;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 8px 4px;
  font-size: var(--adm-t-caption2);
  text-align: center;
}

/* ============ 2. 对话消息（Codex 风格：用户气泡 / AI 整幅） ============ */
.cx-thread {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 4px 16px calc(150px + var(--cx-tabbar-space));
  display: flex;
  flex-direction: column;
  gap: 22px;
}
.cx-msg { display: flex; flex-direction: column; min-width: 0; }

/* 用户：右对齐气泡 */
.cx-msg-user { align-items: flex-end; }
.cx-bubble {
  max-width: 78%;
  padding: 11px 15px;
  border-radius: var(--adm-r-lg) var(--adm-r-lg) 6px var(--adm-r-lg);
  background: var(--adm-fill-2);
  color: var(--adm-label);
  font-size: var(--adm-t-body);
  letter-spacing: var(--adm-ls-body);
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* AI：整幅无气泡。头部那行「模型名」已按用户要求整块删掉 ——
   AI 侧只剩「一行灰字（已耗时）+ 灰线 + 正文」，不再有任何身份信息。 */
.cx-msg-ai { align-items: stretch; }
.cx-msg-body { font-size: var(--adm-t-body); letter-spacing: var(--adm-ls-body); line-height: 1.62; min-width: 0; }
/* 一问一答读作一对：气泡下面紧跟 AI 那行灰字，不要再被 styles.css 的 -12px 拉歪 */
.message-user + .message-assistant { margin-top: 2px; }
/* AI 侧内部不用 flex gap：间距由「思考块自己的 margin-bottom」说了算，
   否则 gap 会和 margin 叠加，正文又被推远（用户：离分隔线太远）。 */
.ai-reply.cx-reply { display: block; }
/* 正文底部的动作行。用户口径：「复制按钮太明显、太大，而且不够靠近文字」——
   所以：间距收到 2px（贴着正文），按钮只剩一个小图标（见 .cx-copy-btn），
   左边距 -7px 让图标的视觉左边缘与正文字符对齐（按钮内部有 7px 内边距）。 */
.cx-msg-actions {
  display: flex;
  gap: 2px;
  margin-top: 2px;
  margin-left: -7px;
  opacity: 1;
  transition: opacity 160ms var(--adm-ease-out);
}

/* 复制答案：**安静的小图标**。
   画出来是 28px 的方框、15px 的图标、静息色 --adm-label-3（三级灰），
   按下去或悬停才提到 --adm-label；命中区用 ::after 撑到 44px —— 看得小、按得到
   （Apple 的最小命中区是 44pt，视觉尺寸不必跟着 44）。
   复制成功换成对勾并点亮 1.2s：反馈落在动作本身，不用去别处找。 */
.cx-copy-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--adm-r-sm);
  background: transparent;
  color: var(--adm-label-3);
  cursor: pointer;
  transition: color 140ms var(--adm-ease-out), background-color 140ms var(--adm-ease-out);
}
.cx-copy-btn svg { width: 15px; height: 15px; }
.cx-copy-btn::after { content: ''; position: absolute; inset: -8px -7px; }   /* 命中区 */
.cx-copy-btn:active { background: var(--adm-fill-2); color: var(--adm-label); }
.cx-copy-btn.is-done { color: var(--adm-ok); }

/* ── 流式渲染容器 ──
   正文在流式途中被切成「已完结的块」+「每帧重排的最后一块」（理由见 app.js 的
   paintStreaming）：完结块渲染一次就不再回头，所以正文排版不会每帧抖动。
   这里只补两条边距规则，让正文与上面那条灰线、下面那行复制按钮保持紧凑 ——
   分块之后 :first-child / :last-child 落在了包装 div 上，原有的归零规则就够不着了。 */
.cx-md-live, .cx-md-done, .cx-md-tail { display: block; }
.cx-md .cx-md-live > div > *:first-child { margin-top: 0; }
.cx-md .cx-md-tail > *:last-child { margin-bottom: 0; }
.cx-md-live > .cx-md-done:empty, .cx-md-live > .cx-md-tail:empty { display: none; }

/* 流式光标 */
.cx-caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 2px;
  vertical-align: -0.15em;
  background: var(--adm-label);
  animation: cxBlink 1s steps(2, start) infinite;
}
@keyframes cxBlink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

/* 打字指示（等待首个 token） */
.cx-typing { display: inline-flex; gap: 4px; align-items: center; padding: 4px 0; }
.cx-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--adm-label-3);
  animation: cxPulse 1.1s var(--adm-ease-in-out) infinite;
}
.cx-typing span:nth-child(2) { animation-delay: 0.14s; }
.cx-typing span:nth-child(3) { animation-delay: 0.28s; }
@keyframes cxPulse { 0%, 100% { opacity: 0.35; transform: translateY(0); } 50% { opacity: 1; transform: translateY(-2px); } }

/* ============ 3. 思考行（Codex 式：灰字 + 灰横线，没有胶囊、没有色块） ============ */
/* 用户口径（原话）：
     「已耗时 5m15s >」—— 时间实时走，箭头能点；点开就是灰色小字，不点开什么都看不见；
     这一块下面压一条灰色横线，思考完毕后在横线下面直接排正式回答。
   所以这里【没有】标签、图标、底色、左边竖线：思考这种东西越不起眼越好，
   一眼扫过去只会看到「已耗时 …」这一行灰字，正文才是重点。 */
.cx-think {
  /* 灰线到正文只留 8px —— 用户口径：正式回答离分隔线太远了，要紧凑 */
  margin: 0 0 8px;
  padding-bottom: 9px;
  /* 那条灰色横线：它是「思考区」的下边缘，展开/收起都跟着走，位置永远正确 */
  border-bottom: 1px solid var(--adm-sep);
}
.cx-think-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--adm-label-2);              /* 灰字 */
  font: inherit;
  font-size: var(--adm-t-footnote);
  font-weight: 500;
  letter-spacing: 0.004rem;
  cursor: pointer;
  transition: color 160ms var(--adm-ease-out);
}
/* 视觉上是纯文字行，可点区域仍要够 44px —— 用一层不改变外观的透明外扩补上 */
.cx-think-toggle::after { content: ''; position: absolute; inset: -7px -10px; }
.cx-think-toggle:active { color: var(--adm-label); }
.cx-think-time { font-variant-numeric: tabular-nums; }
.cx-think-chevron { display: flex; color: var(--adm-label-3); transition: transform 200ms var(--adm-ease-out); }
.cx-think-chevron svg { width: 13px; height: 13px; }
/* 收起 → 朝右（-90°）；展开 → 朝下（0°）。旧版是「朝下/朝上」翻转，
   箭头在收起态朝上，看着别扭 —— 这也是用户说的「箭头不好看」之一。 */
.cx-think:not([data-open="1"]) .cx-think-chevron { transform: rotate(-90deg); }
.cx-think[data-open="1"] .cx-think-chevron { transform: rotate(0deg); }
/* 折叠用 grid-template-rows 过渡，不动 height（不动布局属性） */
.cx-think-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 240ms var(--adm-ease-out), opacity 200ms var(--adm-ease-out);
  opacity: 0;
}
.cx-think[data-open="1"] .cx-think-body { grid-template-rows: 1fr; opacity: 1; }
.cx-think-inner { min-height: 0; overflow: hidden; }
.cx-think-text {
  margin: 10px 0 0;
  padding: 0;
  border: 0;
  color: var(--adm-label-2);              /* 灰色小字 */
  font-size: var(--adm-t-footnote);
  line-height: 1.62;
  overflow-wrap: anywhere;
  /* ⚠️ 这里【不能】写 white-space: pre-wrap：思考内容是按 Markdown 渲染的，
     引擎输出的 HTML 块之间本来就有换行，pre-wrap 会把它们全画成空行。 */
  white-space: normal;
  /* 高度上限在这里【显式声明】一份。原本生效的是 styles.css 里旧
     .thinking-content 的 280px —— 它能生效只是因为 chat.css 没声明同属性，
     等于新样式表一直在偷偷依赖旧样式表。这种依赖很脆：哪天旧表一删，
     思考内容就失去上限，几千字把正式答案一路顶到屏幕外，app.js 里那句
     「跟着最新一行走」的 scrollTop 也会因为没有滚动容器变成一句空话。 */
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}

/* ============ 3b. 正式回复（无框、无气泡、无标签） ============ */
/* 就是普通正文：灰横线以下的这一段就是答案，不需要任何「招牌」。
   整屏只有两种形状 —— 用户的浅色气泡，和 AI 的一行灰字 + 一条灰线 + 正文。 */
.cx-answer { min-width: 0; }

/* ============ 4. Markdown 排版 ============ */
.cx-md > *:first-child { margin-top: 0; }
.cx-md > *:last-child { margin-bottom: 0; }
.cx-md p { margin: 0 0 12px; }
.cx-md h1, .cx-md h2, .cx-md h3, .cx-md h4 {
  margin: 22px 0 10px;
  line-height: 1.32;
  letter-spacing: var(--adm-ls-title3);
}
.cx-md h1 { font-size: var(--adm-t-title3); }
.cx-md h2 { font-size: var(--adm-t-title3); }
.cx-md h3 { font-size: var(--adm-t-headline); }
.cx-md h4 { font-size: var(--adm-t-body); }
.cx-md ul, .cx-md ol { margin: 0 0 12px; padding-left: 1.35em; }
.cx-md li { margin: 4px 0; }
.cx-md li > p { margin: 0 0 6px; }
.cx-md blockquote {
  margin: 0 0 12px;
  padding: 2px 0 2px 12px;
  border-left: 2px solid var(--adm-sep);
  color: var(--adm-label-2);
}
.cx-md hr { height: 1px; margin: 18px 0; border: 0; background: var(--adm-sep-soft); }
.cx-md a { color: var(--adm-label); text-decoration: underline; text-underline-offset: 2px; }
.cx-md strong { font-weight: 600; }
.cx-md table {
  display: block;
  width: max-content;
  max-width: 100%;
  margin: 0 0 12px;
  border-collapse: collapse;
  font-size: var(--adm-t-subhead);
  overflow-x: auto;
}
.cx-md th, .cx-md td {
  padding: 7px 11px;
  border: 0.5px solid var(--adm-sep-soft);
  text-align: left;
  white-space: nowrap;
}
.cx-md th { background: var(--adm-fill-3); font-weight: 600; color: var(--adm-label-2); }
.cx-md img { max-width: 100%; height: auto; border-radius: var(--adm-r-md); }

/* 行内代码 */
.cx-icode {
  padding: 0.15em 0.4em;
  border-radius: 6px;
  background: var(--adm-fill-3);
  font-family: var(--adm-mono);
  font-size: 0.88em;
  overflow-wrap: anywhere;
}
/* 代码块：块内横向滚动，绝不撑破页面 */
.cx-code {
  position: relative;
  margin: 0 0 12px;
  border-radius: var(--adm-r-md);
  background: var(--adm-surface);
  box-shadow: 0 0 0 0.5px var(--adm-sep);
  overflow: hidden;
}
.cx-code-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 8px 6px 12px;
  background: var(--adm-fill-3);
  font-size: var(--adm-t-caption2);
  color: var(--adm-label-2);
}
.cx-code-lang { font-family: var(--adm-mono); letter-spacing: 0.02rem; text-transform: lowercase; }
/* 代码块的复制：只剩一个小图标（用户：两个复制按钮都要更小、更安静）。
   22px 高、13px 图标、静息色 --adm-label-2，按住才亮；命中区由 ::after 撑满整块头。 */
.cx-code-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  padding: 0 5px;
  border: 0;
  border-radius: var(--adm-r-sm);
  background: transparent;
  color: var(--adm-label-2);
  font: inherit;
  font-size: var(--adm-t-caption2);
  cursor: pointer;
  transition: background-color 140ms var(--adm-ease-out), color 140ms var(--adm-ease-out);
}
.cx-code-copy svg { width: 13px; height: 13px; }
.cx-code-copy::after { content: ''; position: absolute; inset: 0; }   /* 命中区撑满整块头 */
.cx-code-copy:active { background: var(--adm-fill-2); color: var(--adm-label); }
.cx-pre {
  margin: 0;
  padding: 12px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  font-family: var(--adm-mono);
  font-size: var(--adm-t-footnote);
  line-height: 1.6;
  color: var(--adm-label);
  tab-size: 2;
}

/* ============ 5. 欢迎区 ============ */
.cx-welcome {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 8vh 20px 40px;
  text-align: center;
}
.cx-welcome-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
  color: var(--adm-label);
}
.cx-welcome-brand svg { width: 34px; height: 34px; }
.cx-welcome-word {
  font-size: var(--adm-t-title2);
  font-weight: 600;
  letter-spacing: var(--adm-ls-title2);
}
.cx-welcome-h1 {
  margin: 0 0 6px;
  font-size: var(--adm-t-title1);
  font-weight: 700;
  letter-spacing: var(--adm-ls-title1);
  overflow-wrap: anywhere;
}
.cx-welcome-sub { margin: 0 0 26px; font-size: var(--adm-t-subhead); color: var(--adm-label-2); }

/* ============ 6. 输入区（悬浮 composer） ============ */
.cx-composer {
  flex: 0 0 auto;
  /* 底部留白取「Tab 栏占位」与「安全区 + 一点余量」两者中更大的那个：
     - 有 Tab 栏时（API/用量/我的）抬到 Tab 栏之上，否则发送按钮被压在下面点不到；
     - 对话页收起了 Tab 栏，占位归零，但输入框不能贴到屏幕最下边 —— 所以给一个保底。
     以后再调占位也不用改这里的数字。 */
  padding: 8px 12px max(calc(12px + env(safe-area-inset-bottom)), var(--cx-tabbar-space));
  background: var(--adm-bg-a);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
}

/* 输入区：内边距【就是】离屏幕的距离（输入区不在 .page-content 里）。
   ⚠️ 必须排在上面那条 padding 简写【之后】：padding 是简写，会重置四个方向，
   同权重下后加载的赢 —— 我第一版把它写在前面，左右内边距等于没写，
   输入框依旧贴着屏幕边，而且不会有任何报错，只能靠眼睛发现。

   用户口径（2026-09-15）：「提问框太窄了，两边距离屏幕一点点就行」+「不要额外挡住别的东西」。
   两处都改了：
     · 宽度：手机上两侧只留 12px（原来 26~30px）；桌面两侧对齐 .page-content 的 24px，
       并且把内框上限从写死的 720px 换成 --content-max-width（900px）——
       之前正文栏最宽 900px、输入框却只有 720px，桌面上一眼就看出「输入框比正文窄一截」。
     · 输入区本身也不再做那层「滚动边缘淡化」了（见下面那条注释）。 */
.cx-composer {
  padding-left: 12px;
  padding-right: 12px;
  /* ⚠️ 这里【不能】再写 mask-image 或半透明的渐变背景。
     上一版给 .cx-composer 加了一层 mask（上透明、下实心），本意是「正文滑到输入框
     底下时柔和淡出、不要被一条硬边切断」。但 mask 是作用在**整个元素及其所有子元素**
     上的 —— 输入框（.cx-composer-inner）正好是它的子元素，于是输入框自己的上半截
     跟着一起变透明，看上去就是「输入框在渐渐显出来」。
     用户 2026-09-15：「那个对话框里怎么渐渐显示了渐显，他那个要完整的」。

     而且这个「淡化」在这里根本没有收益：输入区是 .cx-main 里 .cx-scroll 的
     flex 兄弟（`.cx-main` = 导航栏 + .cx-scroll + .cx-composer-holder），
     正文是**滚动到它上方**的，不会从它底下穿过 —— 没有任何东西需要被「柔和地淡出」。
     结论：背景恢复实心，输入框永远是一整块完整的。 */
  background: var(--adm-bg-a);
}
@media (min-width: 769px) {
  .cx-composer {
    padding-left: var(--cx-page-pad);
    padding-right: var(--cx-page-pad);
  }
}
.cx-composer-inner {
  width: 100%;
  max-width: var(--content-max-width, 900px);   /* 与消息栏同宽：输入框不会比正文窄一截 */
  margin: 0 auto;
  padding: 8px 8px 8px 14px;
  border-radius: var(--adm-r-lg);
  background: var(--adm-surface);
  box-shadow: 0 0 0 0.5px var(--adm-sep), var(--adm-shadow-raise);
  transition: box-shadow 160ms var(--adm-ease-out);
}
/* 聚焦：细环从 0.5px 的半透明分隔线换成 1px 的 --adm-label-4（跟随主题），
   投影本身不再写死黑 —— 浅色下原来那层 rgba(0,0,0,.6) 是最重的脏灰来源。 */
.cx-composer-inner:focus-within { box-shadow: 0 0 0 1px var(--adm-label-4), var(--adm-shadow-raise); }
.cx-ta {
  display: block;
  width: 100%;
  max-height: 168px;
  padding: 4px 6px 4px 0;
  border: 0;
  background: transparent;
  color: var(--adm-label);
  font-family: var(--adm-font);
  font-size: max(16px, 1rem);
  line-height: 1.5;
  letter-spacing: -0.02rem;
  resize: none;
  outline: none;
  overflow-y: auto;
}
.cx-ta::placeholder { color: var(--adm-label-3); }
.cx-composer-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
}
/* ── 当前模型胶囊（输入框左下角）────────────────────────────────────────
   用户口径：按钮要小、要安静、要贴着内容；**对比要够、别太淡**。

   旧版为什么在浅色主题下「像坏了」：
     · 底色 --adm-fill-3（浅色只有 8% 灰）+ 文字 --adm-label-2（60% 灰）
       + 13px —— 三样都往「退后」的方向叠，叠出来就是一颗「已禁用」的灰片。
     · 没有任何主题相关的补正：同一个灰值在纯黑和纯白上感知浓度差了近一倍。
   这一版：
     · 底 → --adm-fill-2（浅色 14% / 暗色 24%），浅色下也看得出是一颗控件；
     · 字 → 15px / 500 / --adm-label（跟随主题的正文色），可读，但不加粗不抢戏；
     · 高 34px（苹果 Small 档），名字长了省略号收尾；
     · 箭头单独降到 --adm-label-2 —— 它是「这里可以展开」的提示，不该和名字一样重；
     · 展开时只把底色提到 --adm-fill + 箭头翻 180°，不加环、不加色。 */
.cx-model-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  max-width: 62%;
  padding: 0 9px 0 11px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: var(--adm-fill-2);
  color: var(--adm-label);
  font: inherit;
  font-size: var(--adm-t-subhead);
  font-weight: 500;
  letter-spacing: var(--adm-ls-sub);
  cursor: pointer;
  transition: background-color 160ms var(--adm-ease-out), transform 160ms var(--adm-ease-out);
}
.cx-model-chip:active { transform: scale(0.97); }
.cx-model-chip .cx-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--adm-ok); flex: 0 0 auto; }
.cx-model-chip .cx-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cx-model-chip svg { width: 12px; height: 12px; flex: 0 0 auto; color: var(--adm-label-2); transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1); }
/* 菜单开着时：胶囊再提一档 + 箭头翻过来 —— 一眼看出「这个菜单是从这儿出来的」 */
.cx-model-chip[aria-expanded="true"] { background: var(--adm-fill); }
.cx-model-chip[aria-expanded="true"] svg { transform: rotate(180deg); }
.cx-composer-right { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.cx-send, .cx-stop {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: var(--adm-fill-2);
  color: var(--adm-label-3);
  cursor: pointer;
  transition: background-color 180ms var(--adm-ease-out), color 180ms var(--adm-ease-out),
    transform 160ms var(--adm-ease-out);
}
.cx-send svg, .cx-stop svg { width: 20px; height: 20px; }
.cx-send:active, .cx-stop:active { transform: scale(0.94); }
.cx-send.is-active { background: var(--adm-accent); color: var(--adm-on-accent); }
.cx-stop { background: var(--adm-fill-2); color: var(--adm-label); }

/* 手机：composer 固定占位（对话页显示时由 JS 打开） */
.cx-composer-holder { flex: 0 0 auto; display: flex; flex-direction: column; }

/* ============ 7. 历史会话（手机抽屉 / 桌面侧栏共用一套行） ============ */
.cx-sheet-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(0, 0, 0, 0);
  transition: background-color 220ms var(--adm-ease-out);
}
.cx-sheet-overlay.is-open { background: rgba(0, 0, 0, 0.45); }
.cx-sheet {
  width: 100%;
  max-width: 640px;
  max-height: 86dvh;
  display: flex;
  flex-direction: column;
  border-radius: var(--adm-r-xl) var(--adm-r-xl) 0 0;
  background: var(--adm-surface);
  box-shadow: 0 -12px 40px -16px rgba(0, 0, 0, 0.7);
  transform: translateY(100%);
  transition: transform 320ms var(--adm-ease-drawer);
  padding-bottom: env(safe-area-inset-bottom);
}
.cx-sheet.is-open { transform: translateY(0); }
.cx-sheet-grab { width: 36px; height: 5px; margin: 8px auto 0; border-radius: 999px; background: var(--adm-label-4); }
.cx-sheet-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px 8px;
}
.cx-sheet-head h2 {
  flex: 1 1 auto;
  margin: 0;
  font-size: var(--adm-t-headline);
  font-weight: 600;
}
.cx-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 0 16px 16px; }

.cx-conv-list { display: flex; flex-direction: column; gap: 4px; padding: 4px 0 8px; }
.cx-group-label {
  margin: 12px 2px 4px;
  font-size: var(--adm-t-caption2);
  font-weight: 600;
  letter-spacing: 0.02rem;
  color: var(--adm-label-2);
}
.cx-conv {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 8px 6px 8px 12px;
  border: 0;
  border-radius: var(--adm-r-md);
  background: transparent;
  color: var(--adm-label);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 140ms var(--adm-ease-out);
}
.cx-conv:active { background: var(--adm-fill-3); }
.cx-conv.is-active { background: var(--adm-fill-2); }
.cx-conv-main { flex: 1 1 auto; min-width: 0; }
.cx-conv-title {
  font-size: var(--adm-t-body);
  letter-spacing: var(--adm-ls-body);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cx-conv-meta { margin-top: 2px; font-size: var(--adm-t-caption2); color: var(--adm-label-3); }
.cx-conv-del {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--adm-r-full);
  background: transparent;
  color: var(--adm-label-3);
  cursor: pointer;
}
.cx-conv-del:active { background: var(--adm-bad); color: #fff; }
.cx-conv-del svg { width: 16px; height: 16px; }
.cx-search { padding: 0 16px 10px; }

/* ============ 8. 模型选择器（从输入框那颗胶囊上长出来的浮层） ============ */
/* 形态照 Claude / OpenAI：不盖整屏的弹窗，菜单就贴着「你按下去的那颗胶囊」往上涨出来，
   选完立刻收回，视线不用离开输入框。
   结构：小标题「模型」/ 模型列表（名字 + 描述 + 勾，行间半像素分隔线）/ 底部一块
   「推理强度」滑块（原生 range，拖动、键盘、无障碍都是白送的）。
   规矩照旧：玻璃只给浮层这种控制层（对话内容一律不透明）；
   圆角同心 —— 外 22px、内边距 8px，所以行是 14px（22 = 14 + 8）。 */
.cx-menu-scrim { position: fixed; inset: 0; z-index: 68; background: transparent; }
.cx-model-menu {
  position: fixed;
  z-index: 69;
  width: min(360px, calc(100vw - 24px));
  padding: 8px;
  border-radius: 22px;
  background: var(--adm-glass-strong);
  -webkit-backdrop-filter: blur(30px) saturate(180%);
  backdrop-filter: blur(30px) saturate(180%);
  box-shadow: var(--adm-shadow-pop);
  /* 从触发它的那颗胶囊长出来（--cx-origin 由 JS 按胶囊位置算，不是写死的中心） */
  transform-origin: var(--cx-origin, 0 100%);
  transform: scale(0.96) translateY(4px);
  opacity: 0;
  filter: blur(6px);          /* 材料是「到货」：缩放 + 位移 + 淡入 + 散焦一起收 */
  pointer-events: none;
  outline: none;
  /* 进场 220ms（浮层档位 150~250ms）：位移走抽屉曲线（先快后慢、落得稳），
     透明度/散焦走 ease-out。出场上限压在 200ms 内，收比开快一档。 */
  transition: transform 220ms var(--adm-ease-drawer), opacity 160ms var(--adm-ease-out),
    filter 200ms var(--adm-ease-out);
}
.cx-model-menu.is-open { transform: none; opacity: 1; filter: none; pointer-events: auto; }
.cx-model-menu-head {
  padding: 6px 12px 8px;
  font-size: var(--adm-t-caption1);
  font-weight: 600;
  letter-spacing: 0.02rem;
  /* 原来是 --adm-label-3（浅色下只有 30% 灰）—— 小标题几乎看不见。
     用户口径：「对比要够，别太淡」。抬到 label-2，仍然是「标签」不是「正文」。 */
  color: var(--adm-label-2);
}
.cx-model-menu-list {
  max-height: min(46dvh, 300px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
/* 滚动边缘：列表真的溢出时，把上下两端各 10px 渐隐掉 —— 内容滑进圆角就不硬切一刀。
   ⚠️ 只有溢出（JS 加的 .is-scroll）才加 mask：不溢出还加，会把第一行、
   最后一行的上/下边角咬掉一块，那是「看着坏了」，不是精致。
   滚到顶/底时对应那一端的渐隐归零，否则第一行永远有一层去不掉的灰。
   （mask 加在**列表**上，不是输入区 —— 那个坑见 §6 的长注释。） */
.cx-model-menu-list.is-scroll {
  --cx-fade-t: 10px;
  --cx-fade-b: 10px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--cx-fade-t),
    #000 calc(100% - var(--cx-fade-b)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--cx-fade-t),
    #000 calc(100% - var(--cx-fade-b)), transparent 100%);
}
.cx-model-menu-list.is-scroll.at-top { --cx-fade-t: 0px; }
.cx-model-menu-list.is-scroll.at-bottom { --cx-fade-b: 0px; }
.cx-model-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 54px;
  padding: 9px 12px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--adm-label);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 140ms var(--adm-ease-out);
}
/* 行间分隔线：从左边 12px 起（iOS 的分隔线不顶到边），半像素、够轻 */
.cx-model-item + .cx-model-item::before {
  content: '';
  position: absolute;
  top: 0;
  left: 12px;
  right: 12px;
  height: 0.5px;
  background: var(--adm-sep-soft);
}
/* 选中态**比按下态重**（原版是反的：选中只有 8% 灰、按下却 14%，
   结果「当前选的是哪条」比「手指压在哪条」还淡，读不出来）。
   另外这里**不再缩放整行** —— 列表行不是按钮，缩起来会在圆角内侧露出底缝。 */
.cx-model-item.is-active { background: var(--adm-fill-2); }
/* 按下即有反馈（不等 click）—— Apple 的第一条：反馈落在 pointerdown 上 */
.cx-model-item.is-pressed { background: var(--adm-fill-3); }
.cx-model-item-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cx-model-item-name { font-size: var(--adm-t-callout); font-weight: 600; letter-spacing: -0.01rem; }
.cx-model-item-desc {
  font-size: var(--adm-t-footnote);
  line-height: 1.35;
  color: var(--adm-label-2);
  overflow-wrap: anywhere;
}
.cx-model-item-check {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  margin-left: auto;
  color: var(--adm-label);
  opacity: 0;
  transform: scale(0.82);
  transition: opacity 160ms var(--adm-ease-out), transform 200ms var(--adm-ease-out);
}
.cx-model-item.is-active .cx-model-item-check { opacity: 1; transform: none; }
.cx-model-item-check svg { width: 17px; height: 17px; }
.cx-model-empty { padding: 14px 12px; font-size: var(--adm-t-footnote); color: var(--adm-label-2); text-align: center; }
.cx-model-menu-foot {
  display: block;
  margin-top: 6px;
  padding: 10px 12px 6px;
  border-top: 0.5px solid var(--adm-sep-soft);
}
.cx-model-foot-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: var(--adm-t-caption1);
  font-weight: 600;
  letter-spacing: 0.02rem;
  /* 同小标题的理由：label-3 在浅色下太淡，用户明确要过「对比要够」 */
  color: var(--adm-label-2);
}
/* 右侧的当前档位名：它是**读数**不是标签，给足对比（跟随主题的正文色） */
.cx-slider-value { color: var(--adm-label); font-weight: 600; letter-spacing: 0; }

/* ── 推理强度滑块（自定义控件，不是原生 range）──
   用户口径（历次原话，按时间）：
     ① 「那个滑动动画太差了，一点一点动的，要么高要么中要么低；应该是非线性的、
        跟随用户的光标位置；中间那根线太细了，应该是整个一个长条的椭圆，一个圆在里面滑动；
        左边那些长条不能是空空的，要覆盖点东西进去 —— 拉的越多，里面的动画越激进」；
     ② 「整体都是蓝色，实时的渐变，滑的越多渐变的效果越明显」；
     ③ 「松手之后，要根据偏向于低/中/高哪一个，然后自动滑到那边去」；
     ④ 「你那个渐变推过去的就是一条直线，太怪了、太不流畅了、太难受了 ——
        就把它做成在蓝色色调里面来回切换，做成渐变，跟 Apple 一样，动画流畅」。

   所以这一版的规矩：
     · **位置只有 JS 一个来源**：拖动时 1:1 贴着光标；松手交给弹簧吸到那一档的「家」；
       键盘/点刻度也是同一个弹簧。位置从不由 CSS transition 驱动
       （CSS 只会「补间」，补间中途抓不住、也接不上手指的速度）。
     · **胶囊里所有会动的东西，周期都是写死的常数**。上一版把流动周期做成
       随位置实时变化的 --cx-flow，那会让正在跑的 CSS 动画每帧重算时间轴，
       画面直接「跳一下」—— 这就是 ④ 里说的「太不流畅」的真凶（不是曲线不好看）。
     · **没有「一条直线扫过去」**：原来是让一条直的高光带从右往左横穿整条胶囊。
       现在连「会漂移的光斑」也删掉了（见下面 ④）：UltraCode 只剩**一层静态渐变**，
       动的只有它自己的颜色。
     · **颜色在蓝色系里来回走**：靠 cxTint 在整层上做 hue-rotate ±24° + 明暗呼吸，
       蓝会稳稳地在「深蓝 → 亮蓝 → 青蓝」之间走，不跑出蓝色系。
   颜色 token：:root 是暗色（亮一档的 Apple 系统色），[data-theme="light"] 是标准色。 */
.cx-slider {
  --cx-b1: #00c8ff;                /* 亮青蓝（⚠️ 别用淡蓝/近白色：那会让整条中间发白） */
  --cx-b2: #0a84ff;                /* 正蓝 */
  --cx-b3: #0038bd;                /* 深蓝 */
  --cx-b4: #4b6bff;                /* 靛蓝：让"颜色在变"这件事看得出跨了几个色相 */
  display: block;
  padding: 4px 0 0;
  cursor: pointer;
  touch-action: none;              /* 手机上拖滑块别顺手把页面滚走 */
  -webkit-user-select: none;
  user-select: none;
  outline: none;
}
/* ── 档位决定「已选段」长什么样（用户口径 2026-09-15 第五轮）──
   「中、低这两个档位只是纯调节，下面只有颜色、没有渐变，调到高才有高级动画；
     再加一个档位 UltraCode，实际没啥用，只是默认高 / 映射到高，纯看着牛逼而已。」
   所以：低 / 中 = **一条纯色的蓝**（只有颜色深浅的区别，没有任何动画）；
        高 / UltraCode = 会流动的蓝渐变 + 流光 + 圆的辉光（高级动画从这一档才出现）。
   颜色照旧走主题 token：:root 是暗色（亮一档的 Apple 系统色），浅色主题单独覆盖。
   `--cx-flat` 就是低/中那一层纯色；高/UltraCode 时它只是垫底（上面盖着渐变层）。 */
.cx-slider { --cx-flat: #2d6fd8; }
.cx-slider[data-lv="1"] { --cx-flat: #0a84ff; }
.cx-slider[data-lv="2"] { --cx-flat: #0050d0; }
.cx-slider[data-lv="3"] { --cx-flat: #0038bd; }
[data-theme="light"] .cx-slider { --cx-flat: #6aa9ea; }
[data-theme="light"] .cx-slider[data-lv="1"] { --cx-flat: #007aff; }
[data-theme="light"] .cx-slider[data-lv="2"] { --cx-flat: #0040c8; }
[data-theme="light"] .cx-slider[data-lv="3"] { --cx-flat: #00359e; }
[data-theme="light"] .cx-slider {
  --cx-b1: #00a8f0;
  --cx-b2: #007aff;
  --cx-b3: #0040c8;
  --cx-b4: #3a5cff;
}
.cx-slider:focus-visible .cx-slider-track { box-shadow: 0 0 0 3px var(--adm-accent-soft); }
/* 一条「凹槽」，不是一根「杠」：外圈一条细边 + 内侧顶部压一道阴影 + 底边留一点亮，
   上暗下亮就是「凹进去」的读法（用户：「它就一定是条直杠吗，还是不够优雅」）。 */
.cx-slider-track {
  position: relative;
  height: 30px;                    /* 一整个长条椭圆 */
  border-radius: var(--adm-r-full);
  background: var(--adm-fill-3);
  overflow: hidden;
  box-shadow:
    inset 0 0 0 0.5px var(--adm-sep-soft),
    inset 0 1.5px 3px rgba(0, 0, 0, 0.26),
    inset 0 -0.5px 0 rgba(255, 255, 255, 0.07);
}
/* ── 已选段 ──
   用户口径（2026-09-15 第七轮，每一条都对应下面一个决定）：
     「低中高，它还是只有蓝色的覆盖吧，只有到 UltraCode 才会有动画；
       你现在的那个动画太慢了，然后它是可以左右飘的，不一定是从左到右；
       那个圆点会被那个飘来飘去的动画给覆盖掉，就看不清了，它就变色了，不是白色的；
       然后它那个太小了，最好就比那个外面的框框小一点就行；
       调到低，那左边那个动画就完全没有了，就是空着的，就只有那个白色点。」
   ① **动画只在 UltraCode**：低 / 中 / 高 = 一层纯色的蓝（`--cx-flat`，换档只换深浅），
      会变色的渐变层整层 `opacity: 0`；白点自己一点装饰都没有（辉光/光环第十二轮已删）。
   ② **白点永远是白的**：会变色的那一层画在圆的**下面**（DOM 里排在 knob 之前）——
      它只染圆【左边】那块填色，圆本身还是干净的白色，
      再也不会出现「白点被淹掉、变色、看不清」。
   ③ **蓝色的底要把白点整个包住**（“托住”它）：裁切线取圆的**右边缘**，
      而且**上下也各收 2px**（和圆四周留的那 2px 一样高）—— 于是低档（f=0）时
      这块底和圆**一样大、位置也一样**，整块藏在圆底下，**一颗蓝都不露**。
      （第十二轮：用户「我调到低之后，下面还有蓝色的」——旧版这块底是 30px 满高、
      从胶囊最左边铺起，会在圆的上下各露出 2px 的一条蓝边。）
      这块底本身是一条长条椭圆、两端都是圆头（裁切带 `round`）。
   ④ **UltraCode 只剩「一层渐变在变颜色」**（第十二轮定稿，用户原话：
      「太有层次感了，很多个图层在那乱七八糟的动，这个要改掉。太乱了。
        只要渐变颜色就行了，速度快一点」）：
      **只剩一层** `.cx-slider-liquid` —— 一条静态的蓝色渐变，只有它的**颜色**在快速变
      （`cxTint` 1.5s：色相 ±24° + 明暗 ±18% + 饱和度呼吸）。
      ⚠️ 第十一轮那两层「反向漂移的光斑」（`.cx-slider-drift*` / `cxDriftA` / `cxDriftB`）
      和圆外面那圈呼吸光环（`cxHalo`）**整块删掉了**，DOM 里也不再有两个 <i>。
      **别再加回来** —— 用户嫌的是"图层多、各动各的"，不是嫌颜色不够花。 */
.cx-slider-fill,
.cx-slider-liquid {
  position: absolute;
  border-radius: inherit;
  overflow: hidden;
}
/* ── 已选段底色（低 / 中 / 高 = 就是这一层纯色，没有渐变、没有动画，换档只换深浅）──
   `inset: 2px`（不是 0）：这一层和圆一样高 —— 26px（圆的直径），
   上下左右正好留出那 2px 的凹槽边，所以低档时它 ≙ 圆底下那一个 26px 的圆、完全看不见。
   ⚠️ 别改回 inset: 0 —— 满高会在圆的上下各露一条 2px 的蓝边（用户第十二轮的投诉）。
   右端裁到**圆的右边缘**（白的点整个坐在它上面），裁切口两端都是圆头。
   ⚠️ 这里量的是**元素自己的盒子**（它已经 inset: 2px 了，宽 = 胶囊宽 − 4px）：
   `--cx-w = calc(26px + (100% - 26px) × f)`：
     · f=0 → 26px = 圆的直径 → 这块底 ≙ 圆本身，全藏在圆底下（低档一颗蓝都不露）；
     · f=1 → 100% = 元素右边缘 = 圆的右边缘（白点顶到最右时，蓝底正好被它吃满）。
   右端是**圆角**（`round`）：蓝色的底本来就是一条长条椭圆 —— 右端也必须是圆的，
   不然白点旁边会看到一条直切的边（用户：「它的右边怎么还是个平的呢，就是一个直的」）。
   ⚠️ 这里【故意不加】transition —— 拖动时填色必须和圆一样贴着手，
   多 90ms 的补间就是「填色慢半拍」；键盘/吸附的平滑由 JS 的弹簧负责。 */
.cx-slider-fill {
  inset: 2px;
  background: var(--cx-flat);
  transition: background-color 260ms var(--adm-ease-out);
  clip-path: inset(0 calc(100% - var(--cx-w, 26px)) 0 0 round var(--adm-r-full));
}
/* ── 液体层（UltraCode 专属）：**整条胶囊**都在这层里，颜色自己流、自己变 ──
   用户 2026-09-15 第十三轮原话：「为什么 UltraCode 它还是中间有一条杠，它就白色呢，
   我需要整个条，就是变成其他颜色这样子动，比较激进的」。所以这一层有三条硬规矩：
     ① **铺满整条胶囊**：`inset: 0`、**不裁切**（连那 2px 的槽边也盖住）——
        不像下面那层纯色只铺到圆为止；UltraCode 一开，整条从最左到最右都是彩色的。
     ② **不许有发白的色标**：色板只用饱和的深蓝 / 正蓝 / 青蓝 / 靛蓝，
        并且**不做 brightness 加亮**（加亮 + 淡蓝 = 整条中间发白，用户看到的就是"一条白杠"）。
     ③ **动得明显**：`background-size: 260%` + 动画推 `background-position`，
        颜色**从条的一头流到另一头**（cxFlow 1.4s，来回）；再叠一层 hue-rotate 让色相整体呼吸
        （cxTint 3s）—— 两个动画都在**这一层**上，不新增图层。
   低 / 中 / 高整层 opacity 0（「低中高只有蓝色的覆盖」），只有 UltraCode 才淡入；
   而且它画在圆的**下面**，圆永远是白的。
   ⚠️ 会动的图层一律**先铺好、靠 opacity 决定看不看得见**，绝不去加/删动画或改 animation-duration：
   改正在跑的动画时长会让它当场重算时间轴、画面跳一下（前面两轮都踩过）。 */
.cx-slider-liquid {
  inset: 0;                        /* 铺满整条胶囊：不留槽边、也不跟圆走 */
  opacity: 0;
  transition: opacity 340ms var(--adm-ease-out);
  background-image: linear-gradient(100deg,
    var(--cx-b3) 0%, var(--cx-b2) 16%, var(--cx-b1) 33%, var(--cx-b4) 50%,
    var(--cx-b1) 67%, var(--cx-b2) 84%, var(--cx-b3) 100%);
  background-size: 260% 100%;
  background-position: 0% 50%;
  animation: cxFlow 1.4s ease-in-out infinite alternate,
             cxTint 3s ease-in-out infinite;
}
.cx-slider[data-lv="3"] .cx-slider-liquid { opacity: 1; }
/* 颜色从条的一头**流**到另一头（`background-position` 推一条 260% 宽的渐变）。
   来回走（`alternate`）而不是单向循环：单向循环在回到起点时会"啪"地跳一下。 */
@keyframes cxFlow {
  from { background-position:   0% 50%; }
  to   { background-position: 100% 50%; }
}
/* 整体色相在这几个颜色之间来回呼吸：正蓝 ⇄ 青蓝 ⇄ 靛蓝（±38°，不跑出蓝紫这一片）。
   ⚠️ 不加 brightness —— 一加亮，中间那段就发白（用户第十三轮的投诉）。 */
@keyframes cxTint {
  0%   { filter: hue-rotate(0deg) saturate(1.2); }
  50%  { filter: hue-rotate(38deg) saturate(1.6); }
  100% { filter: hue-rotate(0deg) saturate(1.2); }
}
/* 圆：一个 26px 的圆在 30px 的胶囊【里面】滑，四周各留 2px ——
   用户：「它那个太小了，最好就比那个外面的框框小一点就行」。
   位置用 transform 走（不写 left —— left 是布局属性，每帧改它是性能陷阱，
   也和「组件不动布局」的规矩冲突）。
   容器 left/right/top 各 2px → 容器宽 = 胶囊宽 − 4px；
   translateX 的百分比是按**自己的宽度**算的，所以 (100% − 26px) 正好 = 胶囊宽 − 30px = 原来的行程。
   圆心 = 2 + 13 + 行程×f = 15px + 行程×f —— 和四个档位点用的算式完全一致（点与圆严格重合）。
   这样 f=0 时圆的左边缘正好贴在胶囊内壁（上下也各 2px），
   f=1 时贴着右内壁 —— 「低档能顶到最左边、高档能顶到最右边」靠的就是落点取 0 和 1。 */
.cx-slider-knob {
  position: absolute;
  left: 2px;
  right: 2px;
  top: 2px;
  height: 26px;
  pointer-events: none;
  will-change: transform;
  transform: translateX(calc(var(--cx-f, 0) * (100% - 26px)));
}
.cx-slider-knob::before {          /* 圆本体 */
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #ffffff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35), 0 0 0 0.5px rgba(0, 0, 0, 0.16);
  transition: transform 150ms var(--adm-ease-out);
}
.cx-slider.is-drag .cx-slider-knob::before { transform: scale(1.12); }   /* 抓住时变大：手感 */
/* ⚠️ 圆旁边的柔光 / 会呼吸的光环（`.cx-slider-knob::after` + `cxHalo`）第十二轮删掉了：
   用户嫌「很多个图层在那乱七八糟的动」。现在 UltraCode 只有一层渐变在变颜色，
   白点周围干干净净 —— 别再给它加辉光、光环、脉冲。 */
/* ── 轨道上的四个档位点 ──
   它就是「这是一把尺子、不是一根杠」的那部分：四个点标出四个档位的落点，
   点跟着 --cx-w 用的是同一套算式（15px + (100% − 30px) × f），
   所以点和圆的落点严格重合（圆停在档位上时正好把点盖住，像卡进卡槽）。
   用一层 background-image 画四个小圆点，不额外加 DOM。 */
.cx-slider-dots {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    radial-gradient(circle 1.4px at 15px 50%, rgba(255, 255, 255, 0.5) 0%, transparent 100%),
    radial-gradient(circle 1.4px at calc(15px + (100% - 30px) * 0.3333) 50%, rgba(255, 255, 255, 0.5) 0%, transparent 100%),
    radial-gradient(circle 1.4px at calc(15px + (100% - 30px) * 0.6667) 50%, rgba(255, 255, 255, 0.5) 0%, transparent 100%),
    radial-gradient(circle 1.4px at calc(100% - 15px) 50%, rgba(255, 255, 255, 0.5) 0%, transparent 100%);
}
/* ── 四个档位名 ──
   每个名字**居中压在它那一档的落点上**（left 用和圆/点同一套算式，再 translateX(-50%)），
   所以「低」正好在最左边的圆下面、「UltraCode」正好在最右边的圆下面，不会错开半格。
   wrap 左右各留 28px 内边距，正好让两端的名字（UltraCode 约 50px 宽）伸进这段留白里，不越界。 */
.cx-slider-wrap { padding: 0 28px; }
.cx-slider-ticks {
  position: relative;
  height: 24px;
  margin-top: 2px;
}
.cx-slider-ticks button {
  position: absolute;
  top: 0;
  height: 24px;
  min-height: 24px;
  padding: 2px 4px;
  transform: translateX(-50%);
  border: 0;
  border-radius: var(--adm-r-sm);
  background: transparent;
  color: var(--adm-label-3);
  font: inherit;
  font-size: var(--adm-t-caption2);
  letter-spacing: 0;
  white-space: nowrap;
  cursor: pointer;
  transition: color 160ms var(--adm-ease-out);
}
.cx-slider-ticks button:nth-child(1) { left: 15px; }
.cx-slider-ticks button:nth-child(2) { left: calc(15px + (100% - 30px) * 0.3333); }
.cx-slider-ticks button:nth-child(3) { left: calc(15px + (100% - 30px) * 0.6667); }
.cx-slider-ticks button:nth-child(4) { left: calc(100% - 15px); }
.cx-slider-ticks button.is-active { color: var(--adm-label); font-weight: 600; }
/* 设置页里那一份：跟 .settings-row 的右侧控件位对齐。
   宽度要够放四个档位名（「极高」那一个最长），否则最右边的名字会顶到行尾。 */
.cx-slider-field { flex: 0 0 240px; max-width: 46%; }

/* ⚠️⚠️ 手机上是**另一种布局**，这两条必须要，缺了就是线上那个 bug
   ────────────────────────────────────────────────────────────────
   styles.css 的 @media (max-width: 768px) 把 .settings-row 改成了
   `flex-direction: column`。那一刻主轴从「横」翻成「竖」，
   上面那条 `flex: 0 0 240px` 的含义就**整个反过来**了：

     · flex-basis 量的是**主轴**，不是宽度。竖排下主轴是高度 ——
       于是这个滑块盒子拿到的是 **height: 240px**；
     · flex-grow / flex-shrink 都是 0，所以那 240px 既不会被内容撑开、
       也不会被压缩，就是一整块 240px 高的空盒子；
     · 再叠上 `max-width: 46%`，宽度只剩卡片的一半不到。

   用户看到的就是：说明文字下面，贴左边缩着一条半宽的滑块，
   底下拖着一大块什么都没有的空白 —— 原话「这个里面下面的那个地方有问题」。
   而且滑块本体（30px 的胶囊 + 一排档位名）全都挤在盒子最顶上，
   看起来像「没渲染完」。

   竖排下想要的是「占满一行、高度交给内容」：
     flex 改成不参与（0 0 auto）、宽度自己撑满（width:100%）、
     max-width 放开（否则 46% 还在）。 */
@media (max-width: 768px) {
  .cx-slider-field { flex: 0 0 auto; width: 100%; max-width: 100%; }
}

/* ============ 8b. 速率限额提示（图标 + 两行字，高度随内容） ============ */
/* 用户口径（原话）：
     「高度由要显示的内容实时决定：先是比较大的加粗字体（5 小时用完就写
       『你的 5 小时配额已耗尽』），然后换行，下面灰色稍小的字
       『你的使用限制将在 22:09 重置』；超过一天就把几月几日补在前面。
       不一定橙色，黑/灰也行，跟着主题（暗色/浅色）适配，对比要够、别太淡。」
   所以：没有固定高度、没有行数裁剪、没有进度条、没有按钮、没有 Request ID。
   颜色全走主题 token：标题 --adm-label（最高对比）+ 700 字重，副行 --adm-label-2，
   底 --adm-surface-2，描边 --adm-sep（半像素、看得见但不刺眼）。 */
.cx-ratelimit {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  margin: 2px 0;
  padding: 12px 14px;
  border-radius: var(--adm-r-lg);
  background: var(--adm-surface-2);
  box-shadow: 0 0 0 0.5px var(--adm-sep);
}
.cx-ratelimit-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-top: 1px;                         /* 与第一行文字的大写字高对齐 */
  border-radius: 50%;
  background: var(--adm-fill-3);
  color: var(--adm-label-2);
}
.cx-ratelimit-icon svg { width: 15px; height: 15px; }
.cx-ratelimit-body { min-width: 0; }
/* 第一行：大一号 + 加粗 + 最高对比 */
.cx-ratelimit-title {
  font-size: var(--adm-t-headline);        /* 17px */
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -0.01rem;
  color: var(--adm-label);
}
/* 第二行：换行后的灰色小字 */
.cx-ratelimit-sub {
  margin-top: 4px;
  font-size: var(--adm-t-footnote);        /* 13px */
  line-height: 1.5;
  color: var(--adm-label-2);
}
.cx-ratelimit-sub [data-left] { font-variant-numeric: tabular-nums; }

/* ============ 9. 额度卡片（取代原来那张只有图标+一句话的简陋版） ============ */
.cx-quota {
  display: flex;
  flex-direction: column;
  gap: 11px;
  width: 100%;
  margin: 4px 0 2px;
  padding: 15px;
  border-radius: var(--adm-r-lg);
  background: var(--adm-surface);
  box-shadow: 0 0 0 0.5px var(--adm-sep);
}
.cx-quota-head { display: flex; align-items: flex-start; gap: 10px; }
.cx-quota-icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--adm-warn) 18%, transparent);
  color: var(--adm-warn-text);
}
.cx-quota-icon svg { width: 16px; height: 16px; }
.cx-quota-title { font-size: var(--adm-t-headline); font-weight: 600; letter-spacing: var(--adm-ls-body); }
.cx-quota-sub { margin-top: 3px; font-size: var(--adm-t-footnote); line-height: 1.5; color: var(--adm-label-2); }
.cx-quota-bar { height: 8px; border-radius: 999px; background: var(--adm-fill-3); overflow: hidden; }
.cx-quota-fill {
  display: block; height: 100%; border-radius: 999px;
  background: var(--adm-ok);
  transition: width 400ms var(--adm-ease-out), background-color 200ms var(--adm-ease-out);
}
.cx-quota-fill.is-warn { background: var(--adm-warn); }
.cx-quota-fill.is-bad { background: var(--adm-bad); }
/* 「一行就是一行」：每条事实独占一行，且行内不再折行。
   以前是 flex-wrap + 一行里并排两件事，窄屏上会在数字中间断开，
   读的人分不清后半截属于哪一条（用户原话：一行字不知道是一行）。
   现在按 iOS 顶部对齐的规格行来排。 */
.cx-quota-meta {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: var(--adm-t-caption1);
  color: var(--adm-label-2);
}
.cx-quota-meta > span {
  display: block;
  white-space: nowrap;         /* 短句，一行就该是一行 */
  overflow: hidden;
  text-overflow: ellipsis;     /* 万一真放不下，省略号而不是断成两半 */
}
.cx-quota-meta b { color: var(--adm-label); font-variant-numeric: tabular-nums; }
/* 动作区：一级动作整行，次要动作排在下面。
   并排 + wrap 的问题是「升级套餐 · 推荐 Pro 5X」和「复制 Request ID」宽度差很多，
   一换行就参差不齐。 */
.cx-quota-actions { display: flex; flex-direction: column; gap: 8px; }
.cx-quota-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 100%;
  min-height: 44px; padding: 0 16px;
  border: 0.5px solid var(--adm-sep-soft);
  border-radius: var(--adm-r-full);
  background: var(--adm-fill-3);
  color: var(--adm-label);
  font: inherit; font-size: var(--adm-t-subhead); font-weight: 600;
  cursor: pointer;
  transition: transform 160ms var(--adm-ease-out), background-color 160ms var(--adm-ease-out);
}
.cx-quota-btn:active { transform: scale(0.97); }
.cx-quota-btn.is-primary { border-color: transparent; background: var(--adm-accent); color: var(--adm-on-accent); }
/* Request ID：标签一行，值单独一整行。
   ⚠️ 值必须 white-space: nowrap —— 45 个字符的机器 ID 一旦折行，
   看起来就像两个不同的值，用户抄给客服时会抄错半截。
   放不下时横向滚动（它是只读值，不是点击目标，滚动不吞点击）。 */
.cx-quota-rid {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: var(--adm-t-caption2);
  color: var(--adm-label-3);
}
.cx-quota-rid code {
  display: block;
  width: 100%;
  padding: 5px 8px;
  border-radius: var(--adm-r-sm);
  background: var(--adm-fill-3);
  font-family: var(--adm-mono);
  font-size: var(--adm-t-caption2);
  color: var(--adm-label-2);
  white-space: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  user-select: all;              /* 点一下即全选，方便复制给客服 */
}
/* 窄屏（320px 级）再收一点字号，尽量让 45 字符正好一行放下 */
@media (max-width: 360px) {
  .cx-quota-rid code { font-size: 10px; letter-spacing: -0.02em; }
}
.cx-quota-foot { font-size: var(--adm-t-caption1); line-height: 1.55; color: var(--adm-label-3); }
.cx-quota.is-plain .cx-quota-icon { background: color-mix(in srgb, var(--adm-bad) 18%, transparent); color: var(--adm-bad-text); }

/* ============ 10. 其它页面统一外壳（用量 / 设置 / API / 升级） ============ */
.cx-page { width: 100%; max-width: 760px; margin: 0 auto; padding: 4px 16px calc(40px + var(--cx-tabbar-space)); }
.cx-card {
  border-radius: var(--adm-r-lg);
  background: var(--adm-surface);
  padding: 15px;
  box-shadow: 0 0 0 0.5px var(--adm-sep);
  margin-bottom: 14px;
}
.cx-card-title { margin: 0 0 4px; font-size: var(--adm-t-headline); font-weight: 600; }
.cx-card-sub { margin: 0 0 12px; font-size: var(--adm-t-footnote); color: var(--adm-label-2); }
.cx-card-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.cx-note { margin: 10px 2px 0; font-size: var(--adm-t-caption1); line-height: 1.5; color: var(--adm-label-2); }

/* ============ 11. 悬停（只在真指针设备） ============ */
@media (hover: hover) and (pointer: fine) {
  .cx-nav-btn:hover { background: var(--adm-fill-3); }
  .cx-nav-btn.is-primary:hover { filter: brightness(1.12); }
  .cx-tab:hover { color: var(--adm-label); }
  .cx-conv:hover { background: var(--adm-fill-3); }
  .cx-rail-link:hover { background: var(--adm-fill-3); color: var(--adm-label); }
  .cx-menu-item:hover { background: var(--adm-fill-3); }
  .cx-code-copy:hover { background: var(--adm-fill-2); color: var(--adm-label); }
  .cx-quota-btn:hover { background: var(--adm-fill-2); }
  .cx-quota-btn.is-primary:hover { filter: brightness(1.12); }
  .cx-msg-actions .cx-copy-btn:hover { background: var(--adm-fill-2); color: var(--adm-label); }
  .cx-model-item:hover { background: var(--adm-fill-3); }
  .cx-model-item.is-active:hover { background: var(--adm-fill-2); }
  .cx-slider-ticks button:not(.is-active):hover { color: var(--adm-label-2); }
}

/* ============ 12. 无障碍三档 ============ */
@media (prefers-reduced-motion: reduce) {
  .cx-nav, .cx-tab, .cx-tabbar, .cx-sheet, .cx-menu, .cx-model-menu, .cx-slider,
  .cx-think-chevron, .cx-think-body, .cx-model-item-check,
  .cx-model-chip, .cx-model-chip svg,
  .cx-msg-actions, .cx-caret, .cx-typing span {
    transition-duration: 1ms !important;
    animation: none !important;
  }
  /* 光标改为静态（去掉闪烁），但仍能看出「正在输出」 */
  .cx-caret { opacity: 1; }
  /* 滑块的颜色变化停下（位置反馈、档位文字照旧 —— 减的是「动」，不是信息）。
     停的是那一层会变色的渐变，不是整条胶囊，`:focus-visible` 的焦点环等一律保留。
     （UltraCode 的渐变底色照旧显示，只是不再变颜色。） */
  .cx-slider-liquid {
    animation: none !important;
  }
}
@media (prefers-reduced-transparency: reduce) {
  .cx-nav, .cx-nav.is-scrolled, .cx-tabbar, .cx-composer, .cx-menu, .cx-model-menu {
    background: var(--adm-surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  /* 输入区在弱透明度下退回实心底（上一版这里还要顺手把 mask 关掉；
     现在 mask 已经从 .cx-composer 上彻底删掉了，只剩这一条实心底）。 */
  .cx-composer {
    background: var(--adm-bg);
  }
  /* 弱透明度：玻璃换实心，弹层阴影也一起收掉（阴影本来就是用来「托起玻璃」的，
     变成实心面之后留着就是一圈多余的灰）。 */
  .cx-model-menu {
    filter: none;
    box-shadow: 0 0 0 0.5px var(--adm-sep);
  }
  /* 渐隐是一种「弱化」，和弱透明度/高对比的诉求相反，直接关掉 */
  .cx-model-menu-list.is-scroll { --cx-fade-t: 0px; --cx-fade-b: 0px; }
}
@media (prefers-contrast: more) {
  .cx-card, .cx-composer-inner, .cx-quota, .cx-code, .cx-ratelimit, .cx-model-menu { box-shadow: 0 0 0 1px currentColor; }
  .cx-think { border-bottom-color: currentColor; }     /* 思考区那条灰线在「提高对比度」下变实线 */
  .cx-ratelimit-sub { color: var(--adm-label); }
  /* 胶囊也补一圈实边：它的底本来只是个半透明 fill，高对比下不够界 */
  .cx-model-chip { background: var(--adm-fill-2); box-shadow: 0 0 0 1px currentColor; }
  .cx-model-menu-list.is-scroll { --cx-fade-t: 0px; --cx-fade-b: 0px; }
}

/* ============ 13. 响应式 ============ */
@media (min-width: 769px) {
  :root { --cx-tabbar-space: 0px; }        /* 桌面没有浮动 Tab 栏，占位归零 */
  .cx-rail { display: flex; }
  .cx-tabbar { display: none; }
  #menu-btn { display: none; }        /* 桌面历史栏常驻，不需要「记录」按钮 */
  .cx-thread { padding-bottom: 40px; }
  .cx-page { padding-bottom: 40px; }
  .cx-composer { padding-bottom: 16px; }          /* 桌面没有浮动 Tab 栏，不用让位 */
  .cx-nav-inner { padding: 0 16px; }
  .cx-largetitle { padding: 8px 24px 14px; }
  .cx-sheet-overlay { align-items: center; padding: 24px; }
  .cx-sheet {
    max-width: 560px;
    max-height: 80dvh;
    border-radius: var(--adm-r-lg);
    padding-bottom: 0;
    transform: scale(0.96);
    opacity: 0;
    transition: transform 220ms var(--adm-ease-out), opacity 200ms var(--adm-ease-out);
  }
  .cx-sheet.is-open { transform: none; opacity: 1; }
  .cx-sheet-grab { display: none; }
}
/* ============ 手机端整体缩一档 ============
   新界面的正文按 iOS 规范是 17px，但在手机上实测偏大（用户反馈「整体放大了」，
   要求缩约 20%）。这里只缩「对话这条线」的排版，不动后台管理那套（那边是数据表，
   字号本来就小、也已经被接受）。 */
@media (max-width: 768px) {
  .cx-bubble, .cx-msg-body { font-size: 0.9375rem; }        /* 17 → 15 */
  .cx-md p, .cx-md li { line-height: 1.58; }
  .cx-md h1, .cx-md h2, .cx-md h3 { margin: 18px 0 8px; }
  .cx-md h2 { font-size: 1.0625rem; }                        /* 20 → 17 */
  .cx-md h3 { font-size: 1rem; }
  .cx-md table { font-size: 0.875rem; }
  .cx-pre { font-size: clamp(0.75rem, 3.2vw, 0.8125rem); }   /* 13 → 12~13，长代码更好读 */
  .cx-thread { gap: 18px; padding-left: 14px; padding-right: 14px; }
  .cx-msg-head { font-size: var(--adm-t-caption2); }
  .cx-think-text { font-size: var(--adm-t-caption1); }
  .cx-welcome-h1 { font-size: var(--adm-t-title2); }         /* 28 → 22 */
  .cx-welcome-sub { font-size: var(--adm-t-footnote); }
  .cx-welcome { padding-top: 6vh; }
  .cx-quota-title { font-size: var(--adm-t-body); }
  .cx-quota-btn { font-size: var(--adm-t-footnote); }
}

/* 抽屉：给一个最小高度，否则没有历史时它只有一条，看着像坏了 */
.cx-sheet { min-height: 46dvh; }
.cx-sheet-nav { border-bottom: 0.5px solid var(--adm-sep); padding-bottom: 10px; margin-bottom: 2px; }
.cx-empty { padding: 26px 12px; text-align: center; }
.cx-empty-title { font-size: var(--adm-t-subhead); font-weight: 600; }
.cx-empty-desc { margin-top: 5px; font-size: var(--adm-t-caption1); line-height: 1.55; color: var(--adm-label-2); }

@media (max-width: 420px) {
  .cx-bubble { max-width: 88%; }
  .cx-welcome { padding-top: 6vh; }
  .cx-largetitle h1 { font-size: var(--adm-t-title2); }
  .cx-model-chip { max-width: 54%; }
}

/* ============================================================================
   ★ 说明句里的 <b>：不加粗、跟正文同色（2026-09-16 用户要求，别再改回去）
   ----------------------------------------------------------------------------
   用户原话：「我是说这种解释词，[开启后本账号的 API 调用不再受下行限速约束…]，
             像这种词，这里面就有加粗的文字，它不需要加粗，然后把它变成灰色就行了。
             并不是网站里的所有东西的加粗都给我取消」。

   所以这里**只针对「说明/解释性文字」这一个语境**：
     · 里面的 <b> / <strong> 既不加粗，也不再用高亮色，直接继承所在段落的颜色
       （这些段落本来就是灰的：.u-meta 用 --u-muted、.adm-hint 用 --adm-label-2…）；
     · 标题、卡片标题、按钮、数据值（.adm-kv 的右列）、徽标……**一律照旧**，不受影响。

   ⚠️ 曾经误做过一次「全站压平所有 font-weight」（css/no-bold.css），
      那是理解错了 —— 标题也跟着塌掉，整个界面失去层次。已删除，别再复活。
      判据很简单：**这个 <b> 是在一句话里当语气强调用的，还是在展示一个值？**
      是前者 → 归到下面这份名单里；是后者 → 不要动。
   ============================================================================ */
.u-meta b, .u-meta strong,
.u-sub b, .u-sub strong,
.u-count b, .u-count strong,
.speed-note b, .speed-note strong,
.up-done-desc b, .up-done-desc strong,
.up-req-warn b, .up-req-warn strong,
.pr-modal-sub b, .pr-modal-sub strong,
.adm-hint b, .adm-hint strong,
.adm-group-note b, .adm-group-note strong,
.adm-card-sub b, .adm-card-sub strong,
.adm-empty b, .adm-empty strong,
.adm-banner-body b, .adm-banner-body strong,
.adm-field-hint b, .adm-field-hint strong,
.cx-quota-meta b, .cx-quota-meta strong {
  font-weight: 400;
  color: inherit;
}
