/* ==================================================================
   app.css — 布局与组件
   结构：浮动顶栏（左：品牌 + 胶囊按钮组，右：设置） / 居中主体 / 底部输入框
   ================================================================== */

/* 顶栏、侧边栏、抽屉都是 position: fixed，不参与文档流。
   因此这里用纵向 flex 而不是 grid —— grid 会把 fixed 子元素之外的内容
   按顺序塞进 auto / 1fr 行，导致 main 落进 auto 行。 */
.app-shell {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* ================================================================== */
/* 浮动顶栏                                                            */
/* ================================================================== */

.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  padding-top: calc(14px + env(safe-area-inset-top));
  pointer-events: none;
}

.topbar > * {
  pointer-events: auto;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 6px;
}

.brand {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 11px;
  will-change: transform;
}

.brand:hover {
  text-decoration: none;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  color: #fff;
  background: var(--gradient);
  border-radius: 10px;
  box-shadow: 0 4px 14px rgba(17, 24, 39, 0.26);
  will-change: transform;
}

.brand-mark .icon {
  width: 19px;
  height: 19px;
}

/* 胶囊按钮组 */
.control-cluster {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  box-shadow: var(--shadow-sm);
}

.cluster-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  color: var(--text-2);
  border-radius: 50%;
  overflow: hidden;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.cluster-btn:hover {
  color: var(--text);
  background: var(--surface-3);
}

.cluster-btn.is-active {
  color: var(--accent);
  background: var(--accent-soft);
}

.cluster-btn .icon {
  width: 17px;
  height: 17px;
}

.cluster-btn-lg {
  width: 38px;
  height: 38px;
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: saturate(1.6) blur(12px);
  -webkit-backdrop-filter: saturate(1.6) blur(12px);
  border: 1px solid var(--border);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
}

.cluster-btn-lg:hover {
  border-color: var(--border-strong);
  background: #fff;
}

.cluster-btn-lg .icon {
  width: 18px;
  height: 18px;
}

.cluster-divider {
  width: 1px;
  height: 16px;
  background: var(--border);
  flex: none;
}

/* ================================================================== */
/* 侧边栏                                                              */
/* ================================================================== */

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  width: var(--sidebar-w);
  padding-top: env(safe-area-inset-top);
  background: var(--surface);
  border-right: 1px solid var(--border);
  border-radius: 0 var(--r-xl) var(--r-xl) 0;
  box-shadow: var(--shadow-lg);
  transform: translate3d(-102%, 0, 0);
  will-change: transform;
  visibility: hidden;
}

.sidebar[data-open='true'] {
  visibility: visible;
}

.sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 62px;
  padding: 0 10px 0 20px;
  border-bottom: 1px solid var(--border);
}

.sidebar-title {
  font-size: var(--fs-sm);
  font-weight: 640;
  letter-spacing: -0.01em;
  color: var(--text-2);
}

.session-list {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.session-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 10px;
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.session-item:hover {
  background: var(--surface-2);
}

.session-item.is-active {
  background: var(--accent-soft);
}

.session-item-icon {
  display: grid;
  place-items: center;
  flex: none;
  color: var(--text-3);
}

.session-item.is-active .session-item-icon {
  color: var(--accent);
}

.session-item-text {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-sm);
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-item.is-active .session-item-text {
  color: var(--accent);
  font-weight: 550;
}

.session-item-time {
  font-size: 10px;
  color: var(--text-3);
  flex: none;
}

.session-del {
  flex: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.session-item:hover .session-del,
.session-del:focus-visible {
  opacity: 1;
}

.sidebar-empty {
  padding: 28px 16px;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-3);
  line-height: 1.8;
}

.sidebar-foot {
  padding: 12px;
  border-top: 1px solid var(--border);
}

.identity-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  cursor: default;
}

.identity-icon {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-xs);
}

.identity-icon .icon {
  width: 15px;
  height: 15px;
}

.identity-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.5;
  color: var(--text-2);
}

.identity-text small {
  color: var(--text-3);
  font-size: 10px;
}

.scrim {
  position: fixed;
  inset: 0;
  z-index: 35;
  background: rgba(15, 23, 42, 0.24);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  will-change: opacity;
}

/* ================================================================== */
/* 主体                                                                */
/* ================================================================== */

.main {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
}

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex: 1 1 0;
  min-height: 0;
  padding: 72px 24px 24px;
  text-align: center;
  will-change: transform, opacity;
}

.hero[hidden] {
  display: none;
}

.hero-badge {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 2px;
  color: #fff;
  background: var(--gradient);
  border-radius: 14px;
  box-shadow: 0 8px 22px rgba(17, 24, 39, 0.24);
  will-change: transform;
}

.hero-badge .icon {
  width: 24px;
  height: 24px;
}

.hero-title {
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  font-weight: 660;
  letter-spacing: -0.025em;
  line-height: 1.3;
  color: var(--text);
}

.hero-sub {
  max-width: 480px;
  font-size: var(--fs-base);
  color: var(--text-3);
}

.hero-sub code {
  padding: 2px 6px;
  font-size: 0.86em;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-xs);
}

.suggestions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 10px;
  width: 100%;
  max-width: 760px;
  margin-top: 18px;
}

.suggestion {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 15px;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  transition:
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    background-color var(--dur-base) var(--ease-out);
  will-change: transform;
}

.suggestion:hover {
  border-color: var(--border-focus);
  background: var(--bg-tint);
  box-shadow: var(--shadow-md);
}

.suggestion-icon {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
}

.suggestion-icon .icon {
  width: 15px;
  height: 15px;
}

.suggestion-text {
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--text-2);
}

/* transcript -------------------------------------------------------- */

.transcript {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /*
   * 顶部不留 padding，首条消息的避让交给 .transcript-inner。
   *
   * 原因：sticky 的约束矩形是滚动容器的**内容盒**。只要这里有 padding-top，
   * 内容盒顶就在 y=72，`top: 0` 也只能停在 y=72 —— 而那 72px 仍属于滚动区，
   * 代码会从吸顶栏上方滚过去，看起来就像顶栏没置顶。
   */
  padding: 0 24px 26px;
  outline: none;
  scroll-padding-top: 72px;
  /* 吸顶元素停在页面最顶部 */
  --sticky-top: 0px;
}

.transcript[hidden] {
  display: none;
}

.transcript-inner {
  max-width: var(--transcript-max);
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 26px;
  /* 首条消息避开浮动顶栏。放在这里而不是 .transcript 的 padding 上，
     否则会把 sticky 的约束矩形一起推下去。 */
  padding-top: 72px;
}

.message {
  display: flex;
  gap: 13px;
  will-change: transform, opacity;
}

.message-avatar {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  margin-top: 2px;
  border-radius: var(--r-sm);
}

.message-avatar .icon {
  width: 17px;
  height: 17px;
}

.message-user .message-avatar {
  color: var(--text-2);
  background: var(--surface-3);
  border: 1px solid var(--border);
}

.message-assistant .message-avatar {
  color: #fff;
  background: var(--gradient);
  box-shadow: 0 3px 10px rgba(17, 24, 39, 0.22);
}

.message-body {
  flex: 1 1 auto;
  min-width: 0;
}

/* 用户消息：靠右的独立气泡，头像在右侧（与助手的左侧布局对称） */
.message-user {
  flex-direction: row-reverse;
  justify-content: flex-start;
}

.message-user .message-body {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 1 auto;
  max-width: min(78%, 620px);
}

.message-user .message-head {
  flex-direction: row-reverse;
}

.message-user .message-content {
  display: inline-block;
  max-width: 100%;
  padding: 11px 16px;
  font-size: var(--fs-base);
  line-height: 1.7;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  text-align: left;
  background: var(--accent-soft);
  border: 1px solid #e5e7eb;
  border-radius: 16px 4px 16px 16px;
}

.message-user .message-actions {
  justify-content: flex-end;
}

.message-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  font-size: var(--fs-xs);
  color: var(--text-3);
}

.message-role {
  font-weight: 620;
  color: var(--text-2);
  letter-spacing: 0.01em;
}

.message-meta {
  font-family: var(--font-mono);
  font-size: 10.5px;
}

.message-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: 10px;
  /* 默认整行隐藏（含时间戳），鼠标移到消息上才浮现 */
  opacity: 0;
  transform: translateY(-3px);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}

.message:hover .message-actions,
.message:focus-within .message-actions,
.message.is-streaming .message-actions {
  opacity: 1;
  transform: translateY(0);
}

