/* ==========================================================================
   对话流、气泡、输入区、会话列表、模型选择。
   只用 tokens.css 里的变量，不写死色值。
   ========================================================================== */

/* ------------------------------------------------------------------ 布局 */

/**
 * 三列：常驻窄条 + 会话列表 + 对话区。
 * 窄条永不消失 —— 头像、新对话、收放、主题这些入口不能藏在
 * 一个能被收掉的容器里（早先退出登录的入口就在会话列表底部，
 * 侧栏一收就找不到了）。
 */
.app {
  display: grid;
  grid-template-columns: var(--dock-w) var(--sidebar-w) 1fr;
  height: 100vh;
  overflow: hidden;
  transition: grid-template-columns var(--dur) var(--ease);
}
.app.is-rail-off { grid-template-columns: var(--dock-w) 0 1fr; }
.app.is-rail-off .rail { opacity: 0; pointer-events: none; }

/* -------------------------------------------------------- 常驻窄条 */

.dock {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: var(--sp-2) 0;
  border-right: 1px solid var(--line-s);
  background: var(--surface);
}
.dock-spacer { flex: 1; }
.dock-div {
  width: 20px; height: 1px;
  margin: 3px 0 5px;
  background: var(--line-s);
}

/* 头像：品牌字 + 黄铜细框。点开是账号菜单 */
.dock-avatar {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  margin-bottom: 2px;
  background: transparent;
  border: 1px solid var(--brass);
  border-radius: 50%;
  color: var(--brass);
  font-family: var(--font-serif);
  font-size: var(--fs-md);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.dock-avatar:hover { background: var(--brass); color: var(--surface); }
.dock-avatar.is-on { background: var(--brass); color: var(--surface); }

.dock-btn {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  background: transparent; border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink3);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.dock-btn:hover { color: var(--ink); background: var(--surface-2); }
.dock-btn svg { width: 16px; height: 16px; }
.dock-btn.is-on { color: var(--brass); }

/* ------------------------------------------------------- 账号菜单 */

.user-pop {
  position: fixed;
  left: calc(var(--dock-w) + 6px);
  top: var(--sp-2);
  min-width: 216px;
  padding: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  z-index: 60;
}
.user-pop[hidden] { display: none; }

.up-head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 4px 8px var(--sp-2);
  border-bottom: 1px solid var(--line-s);
  margin-bottom: var(--sp-2);
}
.up-mark {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  flex-shrink: 0;
  border: 1px solid var(--brass);
  border-radius: 50%;
  color: var(--brass);
  font-family: var(--font-serif);
  font-size: var(--fs-md);
}
.up-who { min-width: 0; }
.up-name {
  display: block;
  font-size: var(--fs-base);
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.up-role {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .06em;
  color: var(--ink3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.up-item {
  display: flex; align-items: center; gap: var(--sp-2);
  width: 100%;
  padding: 6px 8px;
  background: transparent; border: 0;
  border-radius: var(--radius-sm);
  text-align: left;
  color: var(--ink2);
  font-size: var(--fs-base);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.up-item:hover { background: var(--surface-2); color: var(--ink); }
.up-item svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--ink3); }
.up-item:hover svg { color: var(--brass); }
.up-item.is-danger:hover { color: var(--high); }
.up-item.is-danger:hover svg { color: var(--high); }

.up-div {
  height: 1px;
  margin: var(--sp-2) 4px;
  background: var(--line-s);
}
.up-note {
  padding: 4px 8px 2px;
  font-size: var(--fs-label);
  line-height: 1.7;
  color: var(--ink3);
}

/* -------------------------------------------------------------- 左侧栏 */

.rail {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-right: 1px solid var(--line-s);
  background: var(--surface);
  overflow: hidden;
  transition: opacity var(--dur) var(--ease);
}

.rail-top {
  padding: var(--sp-3) var(--sp-3) var(--sp-2);
}
.brand { min-width: 0; }
.brand-name {
  display: block;
  font-family: var(--font-serif);
  font-size: var(--fs-md);
  letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.brand-org {
  display: block;
  font-size: var(--fs-label);
  color: var(--ink3);
  margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.rail-list {
  flex: 1;
  overflow-y: auto;
  padding: 0 var(--sp-2);
}
.rail-empty {
  padding: var(--sp-4) var(--sp-3);
  color: var(--ink3);
  font-size: var(--fs-sm);
}

.sess {
  position: relative;
  display: block; width: 100%;
  text-align: left;
  padding: 7px 26px 7px 10px;
  margin-bottom: 2px;
  background: transparent;
  border: 0; border-radius: var(--radius-sm);
  color: var(--ink2);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.sess:hover { background: var(--surface-2); color: var(--ink); }
.sess.is-on { background: var(--surface-2); color: var(--ink); }
.sess.is-on::before {
  content: ''; position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 2px; background: var(--brass); border-radius: 1px;
}
.sess-title {
  display: block;
  font-size: var(--fs-base);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sess-meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  color: var(--ink3);
  margin-top: 1px;
}
.sess-del {
  position: absolute; right: 6px; top: 50%;
  transform: translateY(-50%);
  width: 18px; height: 18px;
  display: grid; place-items: center;
  border-radius: 4px;
  color: var(--ink3);
  font-size: 15px; line-height: 1;
  opacity: 0;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease);
}
.sess:hover .sess-del { opacity: 1; }
.sess-del:hover { color: var(--high); background: var(--bg); }

/* -------------------------------------------------------------- 对话区 */

/**
 * 两行栅格：上行滚动，下行输入区。
 * 输入区不在滚动容器里，所以内容多少都恒定贴底 ——
 * 早先用 flex + position:sticky，hello 一隐藏就没有东西撑高度，
 * 输入框会浮到屏幕中部。
 */
.chat {
  position: relative;
  display: grid;
  grid-template-rows: 1fr auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

/**
 * 装饰层：极淡的手写公式底纹 + 边角几张大几何。
 *
 * 参考的那张图有三条规矩，照着来的：
 *
 * 一、底纹是**纸的颗粒**，不是图案。走过两条死路：48px 几何密铺
 *     （每格权重相同，铺满整屏没有落点，就是「乱」），以及手搓路径
 *     模拟手写公式（符号数得出来，420px 循环肉眼可见，比密铺更乱）。
 *     现在用 feTurbulence 生成伪随机噪点，没有可识别形状也就没有
 *     重复感。见 tokens.css 的 --formula-wash。
 *
 * 二、几何是**少数几个大形压在边角**，中间大片留空。视觉重量集中在
 *     两三处，才有主次。所以这里只有右上、右下两组，尺寸按 vmin 走
 *     （窗口越大形越大，保持构图比例）。
 *
 * 三、大形要有**内描线**（比填充亮一档的一圈线，往内缩一点）。那张图
 *     里每个六芒星和三角都带，是它显得「设计过」而不是「贴了个色块」
 *     的关键。这里用两层同形渐变错开尺寸实现。
 *
 * 画在 .chat 的 ::before 而不是 body：body 的背景会被左侧窄条与会话
 * 列表盖住一部分,而那两块有自己的 --surface 底色 —— 装饰只在中间露
 * 出来就成了「右边有、左边没有」。
 *
 * 不用 .chat::after：拖文件时「松手即可加入附件」那圈虚线占着它,
 * 装饰会在拖拽过程中整组消失。
 */
.chat::before {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 0;

  background-image:
    var(--deco-star-tr),      /* 右上 六芒星，黄铜 */
    var(--deco-tri-r),        /* 右中 三角描线，青 */
    var(--deco-star-br),      /* 右下 六芒星，蓝 */
    var(--formula-wash);      /* 全幅 纸张颗粒 */
  background-position:
    right -6vmin top 8%,
    right 14vmin center,
    right 4vmin bottom -10vmin,
    0 0;
  background-size:
    34vmin 34vmin,
    11vmin 11vmin,
    30vmin 30vmin,
    180px 180px;
  background-repeat:
    no-repeat, no-repeat, no-repeat, repeat;
}

/* 内容必须压在装饰之上 */
.chat > * { position: relative; z-index: 1; }

/* 窄屏时几何撤掉,只留底纹 —— 手机上那几个大形会直接压在文字下面 */
@media (max-width: 900px) {
  .chat::before {
    background-image: var(--formula-wash);
    background-position: 0 0;
    background-size: 180px 180px;
    background-repeat: repeat;
  }
}

/**
 * 不设 scroll-behavior: smooth。
 * 一旦设了，所有 scrollTop 赋值都变成动画，之后立刻读回的还是旧值 ——
 * 「用户是否贴着底」这个判断就不可靠了，跟随滚动会时灵时不灵。
 * 平滑只用在「回到底部」按钮上，那里显式传 behavior。
 */
.scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
}
/* 内层撑满，让 hello 能垂直居中 */
.scroll-inner {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* 回到底部 */
.to-bottom {
  position: absolute;
  right: var(--sp-5);
  bottom: 96px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--ink2);
  box-shadow: var(--shadow-lift);
  z-index: 12;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.to-bottom:hover { color: var(--brass); border-color: var(--brass); }
.to-bottom svg { width: 14px; height: 14px; }
.to-bottom[hidden] { display: none; }

/* 拖文件经过时的提示边框 */
.chat.is-drop::after {
  content: '松手即可加入附件';
  position: absolute; inset: var(--sp-3);
  display: grid; place-items: center;
  border: 1px dashed var(--brass);
  border-radius: var(--radius);
  color: var(--brass);
  font-size: var(--fs-md);
  background: color-mix(in srgb, var(--bg) 80%, transparent);
  pointer-events: none;
  z-index: 5;
}

/* ---------------------------------------------------------------- 首屏 */

.hello {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
}
.hello-title {
  font-family: var(--font-serif);
  font-size: var(--fs-xl);
  letter-spacing: .02em;
  margin-bottom: var(--sp-2);
}
.hello-sub {
  max-width: 460px;
  color: var(--ink2);
  font-size: var(--fs-base);
  margin-bottom: var(--sp-6);
}

.hello-kpi {
  display: flex; gap: var(--sp-6);
  margin-bottom: var(--sp-8);
  padding-bottom: var(--sp-6);
  border-bottom: 1px solid var(--line-s);
}
/* KPI 可点：点任一项等于问一句「看在办合同」（spec §4.1） */
.kpi {
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px 10px;
  background: transparent;
  border: 0; border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.kpi:hover { background: var(--surface-2); }
.kpi:hover b { color: var(--brass-l); }
.kpi b {
  font-family: var(--font-serif);
  font-size: 22px; font-weight: 400;
  color: var(--brass);
  transition: color var(--dur) var(--ease);
}
.kpi span {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .1em;
  color: var(--ink3);
  white-space: nowrap;
}

.hello-chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 200px));
  gap: var(--sp-3);
}
.chip {
  text-align: left;
  padding: 10px 13px;
  background: var(--surface);
  border: 1px solid var(--line-s);
  border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.chip:hover { border-color: var(--brass); background: var(--surface-2); }
.chip b {
  display: block;
  font-weight: 400;
  font-size: var(--fs-base);
  color: var(--ink);
  margin-bottom: 2px;
}
.chip span { font-size: var(--fs-xs); color: var(--ink3); line-height: 1.5; }

/* ==================================================== 首页「最近对话」
   只在首页态显示（.hello.is-hub）。进了会话就没有它 ——
   那时候会话列表在左边，不需要在正中再摆一份。 */

.hub { display: none; width: 100%; max-width: 520px; }
.hello.is-hub .hub { display: block; margin-bottom: var(--sp-6); }

.hub-head {
  display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}
/* 字号与颜色现在由 base.css 的 .label 统一给，这里只管排布 */
.hub-head .label { flex-shrink: 0; }
/* 细线要落在两个标签中间，而伪元素天然排在最后 —— 用 order 拨回来 */
.hub-head::after {
  content: '';
  order: 1;
  flex: 1; height: 1px;
  background: var(--line-s);
}
/* 「另有 N 个在左侧」是附注，仍走淡的一档 —— 它不该跟标题抢 */
.hub-head .hub-rest {
  order: 2;
  font-size: var(--fs-xs);
  letter-spacing: .06em;
}

.hub-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 7px;
}

.hub-item {
  position: relative;
  text-align: left;
  padding: 9px 30px 9px 12px;
  background: var(--surface);
  border: 1px solid var(--line-s);
  border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease),
              background var(--dur) var(--ease);
}
.hub-item:hover { border-color: var(--brass); background: var(--surface-2); }

.hub-title {
  display: block;
  font-size: var(--fs-base);
  color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 「1 轮 · 09:14」。同样从 10px/--ink3 提一档 ——
   轮数与时间是用户挑对话的依据，不是装饰。 */
.hub-meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .04em;
  color: var(--ink2);
  margin-top: 3px;
}

/* 最近一条：黄铜左界，不用文字标「最近」。
   走 inset box-shadow 而不是 border-left —— 换 border 宽度会让这张卡
   的内容比其余几张往右挪 2px，一行里就看得出错位。 */
.hub-item.is-recent {
  border-color: var(--line);
  box-shadow: inset 2px 0 0 var(--brass-l);
}
.hub-item.is-recent .hub-title { color: var(--ink); }

.hub-del {
  position: absolute; right: 7px; top: 50%;
  transform: translateY(-50%);
  width: 20px; height: 20px;
  display: grid; place-items: center;
  border: 0; background: transparent;
  border-radius: 4px;
  color: var(--ink3);
  font-size: 15px; line-height: 1;
  opacity: 0;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease);
}
.hub-item:hover .hub-del { opacity: 1; }
.hub-del:hover { color: var(--high); background: var(--bg); }

.hub-empty {
  padding: 2px 0 var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--ink3);
}

