/* 深聊 DeepChat — 设计令牌与样式
   核心六色：画布 / 表面 / 正文 / 次文 / 动作 / 状态；危险色是状态色的语义例外。
   对比度（正文/次要文字在大背景）：
   浅色  #101218 on #F6F7F8 = 17.45:1  #3B4244 on #F6F7F8 = 9.56:1
   深色  #F6F7F8 on #101218 = 17.45:1  #C2C7D0 on #101218 = 11.03:1
   触控目标：交互控件至少 44×44；紧凑信息标签不承担点击。 */

@font-face {
  font-family: "Montserrat";
  src: url("./assets/fonts/montserrat-latin.woff2") format("woff2");
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
}
@font-face {
  font-family: "Zen Dots";
  src: url("./assets/fonts/zendots-latin.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

:root {
  --r-sm: 8px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-xl: 18px;

  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 32px;

  --tap: 44px;
  --fs-scale: 1;
  --fs-body: calc(17px * var(--fs-scale));
  --fs-callout: calc(15.5px * var(--fs-scale));
  --fs-foot: calc(13px * var(--fs-scale));
  --fs-head: calc(17px * var(--fs-scale));
  --fs-title: calc(20px * var(--fs-scale));
  /* 字体栈按平台排优先级：Apple 上用 SF Pro + 苹方，Windows 上退到 Segoe UI Variable，
     最后才轮到微软雅黑——之前 Windows 直接掉到雅黑，这是"字体不好看"的主因。 */
  --font-ui: "Montserrat", -apple-system, "PingFang SC", "Segoe UI Variable Text",
             "Segoe UI", "Microsoft YaHei UI", "Microsoft YaHei", sans-serif;
  --font-display: "Zen Dots", "Montserrat", -apple-system, "PingFang SC", sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", "Cascadia Code",
               Menlo, Consolas, "PingFang SC", monospace;
}

html[data-font="sm"] { --fs-scale: .93; }
html[data-font="md"] { --fs-scale: 1; }
html[data-font="lg"] { --fs-scale: 1.12; }
html[data-font="xl"] { --fs-scale: 1.26; }

/* ── 浅色 ───────────────────────────────────────────────── */
html[data-theme="light"] {
  --bg: #F6F7F8;
  --surface: #FFFFFF;
  --surface-2: #E9EBEF;
  --surface-3: #DDE1E7;
  --text: #101218;
  --text-2: #3B4244;
  /* aaastro 的 #828282 在此浅底仅 3.58:1；压暗到同色相关系以满足普通文字 AA。 */
  --text-3: #6B7072;
  --accent: #084AF3;
  --accent-on: #FFFFFF;
  --accent-soft: #E8EEFF;
  --signal: #087A68;
  --signal-soft: #DDF1EC;
  --danger: #C23B32;
  --danger-soft: #F9E7E5;
  --warn: #815500;
  --sep: rgba(23, 25, 29, .11);
  --sep-strong: rgba(23, 25, 29, .22);
  --bar: rgba(246, 247, 248, .9);
  --code-bg: #EEEEEE;
  --code-text: #101218;
  --shadow: 0 1px 2px rgba(16, 20, 30, .07), 0 12px 36px rgba(16, 20, 30, .11);
  color-scheme: light;
}

/* ── 深色 ───────────────────────────────────────────────── */
html[data-theme="dark"] {
  --bg: #101218;
  --surface: #15171C;
  --surface-2: #1D2026;
  --surface-3: #292D35;
  --text: #F6F7F8;
  --text-2: #C2C7D0;
  --text-3: #9AA1AE;
  --accent: #4175FC;
  --accent-on: #FFFFFF;
  --accent-soft: #1D2C59;
  --signal: #43C6AF;
  --signal-soft: #14342F;
  --danger: #FF746A;
  --danger-soft: #33191A;
  --warn: #F0BD55;
  --sep: rgba(255, 255, 255, .10);
  --sep-strong: rgba(255, 255, 255, .20);
  --bar: rgba(16, 18, 24, .9);
  --code-bg: #1B1E26;
  --code-text: #F6F7F8;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 32px rgba(0, 0, 0, .5);
  color-scheme: dark;
}

@media (prefers-reduced-transparency: reduce) {
  html[data-theme="light"] { --bar: #F3F4F6; }
  html[data-theme="dark"]  { --bar: #0A0B0E; }
}
@media (prefers-contrast: more) {
  html[data-theme="light"] { --sep: rgba(0,0,0,.28); --text-2: #454B57; }
  html[data-theme="dark"]  { --sep: rgba(255,255,255,.30); --text-2: #B4BCC9; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* 关键：类选择器里的 display 会盖过 UA 的 [hidden]，这里统一兜底 */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  overscroll-behavior: none;
}
body.sheet-open { overflow: hidden; }

button { font-family: inherit; font-size: inherit; color: inherit; }
:focus-visible { outline: 2.5px solid var(--accent); outline-offset: 2px; border-radius: 6px; }

/* ── 启动态 ─────────────────────────────────────────────── */
.boot {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; inset: 0;   /* 老 WebView 不认 inset，用四边兜底 */
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-4);
  color: var(--accent);
}
.boot-text { color: var(--text-2); font-size: var(--fs-callout); margin: 0; }
.boot-mark { animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: .55; transform: scale(.97); } 50% { opacity: 1; transform: scale(1); } }

/* ── 主结构 ─────────────────────────────────────────────── */
.app {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; inset: 0;   /* 同上：老 WebView 兜底 */
  display: flex; flex-direction: column;
  background: var(--bg);
}

.toolbar {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: var(--tap) 1fr var(--tap);
  align-items: center;
  gap: var(--sp-1);
  padding: 0 var(--sp-2);
  min-height: var(--tap);
  padding-top: env(safe-area-inset-top);
  background: var(--bar);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--sep);
}
.tb-btn {
  width: var(--tap); height: var(--tap);
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--r-md);
  color: var(--accent);
}
.tb-btn:active { background: var(--surface-2); }
.tb-title {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; min-height: var(--tap);
  background: none; border: 0; border-radius: var(--r-md);
  padding: 0 var(--sp-2);
  overflow: hidden;
}
.tb-title:active { background: var(--surface-2); }
.tb-title-main {
  font-size: var(--fs-title); font-weight: 600; letter-spacing: -.01em;
  max-width: 62vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-title-main, .token-total, .usage-number, .stat-value { font-family: var(--font-display); }
.tb-title-sub {
  font-size: calc(11.5px * var(--fs-scale)); color: var(--text-2);
  max-width: 62vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── 消息流 ─────────────────────────────────────────────── */
.stream {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: var(--sp-4) var(--sp-4) var(--sp-2);
  display: flex; flex-direction: column; gap: var(--sp-5);
}
/* 注意：这里故意不设 scroll-behavior: smooth。
   它会让 JS 里的 behavior:'auto' 也变成动画，流式输出时每 90ms 重启一次平滑滚动会明显卡顿。
   滚动是否平滑改由 chat.js 的 scrollToBottom() 逐次显式指定。 */

.empty {
  margin: auto;
  max-width: 30rem;
  text-align: center;
  color: var(--text-2);
  padding: var(--sp-6) var(--sp-2);
}
.empty-mark {
  width: 56px; height: 56px; margin: 0 0 var(--sp-4);
  color: var(--accent);
}
.empty-mark svg { width: 56px; height: 56px; }
.empty h1 {
  font-size: calc(28px * var(--fs-scale));
  font-weight: 700;
  letter-spacing: -.022em;
  color: var(--text);
  margin: 0 0 var(--sp-2);
}
.empty p { margin: 0 0 var(--sp-5); font-size: var(--fs-callout); }
.empty-sug {
  display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: center;
}
.sug {
  min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface); border: 1px solid var(--sep); border-radius: var(--r-xl);
  color: var(--text); font-size: var(--fs-callout);
}
.sug:active { background: var(--surface-2); }

.msg { display: flex; flex-direction: column; max-width: 46rem; width: 100%; margin: 0 auto; }
.msg-user { align-items: flex-end; }
.bubble-user {
  background: var(--accent);
  color: var(--accent-on);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-xl) var(--r-xl) var(--r-sm) var(--r-xl);
  max-width: min(84%, 34rem);
  white-space: pre-wrap;
  /* 注意：这里必须用 break-word，anywhere 会参与固有尺寸计算，把气泡压成一个字符宽 */
  overflow-wrap: break-word;
}

.msg-ai { align-items: stretch; gap: var(--sp-3); }
.ai-body { min-width: 0; }
.ai-foot { display: flex; gap: var(--sp-1); margin-top: var(--sp-1); flex-wrap: wrap; }
.icon-btn {
  min-width: var(--tap); min-height: 36px;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  background: none; border: 0; border-radius: var(--r-md);
  color: var(--text-2); font-size: var(--fs-foot);
}
.icon-btn:active { background: var(--surface-2); }
.icon-btn[disabled] { opacity: .45; }
.icon-btn svg { flex: none; }
.usage {
  display: inline-flex; align-items: center;
  padding: 6px 4px; color: var(--text-3); font-size: var(--fs-foot);
  font-variant-numeric: tabular-nums;
}
.skill-prompt {
  margin: 0; padding: var(--sp-3) var(--sp-4);
  font-family: var(--font-ui); font-size: var(--fs-foot); line-height: 1.65;
  color: var(--text-2); white-space: pre-wrap; word-break: break-word;
}

/* 思考过程 */
.think { border-left: 2px solid var(--sep-strong); padding-left: var(--sp-3); }
.think-head {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 0 6px; margin-left: -6px;
  background: none; border: 0; border-radius: var(--r-sm);
  color: var(--text-2); font-size: var(--fs-foot); font-weight: 600;
}
.think-body {
  margin-top: var(--sp-1);
  color: var(--text-2); font-size: var(--fs-foot);
  white-space: pre-wrap; word-break: break-word;
  max-height: 22rem; overflow-y: auto;
}
.think-body[hidden] { display: none; }
.caret { transition: transform .2s ease; }
.caret.open { transform: rotate(90deg); }

/* 技能调用卡片 */
.tool {
  border: 1px solid var(--sep);
  background: var(--surface);
  border-radius: var(--r-md);
  overflow: hidden;
}
.tool + .tool { margin-top: var(--sp-2); }
.tool-head {
  display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  background: none; border: 0; text-align: left;
}
.tool-head:active { background: var(--surface-2); }
.tool-ic {
  flex: none; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--signal-soft); color: var(--signal);
}
.tool-ic .dc-icon { width: 18px; height: 18px; }
.tool-ic.fail { background: var(--danger-soft); color: var(--danger); }
.tool-main { flex: 1 1 auto; min-width: 0; }
.tool-name { display: block; font-size: var(--fs-callout); font-weight: 600; }
.tool-arg {
  display: block;
  font-size: var(--fs-foot); color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tool-state { font-size: var(--fs-foot); color: var(--text-2); flex: none; }
.tool-out {
  border-top: 1px solid var(--sep);
  padding: var(--sp-3);
  background: var(--code-bg);
  font-family: var(--font-mono);
  font-size: calc(12.5px * var(--fs-scale));
  color: var(--text-2);
  white-space: pre-wrap; word-break: break-word;
  max-height: 20rem; overflow: auto;
}
.tool-out[hidden] { display: none; }

/* 检索来源 */
.sources { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.src {
  display: inline-flex; align-items: center; gap: 6px;
  max-width: 100%; padding: 6px var(--sp-3);
  background: var(--surface); border: 1px solid var(--sep); border-radius: var(--r-xl);
  font-size: var(--fs-foot); color: var(--text-2); text-decoration: none;
}
.src b { color: var(--text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 15rem; }

/* 错误 / 提示条 */
.notice {
  display: flex; gap: var(--sp-2);
  padding: var(--sp-3);
  border-radius: var(--r-md);
  background: var(--danger-soft); color: var(--danger);
  font-size: var(--fs-callout);
  word-break: break-word;
}
.notice.info { background: var(--surface-2); color: var(--text-2); }

/* 生成中的光标 */
.cursor {
  display: inline-block; width: 2px; height: 1.05em;
  background: var(--accent); vertical-align: text-bottom;
  animation: blink 1.05s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ── Markdown ───────────────────────────────────────────── */
.md > *:first-child { margin-top: 0; }
.md > *:last-child { margin-bottom: 0; }
.md p { margin: 0 0 .8em; }
.md h1, .md h2, .md h3, .md h4 {
  margin: 1.3em 0 .5em; line-height: 1.3; letter-spacing: -.01em;
}
/* 标题字号对齐 iOS 的动态字型层级：Title 2 = 22、Title 3 = 20、Headline = 17 */
.md h1 { font-size: calc(22px * var(--fs-scale)); font-weight: 700; letter-spacing: -.012em; }
.md h2 { font-size: calc(20px * var(--fs-scale)); font-weight: 600; letter-spacing: -.014em; }
.md h3 { font-size: var(--fs-body); font-weight: 600; }
.md h4 { font-size: var(--fs-callout); font-weight: 600; color: var(--text-2); }
.md ul, .md ol { margin: 0 0 .8em; padding-left: 1.45em; }
.md li { margin: .3em 0; }
.md li::marker { color: var(--text-3); }
.md blockquote {
  margin: 0 0 .8em; padding: .1em 0 .1em var(--sp-3);
  border-left: 3px solid var(--sep-strong); color: var(--text-2);
}
.md hr { border: 0; border-top: 1px solid var(--sep); margin: 1.2em 0; }
/* 链接靠下划线表达，不只用颜色：老 Safari（< 16.2）不认 color-mix，
   先给一条普通颜色的下划线兜底，再让支持的浏览器用更淡的颜色覆盖。 */
.md a {
  color: var(--accent); text-decoration: none;
  border-bottom: 1px solid var(--accent-soft);
  border-bottom-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.md code {
  font-family: var(--font-mono);
  font-size: .88em;
  background: var(--code-bg); color: var(--code-text);
  padding: .14em .38em; border-radius: 5px;
  word-break: break-word;
}
.md pre {
  margin: 0 0 .8em; background: var(--code-bg);
  border: 1px solid var(--sep); border-radius: var(--r-md);
  overflow: hidden;
}
.code-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 6px 4px var(--sp-3);
  border-bottom: 1px solid var(--sep);
  font-size: calc(11.5px * var(--fs-scale)); color: var(--text-2);
  font-family: var(--font-mono);
}
.code-copy {
  min-height: 30px; padding: 2px var(--sp-2);
  background: none; border: 0; border-radius: 6px;
  color: var(--text-2); font-size: calc(11.5px * var(--fs-scale)); font-family: var(--font-ui);
}
.code-copy:active { background: var(--surface-3); }
.md pre > code {
  display: block; padding: var(--sp-3);
  background: none; color: var(--code-text);
  overflow-x: auto; white-space: pre; font-size: calc(13px * var(--fs-scale));
}
.md table {
  width: 100%; border-collapse: collapse; margin: 0 0 .9em;
  font-size: var(--fs-callout); display: block; overflow-x: auto;
}
.md th, .md td { border: 1px solid var(--sep); padding: 6px 10px; text-align: left; }
.md th { background: var(--surface-2); font-weight: 600; }

/* ── 回到底部 ───────────────────────────────────────────── */
.jump {
  position: absolute; right: var(--sp-4);
  bottom: calc(var(--composer-h, 150px) + var(--sp-3));
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface); color: var(--text-2);
  border: 1px solid var(--sep);
  box-shadow: var(--shadow);
  z-index: 5;
}

/* ── 输入区 ─────────────────────────────────────────────── */
.composer {
  flex: 0 0 auto;
  background: var(--bar);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--sep);
  padding: var(--sp-2) var(--sp-3) calc(var(--sp-2) + env(safe-area-inset-bottom));
}
.chips {
  display: flex; align-items: center; gap: 4px;
  margin-bottom: var(--sp-2);
  overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none;
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 34px; padding: 0 10px;
  background: var(--surface); border: 1px solid var(--sep); border-radius: 999px;
  color: var(--text-2); font-size: var(--fs-foot);
}
/* 模型名长度不可控，让它独占弹性空间，窄屏（SE 的 375pt、320pt）也不会把整条挤爆 */
#chip-model { flex: 1 1 auto; min-width: 0; }
#chip-model .chip-text { max-width: 100%; }
.chip:active { background: var(--surface-2); }
.chip-text { max-width: 9.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip.on { background: var(--accent-soft); border-color: transparent; color: var(--accent); font-weight: 600; }
.chip.live { background: var(--signal-soft); border-color: transparent; color: var(--signal); font-weight: 600; }
.chip-icon { padding: 0 var(--sp-2); }
.chip-icon[aria-pressed="false"] { opacity: .55; }

.input-row { display: flex; align-items: flex-end; gap: var(--sp-2); }
.input-wrap {
  flex: 1 1 auto;
  background: var(--surface);
  border: 1px solid var(--sep);
  border-radius: var(--r-lg);
  padding: 7px var(--sp-3);
}
#input {
  display: block; width: 100%;
  border: 0; background: none; color: var(--text);
  font-family: var(--font-ui); font-size: var(--fs-body); line-height: 1.45;
  resize: none; outline: none;
  max-height: 34vh;
  padding: 2px 0;
}
#input::placeholder { color: var(--text-3); }
.send {
  flex: none; width: var(--tap); height: var(--tap);
  border: 0; border-radius: 50%;
  background: var(--accent); color: var(--accent-on);
  display: grid; place-items: center;
}
.send[disabled] { background: var(--surface-3); color: var(--text-3); }
.ic-stop { display: none; width: 13px; height: 13px; border-radius: 2.5px; background: currentColor; }
.send.busy .ic-send { display: none; }
.send.busy .ic-stop { display: block; }
.send.busy { background: var(--surface-3); color: var(--text); }

/* ── 抽屉（Bottom Sheet）──────────────────────────────── */
.scrim {
  /* 先写 top/right/bottom/left 再写 inset：老 WebView（Chromium < 87）不认 inset，
     只写 inset 会让遮罩变成 0 尺寸。 */
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; inset: 0; z-index: 40;
  background: rgba(0, 0, 0, .4);
  animation: fade .3s ease;
}
@keyframes fade { from { opacity: 0; } }
.scrim.closing { animation: fade-out .26s cubic-bezier(.32, .72, 0, 1) forwards; }
@keyframes fade-out { to { opacity: 0; } }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; flex-direction: column;
  /* 先写 vh 再写 dvh：老 WebView（vivo Funtouch 10.5 那代，Chromium 7x/8x）不认 dvh，
     只写 90dvh 会让面板**没有高度上限**——长列表不会变成滚动区域，scrollTop 恒为 0，
     于是任何下滑都被当成「下拉关闭」（一滑就弹回主页）；
     入场动画的 translateY(100%) 也会因为面板被撑得多高就从多远飞进来（动画看着不对）。
     vh 是兜底，支持的浏览器会被下一行的 dvh 覆盖。 */
  max-height: 90vh;
  max-height: 90dvh;
  background: var(--bg);
  border-top-left-radius: var(--r-xl); border-top-right-radius: var(--r-xl);
  box-shadow: 0 -8px 40px rgba(0, 0, 0, .22);
  padding-bottom: env(safe-area-inset-bottom);
  transform: translateY(0);
  /* 纯纵向位移。居中**不能**再靠 transform（见下面的宽屏媒体查询），
     两者混在一起就会出现「面板从右下角斜着飞进来」。 */
  animation: up .38s cubic-bezier(.32, .72, 0, 1);
}
@keyframes up { from { transform: translateY(100%); } }
.sheet.closing { animation: down .26s cubic-bezier(.32, .72, 0, 1) forwards; }
@keyframes down { to { transform: translateY(100%); } }
.sheet.dragging { animation: none; }
/* 拖拽松手后回到原位：没有这层过渡就是「啪」地跳回去（用户会觉得不跟手）。
   仅在松手回弹时加这个类，过渡结束就摘掉。 */
.sheet.settling {
  animation: none;
  transition: transform .18s cubic-bezier(.32, .72, 0, 1);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .sheet.settling { transition: none; }
}
/* 动画期间把抽屉提升为合成层：iOS Safari 上明显更稳；动画一结束就撤掉，别长期占内存 */
.sheet.animating { will-change: transform; }
/* 抽屉升降的这 300ms 里，先把顶栏 / 输入区的毛玻璃关掉。
   iOS Safari 会在每一帧重新计算 20px 模糊，这是抽屉动画掉帧的主要来源。 */
body.sheet-animating .toolbar,
body.sheet-animating .composer,
body.sheet-animating .scrim {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
body.sheet-animating .toolbar,
body.sheet-animating .composer {
  background: var(--bg);
}
.sheet-grab { flex: none; display: grid; place-items: center; height: 22px; touch-action: none; cursor: grab; }
.sheet-grab span { width: 36px; height: 5px; border-radius: 3px; background: var(--sep-strong); }
.sheet-head {
  flex: none; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: var(--sp-2); padding: 0 var(--sp-3) var(--sp-2);
}
.sheet-title { margin: 0; font-size: var(--fs-title); font-weight: 600; text-align: center; letter-spacing: -.01em; }
.sheet-cancel, .sheet-done {
  min-height: var(--tap); padding: 0 var(--sp-2);
  background: none; border: 0; border-radius: var(--r-md); color: var(--accent);
}
.sheet-cancel { display: inline-flex; align-items: center; gap: 2px; }
.sheet-cancel svg { flex: none; margin-left: -6px; }
.sheet-cancel:active, .sheet-done:active { background: var(--surface-2); }
.sheet-cancel { justify-self: start; }
.sheet-done { justify-self: end; font-weight: 600; }
.sheet-body {
  /* min-height: 0 是 column flex + overflow:auto 的关键：不加的话某些实现会保留
     flex item 的自动最小高度，内容区不收缩（老内核尤其明显）。
     这里**故意不写 -webkit-overflow-scrolling: touch**：iOS Safari 会因此给内容区单独开一层
     滚动层，而抽屉本身在做 transform 动画，两者叠加会出现滚动内容闪白/抽动（iOS 上的经典坑）；
     iOS 13 以后本来就有惯性滚动，不需要这个属性。 */
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 var(--sp-4) var(--sp-5);
}
/* 面板之间**不做**左右滑动：切换层级时内容直接就位，只有抽屉本身的升降动画。
   需要方向感时靠返回键显示上一级标题（‹ 设置）来表达，不靠位移。 */

/* 设置 / 列表行（HIG: 内嵌分组列表）*/
.group { margin: 0 0 var(--sp-5); }
.group-title {
  margin: 0 0 var(--sp-2); padding-left: var(--sp-1);
  font-size: var(--fs-foot); font-weight: 600; color: var(--text-2);
  text-transform: none;
}
.group-head {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
}
.group-head .group-title { flex: 1 1 auto; margin: 0; display: flex; align-items: center; gap: 6px; }
.link-btn {
  min-height: 32px; padding: 0 6px;
  background: none; border: 0; border-radius: 6px;
  color: var(--accent); font-size: var(--fs-foot);
}
.link-btn:active { background: var(--surface-2); }
.cb {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  border: 1.5px solid var(--sep-strong);
  display: grid; place-items: center;
  font-size: 13px; line-height: 1; color: transparent;
}
.cb.on { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
.row-side {
  width: 62px; flex: none;
  background: none; border: 0; border-left: 1px solid var(--sep);
  color: var(--accent); font-size: var(--fs-foot);
}
.row-side:active { background: var(--surface-2); }
.row-side[disabled] { color: var(--text-3); }
.list {
  background: var(--surface);
  border: 1px solid var(--sep);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.row {
  display: flex; align-items: center; gap: var(--sp-3);
  width: 100%; min-height: var(--tap);
  padding: var(--sp-2) var(--sp-4);
  background: none; border: 0; text-align: left;
  color: var(--text);
}
.row + .row { border-top: 1px solid var(--sep); }
button.row:active { background: var(--surface-2); }
.row-main { flex: 1 1 auto; min-width: 0; }
.row-main { display: block; }
.row-sub {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.row-ic { flex: none; display: grid; place-items: center; min-width: 28px; color: var(--accent); }
.row-ic .dc-icon { width: 22px; height: 22px; }
.dc-icon { display: block; }
.md-inline-icon { width: 15px; height: 15px; display: inline-block; vertical-align: -3px; }
.row-wrap { display: flex; align-items: stretch; }
.row-wrap + .row-wrap { border-top: 1px solid var(--sep); }
.row-wrap > .row { flex: 1 1 auto; }
.del-btn {
  width: var(--tap); flex: none;
  display: grid; place-items: center;
  background: none; border: 0; border-left: 1px solid var(--sep);
  color: var(--danger);
}
.del-btn:active { background: var(--danger-soft); }
.row-label { font-size: var(--fs-body); display: flex; align-items: center; gap: 6px; }
.row-sub { font-size: var(--fs-foot); color: var(--text-2); margin-top: 1px; word-break: break-word; }
.row-value { flex: none; font-size: var(--fs-callout); color: var(--text-2); max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-chev { flex: none; color: var(--text-3); }
.row[aria-checked="true"] .row-value { color: var(--accent); font-weight: 600; }
.row.danger .row-label { color: var(--danger); }
.row[href] { text-decoration: none; color: var(--text); }
.row[href]:active { background: var(--surface-2); }
.row-chev svg { display: block; }
.row .dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--sep-strong); }
.row .dot.ok { background: var(--signal); }

.form { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font-size: var(--fs-foot); color: var(--text-2); font-weight: 600; }
.field-hint { font-size: var(--fs-foot); color: var(--text-3); line-height: 1.45; }
input[type="text"], input[type="password"], input[type="url"], input[type="number"], textarea, select {
  width: 100%; min-height: var(--tap);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface); color: var(--text);
  border: 1px solid var(--sep); border-radius: var(--r-md);
  font-family: var(--font-ui); font-size: var(--fs-body);
  -webkit-appearance: none; appearance: none;
}
textarea { min-height: 82px; font-family: var(--font-mono); font-size: calc(13px * var(--fs-scale)); line-height: 1.5; resize: vertical; }
select {
  background-image: linear-gradient(45deg, transparent 50%, var(--text-2) 50%), linear-gradient(135deg, var(--text-2) 50%, transparent 50%);
  background-position: calc(100% - 18px) 20px, calc(100% - 13px) 20px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 34px;
}
.seg { display: flex; gap: var(--sp-1); background: var(--surface-2); padding: 3px; border-radius: var(--r-md); }
.seg button {
  flex: 1 1 0; min-height: 38px; padding: 0 6px;
  background: none; border: 0; border-radius: 9px;
  color: var(--text-2); font-size: var(--fs-callout);
}
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--text); font-weight: 600; box-shadow: var(--shadow); }

.btn-primary {
  width: 100%; min-height: var(--tap);
  background: var(--accent); color: var(--accent-on);
  border: 0; border-radius: var(--r-md);
  font-weight: 600;
}
.btn-primary:active { opacity: .86; }
.btn-plain {
  min-height: var(--tap); padding: 0 var(--sp-4);
  background: var(--surface-2); color: var(--text);
  border: 0; border-radius: var(--r-md);
}
.btn-row { display: flex; gap: var(--sp-2); }
.btn-row > * { flex: 1 1 0; }

.searchbar {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface); border: 1px solid var(--sep); border-radius: var(--r-md);
  margin-bottom: var(--sp-3);
}
.searchbar input { border: 0; min-height: 32px; padding: 0; background: none; }
.searchbar svg { flex: none; color: var(--text-3); }

.kv { font-family: var(--font-mono); font-size: calc(12px * var(--fs-scale)); color: var(--text-2); word-break: break-all; }
.pill-badge {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  background: var(--surface-3); color: var(--text-2);
  font-size: calc(11px * var(--fs-scale)); font-weight: 600;
  white-space: nowrap;
}
.pill-badge.ok { background: var(--signal-soft); color: var(--signal); }
.pill-badge.warn { background: var(--surface-3); color: var(--warn); }

/* ── Toast ──────────────────────────────────────────────── */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom) + 96px); z-index: 80;
  max-width: 86vw;
  padding: 10px var(--sp-4);
  background: rgba(28, 30, 36, .94); color: #fff;
  border-radius: var(--r-xl);
  font-size: var(--fs-callout); text-align: center;
  box-shadow: var(--shadow);
  animation: fade .2s ease;
}