/* 消息结束时间：位于复制 / 重新生成右侧 */
.message-time {
  margin-left: 8px;
  padding: 2px 8px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  line-height: 1.6;
  color: var(--text-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  white-space: nowrap;
  cursor: default;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.message-time:hover {
  color: var(--text-2);
  border-color: var(--border-strong);
}

.message-actions .icon-btn {
  width: 30px;
  height: 30px;
}

.message-actions .icon-btn .icon {
  width: 15px;
  height: 15px;
}

/* reasoning --------------------------------------------------------- */

.reasoning {
  margin-bottom: 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  /* 同 .md-code：clip 而不是 hidden，否则里面的 sticky 失效 */
  overflow: clip;
  overflow-clip-margin: 0;
}

/* 展开时「思考过程」这一行吸顶，滚动浏览长思考时始终能收起/展开 */
.reasoning[data-open='true'] .reasoning-toggle {
  position: sticky;
  top: var(--sticky-top);
  z-index: 3;
  background: var(--surface);
  border-radius: calc(var(--r-md) - 1px) calc(var(--r-md) - 1px) 0 0;
}

.reasoning-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  font-size: var(--fs-xs);
  font-weight: 550;
  color: var(--text-3);
  will-change: transform;
}

.reasoning-toggle:hover {
  color: var(--text-2);
  background: var(--surface-3);
}

.reasoning-toggle .icon-caret {
  width: 13px;
  height: 13px;
  /* 用轻微过冲而不是 --ease-back（1.56 的过冲对 90° 旋转会转过头） */
  transition: transform var(--dur-base) cubic-bezier(0.34, 1.18, 0.64, 1);
}

.reasoning[data-open='false'] .reasoning-toggle .icon-caret {
  transform: rotate(-90deg);
}

.reasoning-body {
  display: none;
  padding: 2px 13px 11px;
  font-size: var(--fs-sm);
  line-height: 1.72;
  color: var(--text-2);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* 虚拟滚动需要自己算高度：限高并自己滚动，而不是让它把整段撑开 */
  max-height: 62vh;
  overflow-y: auto;
  /*
   * 这里**不能**用 overscroll-behavior: contain。
   *
   * 它会让思考块内部滚到底之后不再把滚轮事件交给外层 —— 用户把鼠标停在
   * 展开的长思考上往下滚，滚到思考块底部就整个卡住，看起来就是
   * 「滚动到一半停了」。默认的链式滚动才是这里想要的行为。
   */
  overscroll-behavior: auto;
}

.reasoning-chars {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
}

/* 思考的 token 数：字符数右侧。估算值用「≈」标明，不冒充精确值 */
.reasoning-tokens {
  margin-left: 8px;
  padding: 1px 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  color: #111827;
  background: #f3f4f6;
  border-radius: var(--r-full);
}

.reasoning-tokens[hidden] {
  display: none;
}

.reasoning-tokens[data-exact='0'] {
  color: var(--text-3);
  background: var(--surface-3);
}

/* 虚拟文本：只有视口附近的一小段真实存在，其余靠上下占位撑高度 */
.vtext {
  display: flow-root;
}

.vtext-pad {
  width: 1px;
  pointer-events: none;
}

.vtext-content {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.reasoning[data-open='true'] .reasoning-body {
  display: block;
}

/* 展开时给个明显的容器：思考阶段正文还没出现，它得撑住整个视线 */
.reasoning[data-open='true'] {
  background: #faf9ff;
  border-color: #e5e7eb;
}

.reasoning[data-open='true'] .reasoning-toggle {
  color: #111827;
}

.reasoning[data-open='true'] .reasoning-body {
  border-top-color: #f3f4f6;
}

/* tool call --------------------------------------------------------- */

.tool-call {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 10px 0;
  padding: 10px 13px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
  will-change: transform, opacity;
}

.tool-call[data-state='running'] {
  border-color: #d1d5db;
  background: linear-gradient(90deg, var(--accent-soft), var(--surface) 55%);
}

.tool-call[data-state='ok'] {
  border-color: #a7f3d0;
}

.tool-call[data-state='error'] {
  border-color: #fecaca;
  background: var(--danger-soft);
}

/*
 * 卡片挂上「大块内容」（组件 iframe / 文件正文）之后要换行。
 *
 * .tool-call 本身是横向 flex，iframe 作为第 4 个子元素会被排在同一行的
 * 下一列 —— 于是标签挤在左半边、组件占右半边，中间空出一大片，
 * 标签还被 align-items:center 垂直居中。让内容独占一整行即可，DOM 不用动。
 */
.tool-call[data-expanded='1'] {
  flex-wrap: wrap;
}

.tool-call[data-expanded='1'] > .widget,
.tool-call[data-expanded='1'] > .tool-file-view {
  flex: 0 0 100%;
  width: 100%;
  min-width: 0;
}

.tool-icon {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
}

.tool-call[data-state='ok'] .tool-icon {
  color: #047857;
  background: var(--ok-soft);
}

.tool-call[data-state='error'] .tool-icon {
  color: #b91c1c;
  background: #fee2e2;
}

.tool-icon .icon {
  width: 16px;
  height: 16px;
}

.tool-info {
  flex: 1;
  min-width: 0;
}

.tool-name {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}

.tool-detail {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tool-time {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 34px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
}

.tool-spinner {
  width: 14px;
  height: 14px;
  border: 2px solid rgba(55, 65, 81, 0.22);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.stream-caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background: var(--accent);
  border-radius: 1px;
  opacity: 0;
  will-change: opacity;
}

.message.is-streaming .stream-caret {
  opacity: 1;
}

.message-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
}

.foot-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
}

/* 压缩标记 */
.compress-marker {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 4px 0 4px 43px;
  padding: 7px 13px;
  font-size: var(--fs-xs);
  color: var(--text-3);
  background: var(--surface-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-md);
  will-change: transform, opacity;
}

.compress-marker .icon {
  width: 14px;
  height: 14px;
  flex: none;
  color: var(--accent);
}

.compress-marker strong {
  color: var(--text-2);
  font-weight: 600;
}

.compress-marker button {
  margin-left: auto;
  font-size: 11px;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* notices ----------------------------------------------------------- */

.notice {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 10px 0;
  padding: 11px 14px;
  font-size: var(--fs-sm);
  line-height: 1.6;
  border-radius: var(--r-md);
  will-change: transform, opacity;
}

.notice-icon {
  flex: none;
  margin-top: 1px;
}

.notice-icon .icon {
  width: 16px;
  height: 16px;
}

.notice-error {
  color: #991b1b;
  background: var(--danger-soft);
  border: 1px solid #fecaca;
}

.notice-warn {
  color: #92400e;
  background: var(--warn-soft);
  border: 1px solid #fde68a;
}

.notice-body {
  flex: 1;
  min-width: 0;
}

.notice-retry {
  margin-top: 7px;
}

/* scroll to bottom -------------------------------------------------- */

.scroll-down {
  position: absolute;
  right: max(24px, calc(50% - var(--transcript-max) / 2 + 24px));
  bottom: 20px;
  z-index: 12;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 50%;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  will-change: transform, opacity;
}

.scroll-down:hover {
  color: var(--text);
  border-color: var(--border-focus);
}

.scroll-down[hidden] {
  display: none;
}

/* ================================================================== */
/* 输入区                                                              */
/* ================================================================== */

.composer-wrap {
  flex: none;
  padding: 0 24px calc(14px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.92) 34%, #fff 100%);
}

.composer {
  position: relative;
  max-width: var(--composer-max);
  margin: 0 auto;
}

/* 聚焦时用「白色内芯 + 渐变描边」实现渐变边框。
   这里不能用 ::before + z-index:-1 —— composer-box 上的 will-change: transform
   会创建层叠上下文，负 z-index 的伪元素会盖在白色背景之上而不是之下。 */
.composer-box {
  position: relative;
  padding: 14px 12px 10px 16px;
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    linear-gradient(var(--border), var(--border)) border-box;
  border: 1px solid transparent;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-md);
  transition:
    background var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
  will-change: transform;
}

.composer-box:focus-within {
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--gradient) border-box;
  box-shadow: var(--shadow-xl);
}

.composer-box.is-dragging {
  background:
    linear-gradient(var(--surface), var(--surface)) padding-box,
    var(--gradient) border-box;
  box-shadow: 0 0 0 4px var(--accent-ring), var(--shadow-lg);
}

.composer-input-row {
  display: flex;
  align-items: flex-start;
}

.composer-input {
  flex: 1;
  min-width: 0;
  min-height: 26px;
  max-height: 220px;
  padding: 2px 4px;
  font-size: var(--fs-md);
  line-height: 1.65;
  color: var(--text);
  resize: none;
  overflow-y: auto;
  scrollbar-width: thin;
}

.composer-input::placeholder {
  color: #a6abbb;
}

.composer-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
}

.composer-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  flex-wrap: wrap;
}

.composer-toolbar:empty {
  display: none;
}

.composer-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

.toolbar-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px 6px 9px;
  font-size: var(--fs-xs);
  font-weight: 550;
  color: var(--text-3);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  overflow: hidden;
  white-space: nowrap;
  transition:
    color var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.toolbar-chip:hover {
  color: var(--text-2);
  background: var(--surface-2);
}

.toolbar-chip .icon {
  width: 14px;
  height: 14px;
}

.toolbar-chip.is-on {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: #d1d5db;
}

.toolbar-chip.is-model {
  color: var(--text-2);
  background: var(--surface-2);
  font-weight: 600;
}