@media (max-width: 700px) {
  .hub-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ---- 「还能做什么」：十二项能力收在这里，首屏不摆十二个按钮 ---- */

/**
 * 「还能做什么」。
 *
 * 原先 --ink3 + 12px，在瓷白底上约 2.6:1 —— 淡得像一句说明文字。
 * 但它是**其余八项能力的唯一入口**，用户找不到它就等于那八项不存在。
 * 提到 --ink2（约 5.2:1）并加两道括号刻度：形态仍是一行文字、仍是
 * 点开才展开，只是不再需要找。
 */
.hello-more {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: var(--sp-4);
  padding: 5px 12px;
  background: transparent;
  border: 0;
  color: var(--ink2);
  font-size: var(--fs-base);
  letter-spacing: .02em;
}
.hello-more:hover { color: var(--brass); }
.hello-more svg {
  width: 12px; height: 12px;
  transition: transform var(--dur) var(--ease);
}
.hello-more.is-open svg { transform: rotate(180deg); }

/* 展开区跟 .hub 用同一个宽度（520px）—— 两块都是首页的内容分区,
   宽度不一致会看出参差。 */
.hello-all {
  width: 100%;
  max-width: 520px;
  margin-top: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line-s);
  text-align: left;
}
.hello-all[hidden] { display: none; }

