/* ChatBox —— 手机/平板对话界面
 *
 * 设计立场：这是一台你自己的机器上的仪表，不是消费级聊天产品。
 * ★界面通体灰阶，只有两种情况上色：可操作的（发送键）、出问题的（琥珀/红）。★
 * 于是颜色本身携带信息 —— 看到有色就是有事，不必再读文字。
 * 每条助手消息左侧有一条 3px 状态带，把这条消息当时的状态永久记在那儿，
 * 翻历史时一眼扫得到，而不是靠一闪而过的提示。
 */

:root {
  color-scheme: light dark;

  --bg: #FAF9F7;
  --surface: #FFFFFF;
  --surface-2: #F1EFEC;
  --line: #E4E1DC;
  --text: #1A1C20;
  --muted: #6B7078;
  --accent: #35608F;
  --warn: #8A6516;
  --warn-rail: #C79A4A;
  --danger: #B4423B;

  --r: 14px;
  --gap: 12px;
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text",
          "PingFang SC", "Microsoft YaHei UI", "Noto Sans SC", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #15171B;
    --surface: #1D2026;
    --surface-2: #252931;
    --line: #2B2F36;
    --text: #E8E5E0;
    --muted: #8C919A;
    --accent: #7FA3D6;
    --warn: #D3A55C;
    --warn-rail: #D3A55C;
    --danger: #E08A84;
  }
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font: 16px/1.65 var(--font);
  -webkit-text-size-adjust: 100%;
  overscroll-behavior-y: none;
}
svg { width: 1.25em; height: 1.25em; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.screen { display: flex; flex-direction: column; height: 100dvh; }
[hidden] { display: none !important; }

/* ── 配对页：全局唯一让排版说话的地方 ── */
.pair { align-items: center; justify-content: center; padding: 24px; }
.pair-inner { width: 100%; max-width: 22rem; text-align: center; }
.pair h1 { font-size: 1.25rem; font-weight: 600; letter-spacing: .02em; margin: 0 0 1.75rem; }

/* 配对码输入：一个真正看得出是输入框的框。
   ★第一版用背景渐变画了 8 条细横线当字符位，实拍下来像装饰性分隔线，
   完全读不出「点这里输入」—— 占位符的 8 个圆点把「要找 8 个字符」这件事
   说得更直接，且不需要用户先理解一个自创的视觉约定。★ */
.code {
  width: 100%;
  font: 600 1.625rem/1 var(--mono);
  letter-spacing: .42em;
  text-indent: .42em;
  text-align: center;
  padding: 1rem .5rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  text-transform: uppercase;
  transition: border-color .15s;
}
/* 占位符要够看清 —— 它承担的是「这里要填 8 个字符」这条信息，太淡等于没说 */
.code::placeholder { color: var(--muted); opacity: .85; }
/* 只改边框、★不去掉 focus-visible 的 outline★：高对比度模式下仅靠 1px 边框认不出焦点 */
.code:focus { border-color: var(--accent); }

.pair-msg { min-height: 1.65em; margin: 1rem 0 .5rem; color: var(--danger); font-size: .875rem; }
.pair-note { margin: 1.5rem 0 0; color: var(--muted); font-size: .8125rem; line-height: 1.7; }

.btn-primary {
  width: 100%; padding: .875rem;
  background: var(--accent); color: var(--bg);
  border: 0; border-radius: var(--r);
  font: 600 1rem/1 var(--font);
  cursor: pointer;
}
.btn-primary:active { opacity: .85; }

/* ── 顶栏 ── */
.topbar {
  position: relative;
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px; padding-top: calc(8px + env(safe-area-inset-top));
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
/* ★上下文占用就画在顶栏底边上★：不占任何横向空间 —— 390px 上汉堡加两个模型选择器
   已经吃掉约 280px，再塞一个「上下文 62%」必然挤爆。平时灰阶，≥85% 转琥珀：
   按本文件开头的立场，有色就是有事，看到发黄就知道该开新会话了。 */
.topbar::after {
  content: ""; position: absolute; left: 0; bottom: -1px;
  height: 2px; width: calc(var(--ctx, 0) * 100%);
  background: var(--muted); opacity: .45;
  transition: width .3s ease;
}
.topbar.ctx-warn::after { background: var(--warn-rail); opacity: 1; }
.icon-btn {
  display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem; padding: 0;
  background: none; border: 0; color: var(--text); cursor: pointer;
}
.selects { display: flex; gap: 6px; min-width: 0; }
.selects select {
  max-width: 7.5rem;
  padding: .3rem .4rem;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  /* ★16px 不能再小★：iOS Safari 在聚焦字号 <16px 的表单控件时会自动放大页面，
     用户第一次切模型就会把顶栏和对话区的视口搞乱，且关掉选择器也不会自动还原。 */
  font: 16px/1.2 var(--mono);
}
.quota { margin-left: auto; color: var(--muted); font: .75rem/1 var(--mono); white-space: nowrap; }

.banner {
  padding: .625rem 1rem;
  background: color-mix(in srgb, var(--warn-rail) 16%, var(--bg));
  border-bottom: 1px solid color-mix(in srgb, var(--warn-rail) 40%, var(--line));
  color: var(--warn); font-size: .8125rem;
}

/* ── 消息流 ── */
.log {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  padding: 16px 12px 8px;
  display: flex; flex-direction: column; gap: 18px;
}
.empty { margin: auto; color: var(--muted); font-size: .875rem; }

.msg { max-width: 100%; }
.msg.user {
  align-self: flex-end; max-width: 85%;
  background: var(--surface-2);
  padding: .625rem .875rem; border-radius: var(--r) var(--r) 4px var(--r);
  white-space: pre-wrap; word-break: break-word;
}
/* ★状态带★：助手消息左侧 3px，把该轮的状态永久钉在这条消息上 */
.msg.bot {
  align-self: flex-start; max-width: 100%;
  padding-left: 14px;
  border-left: 3px solid var(--line);
  white-space: pre-wrap; word-break: break-word;
}
.msg.bot.is-warn { border-left-color: var(--warn-rail); }
.msg.bot.is-error { border-left-color: var(--danger); }

/* ── Markdown 渲染 ── */
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md p { margin: 0 0 .75em; }
.md h3, .md h4, .md h5, .md h6 {
  margin: 1.1em 0 .5em; font-weight: 600; line-height: 1.4;
}
.md h3 { font-size: 1.0625rem; }
.md h4, .md h5, .md h6 { font-size: 1rem; }
.md ul, .md ol { margin: 0 0 .75em; padding-left: 1.4em; }
.md li { margin: .2em 0; }
.md code {
  font: .875em/1.5 var(--mono);
  background: var(--surface-2); padding: .1em .35em; border-radius: 4px;
  word-break: break-word;
}
.md pre {
  margin: 0 0 .75em; padding: .75em .875em;
  background: var(--surface-2); border-radius: 10px;
  overflow-x: auto;
}
/* 代码块里的 code 不要再套一层底色 */
.md pre code { background: none; padding: 0; font-size: .8125rem; }
.md blockquote {
  margin: 0 0 .75em; padding-left: .9em;
  border-left: 2px solid var(--line); color: var(--muted);
}
.md hr { border: 0; border-top: 1px solid var(--line); margin: 1em 0; }
.md a { color: var(--accent); text-decoration: none; word-break: break-word; }
.md a:hover { text-decoration: underline; }

/* 表格：模型做对比最爱用。★手机上先让它塞进屏宽★ —— 格内正常折行，
   实在塞不下（列多、或格里有长串）才让容器自己横向滚，而不是一律 nowrap
   逼着每张表都要左右滑一遍。
   ★整页永远不许被撑宽★：横滚发生在这个 div 上只影响这张表，
   落到 body 上就是整个界面能左右晃。 */
.md-table { margin: 0 0 .75em; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.md-table table {
  border-collapse: collapse;
  /* ★width 必须显式给 max-content★：自动布局的表格默认收缩到容器宽度，
     光靠格子的 max-width 撑不开，结果还是折成塔（实测）。min-width 保证内容窄时铺满气泡。 */
  width: max-content; min-width: 100%;
  font-size: .875rem; line-height: 1.5;
}
.md-table th, .md-table td {
  padding: .4em .6em;
  border: 1px solid var(--line);
  text-align: left; vertical-align: top;
  white-space: normal;      /* 气泡整体是 pre-wrap，格子里不需要 */
  overflow-wrap: break-word;
  /* ★这两个数是「宁可横滚也不要折成塔」★：第一版只给了 min-width: 4.5rem，
     结果 4 列真实表格每格只有 5 个字宽 —— 一行占大半屏、单词被断成两截
     （smolagent/ns）。截图才看得见，diff 里看不见。 */
  min-width: 4rem;
  max-width: 12rem;
}
.md-table th { font-weight: 600; background: var(--surface-2); white-space: nowrap; }

/* 列表：嵌套层，以及「一项里不止一段」 */
.md li > ul, .md li > ol { margin: .2em 0 0; padding-left: 1.2em; }
.md li > p { margin: 0 0 .4em; }
.md li > p:last-child { margin-bottom: 0; }
.md li > pre { margin: .4em 0; }
.md li.task { list-style: none; }
.md li.task > input { margin: 0 .45em 0 0; vertical-align: -.1em; accent-color: var(--muted); }

.md del { color: var(--muted); }

.msg img {
  max-width: min(100%, 22rem);
  margin-top: .75rem; border-radius: 10px; display: block;
  background: var(--surface-2);
}
/* 用户自己发的附件排成一行小图 —— ★多选把一个能忍的尺寸变成了不能忍的★：
   一张 22rem 的图还行，六张就是六屏，模型的回答被推到要滚很久才看得见。
   点开是原图（见 app.js 的 addAttachment），所以缩小不等于看不了。 */
.attachments { display: flex; flex-wrap: wrap; gap: .375rem; margin-top: .5rem; }
.msg.user .attached { line-height: 0; }
.msg.user .attached img {
  width: 4.5rem; height: 4.5rem; object-fit: cover;
  margin: 0; border-radius: 8px;
}

/* 思考过程：默认折叠，一行摘要 */
.think { margin-bottom: .5rem; }
.think > summary {
  list-style: none; cursor: pointer;
  color: var(--muted); font-size: .8125rem;
  display: inline-flex; align-items: center; gap: .4em;
}
.think > summary::-webkit-details-marker { display: none; }
.think > summary::before { content: "▸"; font-size: .7em; }
.think[open] > summary::before { content: "▾"; }
.think-body {
  margin-top: .5rem; padding-left: .9rem;
  border-left: 1px solid var(--line);
  color: var(--muted); font-size: .875rem; white-space: pre-wrap;
}
.think.streaming > summary { animation: breathe 1.6s ease-in-out infinite; }
@keyframes breathe { 0%, 100% { opacity: .5 } 50% { opacity: 1 } }

/* 等待相位：告诉用户「现在在干什么 + 已经等了多久」。
   慢网下这是「卡住了」与「在等网络」的唯一区别。 */
.status {
  color: var(--muted); font: .8125rem/1.6 var(--font);
  margin-bottom: .5rem;
  animation: breathe 1.6s ease-in-out infinite;
}

/* 状态标注：与状态带同色，文字说清「发生了什么 + 怎么办」 */
.note {
  margin-top: .625rem; font-size: .8125rem; line-height: 1.6;
  color: var(--warn);
}
.note.error { color: var(--danger); }
/* 中性事实标注（如「本轮含联网检索」）：灰字，且**不**染状态带 ——
   这个界面里有色就是有事，把纯信息染成琥珀等于骗人说这轮出问题了。 */
.note.muted { color: var(--muted); }

.cites { margin: .75rem 0 0; padding: 0; list-style: none; font-size: .8125rem; }
.cites li { margin-top: .35rem; }
.cites a { color: var(--accent); text-decoration: none; word-break: break-all; }
.cites a:hover { text-decoration: underline; }

/* ── 输入区 ── */
.composer {
  border-top: 1px solid var(--line); background: var(--bg);
  padding: 10px 12px; padding-bottom: calc(10px + env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 8px;
}
.composer-row { display: flex; gap: 8px; align-items: flex-end; }
#input {
  flex: 1; resize: none; max-height: 40vh;
  padding: .625rem .75rem;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r);
  font: inherit;
}
.send {
  display: grid; place-items: center;
  width: 2.5rem; height: 2.5rem; flex: none;
  background: var(--accent); color: var(--bg);
  border: 0; border-radius: 50%; cursor: pointer;
}
.send:disabled { opacity: .4; cursor: default; }
.composer-tools { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: .35em;
  padding: .3rem .6rem;
  border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); font-size: .8125rem; cursor: pointer;
}
.chip:has(:checked) { color: var(--accent); border-color: currentColor; }
.chip input { accent-color: var(--accent); margin: 0; }
.attach { color: var(--muted); font-size: .8125rem; }
/* 「已忽略 2 张」这类是有事发生，按本文件开头的立场上琥珀色；
   「还在传图…」只是陈述进度，保持灰阶。 */
.attach.warn { color: var(--warn); }

/* ── 附件预览条 ── */
.attach-strip {
  display: flex; gap: 8px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.attach-strip::-webkit-scrollbar { display: none; }
.thumb {
  position: relative; flex: none;
  width: 72px; height: 72px;
  border-radius: 10px; overflow: hidden;
  background: var(--surface-2); border: 1px solid var(--line);
}
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ★进度盖在图自己身上★：遮罩自下而上退去。三张同时在传时一眼看得出是哪张卡着，
   而且不额外占任何高度。★保持灰阶★ —— 按本文件开头的立场，「正在传」既不是
   可操作的也不是出问题的，不该上色。 */
.thumb-fill {
  position: absolute; inset: 0 0 auto 0;
  background: color-mix(in srgb, var(--bg) 74%, transparent);
  transition: height .2s linear;
  pointer-events: none;
}
/* 它是个真 button：禁用时显示百分比（不可点），失败时变成铺满整块的「重试」。
   见 app.js makeThumb 里的说明 —— 给 div 绑 click 键盘够不到。 */
.thumb-label {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  padding: 0; border: 0; background: none;
  font: 600 .75rem/1 var(--mono); color: var(--text);
  cursor: pointer;
}
.thumb-label:disabled { cursor: default; }
.thumb[data-status="failed"] { border-color: var(--danger); cursor: pointer; }
/* 失败态的遮罩要淡一些 —— 遮到看不出是哪张照片的话，「重试还是移除」就没法判断了 */
.thumb[data-status="failed"] .thumb-fill { background: color-mix(in srgb, var(--bg) 42%, transparent); }
.thumb[data-status="failed"] .thumb-label { color: var(--danger); }
/* ★触摸目标 32px★：看得见的只是右上角一个 18px 的小圆，可点区域比它大一圈 ——
   缩略图本身才 72px，按视觉尺寸做热区的话手指点不准，会误点成「重试」。 */
.thumb-x {
  position: absolute; top: 0; right: 0;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  padding: 0; border: 0; background: none; cursor: pointer;
}
.thumb-x span {
  display: grid; place-items: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: color-mix(in srgb, var(--text) 62%, transparent);
  color: var(--bg); font-size: .625rem; line-height: 1;
}

/* ── 抽屉 ── */
.scrim { position: fixed; inset: 0; background: #0006; z-index: 10; }
.drawer {
  position: fixed; inset: 0 auto 0 0; z-index: 11;
  width: min(80vw, 19rem);
  background: var(--surface); border-right: 1px solid var(--line);
  /* 四边安全区都要：Home Indicator 会盖住底部按钮，横屏时刘海会盖住左侧 */
  padding: 12px calc(12px + env(safe-area-inset-right)) calc(12px + env(safe-area-inset-bottom))
           calc(12px + env(safe-area-inset-left));
  padding-top: calc(12px + env(safe-area-inset-top));
  display: flex; flex-direction: column; gap: 10px;
  animation: slide-in .18s ease-out;
}
@keyframes slide-in { from { transform: translateX(-100%) } }
.drawer-h { margin: .5rem 0 0; font-size: .75rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.convs { flex: 1; overflow-y: auto; list-style: none; margin: 0; padding: 0; }
.convs li { border-bottom: 1px solid var(--line); }
/* 用真 button 而不是带 click 的 li —— 后者拿不到键盘焦点，纯键盘/开关控制根本打不开会话 */
.convs button {
  display: block; width: 100%; text-align: left;
  padding: .625rem .5rem; border: 0; background: none;
  color: inherit; font: .9375rem/1.5 var(--font); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.convs button[aria-current="true"] { color: var(--accent); }
.drawer-foot { display: flex; flex-direction: column; gap: 6px; }
.btn-ghost {
  padding: .625rem .75rem; text-align: left;
  background: none; color: var(--text);
  border: 1px solid var(--line); border-radius: 10px;
  font: .875rem/1.2 var(--font); cursor: pointer;
}
.wide { width: 100%; }

.dlg {
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface); color: var(--text);
  padding: 16px; max-width: 92vw; width: 26rem;
}
.dlg::backdrop { background: #0007; }
.dlg h2 { margin: 0 0 .75rem; font-size: 1rem; }
.devices { list-style: none; margin: 0 0 1rem; padding: 0; font-size: .8125rem; }
.devices li { padding: .625rem 0; border-bottom: 1px solid var(--line); display: flex; gap: .75rem; align-items: baseline; }
.devices .meta { color: var(--muted); font-family: var(--mono); font-size: .75rem; }
.devices button { margin-left: auto; flex: none; }

/* ── 平板 / 宽屏：抽屉常驻，气泡限宽 ── */
@media (min-width: 768px) {
  .app { padding-left: 19rem; }
  .drawer { animation: none; }
  .scrim { display: none; }
  #menuBtn { display: none; }
  .log { padding: 24px 32px 12px; }
  .msg.bot, .msg.user { max-width: 44rem; }
  .msg.user { max-width: min(85%, 34rem); }
  .composer { padding: 12px 32px; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