.toolbar-chip .chip-label {
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.attach-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  color: var(--text-3);
  border-radius: 50%;
  overflow: hidden;
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.attach-btn:hover {
  color: var(--text);
  background: var(--surface-2);
}

.attach-btn.is-hot {
  color: var(--warn);
  background: var(--warn-soft);
}

.attach-btn.is-full {
  color: var(--danger);
  background: var(--danger-soft);
}

.attach-btn .icon {
  width: 17px;
  height: 17px;
}

.attach-btn .ctx-badge {
  position: absolute;
  right: -1px;
  bottom: -1px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
  color: #fff;
  background: var(--accent);
  border: 2px solid #fff;
  border-radius: 8px;
  z-index: 1;
}

.send-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  flex: none;
  color: #fff;
  background: linear-gradient(140deg, #374151, #111827 55%, #6b7280);
  border-radius: 50%;
  box-shadow: 0 6px 18px rgba(17, 24, 39, 0.3);
  will-change: transform;
  overflow: hidden;
  transition: box-shadow var(--dur-base) var(--ease-out), background var(--dur-base) var(--ease-out);
}

.send-btn:hover:not([disabled]) {
  box-shadow: 0 9px 26px rgba(17, 24, 39, 0.4);
}

.send-btn[disabled] {
  color: #c3c7d3;
  background: var(--surface-3);
  box-shadow: none;
}

.send-btn .icon {
  width: 18px;
  height: 18px;
  position: relative;
  z-index: 1;
}

.send-btn .icon-stop {
  position: absolute;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-back);
}

.send-btn[data-mode='stop'] .icon-send {
  opacity: 0;
  transform: scale(0.5);
}

.send-btn[data-mode='stop'] .icon-stop {
  opacity: 1;
  transform: scale(1);
}

.send-btn[data-mode='stop'] {
  background: linear-gradient(140deg, #fb7185, #ef4444);
  box-shadow: 0 6px 18px rgba(239, 68, 68, 0.3);
}

.disclaimer {
  margin-top: 9px;
  font-size: 11px;
  color: var(--text-3);
  text-align: center;
}

.disclaimer.is-ok {
  color: #047857;
}

.disclaimer.is-error {
  color: #b91c1c;
}

/* ================================================================== */
/* 上下文用量条 / 面板                                                 */
/* ================================================================== */

.ctx-meter {
  margin: -2px 0 10px -6px;
}

.ctx-meter[hidden] {
  display: none;
}

.ctx-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 3px 6px;
  border-radius: var(--r-full);
  will-change: transform;
}

.ctx-bar:hover {
  background: var(--surface-2);
}

.ctx-track {
  position: relative;
  flex: 1;
  height: 4px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  overflow: hidden;
}

.ctx-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--gradient);
  border-radius: var(--r-full);
  transition: width var(--dur-slow) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}

.ctx-meter[data-state='warn'] .ctx-fill {
  background: linear-gradient(90deg, #f59e0b, #f97316);
}

.ctx-meter[data-state='critical'] .ctx-fill {
  background: linear-gradient(90deg, #ef4444, #f43f5e);
}

.ctx-label {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
  white-space: nowrap;
}

.ctx-chevron {
  width: 12px;
  height: 12px;
  color: var(--text-3);
  transition: transform var(--dur-base) var(--ease-back);
}

.ctx-bar[aria-expanded='true'] .ctx-chevron {
  transform: rotate(180deg);
}

.ctx-popover {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 8px);
  height: 0;
  overflow: hidden;
  opacity: 0;
  will-change: height, opacity;
  pointer-events: none;
}

.ctx-popover[data-open='true'] {
  pointer-events: auto;
}

.ctx-panel-body {
  padding: 16px;
  max-height: 58vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xl);
}

.ctx-overview {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 14px;
}

.ctx-ring-wrap {
  position: relative;
  display: grid;
  place-items: center;
  width: 66px;
  height: 66px;
  flex: none;
}

.ctx-ring {
  position: absolute;
  inset: 0;
  width: 66px;
  height: 66px;
  transform: rotate(-90deg);
}

.ctx-ring-track {
  fill: none;
  stroke: var(--surface-3);
  stroke-width: 3;
}

.ctx-ring-fill {
  fill: none;
  stroke: var(--accent);
  stroke-width: 3;
  stroke-linecap: round;
  will-change: stroke-dashoffset;
}

.ctx-ring-percent {
  position: relative;
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-2);
}

.ctx-facts {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 0;
}

.ctx-fact {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  font-size: 11.5px;
}

.ctx-fact-label {
  color: var(--text-3);
  flex: none;
}

.ctx-fact-value {
  font-family: var(--font-mono);
  color: var(--text-2);
  text-align: right;
  overflow-wrap: anywhere;
}

.ctx-control {
  padding: 4px 0 14px;
  border-bottom: 1px solid var(--border);
}

.ctx-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.ctx-actions .ghost-btn {
  flex: 1;
  justify-content: center;
}

.ctx-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  max-height: 168px;
  overflow-y: auto;
  padding: 12px 2px 2px;
}

.ctx-row {
  display: flex;
  align-items: center;
  gap: 10px;
  will-change: transform;
}

.ctx-role {
  width: 34px;
  flex: none;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-3);
  text-align: right;
}

.ctx-row.is-compressed .ctx-role {
  color: var(--accent);
}

.ctx-row-bar {
  flex: 1;
  height: 5px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  overflow: hidden;
}

.ctx-row-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  opacity: 0.42;
  border-radius: var(--r-full);
  transition: width var(--dur-slow) var(--ease-out);
}

.ctx-row.is-compressed .ctx-row-fill {
  background: var(--gradient);
  opacity: 0.9;
}

.ctx-tokens {
  width: 44px;
  flex: none;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--text-3);
  text-align: right;
}

.ctx-empty {
  padding: 16px 0;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-3);
}

/* ================================================================== */
/* 附件托盘 / 拖放提示                                                 */
/* ================================================================== */

.attachment-tray {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: -2px 0 10px;
}

.attachment-tray[hidden] {
  display: none;
}

.attachment {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 220px;
  padding: 5px 8px 5px 6px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  will-change: transform, opacity;
}

.attachment-image {
  padding: 3px;
  padding-right: 8px;
}

.attachment-image img {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: #fff;
}

.attachment-dims {
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: var(--text-3);
  line-height: 1.35;
  max-width: 116px;
  overflow-wrap: anywhere;
}

.attachment-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
}

.attachment-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.attachment-text strong {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attachment-text small {
  font-family: var(--font-mono);
  font-size: 9.5px;
  color: var(--text-3);
}

.attachment-remove {
  position: absolute;
  top: -6px;
  right: -6px;
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  color: var(--text-inverse);
  background: var(--text-3);
  border: 2px solid #fff;
  border-radius: 50%;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-back);
  will-change: transform;
}

.attachment:hover .attachment-remove,
.attachment-remove:focus-visible {
  opacity: 1;
}

.attachment-remove:hover {
  background: var(--danger);
  transform: scale(1.15);
}

/* 拖放高亮由 .composer-box.is-dragging 的渐变描边负责 */
.drop-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 8px;
  padding: 9px 14px;
  font-size: var(--fs-sm);
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px dashed #d1d5db;
  border-radius: var(--r-md);
}

.drop-hint[hidden] {
  display: none;
}

.drop-hint-icon {
  display: grid;
  place-items: center;
}

.drop-hint-icon .icon {
  width: 16px;
  height: 16px;
}

/* 消息里的附件回显 */
.message-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 9px;
}

.message-attachments img {
  max-width: 240px;
  max-height: 200px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
  background: #fff;
}

.message-file-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  font-size: 11.5px;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
}

.message-file-chip .icon {
  width: 13px;
  height: 13px;
  color: var(--accent);
}

/* ================================================================== */
/* 供应商面板                                                          */
/* ================================================================== */

.settings-input {
  width: 100%;
  padding: 9px 12px;
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}