.ha-group {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 5px;
  margin-bottom: var(--sp-4);
}
.ha-group .label { width: 100%; margin-bottom: 5px; }

.ha-item {
  padding: 4px 11px;
  background: transparent;
  border: 1px solid var(--line-s);
  border-radius: var(--radius-pill);
  color: var(--ink2);
  font-size: var(--fs-sm);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.ha-item:hover { border-color: var(--brass); color: var(--ink); }

/* -------------------------------------------------------------- 消息流 */

.stream,
.typing-slot {
  width: 100%;
  max-width: var(--chat-max);
  margin: 0 auto;
  padding: 0 var(--sp-5);
}
.stream { padding-top: var(--sp-6); }
.stream:empty { padding: 0; }
.typing-slot:empty { display: none; }

.msg { margin-bottom: var(--sp-5); }

/* 新消息入场。重画已有消息时不会重放 —— 增量渲染只给新节点加这个类 */
.msg.is-enter { animation: msgIn 220ms var(--ease) both; }
@keyframes msgIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .msg.is-enter { animation: none; }
}

.msg-user { display: flex; justify-content: flex-end; }
.msg-user .msg-body {
  max-width: 78%;
  padding: 9px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line-s);
  border-radius: var(--radius);
}
.msg-atts { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }

.msg-ai .msg-body { position: relative; }

