/* 聊天层：消息流、气泡、输入区、Markdown 渲染样式、角色扮演语义标注。
   禁止出现 hex / rgb 字面量。 */

.chat {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.chat-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: 0 0 auto;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-1);
}
.chat-head-main { flex: 1 1 auto; min-width: 0; }
.chat-head-title {
  font-family: var(--font-display);
  /* 「与 xxx 的对话」。用户 2026-09-05 点名它太大。 */
  font-size: var(--fs-ui-title);
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-head-sub { font-size: var(--fs-xxs); color: var(--ink-subtle); }
.chat-head-actions { display: flex; gap: var(--sp-1); flex: 0 0 auto; }

.chat-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--sp-5) var(--sp-4);
  scroll-behavior: smooth;
  /* 惯性滚动。与 .view-root 同一处理,理由见 layout.css。 */
  -webkit-overflow-scrolling: touch;
}
.chat-inner {
  max-width: var(--chat-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

/* ---------------- 消息 ---------------- */
.msg {
  display: flex;
  gap: var(--sp-3);
  animation: msgIn var(--dur) var(--ease-out);
}
.msg-user { flex-direction: row-reverse; }

@keyframes msgIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

.msg-body { min-width: 0; max-width: 78%; display: flex; flex-direction: column; gap: var(--sp-1); }
.msg-user .msg-body { align-items: flex-end; }

.msg-name {
  font-size: var(--fs-xxs);
  color: var(--ink-subtle);
  padding: 0 var(--sp-1);
}

.bubble {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-1);
  color: var(--ink-body);
  font-size: var(--fs-body);
  line-height: var(--lh-loose);
  word-break: break-word;
  overflow-wrap: anywhere;
}
.msg-user .bubble {
  background: var(--primary-soft);
  border-color: var(--primary-soft);
  color: var(--ink);
}
/* 旁白气泡。
   形式对齐微信里的时间、拍一拍、撒回提示:居中、浅色半透明、深灰字。
   不带头像与名字 —— 它不是某个角色说的话。 */
.msg-narration {
  display: flex;
  justify-content: center;
  margin: var(--sp-1) 0;
}
.narration-bubble {
  max-width: 72%;
  padding: 5px var(--sp-3);
  /* 小圆角而不是胶囊:内容一多就换行,胶囊形会被拉成椭圆很难看。
     取 --radius-xs(4px):看上去既不像椭圆也不像硬方形。 */
  border-radius: var(--radius-xs);
  /* 与画布不同的底色:让它看起来“浮”在对话流上而不是一条消息。
     色值由 tokens.css 的 --narration-bg 提供(0.48.0 起是实色,
     移除磨砂前它是半透明白 + blur(2px))。 */
  background: var(--narration-bg);
  color: var(--ink-muted);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  text-align: center;
  word-break: break-word;
}
/* 夜间把字色再压一档。底色由 --narration-bg 提供(它引用 --surface-2,
   本身就随模式切换),这里只剩字色差异。 */
:root[data-theme='dark'] .narration-bubble {
  color: var(--ink-subtle);
}

.bubble-error {
  background: var(--danger-soft);
  border-color: var(--danger-soft);
  color: var(--danger);
  font-size: var(--fs-sm);
}

.msg-tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-1);
  padding: 0 var(--sp-1);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.msg:hover .msg-tools, .msg:focus-within .msg-tools { opacity: 1; }
/* 常驻工具条:不需要 hover 也一直显示 */
.msg-tools.is-fixed { opacity: 1; }
/* 纯文字统计(时长、字数),不可点击 */
.msg-stat {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--ink-subtle);
  font-size: var(--fs-xxs);
  font-variant-numeric: tabular-nums;
  cursor: default;
  user-select: none;
}

/* 输入中指示 */
.typing { display: inline-flex; gap: 4px; align-items: center; padding: var(--sp-1) 0; }
.typing span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ink-subtle);
  animation: blink 1.2s infinite;
}
.typing span:nth-child(2) { animation-delay: .2s; }
.typing span:nth-child(3) { animation-delay: .4s; }

@keyframes blink {
  0%, 60%, 100% { opacity: .25; }
  30% { opacity: 1; }
}