.settings-input:focus {
  background: var(--surface);
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.settings-input::placeholder {
  color: var(--text-3);
}

.settings-input:disabled {
  color: var(--text-3);
  cursor: not-allowed;
}

.link-btn {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}


/* 诊断结果 ---------------------------------------------------------- */

.diag-result {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 12px;
  padding: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.diag-result[hidden] {
  display: none;
}

.diag-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: 11.5px;
  line-height: 1.55;
}

.diag-row-label {
  width: 92px;
  flex: none;
  color: var(--text-3);
}

.diag-row-value {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  color: var(--text-2);
  overflow-wrap: anywhere;
}

.diag-row.is-ok .diag-row-value {
  color: #047857;
}

.diag-row.is-error .diag-row-value {
  color: #b91c1c;
}

.diag-row.is-warn .diag-row-value {
  color: #92400e;
}

.diag-row.is-pending .diag-row-value {
  color: var(--accent);
}

/* ================================================================== */
/* markdown                                                            */
/* ================================================================== */

.md {
  font-size: var(--fs-base);
  line-height: 1.75;
  overflow-wrap: anywhere;
}

.md > *:first-child {
  margin-top: 0 !important;
}

.md > *:last-child {
  margin-bottom: 0 !important;
}

.md-paragraph {
  margin: 0.62em 0;
}

.md-heading {
  margin: 1.5em 0 0.5em;
  font-weight: 650;
  line-height: 1.35;
  letter-spacing: -0.015em;
  scroll-margin-top: 20px;
}

h1.md-heading { font-size: 1.42em; }
h2.md-heading { font-size: 1.26em; padding-bottom: 0.3em; border-bottom: 1px solid var(--border); }
h3.md-heading { font-size: 1.12em; }
h4.md-heading,
h5.md-heading,
h6.md-heading { font-size: 1em; color: var(--text-2); }

.md-list {
  margin: 0.6em 0;
  padding-left: 1.35em;
}

ul.md-list {
  list-style: none;
}

ul.md-list > li {
  position: relative;
  margin: 0.3em 0;
}

ul.md-list > li::before {
  content: '';
  position: absolute;
  left: -0.95em;
  top: 0.72em;
  width: 4.5px;
  height: 4.5px;
  background: var(--text-3);
  border-radius: 50%;
  transition: transform var(--dur-base) var(--ease-back), background-color var(--dur-base) var(--ease-out);
}

ul.md-list > li:hover::before {
  background: var(--accent);
  transform: scale(1.35);
}

ol.md-list {
  list-style: decimal;
}

ol.md-list > li {
  margin: 0.3em 0;
  padding-left: 0.2em;
}

ol.md-list > li::marker {
  color: var(--accent);
  font-weight: 600;
  font-size: 0.92em;
}

.md-list .md-list {
  margin: 0.2em 0;
}

.md-list-item.is-task {
  display: flex;
  align-items: flex-start;
  gap: 0.55em;
  padding-left: 0;
}

.md-list-item.is-task::before {
  display: none;
}

.task-box {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 0.28em;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
  border-radius: 5px;
  transition: background-color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}

.md-list-item.is-checked .task-box {
  background: var(--accent);
  border-color: var(--accent);
}

.md-list-item.is-checked .md-list-content {
  color: var(--text-3);
  text-decoration: line-through;
  text-decoration-color: var(--border-strong);
}

.md-quote {
  margin: 0.75em 0;
  padding: 0.15em 0 0.15em 1.05em;
  color: var(--text-2);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  background: linear-gradient(90deg, var(--accent-soft), transparent 55%);
}

.md-quote > *:first-child {
  margin-top: 0;
}

.md-hr {
  margin: 1.5em 0;
}

.md-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: rgba(17, 24, 39, 0.32);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 2.5px;
  transition: text-decoration-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.md-link:hover {
  color: var(--accent-hover);
  text-decoration-color: currentColor;
}

.md-link.is-autolink {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

.md-link.is-blocked {
  color: var(--text-3);
  text-decoration: line-through;
  cursor: not-allowed;
}

.md-inline-code {
  padding: 0.14em 0.4em;
  font-size: 0.87em;
  color: #b4236b;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  overflow-wrap: anywhere;
}

.md strong {
  font-weight: 650;
  color: var(--text);
}

.md del {
  color: var(--text-3);
}

.md mark {
  padding: 0.06em 0.26em;
  color: inherit;
  background: linear-gradient(180deg, transparent 56%, rgba(250, 204, 21, 0.38) 56%);
  border-radius: 3px;
}

.md-image {
  display: inline-block;
  max-width: 100%;
}

.md-image img {
  max-width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-sm);
}

.md-image.is-broken img {
  display: none;
}

.md-image.is-broken::after {
  content: 'image unavailable';
  padding: 6px 10px;
  font-size: var(--fs-xs);
  color: var(--text-3);
  background: var(--surface-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm);
}

.md-code {
  margin: 0.85em 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  /* 用 clip 而不是 hidden。
     hidden 会让 .md-code 成为滚动容器，里面的 sticky 就只相对它定位，
     而它自己并不滚动，于是吸顶完全失效。clip 一样能裁掉圆角外的内容，
     但不建立滚动容器 —— sticky 相对真正的滚动容器（.transcript）生效，
     同时仍被限制在 .md-code 的盒子里，正好是「脱离代码块就松手」。 */
  overflow: clip;
  overflow-clip-margin: 0;
  box-shadow: var(--shadow-sm);
}

.code-bar {
  /* 语言与复制按钮一直悬在代码块顶部，直到整个代码块滚出视野 */
  position: sticky;
  top: var(--sticky-top);
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 8px 6px 14px;
  /* 吸顶后必须是不透明背景，否则代码会从下面透上来 */
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
}

.code-lang {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* 代码块右上角的操作组：预览 / Copy / 下载 */
.code-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}

.code-preview.is-active {
  color: var(--accent);
  border-color: var(--border-focus);
  background: var(--accent-soft);
}

/* 未写完的代码块：按钮置灰不可用 ------------------------------- */

/*
 * 流式输出时代码块还没长完，此时复制/下载/预览拿到的都是半截内容。
 * message.js 会给「正在写的最后一块」打上 data-incomplete。
 */
.md-code[data-incomplete] .code-copy {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

.md-code[data-incomplete] .code-bar::after {
  content: '写入中…';
  margin-left: auto;
  padding-right: 8px;
  font-size: 10.5px;
  color: var(--text-3);
}

.code-copy {
  position: relative;
  padding: 3px 9px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  overflow: hidden;
  transition: all var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.code-copy:hover {
  color: var(--accent);
  border-color: var(--border-focus);
  background: var(--accent-soft);
}

.code-copy.is-copied {
  color: #047857;
  background: var(--ok-soft);
  border-color: #a7f3d0;
}

.md-code pre {
  margin: 0;
  padding: 14px 16px;
  /* 只允许横向滚动。这里必须显式写 overflow-y: hidden ——
     按规范，一个轴设成非 visible 时，另一个 visible 的轴会被计算成 auto，
     于是 pre 会变成纵向滚动容器并把滚轮吞掉，长代码块里的鼠标滚轮
     就再也滚不动外层会话了。 */
  overflow-x: auto;
  overflow-y: hidden;
  background: var(--surface);
}

.md-code code {
  font-family: var(--font-mono);
  font-size: 12.8px;
  line-height: 1.72;
  color: var(--text);
  tab-size: 2;
  white-space: pre;
}

.tok-k { color: #7c3aed; font-weight: 600; }
.tok-b { color: #0891b2; font-weight: 500; }
.tok-s { color: #0f766e; }
.tok-n { color: #c2410c; }
.tok-c { color: #94a3b8; font-style: italic; }
.tok-f { color: #2563eb; }
.tok-t { color: #db2777; }
.tok-a { color: #ca8a04; }
.tok-p { color: #64748b; }

.md-table-wrap {
  margin: 0.9em 0;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-xs);
}

.md-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92em;
}

.md-table th,
.md-table td {
  padding: 9px 13px;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

.md-table th {
  font-weight: 620;
  color: var(--text);
  background: var(--surface-2);
  white-space: nowrap;
}

.md-table tr:last-child td {
  border-bottom: none;
}

.md-table tbody tr {
  transition: background-color var(--dur-fast) var(--ease-out);
}

.md-table tbody tr:hover {
  background: var(--bg-tint);
}

.md-math-display {
  margin: 0.9em 0;
  padding: 14px 16px;
  overflow-x: auto;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

/* ================================================================== */
/* 设置抽屉                                                            */
/* ================================================================== */

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(15, 23, 42, 0.28);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  will-change: opacity;
}

.drawer {
  position: fixed;
  top: 12px;
  right: 12px;
  bottom: 12px;
  z-index: 61;
  display: flex;
  flex-direction: column;
  width: min(420px, calc(100vw - 24px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl);
  transform: translate3d(102%, 0, 0);
  will-change: transform;
  visibility: hidden;
  overflow: hidden;
}

.drawer[data-open='true'] {
  visibility: visible;
}

.drawer-grip {
  display: none;
}

.drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 58px;
  flex: none;
  padding: 0 14px 0 22px;
  border-bottom: 1px solid var(--border);
}

.drawer-head h2 {
  font-size: var(--fs-lg);
  font-weight: 650;
  letter-spacing: -0.015em;
}

.drawer-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 22px 26px;
}

.drawer-foot {
  flex: none;
  padding: 12px 22px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

.drawer-foot .ghost-btn {
  width: 100%;
  justify-content: center;
}

.settings-group {
  padding: 20px 0;
  border-bottom: 1px solid var(--border);
}

.settings-group:last-child {
  border-bottom: none;
}

.settings-heading {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  font-size: var(--fs-xs);
  font-weight: 650;
  letter-spacing: 0.075em;
  text-transform: uppercase;
  color: var(--text-3);
}

.settings-heading-icon {
  display: grid;
  place-items: center;
  color: var(--accent);
}

.settings-heading-icon .icon {
  width: 15px;
  height: 15px;
}

.settings-note {
  margin-bottom: 11px;
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--text-3);
}

.settings-textarea {
  width: 100%;
  padding: 11px 13px;
  font-size: var(--fs-sm);
  line-height: 1.65;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  resize: vertical;
  transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}

.settings-textarea:focus {
  background: var(--surface);
  border-color: var(--border-focus);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* field / slider ---------------------------------------------------- */

.field {
  margin-bottom: 14px;
}

.field-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 7px;
}

.field-label {
  font-size: var(--fs-sm);
  font-weight: 550;
  color: var(--text-2);
}

.field-value {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--accent);
  padding: 1px 7px;
  background: var(--accent-soft);
  border-radius: var(--r-xs);
}

.field-hint {
  margin-top: 5px;
  font-size: 11px;
  line-height: 1.55;
  color: var(--text-3);
}

/* 滑块：拖动手感相关的一整套修正
   - touch-action: none      防止在可滚动容器里拖动被滚动劫持
   - user-select: none       防止拖动时选中文字（浏览器会弹出「松开鼠标以搜索」）
   - 不给 thumb 加 transition  位移由布局驱动，加过渡只会让它看起来在打滑 */
.field input[type='range'] {
  width: 100%;
  height: 22px;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.field input[type='range']:active {
  cursor: grabbing;
}

.field input[type='range']::-webkit-slider-runnable-track {
  height: 5px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  border: 1px solid var(--border);
}

.field input[type='range']::-moz-range-track {
  height: 5px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  border: 1px solid var(--border);
}

.field input[type='range']::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6.5px;
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
}

.field input[type='range']::-moz-range-thumb {
  width: 14px;
  height: 14px;
  background: var(--surface);
  border: 2px solid var(--accent);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
}

/* 悬停/按下的放大只用 box-shadow 与 border，不动 transform ——
   transform 会让 thumb 的中心偏离轨道计算出来的位置。 */
.field input[type='range']:hover::-webkit-slider-thumb {
  border-color: var(--accent-hover);
  box-shadow: 0 0 0 4px var(--accent-ring);
}

.field input[type='range']:active::-webkit-slider-thumb {
  border-color: var(--accent-hover);
  box-shadow: 0 0 0 6px var(--accent-ring);
}

.field input[type='range']:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 5px var(--accent-ring);
}

/* 拖动期间整页禁止选中，作为最后一道保险 */
body.is-dragging-range,
body.is-dragging-range * {
  user-select: none !important;
  -webkit-user-select: none !important;
  cursor: grabbing !important;
}

.segmented {
  display: grid;
  grid-auto-flow: column;
  gap: 3px;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

/* 标签较长的分段项（如思考档位）改两列，否则四列里会被挤成两行 */
.segmented.is-stacked {
  grid-auto-flow: row;
  grid-template-columns: repeat(2, 1fr);
}

.segmented button {
  padding: 6px 4px;
  font-size: 11.5px;
  font-weight: 550;
  color: var(--text-3);
  border-radius: var(--r-sm);
  transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.segmented button:hover {
  color: var(--text-2);
}

.segmented button.is-active {
  color: var(--accent);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}

/* switch ------------------------------------------------------------ */

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 9px 0;
}

.switch-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.switch-copy strong {
  font-size: var(--fs-sm);
  font-weight: 550;
  color: var(--text);
}

.switch-copy span {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-3);
}

.switch {
  position: relative;
  flex: none;
  cursor: pointer;
}

.switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.switch-track {
  display: block;
  width: 42px;
  height: 24px;
  padding: 3px;
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-full);
  transition: background-color var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}

.switch-thumb {
  display: block;
  width: 16px;
  height: 16px;
  background: var(--surface);
  border-radius: 50%;
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-slow) var(--ease-back);
  will-change: transform;
}

.switch input:checked + .switch-track {
  background: var(--accent);
  border-color: var(--accent);
}

.switch input:checked + .switch-track .switch-thumb {
  transform: translateX(18px);
}

.switch input:focus-visible + .switch-track {
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* tool list --------------------------------------------------------- */

.tool-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 10px;
}

.tool-toggle {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 13px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: border-color var(--dur-fast) var(--ease-out), opacity var(--dur-base) var(--ease-out);
  will-change: transform;
}

.tool-toggle.is-on {
  background: var(--surface);
  border-color: #d1d5db;
}

.tool-toggle.is-off {
  opacity: 0.55;
}

.tool-toggle-icon {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: var(--r-sm);
}

.tool-toggle-icon .icon {
  width: 15px;
  height: 15px;
}

.tool-toggle-copy {
  flex: 1;
  min-width: 0;
}

.tool-toggle-copy strong {
  display: block;
  font-size: var(--fs-sm);
  font-weight: 600;
}

.tool-toggle-copy span {
  font-size: 11px;
  line-height: 1.5;
  color: var(--text-3);
}

/* identity card + diagnostics ---------------------------------------- */

.identity-card {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-bottom: 12px;
  padding: 13px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.identity-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 11.5px;
}

.identity-row dt {
  color: var(--text-3);
  flex: none;
}

.identity-row dd {
  font-family: var(--font-mono);
  color: var(--text-2);
  text-align: right;
  overflow-wrap: anywhere;
}

.diag {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 5px 14px;
  font-size: 11.5px;
}

.diag dt {
  color: var(--text-3);
}

.diag dd {
  font-family: var(--font-mono);
  color: var(--text-2);
  text-align: right;
  overflow-wrap: anywhere;
}

/* 等待状态行：请求发出到第一个字之间的唯一反馈 ---------------- */

.message-status {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  padding: 8px 12px;
  font-size: 12.5px;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  animation: status-in 240ms var(--ease-out);
}

.message-status[hidden] {
  display: none;
}

@keyframes status-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.status-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: status-pulse 1.15s var(--ease-in-out) infinite;
}

.message-status[data-tone='thinking'] .status-dot {
  background: #9ca3af;
}

.message-status[data-tone='tool'] .status-dot {
  background: #4b5563;
}

/*
 * 冷启动：上游实例空闲会缩容到零，第一个请求要等它拉起并载入模型。
 * 用暖色而不是错误色 —— 这是「等待」不是「坏了」。
 */
.message-status[data-tone='cold'] .status-dot {
  background: var(--warn);
}

@keyframes status-pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.4;
    transform: scale(0.72);
  }
}

.status-label {
  min-width: 0;
}

/* 截断后的续写入口 ------------------------------------------------ */

.continue-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
  padding: 7px 13px;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-soft);
  border: 1px solid #d1d5db;
  border-radius: var(--r-full);
  transition: background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.continue-btn:hover {
  background: #e5e7eb;
  border-color: var(--accent);
}

.continue-hint {
  padding-left: 7px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  color: var(--text-3);
  border-left: 1px solid #d1d5db;
}

/* 沙箱组件 ---------------------------------------------------------- */

.widget {
  margin: 8px 0 0;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface);
}

.widget-source-btn {
  flex: none;
  margin-left: auto;
  padding: 3px 9px;
  font-size: 10.5px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.widget-source-btn:hover,
.widget-source-btn.is-active {
  color: var(--accent);
  border-color: var(--border-focus);
}

.widget-source {
  margin: 0;
  padding: 12px 14px;
  max-height: 46vh;
  overflow: auto;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--text-2);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  white-space: pre-wrap;
  word-break: break-word;
}

.tool-detail.is-error {
  color: var(--danger);
}

.widget-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}