/* 模型署名：极淡，hover 消息时才提亮 —— 平时不该抢注意力 */
.msg-model {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .1em;
  color: var(--ink3);
  opacity: .5;
  margin-bottom: 4px;
  transition: opacity var(--dur) var(--ease), color var(--dur) var(--ease);
}
.msg-ai:hover .msg-model { opacity: 1; color: var(--ink2); }

.msg-stopped {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--ink3);
}

/* 流式光标 */
.caret {
  display: inline-block;
  width: 6px; height: 14px;
  margin-left: 2px;
  background: var(--brass);
  vertical-align: text-bottom;
  animation: blink 1s steps(2, start) infinite;
}
@keyframes blink { to { visibility: hidden; } }
.msg-ai:not(.is-typing) .caret { display: none; }

/* markdown 正文 */
.md p { margin: 0 0 var(--sp-2); }
.md p:last-child { margin-bottom: 0; }
.md strong { font-weight: 600; color: var(--ink); }
.md code {
  font-family: var(--font-mono);
  font-size: .92em;
  padding: 1px 5px;
  background: var(--surface-2);
  border-radius: 3px;
}
.md ul { margin: 0 0 var(--sp-2); padding-left: 1.3em; }
.md li { margin-bottom: 3px; }

/* 错误条 */
.msg-err {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 9px 13px;
  border: 1px solid var(--high);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
}
.msg-err svg { width: 15px; height: 15px; color: var(--high); flex-shrink: 0; }
.msg-err span { flex: 1; }

