/* Glimpse — one stylesheet, two sheets.
   Everything above the last media block is the original shared + phone CSS, kept
   byte-for-byte so the phone sheet cannot move; the desktop shell is the final
   @media (min-width:721px) block. */
:root { color-scheme: light; --bg:#f6f4ee; --panel:#ffffff; --raised:#efece4; --text:#1c1c1a; --muted:#6f6f66; --line:#e3dfd3; --accent:#2e4a7d; --cta:#1c1c1a; --danger:#a8442f;
  /* 2026-10-12 采用「墨纸 Ink & Paper」方向（用户从四套配色里选定 A）：暖白纸面 + 墨字 + 一枚深靛。
     画布 #f6f4ee 纸感、卡 #ffffff；墨 #1c1c1a 做主文字兼主按钮（近黑暖墨）；深靛 #2e4a7d 是唯一
     可交互主色（链接/选中/焦点/状态），答案字母走靛渐变 #2e4a7d→#5a7ab0，选中态一律实心深靛。
     主动脱离此前的橄榄/陶土/铜橙（那套撞了 AI 默认审美）。布局/媒体查询/--fs/44px/防溢出全部沿用。
     语义：--accent(靛) 给可交互元素/链接/选中/状态文本；--cta(墨) 给主按钮配 --on-accent 纸字；
     --field 输入底。选中/按下填充：2026-10-13 起档位也升实心 --accent（与模式 tab 同一语言），
     浅靛 --accent-soft/-ink 两个令牌随之删除，不再有「白卡内可辨」的例外。 */
  --ok:#2e4a7d; --r-ctl:10px; --r-card:16px; --field:#faf8f2; --on-accent:#f6f4ee; --grad:linear-gradient(92deg,#2e4a7d,#5a7ab0); --faint:#9a978c;
  /* 卡片浮起：纸底白卡靠 1px 发丝描边 + 柔和投影分层。选中态只有一种语言——实心 --accent：
     模式 tab 在纸画布上（2026-10-12 改），档位行方向 B 后也住进了纸画布的工具栏（2026-10-13 改），
     「浅填充在白卡内可辨」的例外随 --accent-soft 令牌一起删除。 */
  --shadow:0 12px 32px rgba(28,26,20,.10);
  /* 浮动采集键的绿（2026-10-14 用户点名「黑色改绿色」）：#16a350。另存一份 rgb 分量，是因为它要
     按 50% 透明度铺底——rgba(var(--go-rgb),.5)；只留 hex 就没法直接调 alpha。
     为什么选这么饱和的绿：50% 压在暖纸 #f6f4ee 上会合成出 #87c59f 一类的薄荷绿，饱和度只剩
     三成；原本同色系的深绿 #2f6b4f 合成后是 #92ab9b（饱和度 0.17）——那已经灰到不像「绿色」，
     截图肉眼复核过。底色越淡，原色就得越艳，否则「改绿色」这条诉求落不下来。 */
  --go-rgb:22,163,80;
  /* 取消动作的砖红（与浮动键停止态 .is-stop 同色系）：#a8442f。同样存 rgb 分量以便 50% 铺底。 */
  --stop-rgb:168,68,47; }
* { box-sizing:border-box; }
body { margin:0; background:var(--bg); color:var(--text); font:15px/1.5 -apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif; }
/* The two-column default below is the pre-split sheet: both media blocks below fully
   specify their own layout, so these numbers no longer reach either one. They are
   left as they were so the phone's computed styles cannot drift. */
main { width:min(100%,860px); min-height:100vh; margin:auto; padding:20px; display:grid; grid-template-columns:220px minmax(0,1fr); grid-template-areas:"header header" "setup work" "history work"; gap:14px 22px; align-content:start; }
header,.row,.meta,.section-head { display:flex; align-items:center; justify-content:space-between; gap:12px; }
header { grid-area:header; padding-bottom:12px; border-bottom:1px solid var(--line); }
header strong { font-size:16px; }
/* 品牌叶标（G1）：内联 SVG 叶子，呼应预览 leaf 标记与 Glimpse.icns 的青柠意象；取代原先的 8px 方点。 */
.brand-leaf { width:18px; height:18px; flex:none; fill:var(--accent); margin-right:8px; vertical-align:-3px; }
#connection { font-size:12px; color:var(--muted); }
#notice { grid-area:header; z-index:1; margin:48px 0 0; padding:8px 10px; color:var(--danger); border:1px solid var(--danger); font-size:13px; }
/* 采集成功瞬时醒目提示（2026-10-13 用户「弹出一下醒目提示」）：居中浮卡，1.8s 自淡出。
   CSP style-src self → 动画走 @keyframes（样式表内，非内联）。与 #notice 分工：
   #notice 是错误/连接态顶条，#toast 是成功动作的即时反馈。
   2026-10-14 用户「气泡用 50% 透明度、改成绿色」：铺底跟手机浮动 ＋ 同一支笔——
   rgba(var(--go-rgb),.5) 而不是 opacity:.5。两个理由：① 元素 opacity 会把字一起拉淡，
   半透合成后墨字对比掉一半、看不清；② fade 动画的峰值被压到 .5，淡入几乎看不见。
   字形改墨色（半透绿压在纸上是浅底，纸白字只有约 2:1 会糊），阴影同步压淡。
   2026-10-14 用户「提示气泡一律在手机底部出现」：原来居中（top:38% + 垂直居中位移）会压住
   模式切换条与答案正文，改成居底。**距底 114px 而不是 20px，是为了让开左下角的浮动 ＋**
   （114 = 浮动键底距 16 + 直径 90 + 8 间距）：贴到 20px 时，最长的那条文案「已采集，正在
   作答…」宽 186px、居中后左缘 94px < 浮动键右缘 106px，**实测压在按钮上**；而按钮高度
   90px 也让垂直方向无处可躲。抬到按钮上方后横向可以自由居中，宽度再变也不会撞——
   探针钉的是「两个矩形不相交」，不是某个具体像素值。 */
#toast { position:fixed; z-index:50; left:50%; bottom:calc(114px + env(safe-area-inset-bottom)); transform:translateX(-50%); margin:0; padding:14px 22px; background:rgba(var(--go-rgb),.5); color:var(--text); border:0; border-radius:12px; font-size:16px; font-weight:650; box-shadow:0 6px 20px rgba(28,26,20,.16); opacity:0; pointer-events:none; }
#toast:not(.is-red) { color:#fff; }
#toast.is-red { background:rgba(var(--stop-rgb),.5); } /* 取消/停止类动作的红气泡，与 .is-stop 同色系 */
#toast.is-show { animation:toast-fade 1.8s ease forwards; }
/* 入场方向跟着改：原来从上方滑入到居中，现在从下方 10px 上滑到位（动画里不能再用
   translate(-50%,-50%)，那会把气泡重新推回屏幕中部）。 */
@keyframes toast-fade { 0%{opacity:0; transform:translateX(-50%) translateY(10px) scale(.96);} 12%{opacity:1; transform:translateX(-50%) translateY(0) scale(1);} 75%{opacity:1; transform:translateX(-50%) translateY(0) scale(1);} 100%{opacity:0; transform:translateX(-50%) translateY(0) scale(1);} }
button,input,select,textarea { font:inherit; }
button { min-height:40px; padding:8px 12px; cursor:pointer; border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--cta); color:var(--on-accent); font-weight:650; }
button:disabled { opacity:.45; cursor:default; }
button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,summary:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
/* 视觉刷新（2026-10-12）：文字选区与输入光标跟随 accent——此前是全界面唯一系统默认蓝。
   active 下压 1px 用 transform，不参与布局、不碰任何测量断言。 */