@media (min-width: 700px) {
  .stream { padding-left: var(--sp-6); padding-right: var(--sp-6); }
  .sheet {
    /* 居中改用左右自动外边距，不用 transform——transform 要留给入场/退场动画。
       之前用 translateX(-50%) 居中，入场动画的 translateY 会把水平位移一起吃掉，
       于是前 300ms 面板偏右半个身位再"啪"地归位，看起来就是"从右下角弹出来"。 */
    left: 0; right: 0; margin-left: auto; margin-right: auto;
    width: 100%; max-width: 620px; width: min(620px, 100%);   /* 老 WebView 不认 min()，用普通写法兜底 */
    border-top-left-radius: var(--r-xl); border-top-right-radius: var(--r-xl);
  }
  .sheet.dragging { animation: none; }
}

/* ── 2026 交互重构：深度轨 + 对话台 ───────────────────── */
.toolbar {
  grid-template-columns: var(--tap) minmax(0, 1fr) var(--tap);
  gap: var(--sp-2);
  min-height: 58px;
  padding: 4px var(--sp-3);
  padding-top: calc(4px + env(safe-area-inset-top));
  background: var(--bar);
  border-bottom-color: color-mix(in srgb, var(--sep) 72%, transparent);
}
.tb-title {
  align-items: flex-start;
  padding: 0 var(--sp-1);
}
.tb-title-main {
  max-width: 100%;
  font-size: var(--fs-head);
  font-weight: 600;
  letter-spacing: -.018em;
}
.tb-title-sub {
  max-width: 100%;
  font-size: calc(12px * var(--fs-scale));
  color: var(--text-2);
}
.tb-btn {
  color: var(--text);
  border-radius: 50%;
}
.tb-btn:active { background: var(--surface-2); transform: scale(.96); }