/* 反问选项 */
.choices { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.choice {
  padding: 6px 13px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--ink);
  font-size: var(--fs-sm);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.choice:hover { border-color: var(--brass); background: var(--surface-2); }

/* 卡片占位（Task 8 前的临时形态） */
.card-todo {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding: 10px 13px;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--ink3);
  font-size: var(--fs-sm);
}

/* -------------------------------------------------------------- 输入区 */

.composer-wrap {
  position: sticky; bottom: 0;
  width: 100%;
  max-width: var(--composer-max);
  margin: 0 auto;
  padding: var(--sp-3) var(--sp-5) var(--sp-4);
  background: linear-gradient(to bottom, transparent, var(--bg) 22%);
}

.composer {
  position: relative;
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line-s);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  transition: border-color var(--dur) var(--ease);
}
.composer:focus-within { border-color: var(--brass); }

.atts { display: none; flex-wrap: wrap; gap: 5px; margin-bottom: var(--sp-2); }
.atts.is-on { display: flex; }

.att {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px;
  background: var(--bg);
  border: 1px solid var(--line-s);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  color: var(--ink2);
}
.att svg { width: 12px; height: 12px; color: var(--ink3); }
.att i { font-style: normal; color: var(--ink3); font-family: var(--font-mono);
  font-size: var(--fs-label); }
.att b { cursor: pointer; color: var(--ink3); font-weight: 400; font-size: 14px;
  line-height: 1; }
.att b:hover { color: var(--high); }
.att-sm { background: var(--surface); }

.composer-input {
  display: block; width: 100%;
  min-height: 24px; max-height: 200px;
  padding: 0; margin-bottom: var(--sp-2);
  background: transparent;
  border: 0; outline: none; resize: none;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-md);
  line-height: 1.7;
  overflow-y: auto;
}
.composer-input::placeholder { color: var(--ink3); }

.composer-bar { display: flex; align-items: center; gap: var(--sp-2); }
.composer-spacer { flex: 1; }
.composer-hint {
  font-size: var(--fs-xs);
  color: var(--ink3);
  max-width: 300px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.composer-hint.is-bad { color: var(--high); }

.composer-note {
  margin-top: var(--sp-2);
  text-align: center;
  font-size: var(--fs-xs);
  color: var(--ink3);
  opacity: .8;
}

/* ------------------------------------------------------------ 模型选择 */

.model-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  background: transparent;
  border: 1px solid var(--line-s);
  border-radius: var(--radius-pill);
  color: var(--ink2);
  font-size: var(--fs-xs);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.model-chip:hover { border-color: var(--line); color: var(--ink); }
.model-chip .dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--low);
}
.model-chip svg { width: 11px; height: 11px; }

.model-pop {
  position: absolute;
  left: var(--sp-3); bottom: calc(100% + 6px);
  min-width: 232px;
  padding: var(--sp-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  z-index: 20;
}
.model-pop .label { display: block; padding: 2px 8px var(--sp-2); }

.model-opt {
  display: block; width: 100%;
  text-align: left;
  padding: 6px 9px;
  background: transparent;
  border: 0; border-radius: var(--radius-sm);
  transition: background var(--dur) var(--ease);
}
.model-opt:hover { background: var(--surface-2); }
.model-opt.is-on { background: var(--surface-2); }
.model-opt.is-on .model-opt-name { color: var(--brass); }

/* 服务端没配这家的 Key。看得见但选不了 ——
   直接藏掉会让人以为「怎么少了几个」，摆着能点则是骗人。 */
.model-opt.is-off { cursor: not-allowed; opacity: .45; }
.model-opt.is-off:hover { background: transparent; }
.model-opt-name {
  display: block;
  font-size: var(--fs-base);
  color: var(--ink);
}
.model-opt-meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .06em;
  color: var(--ink3);
}