::selection { background:rgba(46,74,125,.20); }
input,select,textarea { caret-color:var(--accent); }
button:not(:disabled):active { transform:translateY(1px); }
.secondary,.text-button { background:transparent; color:var(--text); }
.text-button { color:var(--muted); border-color:transparent; min-height:auto; padding:5px 0; font-size:12px; }
/* 表单控件圆角统一走 --r-ctl（2026-10-12）：此前写死 3px，与按钮的 10px 相邻出现时
   两套圆角语言打架（切换模型一行里下拉 3px、切换钮 10px 最明显）。令牌一处改全站生效。 */
input,select,textarea { display:block; width:100%; margin:4px 0 10px; padding:8px; border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--field); color:var(--text); }
textarea { resize:vertical; line-height:1.5; font-size:13px; }
.settings input[type="file"] { width:auto; margin:0; padding:0; border:0; background:none; font-size:12px; }
label,.muted,p { color:var(--muted); font-size:12px; }
details { border:1px solid var(--line); }
/* 自绘 chevron 取代原生 ▼（2026-10-11 评审 P3）：闭合朝下（45°），展开转 180° 朝上。
   margin-left:auto 让箭头靠右，#setup 300px 窄栏与答案卡里都是同一颗。 */
summary { display:flex; align-items:center; gap:8px; list-style:none; cursor:pointer; padding:9px 10px; font-size:13px; }
summary::-webkit-details-marker { display:none; }
summary::after { content:''; flex:none; width:7px; height:7px; margin-left:auto; border-right:1px solid var(--muted); border-bottom:1px solid var(--muted); transform:rotate(45deg); transition:transform 150ms ease; }
details[open]>summary::after { transform:rotate(225deg); }
#setup { grid-area:setup; }
.settings { padding:0 10px 12px; }
.settings h2 { margin:16px 0 6px; font-size:13px; }
.settings .row { justify-content:flex-start; flex-wrap:wrap; margin-top:10px; }
#qr img { width:160px; height:160px; display:block; margin:8px 0; }
#links a { display:block; overflow-wrap:anywhere; color:var(--accent); font-size:12px; margin:5px 0; }
#saved { color:var(--ok); font-size:12px; margin-left:8px; }
.model-switcher { grid-area:model; display:flex; align-items:end; gap:10px; border-bottom:1px solid var(--line); padding-bottom:10px; }
.current-model { font-size:12px; color:var(--muted); white-space:nowrap; }
.current-model strong { color:var(--text); font-weight:500; }
/* 「模型：xxx」标签按用户要求撤掉（2026-10-12）：切换器左侧不再显示当前模型名。DOM 保留、
   render() 照常写值（无害）；桌面头栏 #header-session 仍显示当前模型，无信息损失。 */
.current-model { display:none; }
.model-switcher label { flex:1; }
/* 2026-10-12：模型下拉不拉满——width:auto + justify-self:start 让它收成内容宽（原生
   select 按最宽选项算），与切换钮贴成一组左对齐控件；此前 1fr 把短模型名扯成一条空行。
   max-width:100% 兜底窄屏，justify-self 在桌面 flex 行里被忽略、只对手机 grid 生效。
   min-height:40px 与按钮等高——select 默认只有 34px，底对齐时钮顶高出 6px，一行俩控件
  高度不齐是这个组最显眼的毛边。 */
.model-switcher select { margin:4px 0 0; width:auto; max-width:100%; justify-self:start; min-height:40px; }
.model-switcher p { display:none; }
.mode-switcher { display:flex; align-items:end; gap:10px; border-bottom:1px solid var(--line); padding-bottom:10px; }
/* The phone picks a mode with one tap, so the old current-mode text and its select are
   gone: the pressed tab is the state, and it is readable at a glance. The two tier rows
   below reuse the same idea — a tap is the whole action, and the pressed button is the
   value — so the mode, the 口述时长 and the 篇幅 all read as one control area rather than
   three different widgets. */
.mode-tabs { display:flex; gap:8px; flex:1; min-width:0; }
.mode-tab { flex:1; min-width:0; min-height:40px; background:transparent; color:var(--text); font-weight:600; }
/* 选中态升实心靛（2026-10-12 用户：「这个颜色不够明显」）：浅靛 #e3e8f2 画在纸画布上
   几乎看不出来，而模式 tab 是全场最重要的状态指示（现在答的是什么题型）。实心 --accent
   配 --on-accent 纸字，对比约 7.6:1；focus 环有 2px offset 浮在纸面上，键盘焦点不丢。
   档位 .option 在 2026-10-13 跟上（用户点名「应该使用深蓝色」）：方向 B 之后档位行就在
   纸画布的工具栏里，浅填充同样不可辨，一组控件不该有两种选中语言。 */
.mode-tab[aria-pressed="true"] { background:var(--accent); color:var(--on-accent); border-color:var(--accent); }
.field { flex:1; min-width:0; color:var(--muted); font-size:12px; }
.field > span { display:block; margin:0 0 4px; }
.option-row { display:flex; gap:6px; margin:4px 0 10px; }
/* Four tiers have to stay on one row in the phone's narrow column and in the 300px rail, so
   the buttons share the width and give up padding instead of wrapping onto a second line. */
.option-row .option { flex:1 1 0; min-width:0; min-height:38px; padding:6px 4px; background:transparent; color:var(--text); font-size:12px; font-weight:500; white-space:nowrap; }
/* 选中态与模式 tab 同升实心靛（2026-10-13 用户：「口述时长作答语言类似的按钮选中状态
   应该使用深蓝色」）：实心 --accent 配 --on-accent 纸字，与 .mode-tab 完全同一支笔。 */
.option-row .option[aria-pressed="true"] { background:var(--accent); color:var(--on-accent); border-color:var(--accent); }
/* 打勾 rather than a toggle button: a checkbox states on/off without needing a highlight to
   be read as one, and the enclosing label makes the whole row the tap target. */
.check-row { display:flex; align-items:center; gap:8px; min-height:38px; padding:2px 0; margin:2px 0; color:var(--text); font-size:13px; }
.check-row input[type="checkbox"] { flex:none; width:auto; height:18px; min-height:0; margin:0; padding:0; accent-color:var(--accent); }
.mode-switcher label { flex:1; }
.mode-switcher select { margin:4px 0 0; }
.mode-switcher p { display:none; }
#mode-saved { color:var(--ok); font-size:12px; margin-left:8px; }
/* Wraps on narrow screens: the capture button takes the first row and the
   conversation controls fall to the second, rather than squeezing the row. */