/* ---------------- 输入区 ---------------- */
.composer {
  flex: 0 0 auto;
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  border-top: 1px solid var(--hairline);
  background: var(--surface-1);
}
.composer-inner {
  max-width: var(--chat-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.composer-box {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-1);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.composer-box:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring); }
.composer-input {
  flex: 1 1 auto;
  min-height: 24px;
  max-height: 180px;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  resize: none;
  outline: none;
  padding: var(--sp-1) var(--sp-2);
}
.composer-input::placeholder { color: var(--ink-subtle); }
.composer-send {
  width: 38px; height: 38px;
  border: 0;
  border-radius: var(--radius);
  background: var(--primary);
  color: var(--on-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 0 0 auto;
  transition: background-color var(--dur-fast) var(--ease);
}
.composer-send:hover { background: var(--primary-hover); }
.composer-send:disabled { opacity: .5; cursor: not-allowed; }

/* 输入框气泡条：左侧展开/收回按钮 + 右侧横向滚动气泡。
   普通对话（角色卡 / 组合卡 / 群聊卡）与生成助手共用同一套结构，
   由 ui.js 的 UI.composerChips 生成，不再按页面分叉。 */
.composer-chips-wrap {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
}
/* toggle 展开时旋转 90° 指向下，收起时指向右（chevron-right） */
.composer-chips-wrap:not(.is-collapsed) .chips-toggle { transform: rotate(90deg); }
.chips-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  padding: 0;
  border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-pill);
  background: var(--surface-1);
  color: var(--ink-subtle);
  font-size: var(--fs-xxs);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur) var(--ease-out);
}
.chips-toggle:hover { color: var(--primary); border-color: var(--primary); }
/* 气泡逐个飞入动画（stagger） */
@keyframes chipFlyIn {
  from { opacity: 0; transform: translateX(-8px); }
  to   { opacity: 1; transform: none; }
}
.chip-animate {
  animation: chipFlyIn 180ms cubic-bezier(0, 0, 0.2, 1) both;
}
.composer-chips {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--sp-2);
  overflow-x: auto;
  overflow-y: visible;
  overscroll-behavior-x: contain;
  touch-action: pan-x;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 2px;
  flex: 1 1 auto;
  min-width: 0;
  /* 收起时隐藏 */
  transition: opacity 160ms cubic-bezier(0, 0, 0.2, 1);
}
.composer-chips::-webkit-scrollbar { display: none; }
/* 收起状态：chips 不可见，不可交互 */
.composer-chips-wrap.is-collapsed .composer-chips {
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  width: 0;
  flex: 0 0 0;
  padding: 0;
  gap: 0;
}
.chip-toggle,
.chip-info {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 5px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-pill);
  background: var(--surface-1);
  color: var(--ink-muted);
  font-size: var(--fs-xxs);
  box-shadow: var(--shadow-chip);
}
.chip-toggle { cursor: pointer; }
.chip-toggle:hover { border-color: var(--primary); color: var(--primary); }
.chip-toggle.is-on { border-color: var(--primary); color: var(--primary); background: var(--primary-soft); }
.chip-toggle span,
.chip-info span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-icon { display: inline-flex; flex: 0 0 auto; }

.composer-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: var(--fs-xxs);
  color: var(--ink-subtle);
  flex-wrap: wrap;
}

/* 手机端:气泡区高度限制,超出时内部滚动,不挤占对话区 */
/* 手机端(含平板竖屏以下)。批次 4 把原来的 720 与 768 两块并成一块,
   同一选择器只出现一次 —— 两处写同一属性时后者覆盖前者,是隐蔽的坑。 */