.model-add {
  display: block; width: 100%;
  margin-top: var(--sp-2);
  padding: 6px 9px;
  background: transparent;
  border: 0; border-top: 1px solid var(--line-s);
  border-radius: 0;
  text-align: left;
  color: var(--ink2);
  font-size: var(--fs-sm);
}
.model-add:hover { color: var(--brass); }

/* ----------------------------------------------------- localStorage 满 */

.quota-bar {
  position: fixed; left: 50%; bottom: var(--sp-6);
  transform: translateX(-50%) translateY(16px);
  display: none; align-items: center; gap: var(--sp-3);
  max-width: 560px;
  padding: 10px 14px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--mid);
  border-left-width: 3px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-lift);
  font-size: var(--fs-sm);
  opacity: 0;
  z-index: 80;
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.quota-bar.is-shown {
  display: flex;
  transform: translateX(-50%) translateY(0);
  opacity: 1;
}
.quota-bar svg { width: 16px; height: 16px; color: var(--mid); flex-shrink: 0; }
.quota-bar span { flex: 1; }

/* ------------------------------------------------------------ 窄屏适配 */

/**
 * 窄屏：侧栏改为覆盖式抽屉，默认收起。
 * 要靠 .is-rail-on 显式打开 —— 早先写成「非 is-rail-off 即展开」，
 * 于是窄屏下默认状态反而把侧栏压在对话上面。
 */
@media (max-width: 900px) {
  /* 窄条留着（头像与新对话不能没有入口），只把会话列表收成抽屉 */
  .app { grid-template-columns: var(--dock-w) 0 1fr; }

  .rail {
    position: fixed; left: var(--dock-w); top: 0; bottom: 0;
    width: var(--sidebar-w);
    opacity: 0; pointer-events: none;
    transform: translateX(-100%);
    z-index: 40;
    box-shadow: var(--shadow-lift);
    transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
  }
  .app.is-rail-on .rail {
    opacity: 1; pointer-events: auto;
    transform: translateX(0);
  }
  /* 抽屉开着时点外面关掉。遮罩从窄条右侧开始，别把窄条也盖住 */
  .app.is-rail-on::after {
    content: '';
    position: fixed; left: var(--dock-w); right: 0; top: 0; bottom: 0;
    background: rgba(0,0,0,.28);
    z-index: 35;
  }

  .hello-chips { grid-template-columns: minmax(0, 260px); }
  .hello-kpi { gap: var(--sp-4); flex-wrap: wrap; justify-content: center; }
  .to-bottom { right: var(--sp-3); }
}

/* 首字到达前的等待点。空气泡看不出在动，三点能。 */
.dots { display: flex; gap: 4px; padding: 5px 0; }
.dots i {
  width: 5px; height: 5px;
  background: var(--ink3);
  border-radius: 50%;
  animation: dotPulse 1.1s var(--ease) infinite;
}
.dots i:nth-child(2) { animation-delay: .16s; }
.dots i:nth-child(3) { animation-delay: .32s; }
@keyframes dotPulse {
  0%, 60%, 100% { opacity: .25; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-3px); }
}
/* 等待点在时不显示光标，两个一起晃太吵 */
.msg-ai:has(.dots) .caret { display: none; }

/* 逐条审查进度。
   一份三十条的合同要跑几分钟，没有这一行用户会以为卡死然后刷新，
   那样前面几分钟全白跑。用等宽字体是因为数字每秒在变 ——
   比例字体下「9/30」变「10/30」整行会跳。 */
.rv-prog {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .02em;
  color: var(--ink3);
  padding: 4px 0 6px;
  font-variant-numeric: tabular-nums;
}
/* 进度在显示时也不要光标 —— 同上，两个动的东西太吵 */
.msg-ai:has(.rv-prog) .caret { display: none; }