.widget-icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--accent);
}

.widget-title {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.widget-badge {
  flex: none;
  padding: 1px 7px;
  font-size: 10px;
  font-weight: 600;
  color: #111827;
  background: #f3f4f6;
  border-radius: var(--r-full);
  cursor: help;
}

/* 组件本身：沙箱 iframe。白底是为了不让宿主的暗色/纹理透进组件里，
   否则组件作者按默认白底设计的样式会显得脏。 */
.widget-frame {
  display: block;
  width: 100%;
  border: 0;
  background: #fff;
  color-scheme: light;
}

.widget-console {
  margin-top: 6px;
  border-top: 1px dashed var(--border-strong);
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--text-3);
}

.widget-console summary {
  padding: 4px 0;
  cursor: pointer;
}

.widget-console-body {
  max-height: 140px;
  overflow-y: auto;
  padding-bottom: 4px;
}

.widget-console-line {
  padding: 1px 0;
  overflow-wrap: anywhere;
}

/* 会话重命名输入框：直接顶替标题文本的位置 */
.session-rename {
  flex: 1;
  min-width: 0;
  padding: 2px 6px;
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-focus);
  border-radius: var(--r-xs);
  outline: none;
}

/* 自动生成的标题：给个微妙的标记，让用户知道这是模型起的、可以双击改 */
.session-item-text[data-generated='1']::after {
  content: '·AI';
  margin-left: 5px;
  font-size: 9.5px;
  color: var(--text-3);
  opacity: 0.75;
}

/* 预览面板：主区域向左避让，面板从右滑入 --------------------------- */

/*
 * 两边用同一条缓动与时长，读起来才是「一个动作」：
 *   .main / .composer-wrap 宽度 100% → 50%（右边缘向左扫过去）
 *   .preview-pane 从 translateX(100%) → 0（从右侧滑进来）
 * 关闭时整套反向。
 */
.app-shell {
  --preview-duration: 440ms;
  --preview-ease: cubic-bezier(0.22, 1, 0.36, 1);
}

.main,
.composer-wrap {
  width: 100%;
  transition: width var(--preview-duration) var(--preview-ease);
}

.app-shell.is-previewing .main,
.app-shell.is-previewing .composer-wrap {
  /* 让出右半边，并留出与浮起卡片相同的 12px 边距 */
  width: calc(50% - 12px);
}

.preview-pane {
  position: fixed;
  /* 与设置抽屉同一层级、同一形态：浮起的圆角卡片，而不是整边贴死的面板。
     两者视觉语言一致，切换时不会觉得是两个不同的东西。 */
  top: 12px;
  right: 12px;
  bottom: 12px;
  z-index: 61;
  display: flex;
  flex-direction: column;
  width: calc(50% - 20px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-xl);
  overflow: hidden;
  transform: translate3d(calc(100% + 20px), 0, 0);
  visibility: hidden;
  transition: transform var(--preview-duration) var(--preview-ease),
    visibility 0s linear var(--preview-duration);
}

.app-shell.is-previewing .preview-pane {
  transform: translate3d(0, 0, 0);
  visibility: visible;
  /* 打开时立刻可见，不等动画结束 */
  transition: transform var(--preview-duration) var(--preview-ease), visibility 0s;
}

.preview-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: none;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

.preview-icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--accent);
}