.capture-bar { grid-area:capture; display:flex; align-items:center; flex-wrap:wrap; gap:10px 12px; }
.work-panel { grid-area:work; min-width:0; display:flex; flex-direction:column; align-items:stretch; gap:14px; }
#capture { min-width:180px; }
#cancel { color:var(--danger); }
#thread-turn { margin-left:auto; white-space:nowrap; }
#result { grid-area:result; min-width:0; border-top:1px solid var(--line); padding-top:14px; }
/* 2026-10-13 用户「把答案已返回这行删掉」：#answer-ready 元素与规则整移除。 */
#answer { min-height:80px; white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.7; font-size:calc(17px * var(--fs,1)); } /* 2026-10-13 用户「答案文字太小看不清」+「桌面也该能手动调字号」：桌面答案族整体 +2px 且改为跟随 --fs（calc(17px*--fs)），与手机段共用同一套 A−/A＋ 五档；手机端 base 仍是 15px（下方 max-width:720px 段独自定）。--fs 默认 1，不改时数值与加 --fs 前一致。 */
#answer.is-pending { min-height:0; color:var(--muted); font-size:calc(15px * var(--fs,1)); }
.answer-choice,.answer-process { display:grid; gap:4px; }
.answer-choice { margin-bottom:14px; }
/* 建议作答的档位行（2026-10-14 用户「答案要在；处换行」选定「标签做小胶囊」）：一行一个档位，
   胶囊在左、档位正文在右。用 grid 的 auto + minmax(0,1fr)（不是隐式 auto，见手机端横向不超屏
   那条铁律），正文再长也只会自己折、不会把胶囊挤出屏；baseline 对齐让胶囊文字与大字的基线平齐。 */
.answer-choice-row { display:grid; grid-template-columns:auto minmax(0,1fr); gap:2px 8px; align-items:baseline; }
/* 胶囊字号沿用 .answer-choice span 的 11px 且不参与 --fs（同模式 tab / 档位按钮那条：控件类
   文本放大正是把窄屏挤出去的原因）。 */
/* 答案块标签统一成发丝 pill（2026-10-12）：原先四个块各写一条 12px muted 纯文字规则，
   标签浮在卡顶，像忘了删的说明文字。pill + 11px + 微字距，安静但明确；不借靛——模式 tab
   已是实心靛，阅读卡内再放一枚会抢正文。 */
.answer-choice span,.answer-process span,.answer-speech span,.answer-points span,.answer-body span,.answer-chat span { display:inline-flex; align-items:center; justify-self:start; padding:2px 9px; border:1px solid var(--line); border-radius:999px; background:var(--panel); color:var(--muted); font-size:11px; font-weight:600; letter-spacing:.08em; }
/* 答案字母在浅底上走森林→薄荷渐变字（G1）：去掉深底光晕，改用 --grad 文本裁切。 */
.answer-choice strong { font-size:calc(25px * var(--fs,1)); background:var(--grad); -webkit-background-clip:text; background-clip:text; color:transparent; }
.answer-speech { display:grid; gap:6px; }
.answer-speech div { font-size:calc(19px * var(--fs,1)); line-height:1.8; letter-spacing:.01em; white-space:pre-wrap; overflow-wrap:anywhere; } /* 2026-10-13 用户「答案文字太小看不清」：17→19px；并改为跟随 --fs（桌面也开放 A−/A＋ 调节）。桌面答案列 682px，19px 每行 36 字，落在中文舒适阅读区；手机段 base 17px（下方 max-width:720px 段独自定）。行距 2→1.8（2026-10-13 续：用户「行距太宽」，英文长段尤其明显）。 */
/* Written mode reuses the speech block's shell for its two parts: 答案要点 is a tight
   list of score points, 完整作答 is the prose body, so they share a layout but not a
   type scale. 答案要点 is optional and simply not rendered when the model omits it. */
.answer-points,.answer-body { display:grid; gap:6px; }
.answer-points { margin-bottom:14px; }
.answer-points div { font-size:calc(18px * var(--fs,1)); line-height:1.72; white-space:pre-wrap; overflow-wrap:anywhere; } /* 2026-10-13：14→16px，从「注释级」升到「次要正文级」——要点是面试官扫读的评分点；并改为跟随 --fs。2026-10-13 续：用户要求与「完整作答」同字号，16→18px 对齐 .answer-body div（方向向上、不压正文）。行距 1.9→1.72。 */
.answer-body div { font-size:calc(18px * var(--fs,1)); line-height:1.78; letter-spacing:.01em; white-space:pre-wrap; overflow-wrap:anywhere; } /* 2026-10-13：16→18px，并改为跟随 --fs。行距 1.95→1.78（用户「行距太宽」，英文段落尤甚）。 */
/* 自由对话 has no 题目/答案要点/完整作答 scaffold to split, so its block is the prose and
   nothing else — the same shell as 口述稿, because like a spoken answer it is read straight
   through rather than scanned. */
.answer-chat { display:grid; gap:6px; }
.answer-chat div { font-size:calc(18px * var(--fs,1)); line-height:1.78; white-space:pre-wrap; overflow-wrap:anywhere; } /* 2026-10-13：16→18px，与笔试正文同级；并改为跟随 --fs。行距 1.95→1.78。 */
/* fillParagraphs 切出来的段距（2026-10-12）：.7em 在 2.0 行高下是一口呼吸的量——
   再小看不出分段，再大一篇稿子会散。em 跟着各块的 --fs 字号缩放走，手机端同步。 */
/* 答案段落必须显式继承块的字号与颜色：fillParagraphs 把每段包进 <p>，而上面那条全局
   label/.muted/p 规则（12px + --muted）会直接命中这个 <p>，压过从 .answer-body div
   （18px / --text）继承来的值——答案正文实际一直渲染成 12px 灰字，且只有「流式态」
   因为文字直接挂在 #answer（无 <p> 包裹）才显得大。2026-10-13 用户「作答过程中字变大、
   完成后还是很小」即此。font-size:inherit 让各块各按自己的 base（口述稿 19 / 完整作答 18
   / 答案要点 16 / 自由对话 18）走，连同 --fs 一起继承。 */
#answer :is(.answer-speech,.answer-body,.answer-points,.answer-chat,.answer-process) div p { margin:0 0 .7em; font-size:inherit; color:inherit; }
#answer :is(.answer-speech,.answer-body,.answer-points,.answer-chat,.answer-process) div p:last-child { margin-bottom:0; }
/* 自由对话 is the one mode whose input is typed rather than captured, so it owns a composer of
   its own above the capture bar. The phone and the desktop share this element — there is no
   second copy — so everything outside a media query applies to both sheets. */
.chat-composer { display:grid; }
.chat-composer textarea { margin:4px 0 8px; }
.chat-composer .row { justify-content:flex-start; flex-wrap:wrap; }
/* The shot waiting to be sent. It is shown at a bounded height so a full-screen capture cannot
   push the input box off the sheet — the thumbnail is there to confirm *which* picture is
   attached, not to read it. */
.chat-attachment { display:grid; gap:6px; margin:4px 0 8px; }
.chat-attachment span { color:var(--muted); font-size:11px; }
.chat-attachment img { display:block; width:100%; max-height:180px; object-fit:contain; border:1px solid var(--line); border-radius:4px; background:var(--field); }
.chat-attachment button { justify-self:start; }
.chat-composer p { margin:6px 0 0; }
/* 折起来的长说明（2026-10-11 评审 P2，文字一字未改）：summary 压成一行小字，不占
   采集栏的视觉重量；chevron 走上面统一的 summary::after。 */
.composer-help summary { padding:2px 0; color:var(--muted); font-size:12px; }
.composer-help summary::after { margin-left:auto; }
#send { min-width:96px; }
/* 思考过程折叠面板：与答案正文拉开差距但不至于读不清——12px / --faint（#9a978c，2026-09-27 两轮
   反馈的折中：用户先要"更小更灰"用来和正文区分，看到实际效果后又要"调大一点"；12px 与
   记录列表同级，色值走 --faint 令牌（2026-10-12 墨纸方案起，换配色只动 :root）。行距收紧保持"附属信息"的观感。风格随
   #picture 的 details，只按有无 thinking 内容显隐，不按模式；桌面段与手机段都不需要覆盖。 */