.stream {
  padding: var(--sp-5) var(--sp-5) var(--sp-3);
  gap: var(--sp-6);
}
.msg { max-width: 43rem; }
.msg-user { padding-left: 13%; }
.bubble-user {
  max-width: 100%;
  padding: 11px 15px;
  border-radius: 22px 22px 7px 22px;
  background: var(--accent);
  box-shadow: 0 1px 0 rgba(255,255,255,.12) inset;
}

/* 签名元素：每段模型回复都沿一条“深度轨”展开；只有正在生成的轨点会呼吸。 */
.msg-ai {
  position: relative;
  padding-left: 20px;
  gap: var(--sp-2);
}
.msg-ai::before {
  content: "";
  position: absolute;
  left: 3px;
  top: 7px;
  bottom: 7px;
  width: 1px;
  background: linear-gradient(to bottom, var(--accent), var(--sep-strong) 44%, transparent);
}
.msg-ai::after {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.msg-ai.is-streaming::after { animation: depth-breathe 1.7s ease-in-out infinite; will-change: transform, opacity; }
/* 只动 transform/opacity：两者都能交给合成层。
   以前动的是 box-shadow（每帧重绘），流式输出时会让整页（含抽屉动画）一起掉帧。 */
@keyframes depth-breathe {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.55); opacity: .6; }
}
.ai-body { line-height: 1.62; }
.ai-foot {
  min-height: var(--tap);
  align-items: center;
  gap: 0;
}
.icon-btn {
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 8px 10px;
}
.usage {
  min-height: var(--tap);
  padding: 8px 6px;
  line-height: 1.35;
}
.think {
  padding: 2px 0 2px var(--sp-3);
  border-left: 2px solid var(--accent-soft);
}
.think-head {
  min-height: var(--tap);
  padding: 0 var(--sp-2);
}
.tool {
  border-color: var(--sep);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset;
}
.tool-head { padding: var(--sp-2) var(--sp-3); }
.sources { gap: var(--sp-2); }
.src {
  min-height: var(--tap);
  padding: 8px var(--sp-3);
  border-radius: var(--r-lg);
}
.code-copy { min-width: var(--tap); min-height: var(--tap); }
.code-head { min-height: var(--tap); padding-left: var(--sp-3); }
.md pre { border-radius: var(--r-lg); }
.md blockquote { border-left-color: var(--accent); }