.preview-title {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-sm);
  font-weight: 620;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview-badge {
  flex: none;
  padding: 1px 7px;
  font-size: 10px;
  font-weight: 600;
  color: #111827;
  background: #f3f4f6;
  border-radius: var(--r-full);
  cursor: help;
}

.preview-action {
  flex: none;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}

.preview-action:hover {
  color: var(--accent);
  border-color: var(--border-focus);
}

.preview-body {
  flex: 1;
  min-height: 0;
  background: #fff;
}

/* 沙箱 iframe 填满面板，而不是靠 height:auto —— iframe 的 auto 高度
   会退到内核默认值，内容看起来就是一片空白 */
.preview-body .widget-frame {
  width: 100%;
  height: 100%;
  border: 0;
  background: #fff;
  color-scheme: light;
}

/* 窄屏放不下两栏：面板改为整屏覆盖，主区域不动 */
@media (max-width: 900px) {
  .preview-pane {
    width: 100%;
  }

  .app-shell.is-previewing .main,
  .app-shell.is-previewing .composer-wrap {
    width: 100%;
  }
}

/* ================================================================== */
/* 持久化文件面板                                                      */
/* ================================================================== */

.files-drawer {
  /* 复用设置抽屉的形态与层级：两者是同级的东西 */
  width: min(560px, calc(100vw - 24px));
  /* 自己带一段带一点回弹的滑入。这里用 CSS 而不是弹簧：
     面板是整块的布局位移，交给合成器更稳，也不会和设置抽屉的弹簧抢 inline 样式。 */
  transition: transform 420ms cubic-bezier(0.22, 1.15, 0.36, 1), visibility 0s linear 420ms;
}

/*
 * 设置抽屉的位移是由 JS 弹簧写 inline transform 驱动的，所以 .drawer 本身
 * 一直停在 translate3d(102%)。文件面板走的是类名切换，必须自己补上这个
 * 「打开」状态 —— 否则 dataset.open 已经是 true，面板却还在屏幕外。
 */
.files-drawer[data-open='true'] {
  transform: translate3d(0, 0, 0);
  visibility: visible;
  transition: transform 420ms cubic-bezier(0.22, 1.15, 0.36, 1), visibility 0s;
}

/* 标题、说明、空态一律居中 */
.files-drawer .drawer-head {
  position: relative;
  justify-content: center;
  gap: 0;
}

/*
 * 标题要**真正**居中，两个图标就不能留在 flex 流里 ——
 * 否则左边图标占的宽度会把标题往右推（实测偏 16px）。
 * 都改为绝对定位，标题独占整行再文字居中。
 */
.files-drawer .drawer-icon {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
}

.files-drawer .drawer-title {
  flex: 1;
  text-align: center;
}

.files-drawer .drawer-head .icon-btn {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
}

.files-hint {
  margin: 0 0 12px;
  padding: 0 6px;
  text-align: center;
  font-size: 11.5px;
  line-height: 1.65;
  color: var(--text-3);
}

/* 列表逐行浮现：给每行一点延迟，读起来像「依次出现」而不是整块闪出 */
@keyframes file-row-in {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.985);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.file-row {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  /* hidden 在这里是安全的：行高由内容撑开，只用于裁圆角 */
  overflow: hidden;
  animation: file-row-in 380ms cubic-bezier(0.22, 1.15, 0.36, 1) backwards;
  transition: border-color 180ms ease-out, box-shadow 180ms ease-out, transform 180ms ease-out;
}

.file-row:hover {
  border-color: var(--border-focus);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
  transform: translateY(-1px);
}

/* 第八行之后不再累加延迟，否则长列表要等很久才出现 */
.file-row:nth-child(1) { animation-delay: 20ms; }
.file-row:nth-child(2) { animation-delay: 55ms; }
.file-row:nth-child(3) { animation-delay: 90ms; }
.file-row:nth-child(4) { animation-delay: 125ms; }
.file-row:nth-child(5) { animation-delay: 160ms; }
.file-row:nth-child(6) { animation-delay: 195ms; }
.file-row:nth-child(7) { animation-delay: 230ms; }
.file-row:nth-child(n + 8) { animation-delay: 250ms; }

.files-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 2px 2px 8px;
}

.files-empty {
  padding: 32px 12px;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--text-3);
}

.files-empty.is-error {
  color: var(--danger);
}

.file-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 11px;
}

.file-icon {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  flex: none;
  color: var(--accent);
}

.file-path {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-meta {
  flex: none;
  font-size: 10.5px;
  color: var(--text-3);
}

.file-actions {
  display: flex;
  gap: 6px;
  padding: 0 9px 9px;
}

.file-btn {
  padding: 5px 10px;
  font-size: 11px;
}

.file-confirm-text {
  align-self: center;
  margin-right: auto;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--danger);
}

.file-btn.is-danger {
  color: var(--danger);
  border-color: #fecaca;
}

.file-btn.is-danger:hover {
  background: #fef2f2;
}

/* 组件自己截的图 ------------------------------------------------- */

.shot-card {
  margin: 8px 0 0;
  padding: 0 10px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface);
  animation: file-row-in 320ms cubic-bezier(0.22, 1.15, 0.36, 1);
}

.shot-image {
  display: block;
  /* 缩小展示：截图是拿来核对效果的，不是拿来看像素的，占满整屏反而
     把它下面真正有用的内容挤走了 */
  width: auto;
  max-width: min(100%, 420px);
  margin: 10px auto 0;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  max-height: 32vh;
  object-fit: contain;
  background: var(--surface-2);
  cursor: zoom-in;
}

.shot-image.is-zoomed {
  max-height: none;
  cursor: zoom-out;
}

.shot-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