.answer-thinking { margin:10px 0 0; }
.answer-thinking summary { color:var(--faint); font-size:12px; }
.answer-thinking div { margin-top:5px; color:var(--faint); font-size:12px; line-height:1.6; white-space:pre-wrap; overflow-wrap:anywhere; }
.meta,.timing { margin-top:10px; color:var(--muted); font-size:11px; }
#status { color:var(--ok); }
/* 作答模型戳（2026-10-13）：长 id 在窄屏上截断，不把 meta 行顶横向溢出。 */
#answer-model { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#picture { grid-area:picture; }
#image { display:block; width:100%; height:auto; }
.workspace { grid-area:history; min-height:380px; border-right:1px solid var(--line); padding-right:18px; }
.section-head { padding-bottom:9px; border-bottom:1px solid var(--line); }
/* 区块标题是索引不是正文（2026-10-12）：小一号 + 大字距 + 灰阶，答案区随之更突出。
   手机段共用，无宽度预算风险（标题就两三个字）。 */
.section-head h2 { margin:0; font-size:12px; letter-spacing:.14em; color:var(--muted); font-weight:600; }
#count { color:var(--muted); font-size:12px; }
#empty { padding:16px 0; color:var(--muted); font-size:13px; }
#history { display:grid; gap:4px; margin-top:10px; grid-template-columns:minmax(0,1fr); }
/* 真机横向溢出的元凶就是这一行（2026-09-26 vivo 反馈「历史记录超出宽度」）：隐式轨道是 auto，
   下限跟着内容的 min-content 走。桌面 Chromium 对 min-width:0+overflow:hidden 的省略号标签
   算出 0 的最小贡献所以没事，但真机内核按整段 nowrap 文本算，轨道被撑到比视口宽。
   minmax(0,1fr) 把轨道钉死在容器宽，省略号在任何内核里都由标签自己的 overflow 负责。 */
#history button { display:flex; align-items:center; gap:8px; min-height:34px; padding:6px 8px; min-width:0; text-align:left; background:transparent; color:var(--muted); border-color:transparent; font-size:12px; font-weight:400; }
#history .history-label,#archive-list .history-label { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#history .history-time,#archive-list .history-time { margin-left:auto; flex:none; color:var(--muted); font-size:11px; font-variant-numeric:tabular-nums; white-space:nowrap; }
#history button[aria-current="true"] { color:var(--text); border-color:var(--line); }
footer { grid-area:footer; text-align:right; }
#capture-float { display:none; }
[hidden] { display:none !important; }
/* =========================== 归档（每一场考试/面试） ===========================
   记录（#history）是屏幕上正在滚的工作列表，归档是磁盘上的场次。两块同构，列表行共用
   .history-label / .history-time——上面那两条省略号/tabular 规则的选择器同时挂
   #history 与 #archive-list（2026-10-12 修正：原先只写 #history，归档行吃不到
   nowrap+省略号，长场次标题会换行破行；用户口径「每个归档占一行、全部展开」）。 */
.archive,.archive-detail { border:1px solid var(--line); border-radius:var(--r-card); background:var(--panel); padding:14px; box-shadow:var(--shadow); }
#archive-empty { padding:12px 0; color:var(--muted); font-size:12px; }
/* Same rule as #history: an implicit auto track would be widened by the nowrap title on a
   real phone kernel, which is how the whole page once became horizontally scrollable. */
#archive-list,#archive-turns { display:grid; gap:4px; grid-template-columns:minmax(0,1fr); }
#archive-list button { display:flex; align-items:center; gap:8px; min-height:34px; padding:6px 8px; min-width:0; text-align:left; background:transparent; color:var(--muted); border-color:transparent; font-size:12px; font-weight:400; }
#archive-list button:hover { color:var(--text); background:var(--raised); }
.archive-actions { display:flex; gap:16px; margin-top:10px; }
.text-button.danger { color:var(--danger); }
/* One sitting's turns. Each is a <details> so a long sitting does not have to be read end to
   end on the phone; the newest turn is opened by the caller. */
.archive-turn { border:1px solid var(--line); border-radius:6px; margin:0; }
.archive-turn > summary { font-size:13px; font-weight:600; color:var(--text); }
.archive-turn-body { padding:2px 2px 10px; white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.9; font-size:14px; }
#archive-detail .section-head h2 { flex:1; }
/* 归档详情里的题图与「删除这一问」（2026-10-13 用户要求归档留截图 + 手动清理）：题图折叠块
   默认收起（画面是辅助信息，正文才是主角），点开才加载；删除钮右对齐不抢正文。 */
.turn-shot { margin-top:10px; border:1px solid var(--line); border-radius:6px; }
.turn-shot summary { padding:7px 10px; font-size:12px; color:var(--muted); cursor:pointer; }
.turn-shot img { display:block; width:100%; max-height:420px; object-fit:contain; background:var(--field); }
.turn-delete { margin-top:10px; }
/* #result 里的「删除这条记录」：横跨整卡居中（2026-10-13 用户两次点名）。#result 是
   [1fr | auto] 两列网格（.meta 占 1、.timing 占 2），按钮若自动落入第 1 列，justify-self:center
   只在左列内居中、被 timing 列宽度推得整体偏左——grid-column:1/-1 跨满两列后再居中，
   display:block + auto 边距对非网格容器（理论回归）也稳。 */
.job-delete { grid-column:1/-1; justify-self:center; display:block; margin:4px auto 0; min-height:30px; padding:4px 10px; font-size:12px; }

/* =========================== 手机端字号 ===========================
   One variable, --fs, moved by the A−/A+ pair and set on <html>. Every readable size in the
   PHONE sheet is written as calc(<base> * var(--fs)), so one tap scales the whole sheet and
   the desktop block — which never reads --fs — cannot move at all. It is not applied to
   <body>'s font-size: the answer blocks set their own px sizes, and a multiplier on the
   parent would never reach them.
   紧凑版式（2026-10-04 真机反馈「太大了，太占地方」）：控件住进 capture-bar，与「新对话 /
   本场对话 N 轮」同一行右对齐，不再独占一整行；按钮压到 30px 高。这行是固定尺寸（不读
   --fs），所以它在特大字号下也不会把行撑宽。 */
.font-size { display:flex; align-items:center; gap:4px; flex:none; }
.font-level { min-width:32px; text-align:center; color:var(--muted); font-size:11px; font-variant-numeric:tabular-nums; }
.font-step { min-height:30px; min-width:34px; padding:3px 8px; background:transparent; color:var(--text); font-weight:700; font-size:12px; border-radius:6px; }