.empty {
  text-align: left;
  padding: var(--sp-6) 0;
}
.empty h1 {
  font-size: calc(30px * var(--fs-scale));
  letter-spacing: -.035em;
  margin-bottom: var(--sp-2);
}
.empty-sug { justify-content: flex-start; }
.sug {
  width: 100%;
  text-align: left;
  border-radius: var(--r-lg);
  background: var(--surface);
}

.composer {
  background: var(--bar);
  border-top: 0;
  padding: var(--sp-2) var(--sp-3) calc(var(--sp-2) + env(safe-area-inset-bottom));
}
.compose-dock {
  /* min() 是 Chromium 79+ 的写法，老 WebView 会整条丢掉 → 先用普通写法兜底 */
  width: 100%; max-width: 46rem; width: min(100%, 46rem);
  margin: 0 auto;
  padding: 4px;
  border: 1px solid var(--sep);
  border-radius: 24px;
  background: var(--surface);
  box-shadow: var(--shadow);
}
.chips {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) repeat(3, minmax(0, auto));
  gap: 0;
  margin: 0;
  overflow: visible;
  border-bottom: 1px solid var(--sep);
}
.chip {
  min-width: 0;
  min-height: var(--tap);
  padding: 0 7px;
  justify-content: center;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: var(--text-2);
}
#chip-model {
  min-width: 0;
  justify-content: flex-start;
  padding-left: 10px;
}
.chip.on, .chip.live {
  background: transparent;
  color: var(--accent);
}
.chip.live { color: var(--signal); }
/* 联网开着但没配搜索密钥：用告警色提醒，避免用户以为模型不会搜 */
.chip.warn, .chip.warn.live { background: transparent; color: var(--warn); font-weight: 600; }
.chip:active { background: var(--surface-2); }
.chip-text { max-width: 100%; }
.input-row {
  gap: 4px;
  align-items: flex-end;
}
.input-wrap {
  padding: 7px 8px 7px 10px;
  border: 0;
  border-radius: 18px;
  background: transparent;
}
#input { min-height: 30px; padding: 3px 0; }
.send {
  margin: 4px;
  box-shadow: 0 1px 1px rgba(0,0,0,.12);
}
.jump {
  right: var(--sp-5);
  width: var(--tap);
  height: var(--tap);
  border-color: var(--sep);
}