.shot-label {
  flex: 1;
  min-width: 0;
  font-size: 11px;
  color: var(--text-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.shot-save {
  flex: none;
  padding: 3px 9px;
  font-size: 10.5px;
}

/* 对话里由 file_show 展开的文件内容 ------------------------------- */

.tool-file-view {
  margin-top: 8px;
  border-top: 1px solid var(--border);
  animation: file-content-in 260ms cubic-bezier(0.22, 1.15, 0.36, 1);
}

.tool-file-view[data-state='loading'],
.tool-file-view[data-state='error'] {
  padding: 10px 2px;
  font-size: 11.5px;
  color: var(--text-3);
}

.tool-file-view[data-state='error'] {
  color: var(--danger);
}

.tool-file-view .md-code {
  margin: 8px 0 0;
}

/* 对话里的展示要能一路看下去，同样解除 pre 的高度与纵向裁剪 */
.tool-file-view .md-code pre {
  max-height: none;
  overflow-y: visible;
}

/* 展开的正文：复用 Markdown 渲染出的代码块，自带高亮与复制 */
@keyframes file-content-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.file-content {
  border-top: 1px solid var(--border);
  background: var(--surface);
  animation: file-content-in 260ms cubic-bezier(0.22, 1.15, 0.36, 1);
  transform-origin: top center;
}

/* 面板头需要相对定位，关闭按钮才能绝对定位到右侧 */
.files-drawer .drawer-head {
  position: relative;
}

.file-content .md-code {
  margin: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

/*
 * 展开的正文要能一路看下去。
 *
 * 全局那条 .md-code pre { overflow-y: hidden } 是为了不让长代码块吞掉会话的
 * 滚轮而加的；但在这里它配合 max-height 会把超出部分变成**永远够不到**的内容。
 * 所以面板内解除这两个限制，交给外层 .files-list 统一滚动。
 */
.file-content .md-code pre {
  max-height: none;
  overflow-y: visible;
}

/* 展开的正文本身不设高度上限，让 .files-list 统一滚 */
.file-content {
  overflow: visible;
}

/* ================================================================== */
/* 会话搜索                                                            */
/* ================================================================== */

.search-scrim {
  position: fixed;
  inset: 0;
  z-index: 74;
  background: rgba(15, 23, 42, 0.2);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  will-change: opacity;
}

.search-dialog {
  position: fixed;
  top: 92px;
  left: 50%;
  z-index: 75;
  display: flex;
  flex-direction: column;
  width: min(540px, calc(100vw - 28px));
  max-height: min(460px, 64vh);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xl);
  transform: translate(-50%, -12px);
  opacity: 0;
  will-change: transform, opacity;
  visibility: hidden;
  overflow: hidden;
}

.search-dialog[data-open='true'] {
  visibility: visible;
}

.search-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 12px 12px 16px;
  border-bottom: 1px solid var(--border);
}

.search-icon {
  display: grid;
  place-items: center;
  color: var(--text-3);
}

.search-icon .icon {
  width: 17px;
  height: 17px;
}

.search-input {
  flex: 1;
  min-width: 0;
  font-size: var(--fs-base);
}

.search-input::placeholder {
  color: var(--text-3);
}

.search-results {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.search-result {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 11px;
  text-align: left;
  border-radius: var(--r-md);
  transition: background-color var(--dur-fast) var(--ease-out);
  will-change: transform;
}

.search-result:hover,
.search-result.is-active {
  background: var(--surface-2);
}

.search-result-title {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}

.search-result-snippet {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.search-result-snippet mark {
  background: rgba(250, 204, 21, 0.3);
  color: inherit;
  border-radius: 2px;
}

.search-empty {
  padding: 26px 12px;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--text-3);
}

/* ================================================================== */
/* toasts                                                              */
/* ================================================================== */

.toasts {
  position: fixed;
  right: 18px;
  bottom: calc(18px + env(safe-area-inset-bottom));
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
  pointer-events: none;
}

.toast {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(380px, calc(100vw - 36px));
  padding: 11px 15px;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  pointer-events: auto;
  will-change: transform, opacity;
}

.toast .icon {
  flex: none;
  width: 17px;
  height: 17px;
}

.toast-success .icon { color: var(--ok); }
.toast-error .icon { color: var(--danger); }
.toast-warn .icon { color: var(--warn); }
.toast-info .icon { color: var(--accent); }

/* ================================================================== */
/* responsive                                                          */
/* ================================================================== */

/*
 * ── 平板 / 小笔记本（≤900px）──────────────────────────────────────
 *
 * 这个断点不是随手挑的，它由两处**已经存在**的逻辑反过来定死：
 *   · settings.js 的 #paint() 用 matchMedia('(max-width: 900px)') 决定抽屉
 *     从右边滑入还是从下边滑入；
 *   · .preview-pane 也在这里从「右半屏分栏」变成「整屏覆盖」。
 * CSS 侧边栏必须与它们同拍，否则 375px 的手机上会同时看到两种形态。
 */
@media (max-width: 900px) {
  .transcript {
    padding-inline: 16px;
  }

  .composer-wrap {
    padding-inline: 14px;
    /* 横屏刘海/挖孔机型的左右安全区由 .composer-wrap 统一吸收。
       不放在 .composer 上：那里已经用 max-width + auto 外边距居中，
       再加 padding 会让输入框相对消息流左右不对称。 */
    padding-left: max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
  }

  .drawer {
    top: auto;
    left: 8px;
    right: 8px;
    /* bottom 让开 Home Indicator：dvh 只保证「不被浏览器 UI 遮住」，
       不保证「不被系统手势条盖住」，那一条只有 env() 知道。 */
    bottom: calc(8px + env(safe-area-inset-bottom));
    width: auto;
    max-height: 88dvh;
    border-radius: var(--r-xl);
    transform: translate3d(0, 102%, 0);
  }

  .drawer-grip {
    display: block;
    width: 38px;
    height: 4px;
    margin: 8px auto 0;
    background: var(--border-strong);
    border-radius: var(--r-full);
  }

  .drawer-head {
    padding-left: 18px;
    padding-right: 10px;
  }

  .drawer-body {
    /* 22px 在 375px 上只剩 331px 可用，表单标签会挤成两行 */
    padding: 4px 16px 20px;
  }

  .drawer-foot {
    padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  }

  .files-drawer .drawer-icon {
    left: 12px;
  }

  .files-drawer .drawer-head .icon-btn {
    right: 8px;
  }

  .scroll-down {
    right: 16px;
    /* 让开输入框：这里 bottom 是相对 .main（消息流滚动区）的，
       而输入区是 .main 的兄弟节点，两者不重叠 —— 但横屏时 38px 的
       按钮仍会压住最后一两行文字，抬高一点换阅读余地 */
    bottom: 14px;
  }
}

/*
 * ── 手机（≤620px）────────────────────────────────────────────────
 *
 * 选择 620 而不是任务里建议的 640：两者的覆盖范围只差 20px，但项目里
 * 它已经有一套「窄屏」规则（chip 标签收窄、hero 收紧）。再插一条 640
 * 会得到两条几乎重合的查询，后续维护时很难判断该改哪一条。
 */
@media (max-width: 620px) {
  .topbar {
    padding-inline: max(10px, env(safe-area-inset-left)) max(10px, env(safe-area-inset-right));
  }

  .hero {
    padding: 64px 16px 16px;
  }

  .suggestions {
    grid-template-columns: 1fr;
  }

  .message {
    gap: 10px;
  }

  /* 助手气泡正常占满，用户气泡不必再留 22% 空白 */
  .message-user .message-body {
    max-width: 88%;
  }

  /*
   * 长内容横向滚动而不是撑破布局。
   *
   * 只加 overflow 是没用的：flex 项默认 min-width: auto，子元素一旦比
   * 容器宽，溢出的是父级而不是 pre 自己 —— 所以 max-width: 100% 和
   * min-width: 0 必须一起给，否则 .transcript 会被整条顶宽，
   * 虚拟文本按它算出来的换行数也跟着错。
   *
   * 注意：这些盒子都在「消息流」里，但改动只是把自身限制在既有宽度内，
   * 不会改变消息流容器的高度 —— 虚拟滚动的高度推算不受影响。
   */
  .md-code,
  .md-table-wrap,
  .md-math-display,
  .md-quote,
  .md-image,
  .widget,
  .tool-call,
  .tool-file-view {
    max-width: 100%;
    min-width: 0;
  }

  /*
   * 这里**故意不给 .md-code 加 overflow-x**。
   *
   * .md-code 里既有 sticky 的 .code-bar，又有自己横向滚动的 pre。
   * 一旦它变成横向滚动容器，.code-bar 的 sticky 就改相对它定位，
   * 吸顶当场失效（这正是全局规则用 overflow: clip 的原因）。
   * 横向滚动交给本来就带 overflow-x: auto 的 pre 和 .md-table-wrap，
   * 圆角溢出由 .md-code 自带的 clip 裁掉，两边互不干扰。
   */
  .md-code pre {
    padding: 12px 10px;
  }

  .md-code code {
    /* 比桌面小一点，但不低于可读下限；用 vw 让 320px 的老机型也放得下 */
    font-size: clamp(11.5px, 3.35vw, 12.8px);
  }

  /* 代码块头部在 375px 下要放下「语言 + 预览 + 复制 + 下载」 */
  .code-bar {
    padding: 6px 6px 6px 12px;
    gap: 6px;
  }

  .code-lang {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .composer-box {
    padding: 12px 10px 8px 14px;
  }

  .composer-input {
    max-height: 168px;
  }

  /*
   * 工具开关在手机上只留图标，文字由 aria-label/title 兜底。
   *
   * 选择器必须写成 .toolbar-chip .chip-label：全局那条「收窄」规则
   * 是 .toolbar-chip .chip-label（权重 0,2,0），只写 .chip-label (0,1,0)
   * 是压不过它的 —— 标签会照常占宽，四个药丸一共 350px，
   * 而 375px 屏上 toolbar 只有 225px，直接盖到附件与发送按钮上。
   */
  .toolbar-chip .chip-label {
    display: none;
  }

  /*
   * 一行必须放得下，所以禁掉换行 —— 换行会让 footer 变成两行，
   * 输入框的可视高度被吃掉一块。不用 overflow: hidden 兜底：
   * 它会连带裁掉 :focus-visible 的描边，而键盘/读屏用户最需要它。
   */
  .composer-toolbar {
    flex-wrap: nowrap;
    min-width: 0;
  }

  .toolbar-chip {
    flex: none;
    gap: 0;
    padding: 6px 9px;
  }

  .toasts {
    left: 14px;
    right: 14px;
    align-items: stretch;
  }

  .toast {
    max-width: none;
  }

  .ctx-list {
    max-height: 120px;
  }

  /* 悬浮卡片的 ctx 面板在手机上改为贴住输入框两侧 */
  .ctx-panel-body {
    max-height: 52dvh;
    padding: 14px;
  }

  .ctx-overview {
    gap: 14px;
  }

  /* 搜索框在手机上从顶部下移，避免贴住刘海 */
  .search-dialog {
    top: calc(72px + env(safe-area-inset-top));
    max-height: min(60dvh, 460px);
  }
}

/*
 * ── 小屏手机（≤400px）────────────────────────────────────────────
 *
 * 覆盖 iPhone SE(375)、Galaxy S 系列(360)、以及 320 的老机型。
 * 这一档的关键不是「再小一点」，而是**保证竖屏下不出现横向滚动**：
 * 输入区那片区域在 320px 时，一个胶囊加两个圆形按钮就已经接近极限。
 */
@media (max-width: 400px) {
  .topbar {
    gap: 8px;
    padding-inline: max(8px, env(safe-area-inset-left)) max(8px, env(safe-area-inset-right));
  }

  .topbar-left {
    gap: 6px;
  }

  .brand {
    width: 34px;
    height: 34px;
  }

  .brand-mark {
    width: 30px;
    height: 30px;
  }

  .control-cluster {
    gap: 0;
    padding: 2px;
  }

  /*
   * 关于 320px 上的药丸宽度 —— 实测结论，别再往上加了。
   *
   * 320px 屏的实测预算：composer-footer 292px − 两个 44px 圆钮与间距 94px
   * ≈ 198px 给 toolbar；而 4×40 + 3×6 = 178px 时，药丸右边缘已经压到
   * 附件按钮上（实测 gap −2px，即**重叠**）。40px 放不下。
   *
   * 所以这里刻意保持基础版的 34px 宽：4×34 + 3×18 = 154px，
   * 留出约 44px 余量，才能保证「药丸 + 附件 + 发送」三者在 320px 上
   * 互不重叠、也不会把发送按钮挤到第二行。
   *
   * 代价要说清楚：320px 上的药丸是 44px 高 × 34px 宽，宽度不足 44。
   * 之所以接受：它满足 WCAG 2.5.8 的 24×24 下限，且四周留有间距；
   * 而 360px 及以上的机型仍是 44×44（见上面的 @media 层）。
   * 若日后要照顾 320px，正确的做法是把这一行改成横向滚动，
   * 而不是继续缩尺寸 —— 但滚动容器会把发送按钮带出可视区，代价更大。
   */

  /* 320px 下这一行是「四个工具 + 附件 + 发送」，间距再收一点 */
  .composer-footer {
    gap: 6px;
  }

  .composer-actions {
    gap: 2px;
  }

  .transcript {
    padding-inline: 12px;
  }

  .hero {
    padding: 56px 12px 12px;
  }

  .hero-title {
    font-size: 1.28rem;
  }

  /* 抽屉在两个方向上都要留出呼吸空间，否则 320px 下只剩 288px 内容宽 */
  .drawer {
    left: 6px;
    right: 6px;
  }

  .settings-group {
    padding: 16px 0;
  }

  .md-table th,
  .md-table td {
    padding: 8px 10px;
  }

  .code-bar {
    padding-inline: 10px 4px;
  }
}

/*
 * ── 触摸设备（能力查询，与屏幕宽度无关）──────────────────────────
 *
 * 用 hover / pointer 而不是 max-width 来判定「是不是手指」，因为
 * 15 寸触摸屏笔记本与 iPad 横屏都属于「屏幕很宽，但没有悬停、点不准」，
 * 按宽度判断会把它们漏掉；反之，桌面浏览器把窗口拖到 500px 也会被
 * 误判成手机。
 */
@media (hover: none), (pointer: coarse) {
  /*
   * 悬停才会出现的操作在触摸上没有「悬停」这一刻，等于永远不出现。
   * 这些是消息的复制/重新生成、会话删除、附件移除 —— 全是必需功能，
   * 所以在触摸设备上无条件显示（桌面端保持原来的淡入）。
   */
  .message-actions {
    opacity: 1;
    transform: none;
  }

  .session-del,
  .attachment-remove {
    opacity: 1;
  }

  /* 上面三条只是「让它可见」；可见之后还得点得到，见下面的命中区规则 */

  /*
   * 命中区（hit area）与视觉尺寸分开处理。
   *
   * 开关的 track 只有 42×24、附件移除按钮 18px、小图标按钮 30px ——
   * 把它们的**视觉**尺寸直接放大到 44px 会明显变丑（药丸变胖、圆点变大），
   * 但用透明伪元素把可点区域撑到 44px 既不影响外观，也满足 Apple HIG
   * 与 WCAG 2.5.5 对触摸目标的要求。
   */
  .switch {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .switch::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100%, 44px);
    height: 44px;
    transform: translate(-50%, -50%);
  }

  .attachment-remove {
    top: -10px;
    right: -10px;
  }

  .attachment-remove::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
  }

  .icon-btn,
  .icon-btn-sm,
  .cluster-btn,
  .attach-btn,
  /* .send-btn 原来不在列表里是我看漏了 —— 它是这一屏最重要的按钮，
     36px 在手指下明显偏小，而且它旁边 44px 的附件按钮会显得更突出 */
  .send-btn,
  .session-item,
  .ghost-btn,
  .continue-btn,
  .code-copy,
  .preview-action,
  .file-btn,
  /*
   * 这两个是在真机尺寸的量测里才暴露出来的：思考过程折叠头和上下文
   * 用量条各只有 37px / 23px 高，而它们是消息流里最常点的两处。
   * 它们都是整条按钮，拉高只影响自身行高，不会动消息流的宽度。
   */
  .reasoning-toggle,
  .ctx-bar {
    min-height: 44px;
  }

  .icon-btn,
  .icon-btn-sm {
    min-width: 44px;
  }

  /* 圆形图标按钮是「高=宽」的，只加 min-height 会变成竖向胶囊，
     所以宽度也要一起给，才能保证点得到的是 44×44 而不是 44×30 */
  .attach-btn,
  .cluster-btn,
  .send-btn {
    min-width: 44px;
  }

  .cluster-btn:not(.cluster-btn-lg) {
    min-width: 40px;
  }

  /*
   * 输入区里的工具开关（深度思考 / 联网搜索 / 网页读取）是药丸形，
   * 桌面只有 28px 高。它们是这一屏最常用的控件之一，必须补到 44。
   * 四个药丸横向占 4×34 + 3×6 = 154px（宽度见 400px 段的说明），
   * 在 320px 上给附件与发送按钮留出约 44px 余量，互不重叠。
   */
  .toolbar-chip {
    min-height: 44px;
    justify-content: center;
  }

  .segmented button {
    min-height: 44px;
  }

  /*
   * 滑块的轨道只有 5px 高，22px 的控件在手指下几乎点不中。
   * 高度加到 44 不影响视觉：轨道由 ::-webkit-slider-runnable-track 绘制，
   * 垂直居中，thumb 的 margin-top 也是相对轨道算的。
   */
  .field input[type='range'] {
    height: 44px;
  }

  /* 会话列表在触摸设备上不需要「删除按钮只在悬停时出现」 */
  .session-item {
    padding-block: 11px;
  }

  /* 手指比鼠标粗，消息行的操作之间留点空隙，避免误触 */
  .message-actions {
    gap: 6px;
  }

  /* 横屏刘海机型的左安全区：侧边栏贴在左边缘，标题会被切掉 */
  .sidebar {
    padding-left: env(safe-area-inset-left);
  }

  /*
   * 触屏上不需要下面这些：悬停高亮会在点完之后「粘」在元素上，
   * ::selection 的滚动条美化对手指也没有意义。
   */
  .message:hover .message-actions {
    transform: none;
  }

  /* 触摸设备没有右键菜单，长按会选中文字并弹出系统气泡 —— 关掉 */
  button,
  .session-item,
  .suggestion,
  .tool-toggle {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
}

/*
 * ── iOS 聚焦缩放 ─────────────────────────────────────────────────
 *
 * iOS Safari 在聚焦 font-size < 16px 的输入控件时会自动放大整个页面，
 * 而且**不会自动缩回去** —— 用户接下来看到的是一屏放大的、错位的界面。
 *
 * 修法是把字体提到 16px，而不是在 viewport 里加 maximum-scale=1：
 * 后者虽然也能阻止缩放，但同时会禁掉双指放大，对视力不佳的用户是实质性
 * 的功能损失（WCAG 1.4.4 要求内容可放大到 200%），得不偿失。
 *
 * 放在文件末尾是为了压过 .field input[type='range'] 这类带属性选择器的
 * 规则：它们的选择器权重 (0,2,1) 高于单个类型选择器，只在前面写会失效。
 */
@media (pointer: coarse) {
  .composer-input,
  .search-input,
  .settings-input,
  .settings-textarea,
  .session-rename {
    font-size: 16px;
  }
}

/*
 * ── 触摸设备上的「环境光背景」与自定义光标 ──────────────────────
 *
 * 这里用与上面不同的判据，是刻意的：
 *   (hover: none), (pointer: coarse)      → 「这台设备基本上没鼠标」
 *   (hover: hover) and (pointer: fine)    → 「这台设备确定有精细指针」
 *
 * 环境光是一整屏 Canvas 动画（粒子 + 光晕），在手机上换来的只是掉帧和
 * 耗电，所以用前者：混合设备（触屏笔记本）上仍保留效果，因为那里用户
 * 多半在用鼠标，而且性能不是瓶颈。
 *
 * 自定义光标不同 —— 它是**跟随指针**的装饰，只有确定存在精细指针时才有
 * 意义，用后者（与 cursor.js 的 #shouldEnable() 保持同一套判据，
 * 避免出现「JS 以为该显示、CSS 把它藏了」的不一致）。
 */
@media (hover: none), (pointer: coarse) {
  .ambient-canvas {
    display: none;
  }

  .ambient-grain {
    /* 保留轻微的纸纹：它是纯静态的 radial-gradient，
       既不影响性能，也能让大块白底不发空。 */
    opacity: 0.28;
  }
}

/*
 * 尊重系统的「减弱动态效果」。theme.css 已经把所有 animation / transition
 * 压到 0.001ms，但那条规则管不到 Canvas 每帧重绘 —— 这里直接把静态
 * 背景也去掉，避免「设置为减弱动态效果之后画面还在动」。
 */
@media (prefers-reduced-motion: reduce) {
  .ambient-canvas {
    display: none;
  }
}

/*
 * 横屏手机（高度很小）：竖屏那一套会撑爆视口，这里按高度而不是宽度收。
 *
 * 判据用 max-height 而不是 orientation —— 后者在折叠屏、平板分屏下的
 * 语义并不稳定，而「可用高度不足」才是真正要解决的问题。
 */
@media (orientation: landscape) and (max-height: 500px) {
  .hero {
    padding-top: calc(56px + env(safe-area-inset-top));
    gap: 8px;
  }

  .suggestions {
    margin-top: 10px;
  }

  /* 横屏时抽屉改为右侧贴边，而不是占满整个矮屏幕的下方 */
  .drawer {
    top: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom));
    left: auto;
    right: calc(8px + env(safe-area-inset-right));
    width: min(420px, calc(100vw - 32px));
    max-height: none;
  }

  .reasoning-body {
    max-height: 48dvh;
  }

  .ctx-panel-body {
    max-height: 46dvh;
  }

  .search-dialog {
    top: calc(64px + env(safe-area-inset-top));
    max-height: 72dvh;
  }
}

@media (min-width: 1500px) {
  :root {
    --composer-max: 900px;
    --transcript-max: 900px;
  }
}