@media (max-width: 767px) {
  .chat-head { padding: var(--sp-2) var(--sp-4); }
  .chat-body { padding: var(--sp-4) var(--sp-3); }
  .msg-body { max-width: 86%; }
  /* 键盘避让。

     .composer 不是 fixed 定位(它是 .chat 这个 flex 列的最后一项),
     所以设计稿里的 `bottom: calc(var(--kb-height) + env(...))` 不适用 ——
     用 padding-bottom 吃掉键盘高度:composer 自身变高, 内容(输入框与发送键)
     被顶到键盘上方, 同时 .chat-body 相应变矮, 正好等于键盘遮住的那部分。

     --kb-height 由 app.js 的 visualViewport 监听写入 :root, 无键盘时是 0px
     (tokens.css 有默认值), 所以这条在没有键盘时等价于原来的 var(--sp-3)。

     不改 .app-shell 的高度 —— 批次 4 契约第 13 条明确禁止用 JS 改高度,
     它已经是 100dvh, 两边同时算高度必然打架。 */
  .composer {
    padding: var(--sp-2) var(--sp-3) var(--sp-3);
    padding-bottom: calc(var(--sp-3) + var(--kb-height, 0px));
  }
  /* 输入框与发送键的高度。

     0.47.0 按设计稿 03 第 2 节的「44px 最小触控目标」设成了 44px,
     用户实测反馈太高:输入区占掉的高度会挤压上方的对话内容,
     而手机屏本来就矮。所以下调到 36px —— 这是权衡后的取值:

       输入框单行文字实际只占 27px(apple 的 --fs-body 17px x --lh-body 1.6),
       加 padding 8px 共 35px, 36px 刚好容纳且不留多余空白;
       36px 仍能稳定点中 —— iOS HIG 的 44pt 针对的是**独立小图标**,
       而输入框的可点区域横跨整行, 纵向 36px 足够;
       发送键跟着收到 36px 保持两者对齐(它在同一个 flex 行内)。

     高度账本(sp2=8 sp3=12 border=2):
       0.46.0  input 24 / send 38 -> box 56 -> composer 76px
       0.47.0  input 44 / send 44 -> box 62 -> composer 82px
       0.47.1  input 36 / send 36 -> box 54 -> composer 74px(比改造前还省 2px)

     min-height 而不是 height:textarea 要能随内容长高(JS 的 resize()
     会写 inline height), min-height 只抬下限, 不妨碍长高。 */
  .composer-input { min-height: 36px; }
  .composer-send { width: 36px; height: 36px; }
  /* chips max-height moved to flex layout */
}

/* ============================================================
   Markdown 渲染（markdown.js 生成的类名）
   ============================================================ */
.bubble .md-p { margin: 0 0 var(--sp-3); }
.bubble .md-p:last-child { margin-bottom: 0; }

.bubble .md-h { margin: var(--sp-3) 0 var(--sp-2); font-family: var(--font-display); }
.bubble .md-h:first-child { margin-top: 0; }
.bubble .md-h1 { font-size: var(--fs-h3); }
/* Markdown 标题走文章标题阶梯(--fs-h*),与界面标题分开 */
.bubble .md-h2 { font-size: var(--fs-h3); }
.bubble .md-h3 { font-size: var(--fs-body); }
.bubble .md-h4, .bubble .md-h5, .bubble .md-h6 { font-size: var(--fs-sm); color: var(--ink-muted); }

.bubble strong { color: var(--ink); font-weight: 600; }
.bubble del { color: var(--ink-subtle); }

.bubble .md-ul, .bubble .md-ol { margin: 0 0 var(--sp-3); padding-left: var(--sp-5); }
.bubble .md-ul:last-child, .bubble .md-ol:last-child { margin-bottom: 0; }
.bubble li { margin-bottom: var(--sp-1); }

.bubble .md-quote {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--primary);
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  color: var(--ink-muted);
}
.bubble .md-quote:last-child { margin-bottom: 0; }

.bubble .md-hr { margin: var(--sp-4) 0; height: 1px; border: 0; background: var(--hairline); }

.bubble .md-code {
  padding: 1px 5px;
  border-radius: var(--radius-xs);
  background: var(--surface-3);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
}

.bubble .md-pre {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3);
  border-radius: var(--radius);
  background: var(--surface-invert);
  overflow-x: auto;
  /* 长代码行横滑不带动整页。消息流里代码块很常见,
     滑到端点后带动整页会让人以为页面乱跳。 */
  overscroll-behavior-x: contain;
}
.bubble .md-pre:last-child { margin-bottom: 0; }
.bubble .md-pre code {
  background: none;
  padding: 0;
  color: var(--on-invert);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-body);
}

.bubble .md-link { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }

/* ============================================================
   角色扮演语义标注（rpFormat.js 生成的类名）
   ============================================================ */