.scrim {
  background: rgba(4, 6, 10, .48);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.sheet {
  max-height: 92vh;          /* 老 WebView 兜底（不认 dvh） */
  max-height: 92dvh;
  border: 1px solid var(--sep);
  border-bottom: 0;
  border-top-left-radius: 30px;
  border-top-right-radius: 30px;
  background: var(--bg);
  box-shadow: 0 -18px 60px rgba(0,0,0,.28);
}
.sheet-grab { height: 24px; }
.sheet-grab span { width: 38px; height: 5px; }
.sheet-head {
  min-height: 54px;
  padding: 0 var(--sp-3) var(--sp-2);
}
.sheet-title {
  font-size: var(--fs-title);
  font-weight: 700;
  letter-spacing: -.025em;
}
.sheet-body { padding: 0 var(--sp-4) var(--sp-6); }
.sheet-cancel, .sheet-done { min-width: var(--tap); }

.group { margin-bottom: var(--sp-6); }
.group-title {
  margin-bottom: var(--sp-2);
  padding-left: var(--sp-2);
  font-size: var(--fs-foot);
  letter-spacing: .01em;
}
.list {
  border-color: var(--sep);
  border-radius: var(--r-lg);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset;
}
.row {
  min-height: 54px;
  padding: 10px 14px;
}
.row-label { line-height: 1.35; }
.row-sub { margin-top: 3px; line-height: 1.35; }
.del-btn { width: 54px; }
.link-btn { min-height: var(--tap); padding: 0 var(--sp-2); }
.form { padding: var(--sp-4); gap: var(--sp-4); }
.field { gap: var(--sp-2); }
.searchbar { min-height: 52px; border-radius: var(--r-lg); }
.seg { padding: 4px; border-radius: 15px; }
.seg button { min-height: var(--tap); border-radius: 11px; }
.btn-plain, .btn-primary { min-height: var(--tap); }
.toast { min-height: var(--tap); display: flex; align-items: center; justify-content: center; }

/* 「你正在用离线缓存的旧版本」提示条：贴在最上面，点一下就能更新。
   z-index 比 toast(80) 高、但比抽屉(见 .sheet)低，避免挡住面板操作。 */
.stale-tip {
  position: fixed; left: 0; right: 0; top: 0; z-index: 90;
  padding: calc(env(safe-area-inset-top) + 10px) var(--sp-4) 10px;
  border: none; width: 100%;
  background: var(--warn-bg, var(--surface-2));
  color: var(--text);
  font-family: inherit; font-size: var(--fs-foot); font-weight: 600;
  text-align: center; cursor: pointer;
  box-shadow: var(--shadow);
}
.stale-tip:active { filter: brightness(.96); }

@media (hover: hover) and (pointer: fine) {
  .tb-btn:hover, .icon-btn:hover, .chip:hover, .sheet-cancel:hover, .sheet-done:hover,
  button.row:hover, .link-btn:hover { background: var(--surface-2); }
  .sug:hover, .src:hover { border-color: var(--sep-strong); }
}

@media (max-width: 400px) {
  .stream { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .composer { padding-left: var(--sp-2); padding-right: var(--sp-2); }
  .chip { padding-left: 5px; padding-right: 5px; gap: 4px; }
  .chip svg { width: 14px; height: 14px; }
  .chip-text { font-size: calc(12.5px * var(--fs-scale)); }
}

/* ── 老 WebView 兼容模式（vivo Funtouch OS 10.5 那代）────────────────────
   由 js/ui.js 在启动时判定：UA 里是 Chromium < 90 就加上 data-dc-compat="old"。
   只影响这些老设备，新机型/桌面浏览器/iOS 完全不受影响。 */
html[data-dc-compat="old"] .toolbar,
html[data-dc-compat="old"] .composer,
html[data-dc-compat="old"] .scrim {
  /* 老 GPU 上毛玻璃很贵，还会让抽屉动画掉帧 */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
html[data-dc-compat="old"] .toolbar,
html[data-dc-compat="old"] .composer {
  /* 去掉模糊后底色需要不透明才看得清（--bar 原本是半透明的） */
  background: var(--bg);
}
html[data-dc-compat="old"] .scrim {
  background: rgba(4, 6, 10, .6);   /* 去掉模糊后加深一点，视觉上仍然分得开 */
}
html[data-dc-compat="old"] .sheet-body {
  /* 保证内容区就是滚动容器（老 WebView 上 touch-action / overscroll-behavior 能少一堆意外） */
  overscroll-behavior: contain;
  touch-action: pan-y;
}
html[data-dc-compat="old"] .sheet {
  /* 老设备上把升降动画缩短一点，减少掉帧观感 */
  animation-duration: .22s;
}
html[data-dc-compat="old"] .sheet.closing { animation-duration: .18s; }

/* flex gap 兜底。
   Chromium 84 之前 flex 容器上的 gap 不生效（grid 的 gap 早就支持，所以只有 flex 要补）。
   这些规则只在老设备上生效：用 > * + * 给相邻子元素补回同样的间距。
   列方向补 margin-top，行方向补 margin-left。 */
html[data-dc-compat="old"] .chips > * + * { margin-left: 4px; }
html[data-dc-compat="old"] .input-row > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .group-head > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .group-head .group-title > * + * { margin-left: 6px; }
html[data-dc-compat="old"] .row-label > * + * { margin-left: 6px; }
html[data-dc-compat="old"] .row-side > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .btn-row > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .seg > * + * { margin-left: var(--sp-1); }
html[data-dc-compat="old"] .sources > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .ai-foot > * + * { margin-left: var(--sp-1); }
html[data-dc-compat="old"] .attach-strip > * + * { margin-left: var(--sp-2); }
html[data-dc-compat="old"] .sheet-body .form > * + * { margin-top: var(--sp-3); }
html[data-dc-compat="old"] .sheet-body .field > * + * { margin-top: 6px; }

@media (prefers-reduced-motion: reduce) {
  .msg-ai.is-streaming::after { animation: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .scrim { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ── 文件与照片 ─────────────────────────────────────────── */
/* 视觉隐藏：iOS Safari 会忽略对 display:none 的文件选择框的点击，
   所以不能用 hidden，只能用「移出屏幕但仍在渲染树里」。 */
.vh {
  position: fixed; left: -9999px; top: 0;
  width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.attach-btn {
  flex: none; width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%;
  color: var(--accent);
}
.attach-btn:active { background: var(--surface-2); }

.attach-strip, .msg-files {
  display: flex; gap: var(--sp-2);
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.attach-strip::-webkit-scrollbar, .msg-files::-webkit-scrollbar { display: none; }
.attach-strip { padding: var(--sp-2) 0 0; }
.msg-files { justify-content: flex-end; flex-wrap: wrap; margin-bottom: 2px; }

.file-chip {
  flex: none; display: flex; align-items: center; gap: var(--sp-2);
  max-width: 16rem; padding: 5px var(--sp-2) 5px 5px;
  background: var(--surface); border: 1px solid var(--sep); border-radius: var(--r-md);
}
.file-chip .thumb {
  flex: none; width: 34px; height: 34px; border-radius: 6px;
  object-fit: cover; background: var(--surface-2);
  display: grid; place-items: center; color: var(--text-2); overflow: hidden;
}
.file-chip .thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.file-chip .meta { min-width: 0; }
.file-chip .nm {
  display: block; font-size: var(--fs-foot); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 9.5rem;
}
.file-chip .sz { display: block; font-size: calc(11px * var(--fs-scale)); color: var(--text-3); }
.attach-x {
  flex: none; width: 30px; height: 30px;
  display: grid; place-items: center;
  background: none; border: 0; border-radius: 50%; color: var(--text-3);
}
.attach-x:active { background: var(--surface-3); }

/* 文件库里的预览 */
.file-preview {
  display: block; width: 100%; max-height: 46vh;
  object-fit: contain; border-radius: var(--r-md);
  background: var(--surface-2);
}
.file-text-preview {
  margin: 0; padding: var(--sp-3);
  max-height: 40vh; overflow: auto;
  background: var(--code-bg); color: var(--code-text);
  border-radius: var(--r-md);
  font-family: var(--font-mono); font-size: calc(12.5px * var(--fs-scale));
  white-space: pre-wrap; word-break: break-word;
}

/* ── 情景对话：输入框下方的「当前上下文」入口条 ─────────────────────
   它回答的是「这条消息发往哪个上下文」，所以贴着输入框、常驻但低对比度，
   不去抢聊天本身的注意力。供应商不支持情景对话时整条隐藏。 */
.context-bar {
  display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; max-width: 46rem; width: min(100%, 46rem);
  margin: var(--sp-2) auto 0;
  padding: var(--sp-1) var(--sp-2);
  min-height: var(--tap);
  border: 1px solid var(--sep);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text-2);
  text-align: left;
}
.context-bar[hidden] { display: none; }
.context-bar-label { flex: none; font-size: calc(11.5px * var(--fs-scale)); color: var(--text-3); }
.context-bar-name {
  flex: 1 1 auto; min-width: 0;
  font-size: calc(13px * var(--fs-scale)); font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.context-bar-go { flex: none; font-size: calc(12.5px * var(--fs-scale)); color: var(--accent); }

/* ── 苹果化的手感：按压反馈与微动效 ───────────────────────────────
   目标不是「多加点动画」，而是让每一次点按都有**即时的物理反馈**，
   这是 iOS 手感的真正来源。整层只动 transform / background-color / opacity，
   不碰 width/height/top/left，避免布局抖动；并且是**叠加**在已有抽屉节奏之上，
   没有改原有的动效语言。 */

/* 1) 可点元素：按下缩一点点、松手立刻回弹 */
.chip, .row, .btn-plain, .attach-btn, .send, .context-bar, .tb-btn, .icon-btn,
.sug, .sheet-done, .sheet-cancel, .del-btn, .tool-head, .think-head {
  touch-action: manipulation;   /* 去掉移动端 300ms 点击延迟与双击缩放，手指才会觉得「跟手」 */
  /* 只插值 transform / opacity：底色切换是瞬时的（否则长条控件每按一下都在重绘背景） */
  transition: transform .16s cubic-bezier(.32, .72, 0, 1),
              opacity .16s linear;
}
.chip:active, .btn-plain:active, .attach-btn:active, .context-bar:active,
.tb-btn:active, .icon-btn:active, .sug:active, .sheet-done:active,
.sheet-cancel:active, .del-btn:active, .tool-head:active, .think-head:active {
  transform: scale(.97);
}
/* 列表行：iOS 是按下去变暗（而不是缩），所以这里用底色 + 极小缩放 */
.row:active { transform: scale(.995); background: var(--surface-2); }
.send:active { transform: scale(.94); }
/* 长条控件（入口条、角色主行）不压缩里面的文字，改成轻微通透反馈 */
.context-bar:active { transform: none; opacity: .86; }
.role-block .row:active { transform: none; }

/* 2) 提示条：上浮 + 极轻缩放，比单纯淡入更有「落下来」的感觉 */
/* 注意：每个关键帧都要带上基础定位的 translateX(-50%)，否则动画期间会横向漂移 */
@keyframes toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(8px) scale(.98); }
  to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
}
.toast { animation: toast-in .28s cubic-bezier(.22, 1, .36, 1); }

/* 3) 抽屉入场：换成「快起、慢落」（easeOutQuint）的曲线，落位更稳更像苹果 */
.sheet { animation: up .34s cubic-bezier(.22, 1, .36, 1); }
.sheet.settling { transition: transform .18s cubic-bezier(.22, 1, .36, 1); }

/* 4) 减少动态效果：新加的位移/缩放一律退化为「不变化」 */
@media (prefers-reduced-motion: reduce) {
  .chip, .row, .btn-plain, .attach-btn, .send, .context-bar, .tb-btn, .icon-btn,
  .sug, .sheet-done, .sheet-cancel, .del-btn, .tool-head, .think-head {
    transition: none;
  }
  .chip:active, .row:active, .btn-plain:active, .attach-btn:active, .send:active,
  .context-bar:active, .tb-btn:active, .icon-btn:active, .sug:active,
  .sheet-done:active, .sheet-cancel:active, .del-btn:active,
  .tool-head:active, .think-head:active {
    transform: none;
  }
  .toast { animation: none; }
}

/* 5) 老 WebView 兼容档：那一代 GPU 上按压缩放不划算，直接关掉 */
html[data-dc-compat="old"] .chip,
html[data-dc-compat="old"] .row,
html[data-dc-compat="old"] .btn-plain,
html[data-dc-compat="old"] .attach-btn,
html[data-dc-compat="old"] .send,
html[data-dc-compat="old"] .context-bar,
html[data-dc-compat="old"] .tb-btn,
html[data-dc-compat="old"] .icon-btn,
html[data-dc-compat="old"] .sug,
html[data-dc-compat="old"] .sheet-done,
html[data-dc-compat="old"] .sheet-cancel,
html[data-dc-compat="old"] .del-btn {
  transition: none;
}
html[data-dc-compat="old"] .chip:active,
html[data-dc-compat="old"] .row:active,
html[data-dc-compat="old"] .btn-plain:active,
html[data-dc-compat="old"] .attach-btn:active,
html[data-dc-compat="old"] .send:active,
html[data-dc-compat="old"] .context-bar:active,
html[data-dc-compat="old"] .tb-btn:active,
html[data-dc-compat="old"] .icon-btn:active,
html[data-dc-compat="old"] .sug:active,
html[data-dc-compat="old"] .sheet-done:active,
html[data-dc-compat="old"] .sheet-cancel:active,
html[data-dc-compat="old"] .del-btn:active {
  transform: none;
}

/* ── GPT-6.1 增量（v3 · 苹果化精修）────────────────────────────────
   来源：work/role-ui/角色UI-动效建议.css，逐条复核后并入。
   只动 transform / opacity；无布局动画、无永久 will-change。
   B 段（可打断抽屉）是 OPT-IN，只有挂了 role-motion-ready 的面板才生效，
   其余面板仍走原来的关键帧路径，行为不变。 */

/* ① 已选上下文给一个可见的文字标识（原来是只靠颜色，色盲用户看不出来） */
.row[aria-checked="true"] .row-label::after {
  content: " · 使用中";
  color: var(--text-2);
  font-size: var(--fs-foot);
  font-weight: 400;
}
/* 角色块之间加分隔线；管理条改成等宽三列，长文案不再把按钮挤变形 */
.role-block + .role-block { border-top: 1px solid var(--sep); }
.role-block .role-manage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding: 0 var(--sp-2) var(--sp-2);
  margin: 0;
  gap: var(--sp-1);
}
.role-block .role-manage .btn-plain {
  min-width: 0;
  min-height: var(--tap);
  padding: var(--sp-1);
  white-space: normal;
  font-size: var(--fs-foot);
}
.role-block .role-manage .danger { color: var(--danger); }
.role-textarea { width: 100%; min-height: 10em; resize: vertical; }
.role-empty-title { font-size: var(--fs-body); font-weight: 600; }
.context-bar-label { font-size: var(--fs-foot); }
.context-bar-name { white-space: normal; overflow-wrap: break-word; }
.context-bar-go { color: var(--text-2); }
/* 大字号下长角色名会把「切换」挤出屏幕，这里让它换行而不是硬挤 */
html[data-font="xl"] .context-bar { flex-wrap: wrap; }
html[data-font="xl"] .context-bar-name { flex-basis: 50%; }
html[data-font="xl"] .role-block .role-manage { grid-template-columns: 1fr; }
.context-bar:focus-visible,
.role-block button:focus-visible,
.sheet.role-motion-ready button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -3px;
}

/* ② 可打断的抽屉：OPT-IN。打开时先取消 hidden、下一帧再置 data-role-open=true；
   关闭置 false、过渡结束后才 hidden；中途重开会从「当前呈现值」反向回来，
   而不是把入场关键帧从头播一遍（这是苹果手感里最难也最值钱的一条）。
   过渡期间不能叠加 .closing 关键帧或 inline animation/transform。 */
.sheet.role-motion-ready {
  animation: none;
  transform: translateY(100%);
  transition: transform .26s cubic-bezier(.32, .72, 0, 1);
  pointer-events: none;
}
.sheet.role-motion-ready[data-role-open="true"] {
  transform: translateY(0);
  transition-duration: .34s;
  transition-timing-function: cubic-bezier(.22, 1, .36, 1);
  pointer-events: auto;
}
.sheet.role-motion-ready.dragging { transition: none; }
.sheet.role-motion-ready.settling { transition: transform .18s cubic-bezier(.22, 1, .36, 1); }
.scrim.role-motion-ready {
  animation: none;
  opacity: 0;
  transition: opacity .26s cubic-bezier(.32, .72, 0, 1);
  pointer-events: none;
}
.scrim.role-motion-ready[data-role-open="true"] { opacity: 1; pointer-events: auto; }

/* ③ 面板内换页（来源 → 提取 → 确认）：只让内容层淡一次，外壳不重新升起 */
.role-stage.is-entering { animation: role-stage-in .14s cubic-bezier(.32, .72, 0, 1); }
@keyframes role-stage-in { from { opacity: .72; } to { opacity: 1; } }

/* ④ 顶栏与入口条的双标识：切换角色时只淡文字，不缩放、不移动输入台和整屏消息 */
.role-identity { transition: opacity .14s cubic-bezier(.32, .72, 0, 1); }
.role-surface[data-role-changing="true"] .role-identity { opacity: .55; }

/* ⑤ 老 WebView 档：补上第一版漏掉的几处，并把光标/提示条/抽屉动画整体关掉 */
html[data-dc-compat="old"] .tool-head,
html[data-dc-compat="old"] .think-head { transition: none; }
html[data-dc-compat="old"] .tool-head:active,
html[data-dc-compat="old"] .think-head:active { transform: none; }
html[data-dc-compat="old"] .toast,
html[data-dc-compat="old"] .cursor,
html[data-dc-compat="old"] .sheet,
html[data-dc-compat="old"] .sheet.closing,
html[data-dc-compat="old"] .scrim,
html[data-dc-compat="old"] .scrim.closing { animation: none; }
html[data-dc-compat="old"] .sheet.settling { transition: none; }
html[data-dc-compat="old"] .cursor { opacity: 1; }
html[data-dc-compat="old"] .dc-ripple { animation: none; transform: none; opacity: .12; }

/* ⑥ 新增动效的两档兜底：减少动态 / 老 WebView 下全部瞬时到位 */
@media (prefers-reduced-motion: reduce) {
  .role-identity, .sheet.role-motion-ready, .sheet.role-motion-ready.settling,
  .scrim.role-motion-ready { transition: none; }
  .cursor, .role-stage.is-entering { animation: none; opacity: 1; }
  .sheet.role-motion-ready,
  .sheet.role-motion-ready[data-role-open="true"] { transform: none; }
  .sheet.role-motion-ready { opacity: 0; }
  .sheet.role-motion-ready[data-role-open="true"] { opacity: 1; }
  .role-surface[data-role-changing="true"] .role-identity { opacity: 1; }
  .dc-ripple { animation: none; transform: none; opacity: .12; }
}
html[data-dc-compat="old"] .role-identity,
html[data-dc-compat="old"] .sheet.role-motion-ready,
html[data-dc-compat="old"] .sheet.role-motion-ready.settling,
html[data-dc-compat="old"] .scrim.role-motion-ready { transition: none; }
html[data-dc-compat="old"] .role-stage.is-entering { animation: none; opacity: 1; }
html[data-dc-compat="old"] .sheet.role-motion-ready,
html[data-dc-compat="old"] .sheet.role-motion-ready[data-role-open="true"] { transform: none; }
html[data-dc-compat="old"] .sheet.role-motion-ready { opacity: 0; }
html[data-dc-compat="old"] .sheet.role-motion-ready[data-role-open="true"] { opacity: 1; }
html[data-dc-compat="old"] .role-surface[data-role-changing="true"] .role-identity { opacity: 1; }