@media (max-width:720px) { main { display:flex; flex-direction:column; padding:16px 16px calc(100px + env(safe-area-inset-bottom)); gap:12px; } header { display:none; } .work-panel { display:contents; } .records { display:contents; } .toolbar { display:contents; } .answer-stage { display:contents; } .rail { display:contents; } .rail-panel { display:contents; } .rail-tabs { display:none; } .model-switcher { width:100%; order:3; display:grid; grid-template-columns:1fr auto; align-items:end; } .model-switcher label { grid-column:1/-1; } .model-switcher p { display:block; grid-column:1/-1; margin:0; } .mode-switcher { width:100%; order:4; display:grid; grid-template-columns:1fr; align-items:end; } .mode-switcher label { grid-column:1/-1; } .mode-switcher .field { grid-column:1/-1; } .mode-switcher .check-row { min-height:44px; } .mode-switcher p { display:block; grid-column:1/-1; margin:0; } .answer-speech div { font-size:calc(17px * var(--fs,1)); line-height:1.8; } .answer-body div { font-size:calc(16px * var(--fs,1)); line-height:1.78; } /* 行距 2.05→1.8/1.78（2026-10-13 用户「手机上行距太宽」，英文长段尤其明显；与桌面段同步下调）。 */ /* The archive sits with the records it complements, and the detail panel takes the list's
     place rather than stacking under it — opening a sitting should not push the list out of
     reach, and going back should not leave a scroll position halfway down a page. Both share
     order:5 with .workspace because only one of the two is ever visible (render() and
     openSession toggle them), so they land in the same slot without renumbering anything
     below: a new order value here would have moved footer and #setup, and their order is
     pinned by the desktop layout test. */ .archive,.archive-detail { width:100%; order:5; }
/* The remaining reading surfaces, collected here rather than edited into their own rules:
   自由对话's reply, the 答案要点 list, and the plain prose #answer shows while a written answer
   is still streaming. The mode tabs and the tier buttons are deliberately NOT in this list:
   they are nowrap controls on a 62px-per-tier budget, and growing their type is precisely what
   pushes a narrow sheet sideways. A size control is for reading the answer, not for making
   the buttons harder to hit. */ .answer-chat div { font-size:calc(16px * var(--fs,1)); } .answer-points div { font-size:calc(16px * var(--fs,1)); } #answer { font-size:calc(15px * var(--fs,1)); } .answer-choice strong { font-size:calc(25px * var(--fs,1)); } #history button { font-size:calc(12px * var(--fs,1)); } #archive-list button,#archive-turns .archive-turn { min-height:44px; } .archive-turn > summary { min-height:44px; display:flex; align-items:center; } .archive-turn-body { font-size:calc(14px * var(--fs,1)); } #archive-detail-title { font-size:calc(14px * var(--fs,1)); } .capture-bar { width:100%; order:1; } /* The composer rides with the capture bar: in 自由对话 the capture button is hidden, so
     this row *is* the primary action and it belongs at the same place in the order. */ .chat-composer { width:100%; order:1; } /* A 100% basis keeps 采集 the full-width primary action; a plain flex:1 would
     shrink it to make room for 新对话 and the turn counter on the same line. */
  #capture { display:none; } /* 2026-10-13 用户「手机删除采集并解答按钮」：采集入口收归浮动 ＋；元素与桌面端规则保留 */
  .capture-bar #new-thread { flex:0 0 auto; min-height:36px; padding:6px 12px; border-color:var(--line); color:var(--text); font-size:13px; } /* 2026-10-13 用户「缩小放字号调节按钮同一行」：去掉 flex:1 撑满、收 padding，与 #font-size 同行 */
  #thread-turn { flex:none; } #cancel { display:none; } /* The operator holds the phone and reads the answer, so the result leads the page; every control moves below it. */ #result { width:100%; order:0; border-top:0; border-bottom:1px solid var(--line); padding:0 0 14px; } #picture { width:100%; order:1; } /* 2026-10-13 用户「题目画面移到采集并解答按钮上面」：order 2→1，落在 #result(0) 与 .capture-bar(2) 之间 */ .capture-bar { width:100%; order:2; } .chat-composer { width:100%; order:2; } .workspace { order:5; width:100%; min-height:0; border-right:0; border-top:1px solid var(--line); padding:12px 0 0; } footer { order:6; width:100%; } #setup { width:100%; order:7; } #notice { margin:0; } /* The floating button is pinned over the bottom-left corner, so main reserves room under its last row. */ /* 2026-10-14 用户点名三改：面积翻倍（半径 ×√2，64→90px，(90/64)²≈1.98 即面积约两倍；
   直径翻倍会变四倍面积，那颗键会盖掉小半屏答案，故按面积算）；铺底 50% 透明——用 rgba 而非
   opacity，让底下的答案能透出来，同时字不被一起拉淡；填色由墨黑换深绿。
   50% 绿压在纸面上合成出来是浅薄荷绿（亮度约 0.48），成形层因此一直用墨色而不是纸白——这条在
   2026-10-14 被用户点名反转，见下一段；阴影到这里已压淡——半透明底下压一层 60% 黑边太脏。 */
/* 2026-10-14 用户「里面的十字改成白色十字居中于气泡内」：
   ① 居中不再交给字体。＋ 是全角符号，基线高度与左右边距各系统字体都不一样，而
      place-items:center 居中的是「字身框」不是墨迹——真机换一支字体十字就会偏。改成一个伪元素画：
      left/top 各 50% + translate(-50%,-50%)，圆心就是十字中心，与字体无关。（这个 50% 是相对
      **padding box** 算的：这颗钮还带着 1px 透明边框，所以算出 44px——锚点仍落在 90px 圆的几何
      中心，别按 45px 去核对。）两根臂用两条纯色 linear-gradient 叠出（横 36×6、竖 6×36）。
   ② 十字改纯白是用户点名的。纯白压在半透绿合成出的浅薄荷底（约 #86cb9f）上只有约 1.9:1，
      按说会「浮掉」，但 2026-10-14 用户点名「十字和方块都不要阴影」——故不加任何描边影
      （之前那层 filter:drop-shadow 已删）。不用 box-shadow 的理由也一并作废：后者描的是
      那 36×36 的方框，会在圆里印出一块方影，所以当初选了 drop-shadow；现在二者皆无。
   ③ 字形文本仍留在 DOM 里（app.js 的 ＋/■ 切换、冒烟断言与无障碍名都读它），只是不再参与视觉：
      font-size:0 让它零尺寸，十字由伪元素画。停止态 ■ 用同一支笔（同色无影的白方块，靠那条
      border-radius:3px 收角——十字用不到它，两根胳膊够不到 36×36 的盒角），
      同一颗键来回切时材质不能突变——白十字↔墨方块是最刺眼的突变。 */
#capture-float { display:grid; place-items:center; position:fixed; z-index:2; left:16px; bottom:calc(16px + env(safe-area-inset-bottom)); width:90px; min-height:90px; padding:0; border-radius:50%; font-size:0; line-height:1; background:rgba(var(--go-rgb),.5); border-color:transparent; box-shadow:0 3px 12px rgba(28,26,20,.16); }
#capture-float::before { content:''; position:absolute; left:50%; top:50%; width:36px; height:36px; transform:translate(-50%,-50%); background-image:linear-gradient(#fff,#fff),linear-gradient(#fff,#fff); background-size:36px 6px,6px 36px; background-position:center; background-repeat:no-repeat; border-radius:3px; pointer-events:none; } /* Thumb-sized targets: 40px mode tabs and 38px tier buttons are fine with a mouse and
     poor under a thumb. Only the height grows — the four tiers keep nowrap, so nothing
     moves horizontally and the 62px per-tier budget is untouched. Desktop is unchanged. */ /* A fifth tab now shares what four used to own, so the gap, the 字 size and the padding
     shrink together — that is what keeps all five four-char labels on one line on the
     narrowest phone (273px of tab row minus four 4px gaps leaves 51px per tab; four
     12px chars are 48px, and 1px of padding each side keeps the text inside). nowrap is
     load-bearing here: without it a tab that does not fit wraps to two lines instead of
     being caught.
     Only the phone sheet is touched: the desktop hides #mode-switcher
     outright, so nothing there can move. */ .mode-tabs { gap:4px; } .mode-tab { min-height:44px; font-size:12px; padding:6px 1px; white-space:nowrap; } .option-row .option { min-height:44px; } #picture summary { min-height:44px; } /* iOS delays a tap while it decides between a click, a double-tap zoom and a scroll; on a
     phone that reads as "点了没反应". manipulation opts out of the double-tap decision. */ button, summary, .option { touch-action:manipulation; -webkit-tap-highlight-color:transparent; } /* 横向不超屏（2026-09-26 真机反馈：整页可以左右滑）。确切成因是 #history 的隐式 auto 轨道被 nowrap 记录标题撑宽（见上方 minmax(0,1fr) 的修复）；这里留三道辅助闸防同类问题再犯：① 切换器网格的 1fr 轨道改 minmax(0,1fr)，nowrap 的「模型：xxx」再长也撑不宽网格；② 模型名这行在手机上允许折行——它是一句说明文字，不是控件；③ html/body 横向 clip 兜底：未来任何控件真变宽了，表现为那一块被裁掉，而不是整页横移把所有内容都拖出屏。 */ html, body { overflow-x:hidden; overflow-x:clip; } .model-switcher { grid-template-columns:minmax(0,1fr) auto; } .mode-switcher { grid-template-columns:minmax(0,1fr); } .current-model { white-space:normal; overflow-wrap:anywhere; }
/* 44px 触控目标（2026-10-11 评审 P1）：.text-button 原本约 27px 高，「清空归档」这类
   危险操作的点击面积反而最小。#cancel 由上面的 ID 规则接管（fixed 圆钮），不受影响；
   桌面 foot-strip 的 32px 在桌面段，互不越界。 */
.text-button { min-height:44px; padding:10px 8px; font-size:13px; }
.text-button+.text-button { margin-left:8px; }
/* 浮动 ＋ 回到左下（2026-10-12 用户点名）：同一颗键兼任「开始/停止」——忙碌时 render()
   给它加 .is-stop（半透砖红底 + 白色方块字形——成形层与空闲态同一支笔，理由见上），仍与桌面停止钮
   同一套停止语义，但这是
   90px 的主入口。left:16px 由上面手机段的基础规则给出；「挪右下」的旧覆盖已删（右下压
   「导出日志」、左下是用户要的位置；两者都会在滚到底时压住 footer 行的一端，这是浮动键
   的固有代价，尺寸不动的道理同上：90px 是单手最常触发的命中面积）。 */
#capture-float.is-stop { background:rgba(168,68,47,.5); border-color:transparent; }
/* 停止态：同一颗伪元素换画方块（28px）——依旧是纯白 + 同一层墨影，不再单独调字号/字色。 */
#capture-float.is-stop::before { width:28px; height:28px; background-image:none; background-color:#fff; }
/* 空状态是桌面首启的东西；手机答案第一屏的版式不需要它。 */
#answer-empty { display:none; } }

/* Answer structure is shared by the live view and saved transcripts. */
.answer-choice-row { display:grid; grid-template-columns:max-content minmax(0,1fr); align-items:baseline; column-gap:12px; row-gap:4px; margin-top:12px; }
.answer-choice-row > span { font-size:calc(13px * var(--fs,1)); letter-spacing:0; white-space:nowrap; }
.answer-choice .answer-choice-row > strong { font-size:calc(22px * var(--fs,1)); line-height:1.55; overflow-wrap:anywhere; }
.answer-choice-row.choice-most > strong,.answer-choice-row.choice-most > span { color:#2e4a7d; }
.answer-choice-row.choice-more > strong,.answer-choice-row.choice-more > span { color:#366b51; }
.answer-choice-row.choice-least > strong,.answer-choice-row.choice-least > span { color:#9b432f; }
.archive-prose { white-space:normal; line-height:1.78; font-size:calc(16px * var(--fs,1)); }
.archive-prose p { font-size:inherit; color:inherit; white-space:pre-wrap; margin:0 0 .85em; }
.archive-prose .answer-choice { margin-bottom:18px; }
.type-filter { display:flex; align-items:center; gap:10px; padding:10px 0; }
.type-filter select { flex:1; min-width:0; }
.archive-rules { font-size:12px; line-height:1.65; color:var(--muted); margin:0 0 12px; }
.screen-awake { display:none; }
#awake-video { position:fixed; width:1px; height:1px; bottom:0; right:0; opacity:.01; pointer-events:none; }
@media(max-width:720px){
  .screen-awake { display:flex; order:5; width:100%; align-items:center; flex-wrap:wrap; gap:6px 12px; }
  .screen-awake .check-row { min-height:44px; margin:0; }
  #screen-awake-status { font-size:12px; color:var(--muted); }
  .answer-choice .answer-choice-row > strong { font-size:calc(20px * var(--fs,1)); }
  .answer-choice-row { column-gap:8px; }
}

.routing-note { margin:0 0 14px; padding:10px 12px; border:1px solid var(--line); border-radius:10px; font-size:13px; line-height:1.65; color:var(--accent); background:var(--panel); overflow-wrap:anywhere; }
.model-switcher #auto-routing-hint { display:block; flex:1 1 100%; grid-column:1/-1; margin:0 0 8px; font-size:12px; line-height:1.65; color:var(--muted); }
/* =========================== desktop (>=721px) ===========================
   The desktop window is 1180x820 and the sheet used to be a 860px-wide page
   whose 220px first column held 2234px of settings — a 2339px document inside
   an 820px window, with the answer squeezed into what was left. This block
   turns it into an app shell: the header and the shortcut row stay put, the
   answer column and the settings rail scroll on their own, and nothing the
   operator configures once (个人档案) takes space it does not need.
   The phone sheet is untouched by all of it. */
@media (min-width:721px) {
  main { width:min(100%,1280px); height:100vh; margin:auto; padding:20px 24px; display:grid; gap:14px 24px; grid-template-columns:minmax(0,1fr) clamp(300px,24vw,360px); grid-template-rows:auto auto minmax(0,1fr); grid-template-areas:"header header" "notice notice" "work side"; }
  /* 手机 token 在桌面宽度下打开时 #setup 被隐藏：只把「设置」面板收起（记录/归档照样可用），
     不再像旧版那样把整条 side 列塌掉——B 里 side 还装着记录与归档，塌掉反而丢功能。 */
  main:has(#setup[hidden]) .rail-panel[data-rail-panel="setup"] { display:none; }
  /* 运行日志同理：手机 token 在桌面宽度下打开时整段隐藏，桌面 admin 才显示。 */
  main:has(#log-panel[hidden]) .rail-panel[data-rail-panel="log"] { display:none; }
  header { grid-area:header; justify-content:flex-start; gap:8px; }
  header strong { font-size:15px; }
  /* 品牌叶标（G1）：内联 SVG 叶子取代原先的 8px 方点，呼应预览的 leaf 标记与 Glimpse.icns 的青柠意象（SVG 已写入 index.html 的 header）。 */
  .brand-leaf { width:17px; height:17px; }
  .chip { padding:3px 10px; border:1px solid var(--line); border-radius:999px; font-size:12px; color:var(--muted); white-space:nowrap; }
  #header-session { margin-left:auto; }
  /* 构建时间戳比会话 chip 更安静一档：它是「看一眼」的信息，不抢状态位。 */
  #build { font-size:11px; color:var(--faint); }
  /* Window actions used to sit at the bottom of a 2234px settings column, so hiding
     or quitting meant scrolling to the end of it. They belong with the window. */
  .chip-action { min-height:30px; padding:4px 11px; font-size:12px; font-weight:500; }
  #notice { grid-area:notice; z-index:auto; margin:0; padding:9px 12px; border-radius:8px; }

  /* ---- 工作区：顶部工具条 + 下方答案阅读窗 ---- */
  /* 桌面重排（2026-10-12 方向 B）：工作区是一列 flex——工具条贴顶 sticky，答案窗占满剩余高度。
     overflow-y:auto 让整段工作区可滚、工具条随之钉住；手机段 .work-panel 仍是 display:contents。 */
  .work-panel { grid-area:work; min-height:0; overflow-y:auto; display:flex; flex-direction:column; gap:14px; scrollbar-width:thin; scrollbar-color:#cdc7b6 transparent; }

  /* 工具条：模式 / 模型 / 采集 压进一条，贴顶不随答案滚动。纯白画布下工具条与答案同为白，
     底部加一条发丝描边当「滚动截止线」——否则答案文字滚到工具条下方会白叠白、失去边界。 */
  .toolbar { position:sticky; top:0; z-index:2; background:var(--bg); display:flex; align-items:flex-start; flex-wrap:wrap; gap:10px 16px; padding:4px 0 12px; border-bottom:1px solid var(--line); }
  /* 模式切换条：桌面段只露一排 tab，模式相关的子项（口述时长/篇幅/语言/深度思考）在工具条里
     跟在 tab 之后换行，仅当前模式需要时才出现，不占常驻空间。 */
  .toolbar #mode-switcher { flex:1 1 100%; display:flex; align-items:center; gap:10px 14px; flex-wrap:wrap; border-bottom:0; padding-bottom:0; }
  .toolbar .mode-tabs { flex:1 1 100%; }
  .toolbar #model-switcher { flex:0 0 auto; display:flex; align-items:flex-end; gap:8px; border-bottom:0; padding-bottom:0; }
  .toolbar #model-switcher label { flex:0 1 auto; }
  .toolbar #model-switcher p { display:none; }
  /* 档位/开关行在工具条里按内容占位、不够就整行换行：.field 的 flex:1 均分会把字段压到
     三分之一列宽，里面的档位钮（flex:1 1 0 均分 + nowrap）随之被压到小于文字宽——940 桌面
     实测「1000 字」被裁 9px、「3 分钟」被裁 2.5px。min-width:max-content 只写在桌面段：
     手机段 .mode-switcher 是 minmax(0,1fr) 网格，nowrap 档位行的宽度安全由轨道 clamp 负责，
     这条伸不进去；设置卡里的 .field 随答题模式卡一起隐藏，顺带不受影响。 */
  .toolbar .field { flex:1 1 auto; min-width:max-content; }
  /* 打勾行与档位钮垂直居中（2026-10-13 用户：「接续同一题干和深度思考应该和左边的按钮
     垂直居中对齐」）。根因是盒高不是轴：.field 的盒 = 标签行 + 档位行（.option-row 自带
     4px 上 / 10px 下边距），align-items:center 让打勾行对的是 field 盒中心，比档位钮中心
     高半个标签栈（实测约 7.5px）。修法不是挪打勾行，而是让它的外沿盒复刻 .option-row 的
     外沿盒（margin:4px 0 10px、盒高 38 = 档位钮高）再 align-self:flex-end 同底：底对齐
     则中心对齐，Δcy=0。margin 吃掉的是 field 盒内的空间，不撑高行。设置卡里的打勾行
     不住 .toolbar，不受影响。 */
  .toolbar #mode-switcher .check-row { align-self:flex-end; margin:4px 0 10px; padding:0; min-height:38px; }
  /* chat 模式迁进模式行的「截屏附图」（DOM 迁移在 app.js render()，只桌面）：与打勾行
     同一垂直节奏（margin:4px 0 10px）两者才同底；min-width:0 抵掉 base #capture 的 180px
     （这里要的是内容宽）；height/min-height:38 与档位钮、打勾行盒高一致，三者中心严格重合。
     窗口跨 721px 断点的那一拍按钮还在模式行里、吃的是手机段 #capture 的 flex:1 0 100%，
     下一轮 render（≤1.5s）把它迁回采集栏，自愈。 */
  .toolbar #mode-switcher #capture { flex:0 0 auto; min-width:0; min-height:38px; height:38px; margin:4px 0 10px; }
  .toolbar .capture-bar { flex:0 0 auto; align-items:center; flex-wrap:wrap; gap:10px 12px; }

  /* 答案阅读窗：全宽居中、最大 720px，整段工作区滚动时工具条钉住。 */
  .answer-stage { flex:1 1 auto; min-height:0; display:flex; flex-direction:column; gap:14px; }
  #answer-empty { border:1px dashed var(--line); border-radius:var(--r-card); padding:26px 20px; color:var(--muted); font-size:13px; align-self:center; width:100%; max-width:720px; }
  .answer-empty strong { display:block; margin-bottom:6px; color:var(--text); font-size:15px; }
  .answer-empty p { margin:0; }
  /* 输入框与答案卡同宽同中：max-width 与 #result 一致（720），但居中手法不同——
     .toolbar 是行向 flex，align-self 在这里管纵轴，横轴居中必须靠 margin-inline:auto
     （2026-10-12 真机反馈：composer 贴左、答案卡居中，左右缘各差 85px）。composer 在
      chat 模式独占工具条第二行，auto margin 均分的剩余空间与 #result 的 align-self 居中
     逐像素一致；900 窄档两者都退化全宽时 auto margin 为 0，不影响。 */
  #chat-composer { width:100%; max-width:720px; align-self:center; margin-inline:auto; border:1px solid var(--line); border-radius:8px; background:var(--panel); padding:12px 14px; box-shadow:var(--shadow); }
  #result { border:1px solid var(--line); border-radius:var(--r-card); background:var(--panel); padding:18px 18px 14px; display:grid; grid-template-columns:minmax(0,1fr) auto; width:100%; max-width:720px; align-self:center; box-shadow:var(--shadow); }
  /* 2026-10-17 思考过程移到解题过程下方：思考面板在手机端这张卡是 2 列 grid（.meta / .timing
     分列），若不显式跨列，它会自动只占 col1、被压成半幅宽。它是整块文本，必须与 #answer 同宽。 */
  #answer-ready, #answer, .answer-thinking { grid-column:1/-1; }
  .meta { grid-column:1; justify-content:flex-start; gap:10px; margin-top:16px; padding-top:10px; border-top:1px solid var(--line); }
  .timing { grid-column:2; margin-top:16px; padding-top:10px; border-top:1px solid var(--line); text-align:right; }
  #picture { width:100%; max-width:720px; align-self:center; border:1px solid var(--line); border-radius:var(--r-card); background:var(--panel); box-shadow:var(--shadow); }
  #picture summary { padding:12px 14px; }
  #picture[open] summary { border-bottom:1px solid var(--line); }
  #image { max-height:62vh; object-fit:contain; background:#000; border-radius:0 0 var(--r-card) var(--r-card); }
  .capture-bar #thread-turn { order:1; }
  .capture-bar #new-thread { order:2; }
  #cancel { min-height:40px; padding:8px 14px; border-color:#a8442f; border-radius:4px; }

  /* ---- 右侧三 tab 抽屉 ---- */
  /* 桌面专属：手机端 .rail 与 .rail-panel 都是 display:contents，tab 概念整体不存在。 */
  .rail { grid-area:side; min-height:0; overflow:hidden; display:flex; flex-direction:column; }
  .rail-tabs { display:flex; gap:6px; padding:2px; background:var(--raised); border:1px solid var(--line); border-radius:999px; margin-bottom:12px; flex:none; }
  .rail-tab { flex:1; min-height:30px; padding:4px 10px; border:0; border-radius:999px; background:transparent; color:var(--muted); font-size:13px; font-weight:600; }
  .rail-tab.is-active { background:var(--panel); color:var(--accent); box-shadow:var(--shadow); }
  .rail-panel { display:none; min-height:0; flex:1 1 auto; overflow-y:auto; scrollbar-width:thin; scrollbar-color:#cdc7b6 transparent; }
  .rail-panel.is-active { display:flex; flex-direction:column; }

  /* 记录卡：撑满抽屉高度，列表内部滚。 */
  .rail-panel[data-rail-panel="records"] .records { flex:0 0 auto; min-height:0; display:flex; flex-direction:column; background:var(--panel); border:1px solid var(--line); border-radius:var(--r-card); box-shadow:var(--shadow); overflow:hidden; }
  .rail-panel[data-rail-panel="records"] .records > footer { border:0; border-top:1px solid var(--line); border-radius:0; background:transparent; padding:8px 14px; }
  .workspace { flex:0 0 auto; min-height:0; display:flex; flex-direction:column; min-width:0; padding:14px; border-right:0; }
  /* An empty list should not stretch into a 600px-tall blank panel. */
  .workspace:has(#empty:not([hidden])) { flex:0 1 auto; min-height:0; }
  #history { flex:0 0 auto; }
  /* 记录列表常开（2026-10-13 用户两步定音：先要「可展开」，随后点名「一直展开」——与归档
     同构）。旧版 max-height:34px 只露一行、中间态的「展开/收起」开关都已删：卡片随内容
     长高，滚动交给 .rail-panel 统一处理，与 #archive-list 同一条滚动链。 */
  #history button { border-radius:6px; }
  #history button:hover { color:var(--text); background:var(--raised); }
  #history button[aria-current="true"] { background:var(--raised); box-shadow:inset 2px 0 0 var(--accent); }
  footer { margin-top:0; border:1px solid var(--line); border-top:0; border-radius:0 0 8px 8px; background:var(--panel); padding:8px 14px; }
  /* 导出/复制日志与清除记录同一条 foot strip：同样的描边按钮 + 间距，三个按钮
     （约 4 字宽 × 3 + 间距）在 320px 手机宽度上仍放得下。 */
  footer .text-button { min-height:32px; padding:6px 12px; border-color:var(--line); border-radius:4px; }
  footer .text-button + .text-button { margin-left:10px; }

  /* 归档卡：抽屉内滚动。 */
  .rail-panel[data-rail-panel="archive"] .archive, .rail-panel[data-rail-panel="archive"] .archive-detail { flex:0 0 auto; }
  /* 归档列表不设高度上限（2026-10-12 用户口径：每个归档占一行、全部展开）——场次多了
     由 .rail-panel 自己滚（flex:1 1 auto + overflow-y:auto）。记录 #history 仍固定一行，
     两者口径不同：记录是"当前这场看到第几条"，归档是"一共存了哪些场"。 */

  /* 设置：连接手机 / 模型设置 / 个人档案（答题模式卡在桌面段隐藏，模式切换已交给顶部工具条）。 */
  #setup { min-height:0; overflow-y:auto; }
  #mode-config-card { display:none; }
  #setup .settings { padding:2px 16px; }
  #setup .card { border:0; border-bottom:1px solid var(--line); }
  #setup .card:last-child { border-bottom:0; }
  /* The sheet is a stack of foldable sections: 个人档案 alone is nine fields, and
     leaving it open pushed the model and mode controls off the rail entirely. */
  #setup .card > summary { padding:14px 0; font-size:13px; font-weight:600; color:var(--text); }
  #setup .card-body { padding:0 0 16px; }
  /* 配对卡只留二维码 + 刷新（2026-10-12 用户要求「二维码旁边不要放链接和屏幕录制权限」）：
     链接 #links 与屏幕录制权限 #permission/#request-permission 用 CSS 藏掉——DOM 保留、
     render()/onclick 照常写值与接线，只是桌面不可见（保功能不保可见）。对全部桌面宽度
     生效：原先 721-1040 窄栏段会把 #request-permission 放回来（300px 栏 + 162px 二维码
     挤不下一行），与用户要求冲突，已整块撤掉。flex 单列居中，二维码居中、刷新按钮通栏，
     窄栏也稳。 */
  #setup .pairing { display:flex; flex-direction:column; align-items:center; gap:12px; }
  /* 刷新按钮通栏：2026-10-12 原先写在 index.html 的 style="width:100%" 里，而 CSP 是
     style-src 'self'（无 unsafe-inline）——内联样式属性在运行期被拦，「通栏」从来没生效过，
     每个窗口还白背一条 CSP violation。搬进样式表，HTML 里的 style 属性一并删掉。 */
  #setup .pairing #refresh-qr { width:100%; }
  #setup .pairing #links, #setup .pairing #permission, #setup .pairing #request-permission { display:none; }
  #qr img { width:162px; height:162px; }
  .shortcut { margin:0; padding:12px 16px 14px; border-top:1px solid var(--line); }
  /* secondary 的 hover 底只在桌面段：触屏上 :hover 会粘住，手机端不给。 */
  #setup .secondary:hover { background:var(--raised); }
}
/* ---- 运行日志实时视图（桌面 .rail 抽屉内）---- */
.runtime-log { flex:1 1 auto; min-height:0; display:flex; flex-direction:column; background:var(--panel); border:1px solid var(--line); border-radius:var(--r-card); box-shadow:var(--shadow); overflow:hidden; }
.runtime-log .section-head { padding:12px 14px; border-bottom:1px solid var(--line); }
.log-autoscroll { display:flex; align-items:center; gap:5px; color:var(--muted); font-size:12px; margin:0; }
.log-autoscroll input[type="checkbox"] { width:auto; height:16px; min-height:0; margin:0; padding:0; accent-color:var(--accent); }
/* 日志区：等宽字体 + 浅底深字（亮色主题），贴底滚动；行号感来自 monospace 与紧凑行高。 */
.log-view { flex:1 1 auto; min-height:0; margin:0; padding:12px 14px; overflow:auto; background:var(--field); color:var(--text); border:0; border-radius:0; font:12.5px/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace; white-space:pre-wrap; overflow-wrap:anywhere; scrollbar-width:thin; scrollbar-color:#cdc7b6 transparent; }
.runtime-log > footer { flex:none; display:flex; gap:12px; padding:8px 14px; border-top:1px solid var(--line); }

.phone-choice-popup { display:none; }
@media(max-width:720px){
  .phone-choice-popup { display:block; position:fixed; z-index:60; left:50%; top:50%; transform:translate(-50%,-50%); width:min(360px,calc(100vw - 32px)); max-height:70dvh; padding:18px 20px; border:0; border-radius:20px; color:rgba(255,35,35,.85); background:rgba(255,255,255,.72); box-shadow:0 8px 28px rgba(0,0,0,.12); pointer-events:none; }
  #choice-popup-value { display:block; font-size:clamp(64px,20vw,100px); font-weight:800; line-height:1.15; text-align:center; white-space:pre-wrap; overflow-wrap:anywhere; }
  .phone-choice-popup.is-long #choice-popup-value { font-size:clamp(24px,7vw,36px); line-height:1.35; }
}