/* 台词：主色 + 略加重，让对话在长段落里一眼可辨 */
.rp-speech { color: var(--rp-speech); font-weight: 500; }

/* 心里话 / 内心独白：淡化 + 斜体 */
.rp-inner { color: var(--rp-inner); font-style: italic; }

/* 动作 / 旁白：次级色斜体（由 Markdown 的 em 兼任） */
.rp-action { color: var(--rp-action); font-style: italic; }

/* 系统提示 / OOC */
.rp-system {
  display: inline-block;
  padding: 1px var(--sp-2);
  border-radius: var(--radius-xs);
  background: var(--rp-system-bg);
  color: var(--rp-system-fg);
  font-size: var(--fs-xs);
}

/* ---------------- 响应式 ---------------- */


/* 管理后台会话详情:复用普通消息气泡,但只读且在弹窗内独立滚动 */
.readonly-chat {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-height: min(65vh, 720px);
  overflow-y: auto;
  padding: var(--sp-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
}
.readonly-chat .msg { width: 100%; }
.readonly-chat .bubble { max-width: min(100%, 680px); }

/* 触屏(0.49.0,设计稿 03 第 5 节):常驻工具条隐藏,操作改走长按菜单。
   桌面端保持原样(hover 显隐 + is-fixed 常驻)。 */
@media (hover: none) and (pointer: coarse) {
  .msg-tools { display: none; }
}

/* 透明外层不再挤短消息区。末尾预留可滚动空间,保证最后一条可滚到控件上方。 */
.view-mount { min-height: 100%; height: 100%; }
.chat.chat-floating {
  position: relative;
  overflow: hidden;
  --composer-height: 108px;
  --composer-inset: 0px;
}
.chat-floating > .chat-body {
  padding-bottom: calc(var(--composer-height) + var(--composer-inset) + var(--sp-4));
  scroll-padding-bottom: calc(var(--composer-height) + var(--composer-inset) + var(--sp-4));
}
.chat.chat-floating > .composer {
  position: absolute;
  inset-inline: 0;
  bottom: var(--composer-inset);
  z-index: 4;
  border-top: 0;
  background: transparent;
  padding: var(--sp-2) max(var(--sp-3), env(safe-area-inset-right))
    max(var(--sp-2), env(safe-area-inset-bottom)) max(var(--sp-3), env(safe-area-inset-left));
  pointer-events: none;
}
.chat-floating .composer-inner,
.chat-floating .composer-chips-wrap,
.chat-floating .composer-chips { background: transparent; pointer-events: none; }
/* chips-wrap 行内 toggle 需要可交互 */
.chat-floating .composer-chips-wrap .chips-toggle { pointer-events: auto; }
.chat-floating .composer-box,
.chat-floating .composer button,
.chat-floating .composer .chip-info { pointer-events: auto; }
.chat-floating .composer-box,
.chat-floating .chips-toggle { background: var(--surface-1); }
.chat-floating .composer-chips { max-height: min(92px, 20vh); overflow-y: auto; pointer-events: auto; }
.model-switch { flex: 1 1 auto; min-width: 0; max-width: 100%; }
.model-switch .model-current { flex: 1 1 auto; min-width: 0; text-align: left; }
.model-switch .model-switch-label { flex: 0 0 auto; font-size: inherit; font-weight: 500; white-space: nowrap; }
/* model-switch 在横向 chips 行内固定宽度，不 grow */
.composer-chips .chip-toggle.model-switch {
  flex: 0 0 auto;
  max-width: 200px;
  min-width: 0;
}
.composer-chips .chip-toggle.model-switch .model-current {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 120px;
}
.composer-send.is-stop { background: var(--danger); color: var(--surface-1); }
.stop-mark { width: 13px; height: 13px; border-radius: 2px; background: currentColor; }
.generation-note { align-self: center; }
.model-picker .chip-row > button { max-width: 100%; white-space: normal; overflow-wrap: anywhere; text-align: left; }
@media (max-width: 479px) {
  /* 窄屏下模型气泡再收一点，确保后面的气泡还能横滑露出来 */
  .composer-chips .chip-toggle.model-switch { max-width: 160px; }
  .composer-chips .chip-toggle.model-switch .model-current { max-width: 88px; }
}
