:root {
  color-scheme: light;
  --bg: #f2f4f7;
  --surface: #ffffff;
  --surface-alt: #f8fafc;
  --border: #e4e8ee;
  --border-strong: #cdd3dc;
  --text: #13161c;
  --text-soft: #474e58;
  --muted: #79818d;
  --ink: #0e1014;
  --accent: #c2410c;
  --accent-soft: #fff3ea;
  --ai: #4338ca;
  --ai-soft: #eef0ff;
  /*
   * 「柔和边框 / 柔和底」这一族（2026-09-29 深色漏改审计新增）。
   *
   * 它们原来是散落在各组件里的**硬编码浅色**（#d7dcff / #bcd2ff / #d7d9fb / #f8c9a4 / #fcd9a8 /
   * #fecaca / #f2c5c5 / #fee2e2 / #a7f3d0 / #ecfdf5）—— 深色下每一处都是一块亮斑。
   * 归一成变量后：**浅色支取原值**（外观基本逐像素不变），深色支取"暗底上的一点色"。
   * 同一族里略有差异的几个浅色（如 #bcd2ff / #d7d9fb）在这里归一成同一个值 —— 有意为之。
   */
  --ai-border: #d7dcff;
  --accent-border: #f8c9a4;
  --danger-border: #fecaca;
  --danger-soft: #fee2e2;
  --success-border: #a7f3d0;
  --success-soft: #ecfdf5;
  --success: #047857;
  --warn: #b45309;
  --danger: #b91c1c;
  --radius: 14px;
  --radius-sm: 10px;
  --shadow: 0 1px 2px rgba(16, 18, 22, 0.05), 0 10px 28px rgba(16, 18, 22, 0.06);
  --shadow-lg: 0 18px 48px rgba(16, 18, 22, 0.16);
  --shadow-xl: 0 28px 70px rgba(16, 18, 22, 0.22);
  --mono: "SFMono-Regular", ui-monospace, Menlo, Consolas, "Courier New", monospace;
  --sans: "Inter", system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", sans-serif;
}

/*
 * ── 深色主题（**默认**）────────────────────────────────────────────────
 * 上面 `:root` 是浅色一套；这里只覆盖**颜色类**变量（尺寸 / 字体 / 圆角继续共用）。
 *
 * 默认走深色：`web/index.html` 头部的内联脚本在**样式表之前**把 `data-theme` 设好
 * （读不到偏好时设 `dark`）—— 所以既没有白闪，也不需要在 CSS 里另写一套兜底值。
 *
 * 暗底取色原则：不用纯黑（#000 在 OLED 上边缘发糊）；"面"比"底"亮一档；边框靠"亮一档"而不是加深；
 * 强调色在暗底上**重新取**（浅色那支 #c2410c / #4338ca 压在暗底上会发闷、对比度不够）。
 */
[data-theme='dark'] {
  color-scheme: dark;
  --bg: #0f1116;
  --surface: #171a21;
  --surface-alt: #1f232c;
  --border: #2b303b;
  --border-strong: #3b4250;
  --text: #e9ebf1;
  --text-soft: #b7bdc9;
  --muted: #8c94a4;
  --ink: #f5f7fc;
  --accent: #fb923c;
  --accent-soft: #3a2416;
  --ai: #a5b4fc;
  --ai-soft: #242645;
  /*
   * 柔和边框 / 柔和底：暗底上取"比面略亮、带一点色"的值。
   * 刻意**不是**把浅色支压暗一点点 —— 那样会发脏、像退色的旧图；这里是重新取的一组。
   */
  --ai-border: #3a3f6b;
  --accent-border: #5a3a22;
  --danger-border: #6b3030;
  --danger-soft: #3a1f1f;
  --success-border: #1f4d3a;
  --success-soft: #14322a;
  --success: #34d399;
  --warn: #fbbf24;
  --danger: #f87171;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.55), 0 10px 28px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 18px 48px rgba(0, 0, 0, 0.6);
  --shadow-xl: 0 28px 70px rgba(0, 0, 0, 0.72);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

button,
input,
textarea,
select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

.is-hidden {
  display: none !important;
}

/* ---------- boot ---------- */
/*
 * 启动态的加载动画：三颗点依次亮起（HTML 里是三颗 <i>）。
 * 为什么不用旋转圈：点状进度在"等一两秒"这件事上更安静，
 * 转圈会让人以为比实际更慢。
 */
.boot-spinner {
  display: inline-flex;
  gap: 5px;
  margin: 10px 0 2px;
}
.boot-spinner i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.35;
  animation: boot-dot 1.1s ease-in-out infinite;
}
.boot-spinner i:nth-child(2) {
  animation-delay: 0.15s;
}
.boot-spinner i:nth-child(3) {
  animation-delay: 0.3s;
}
@keyframes boot-dot {
  0%, 100% { opacity: 0.25; transform: translateY(0); }
  50% { opacity: 1; transform: translateY(-2px); }
}
/* 久等的解释：字号小一档、颜色更淡 —— 它是补充说明，不抢主文案的位置 */
.boot-hint {
  margin-top: 8px;
  font-size: 12px;
  color: var(--muted);
  max-width: 320px;
  line-height: 1.6;
}
.boot {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: linear-gradient(160deg, #14161b 0%, #23262f 100%);
  z-index: 100;
}
.boot-card {
  text-align: center;
  color: #f5f6f8;
}
.boot-logo {
  font-size: 22px;
  font-weight: 650;
  letter-spacing: 0.04em;
}
.boot-status {
  margin-top: 14px;
  color: #9aa1ac;
  font-size: 13px;
}
.boot.error .boot-status {
  color: #fca5a5;
}

/* ---------- shell ---------- */
.app {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.topbar {
  height: 60px;
  flex: 0 0 60px;
  background: linear-gradient(100deg, #12151b 0%, #1b202a 55%, #232936 100%);
  color: #f3f4f6;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 18px;
  border-bottom: 1px solid #05070a;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
}
.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  font-weight: 680;
  letter-spacing: 0.03em;
  white-space: nowrap;
  font-size: 14.5px;
}
/*
 * 品牌标记：2026-09-22 从"剧"字方块换成品牌图（Aura Arc｜弧光 的标志）。
 * 形状沿用原来的圆角方块 —— 它同时出现在侧栏品牌区与登录卡上，
 * 尺寸/圆角一动，两处的对齐都要跟着调，所以只换内容不换几何。
 */
.brand-mark {
  width: 30px;
  height: 30px;
  border-radius: 9px;
  display: block;
  object-fit: cover;
  flex: none;
  box-shadow: 0 3px 10px rgba(20, 12, 40, 0.32);
}
.brand-sub {
  color: #98a0ab;
  font-weight: 400;
  font-size: 12px;
}
.topbar-spacer {
  flex: 1;
}
.topbar-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: #e5e7eb;
  font-size: 12px;
  border: 1px solid rgba(255, 255, 255, 0.1);
}
.topbar-chip strong {
  color: #fff;
  font-weight: 600;
}
.user-menu {
  position: relative;
}
.user-btn {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: #e5e7eb;
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.user-btn:hover {
  background: rgba(255, 255, 255, 0.14);
}
.user-dropdown {
  position: absolute;
  right: 0;
  top: 42px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  min-width: 220px;
  padding: 6px;
  z-index: 40;
}
.user-dropdown button {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  padding: 9px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.user-dropdown button:hover {
  background: var(--surface-alt);
}
.user-dropdown .meta {
  padding: 8px 10px;
  color: var(--muted);
  font-size: 12px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
  word-break: break-all;
}

/* ---------- layout ---------- */
.shell {
  flex: 1;
  display: flex;
  min-height: 0;
}
/*
 * 侧栏的可见性**必须**由 `.open` 决定 —— 所有屏幕宽度下都是。
 * 这里曾经桌面端无条件 display:flex，`.open` 类成了摆设：
 * 顶栏的横杠按钮点了之后 state 翻转、界面纹丝不动，
 * 用户看到的就是「三个横杠没有任何作用」。窄屏分支反而是对的，予以保留。
 */
.sidebar {
  display: none;
}
.sidebar.open {
  width: 248px;
  flex: 0 0 248px;
  /* 侧栏底：原来是硬编码的 #ffffff → #fbfcfe（深色下就是一条大白带）⇒ 改走变量 */
  background: linear-gradient(180deg, var(--surface) 0%, var(--bg) 100%);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/*
 * 侧栏收起时，主区左上角的浮动横杠是唯一的开合入口。
 * 它浮在内容之上（fixed），但**不能与内容互相覆盖**：
 * 收起时主区整体让出左侧一条 48px 的走廊（.main.has-float），
 * 页头文字、后台标题都从横杠右侧开始 —— 不再出现"文字压住三条杠"。
 */
.sidebar-float {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 70;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 2px 10px rgba(15, 23, 42, 0.12);
}
.main.has-float {
  padding-left: 48px;
}
.sidebar-section {
  padding: 12px;
  border-bottom: 1px solid var(--border);
}
.sidebar-title {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.project-list {
  overflow-y: auto;
  flex: 1;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.project-item {
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid transparent;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.project-item:hover {
  background: var(--surface-alt);
}
.project-item.active {
  background: var(--accent-soft);
  border-color: var(--accent-border);
}
.project-item .p-title {
  font-weight: 600;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.project-item .p-meta {
  font-size: 11px;
  color: var(--muted);
}
.nav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nav-item {
  text-align: left;
  background: none;
  border: 0;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text-soft);
  display: flex;
  align-items: center;
  gap: 8px;
}
.nav-item:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.nav-item.active {
  background: var(--ai-soft);
  color: var(--ai);
  font-weight: 600;
}

.main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* ---------- workbench ---------- */
.workbench {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  min-height: 0;
}
.pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--surface);
}
.pane + .pane {
  border-left: 1px solid var(--border);
}
.pane-head {
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-height: 48px;
}
.pane-title {
  font-weight: 650;
  font-size: 13px;
}
.tabs {
  display: flex;
  gap: 2px;
  background: var(--surface-alt);
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--border);
}
.tab {
  border: 0;
  background: none;
  padding: 5px 12px;
  border-radius: 999px;
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
}
.tab.active {
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--shadow);
}
.pane-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  display: flex;
  flex-direction: column;
}
.pane-foot {
  border-top: 1px solid var(--border);
  padding: 10px 14px;
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  background: var(--surface-alt);
}

.editor {
  flex: 1;
  min-height: 0;
  border: 0;
  outline: none;
  resize: none;
  padding: 20px 22px;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.9;
  color: var(--text);
  background: var(--surface);
  tab-size: 2;
}
.editor::placeholder {
  color: #b3b9c2;
}

/* ---------- controls ---------- */
.btn {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  border-radius: var(--radius-sm);
  padding: 7px 13px;
  cursor: pointer;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.btn:hover:not(:disabled) {
  background: var(--surface-alt);
  border-color: var(--border-strong);
}
.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn.primary:hover:not(:disabled) {
  background: #9a3412;
}
.btn.ai {
  background: var(--ai);
  border-color: var(--ai);
  color: #fff;
}
.btn.ai:hover:not(:disabled) {
  background: #3730a3;
}
.btn.ghost {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}
.btn.ghost:hover:not(:disabled) {
  background: var(--surface-alt);
  color: var(--text);
}
.btn.danger {
  color: var(--danger);
  border-color: var(--danger-border);
}
.btn.sm {
  padding: 4px 9px;
  font-size: 12px;
  border-radius: 6px;
}

.input,
.select,
.textarea {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  background: var(--surface);
  outline: none;
  width: 100%;
}
.input:focus,
.select:focus,
.textarea:focus {
  border-color: var(--ai);
  box-shadow: 0 0 0 3px var(--ai-soft);
}
.field {
  margin-bottom: 12px;
}
.field label {
  display: block;
  font-size: 12px;
  color: var(--text-soft);
  margin-bottom: 5px;
}
.hint {
  font-size: 12px;
  color: var(--muted);
}

/* ---------- agent panel ---------- */
.agent-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  background: var(--surface-alt);
}
.skill-strip {
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: thin;
}
.skill-chip {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 999px;
  padding: 5px 11px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
  color: var(--text-soft);
}
.skill-chip:hover {
  border-color: var(--ai);
  color: var(--ai);
}
.skill-chip.active {
  background: var(--ai);
  border-color: var(--ai);
  color: #fff;
  font-weight: 600;
}
.model-bar {
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.model-bar .select {
  width: auto;
  min-width: 170px;
  padding: 5px 8px;
  font-size: 12px;
}
.context-pill {
  font-size: 11px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 9px;
}

.chat-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 24px 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  background:
    radial-gradient(1000px 380px at 50% -12%, rgba(67, 56, 202, 0.05), transparent 72%);
}
/* 对话正文限宽居中：宽屏下每行不会过长，读起来更像一份文档 */
.chat-scroll > * {
  width: 100%;
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}
.msg {
  display: flex;
  gap: 12px;
}
.msg .avatar {
  flex: 0 0 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  margin-top: 2px;
}
.msg.user .avatar {
  background: #6b7280;
}
.msg.assistant .avatar {
  background: var(--ai);
}
.msg-body {
  min-width: 0;
  flex: 1;
}
.msg-role {
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 3px;
  display: flex;
  gap: 8px;
  align-items: center;
}
.bubble {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 20px;
  box-shadow: var(--shadow);
  font-size: 14.5px;
  line-height: 1.8;
}
.msg.user .bubble {
  /*
   * 🔴 2026-09-29 用户报的 bug：「深色模式下每一轮我输入的气泡……背景框的颜色没有随深色模式变深」。
   * 这里是**硬编码浅色渐变 + 硬编码浅色边框** ⇒ 深色下就是一块亮斑。
   * 根因是结构：`[data-theme='dark']` **只覆盖变量**，没有任何组件级暗色覆盖 ⇒ **任何硬编码色都逃逸**。
   * 改法：气泡底与边框都改走变量（跟着主题走）。
   */
  background: var(--surface-alt);
  border-color: var(--border);
}
.msg-actions {
  margin-top: 8px;
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.composer {
  /*
   * ⚠️ 这是**同名早期规则**（被下面「输入区：发送按钮进输入框」那一段的 `.composer` 覆盖）——
   * 以后要改输入区的盒子，**先确认改的是哪一条**。
   *
   * 判据历史（如实记录，别再改回去）：
   *   · 2026-09-29 早先：用户说"输入框有个很浅的直角背景框，我不需要" ⇒ 我把这个盒子去掉了；
   *   · 2026-09-29 **更正**：用户澄清「**删错了**，我要保留这个**圆角灰底**，删除他的下一层 ——
   *     直角、半透明、与页面同宽的底部带」。那层带其实在 `.composer-wrap`（见下），不在 `.composer`。
   * ⇒ 盒子**恢复原值**（取值来源：`git show b937e20^:web/styles.css`，未手写猜测）。
   */
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 12px 14px 12px;
  box-shadow: var(--shadow);
}
.composer-wrap {
  /*
   * 🔴 用户说的"**下面那一层**：直角、半透明、与页面同宽的底部带"就是这里的背景。
   * 量过（composer 子树计算样式测量）：改前它是 `linear-gradient(180deg, var(--bg) 0%, var(--surface) 100%)`
   * —— 与 `.composer` 的**圆角**盒不同，它**方角**、且是一条**整幅宽的带** ⇒ 正是要删的那层。
   *
   * 2026-09-29 用户**追加**：「上分界线也不需要」⇒ 连 `border-top: 1px` 也去掉了
   * （上一版"保留分界"的判定随之作废）。现在 `.composer-wrap` **没有任何自己的外观** ——
   * 只剩 `padding`（它承担"输入区与对话区之间的间距"，不是外观）。
   * 目标形态：**只剩那个圆角灰底盒子浮在页面底上，上方没有任何分隔线 / 色带**。
   */
  padding: 12px 24px 16px;
}
.composer-wrap > * {
  width: 100%;
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}
.composer textarea {
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 10px;
  /*
   * `resize: none`：高度**已由 `growComposer` 按内容自动算**（上限 5 行）。
   * 留着 `resize: vertical` 有三个问题：① 与自动增高打架 —— 用户拖高之后**下一键就被弹回**，
   * 看起来像"改不动"；② 右下角那个拖拽把手本身就是一圈"方角痕迹"，正是用户要去掉的那种观感；
   * ③ 拖出来的高度不受 5 行约束，编辑框会把对话区顶走。
   */
  resize: none;
  min-height: 62px;
  outline: none;
  /*
   * 与 `.composer` **同底**：原来这里填的是 `--surface-alt`（浅色下 #f8fafc），
   * 于是在白底的 composer 里嵌出一块灰 —— 就是用户说的"突兀的灰色底纹"。
   * 去掉填充后，输入区与它所在的容器连成一体；焦点态靠下面的边框 + 光环来表达。
   */
  background: transparent;
}
.composer textarea:focus {
  /* 恢复原值：焦点态由**盒子**（`.composer:focus-within` 的边框 + 光环）表达，textarea 自己不再加光环 */
  border-color: var(--ai);
  box-shadow: 0 0 0 3px var(--ai-soft);
}
.composer-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 8px;
  gap: 8px;
  flex-wrap: wrap;
}

/* ---------- markdown ---------- */
.md h1,
.md h2,
.md h3 {
  margin: 16px 0 8px;
  line-height: 1.35;
}
.md h1 {
  font-size: 18px;
}
.md h2 {
  font-size: 15px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--border);
}
.md h3 {
  font-size: 14px;
  color: var(--text-soft);
}
.md p {
  margin: 8px 0;
}
.md ul,
.md ol {
  margin: 8px 0;
  padding-left: 22px;
}
.md li {
  margin: 3px 0;
}
.md code {
  font-family: var(--mono);
  background: var(--surface-alt);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 12px;
}
.md pre {
  background: #1a1d23;
  color: #e8eaed;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.6;
}
.md pre code {
  background: none;
  color: inherit;
  padding: 0;
}
.md table {
  border-collapse: collapse;
  width: 100%;
  margin: 10px 0;
  font-size: 12.5px;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}
.md th,
.md td {
  border: 1px solid var(--border);
  padding: 7px 9px;
  text-align: left;
  vertical-align: top;
}
.md th {
  background: var(--surface-alt);
  font-weight: 650;
  white-space: normal;
}
.md td {
  white-space: normal;
  min-width: 90px;
}
.md blockquote {
  margin: 10px 0;
  padding: 6px 12px;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  color: var(--text-soft);
}
.md hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 14px 0;
}
.thinking {
  background: var(--ai-soft);
  border: 1px dashed #c7cbf5;
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  color: #4c4e8a;
  font-size: 12px;
  margin-bottom: 8px;
  white-space: pre-wrap;
}
.streaming-caret::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 14px;
  background: var(--ai);
  margin-left: 2px;
  vertical-align: -2px;
  animation: blink 1s steps(1) infinite;
}
@keyframes blink {
  50% {
    opacity: 0;
  }
}

/* ---------- cards & generic views ---------- */
.page {
  flex: 1;
  overflow-y: auto;
  padding: 30px 34px 44px;
  background:
    radial-gradient(900px 320px at 15% -8%, rgba(67, 56, 202, 0.06), transparent 70%),
    radial-gradient(700px 300px at 92% 4%, rgba(194, 65, 12, 0.05), transparent 70%);
}
/* 内容统一限宽并居中：宽屏下不会拉成一条长线，观感更沉稳 */
.page > * {
  max-width: 1180px;
  margin-left: auto;
  margin-right: auto;
}
.page-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 14px;
  margin-bottom: 22px;
  flex-wrap: wrap;
}
.page-title {
  font-size: 23px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0;
}
.page-sub {
  color: var(--muted);
  font-size: 13px;
  margin-top: 5px;
  max-width: 760px;
  line-height: 1.7;
}
.grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  box-shadow: var(--shadow);
  margin-bottom: 16px;
}
.card + .card {
  margin-top: 0;
}
.card h4 {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 680;
  letter-spacing: -0.005em;
}
.card p {
  margin: 0;
  color: var(--muted);
  font-size: 12.5px;
}
.card.hover {
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.card.hover:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
.empty {
  text-align: center;
  padding: 46px 20px;
  color: var(--muted);
}
.empty h3 {
  margin: 0 0 6px;
  color: var(--text);
  font-size: 16px;
}
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 14px;
  margin-bottom: 22px;
}
.stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: var(--shadow);
  position: relative;
  overflow: hidden;
}
/* 顶部一道细高光：让统计卡有层次，不靠重色块堆视觉 */
.stat::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(90deg, var(--ai), var(--accent));
  opacity: 0.75;
}
.stat .label {
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.02em;
}
.stat .value {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  margin-top: 4px;
  line-height: 1.2;
}
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  color: var(--text-soft);
}
.badge.accent {
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: #9a3412;
}
.badge.ai {
  background: var(--ai-soft);
  border-color: #c7cbf5;
  color: var(--ai);
}
.badge.ok {
  background: var(--success-soft);
  border-color: var(--success-border);
  color: var(--success);
}

.memory-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-bottom: 10px;
}
.memory-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.memory-title {
  font-weight: 620;
}
.memory-body {
  color: var(--text-soft);
  font-size: 13px;
  white-space: pre-wrap;
  margin-top: 4px;
}

/* ---------- auth ---------- */
.auth-wrap {
  height: 100vh;
  display: grid;
  place-items: center;
  padding: 20px;
  background: radial-gradient(1200px 500px at 50% -10%, #2a2e38 0%, #14161b 60%, #0f1115 100%);
}
.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border-radius: 16px;
  padding: 26px;
  box-shadow: var(--shadow-lg);
}
.auth-card h1 {
  margin: 0 0 4px;
  font-size: 20px;
}
/*
 * 登录卡顶部的品牌图（带「Aura Arc 弧光」字标那张）。
 * 它是登录页唯一的视觉识别，给足宽度但别喧宾夺主 ——
 * 下面紧跟着的就是 h1 与登录表单，图太大反而把表单挤出首屏。
 */
.auth-logo {
  display: block;
  width: 108px;
  height: 108px;
  margin: 0 auto 12px;
  border-radius: 24px;
  object-fit: cover;
  box-shadow: 0 8px 22px rgba(20, 12, 40, 0.22);
}
.auth-card .sub {
  color: var(--muted);
  font-size: 13px;
  margin-bottom: 18px;
}
.seg {
  display: flex;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
  margin-bottom: 16px;
}
.seg button {
  flex: 1;
  border: 0;
  background: none;
  padding: 7px;
  border-radius: 999px;
  cursor: pointer;
  color: var(--muted);
  font-size: 13px;
}
.seg button.active {
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--shadow);
}
/*
 * 次级切换（邮箱 / 手机号）。
 *
 * 刻意比主 Tab 小一号、不加粗、且**不撑满整行宽度** ——
 * 它与上面的「密码登录 / 验证码登录 / 注册」是包含关系而不是并列关系，
 * 视觉上必须一眼看出从属，否则用户会以为是两排同级的选项。
 */
.seg-sub {
  width: auto;
  display: inline-flex;
  padding: 2px;
  margin-bottom: 10px;
}
.seg-sub button {
  flex: 0 0 auto;
  padding: 4px 14px;
  font-size: 12.5px;
}
/* 输入框下面的说明句：解释「为什么」而不只是提示「要填什么」 */
.field-hint {
  /*
   * 下边距必须留出来：这个说明句常常跟在输入框后面、紧挨着主按钮
   * （注册页的「手机号注册成功后…」就是），下边距给 0 时两行文字会贴着按钮，
   * 看起来像按钮的一部分。这是截图人眼核对发现后补的，静态门禁看不出这类问题。
   */
  margin: 8px 0 4px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--muted);
}
.auth-note {
  margin-top: 12px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  line-height: 1.6;
  word-break: break-all;
}
.auth-note.is-pending {
  background: rgba(245, 158, 11, 0.12);
  color: #b45309;
}
.auth-note.is-error {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}
.auth-note.is-success {
  background: rgba(5, 150, 105, 0.12);
  color: #047857;
}
.auth-alt {
  margin-top: 14px;
  font-size: 12.5px;
  color: var(--muted);
  display: flex;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}
.auth-alt button {
  background: none;
  border: 0;
  color: var(--ai);
  cursor: pointer;
  padding: 0;
  font-size: 12.5px;
}
.notice {
  background: var(--accent-soft);
  border: 1px solid var(--accent-border);
  color: #9a3412;
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 12.5px;
  margin-bottom: 12px;
}

/* ---------- toast & modal ---------- */
.toast-host {
  position: fixed;
  right: 16px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 200;
}
.toast {
  background: var(--ink);
  color: #f3f4f6;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  max-width: 380px;
  animation: slidein 0.16s ease;
}
.toast.error {
  background: #7f1d1d;
}
.toast.success {
  background: #065f46;
}
/*
 * 警示语气（部分成功 / 有内容被拦下）。
 *
 * 为什么单独一档而不是复用 .error：内测入口保存是"合法项已存下、只有几项被拦"，
 * 把它染成报错的红色会让人以为"整个保存失败了"，从而重试或放弃；
 * 但它也绝不能是 success 的绿色 —— 绿色会让人以为全存下了。
 * 琥珀色是"已保存但有话说"的准确语气，与文案里的分号列表配套。
 */
.toast.warn {
  background: #92400e;
}
/*
 * 流式状态行：生成期间必须让用户看见「在动」。
 * 反例（已改）：深度思考的模型先跑很长的推理，界面只有一个空气泡和光标，
 * 用户判断不了是在工作还是坏了 —— 真实反馈「点了没有任何动静，像摆设」。
 */
.stream-status {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.stream-status::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent, #2563eb);
  animation: pulse 1.2s ease-in-out infinite;
}
/*
 * 超级智脑那一行的**单行约束**（用户定版：不可展开、不折行）。
 *
 * 那一行的读数是四段拼起来的（正在思考 / 需求判断 N 字 / 已调用 K 个技能 / 已思考 M 字 / 已等待 S 秒），
 * 会随着生成推进越来越长。不强制 nowrap + 省略号，它就会在窄屏或长推理要点下折成两行 ——
 * 「单行」这条约定于是只在宽屏成立，而它失效时没有任何报错，只能靠肉眼发现。
 * 省略号优于裁字：截断的那是尾部的秒数，用户看不懂被删了什么。
 */
.stream-status.is-oneline {
  max-width: 100%;
}
.stream-status.is-oneline .status-text {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}
@keyframes pulse {
  0%,
  100% {
    opacity: 0.25;
  }
  50% {
    opacity: 1;
  }
}
/*
 * 输入区上方的提示条。
 * 与右下角 toast 的分工：toast 只说「你刚点的这件事成/败了」；
 * 凡是用户需要据此**改变下一步动作**的说明都放这里，因为它在输入框上方、
 * 不会被忽略，也不会三秒后消失。
 */
.composer-notice {
  margin-bottom: 8px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  line-height: 1.7;
  text-align: center;
}
.composer-notice.is-pending {
  background: rgba(245, 158, 11, 0.12);
  color: #b45309;
}
.composer-notice.is-error {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}
.composer-notice.is-success {
  background: rgba(5, 150, 105, 0.12);
  color: #047857;
}
@keyframes slidein {
  from {
    transform: translateY(6px);
    opacity: 0;
  }
}
.modal-host:empty {
  display: none;
}
.modal-mask {
  position: fixed;
  inset: 0;
  background: rgba(16, 18, 22, 0.45);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 300;
}
.modal {
  background: var(--surface);
  border-radius: 14px;
  width: min(560px, 100%);
  max-height: 84vh;
  overflow: auto;
  box-shadow: var(--shadow-lg);
}
.modal-head {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.modal-head h3 {
  margin: 0;
  font-size: 15px;
}
.modal-body {
  padding: 18px;
}
.modal-foot {
  padding: 12px 18px;
  border-top: 1px solid var(--border);
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
/*
 * 弹窗内提示：失败原因要留在**用户正在看的弹窗里**，并且居中显眼。
 * 反例（已改）：只弹右下角 toast —— 用户看不到，且三秒后自己消失；文案还是
 * friendlyError 对 INVALID_ARGUMENT 的通用映射，读起来像"页面坏了"而不是"验证码错了"。
 * 配色与登录卡上的 .auth-note 保持一致，用户不必重新学习"红=出错了"。
 */
.modal-note {
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  line-height: 1.7;
  text-align: center;
  word-break: break-word;
}
.modal-note.is-pending {
  background: rgba(245, 158, 11, 0.12);
  color: #b45309;
}
.modal-note.is-error {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}
.modal-note.is-success {
  background: rgba(5, 150, 105, 0.12);
  color: #047857;
}
.modal-hint {
  margin: 0 0 14px;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--muted);
}
.modal-hint strong {
  color: var(--text);
}
.row-inline {
  display: flex;
  gap: 8px;
  align-items: center;
}
.row-inline .input {
  flex: 1;
  min-width: 0;
}

/* ---------- 对话优先布局 ---------- */
.space-picker {
  display: flex;
  gap: 6px;
  padding: 0 12px 10px;
  border-bottom: 1px solid var(--border);
}
.space-picker .select {
  flex: 1;
  min-width: 0;
}
.conv-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 8px 8px;
}
.conv-group + .conv-group {
  margin-top: 4px;
}
.conv-group .sidebar-title {
  padding: 8px 4px 4px;
  margin: 0;
}
.conv-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid transparent;
}
.conv-item:hover {
  background: var(--surface-alt);
}
.conv-item.active {
  background: var(--ai-soft);
  border-color: var(--ai-border);
}
.conv-item .c-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.conv-item .c-title {
  font-size: 13px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-item .c-meta {
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.conv-item .c-del {
  opacity: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
  padding: 2px 5px;
  border-radius: 4px;
}
.conv-item:hover .c-del {
  opacity: 1;
}
.conv-item .c-del:hover {
  background: var(--danger-soft);
  color: #b91c1c;
}
.sidebar-foot {
  border-top: 1px solid var(--border);
  padding: 8px;
  flex: 0 0 auto;
}

.chat-view {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.chat-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.chat-head-main {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}
.chat-title {
  font-weight: 600;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 320px;
}
.chat-space-tag {
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 8px;
  white-space: nowrap;
}
.chat-head-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.chat-head-tools .select {
  width: auto;
  max-width: 190px;
}
.context-pill.level-high {
  color: #b45309;
  border-color: var(--accent-border);
  background: var(--accent-soft);
}
.context-pill.level-full {
  color: #b91c1c;
  border-color: var(--danger-border);
  background: var(--danger-soft);
}

.welcome {
  padding: 40px 22px 30px;
  max-width: 920px;
  margin: 0 auto;
  width: 100%;
}
.welcome-head {
  text-align: center;
  margin-bottom: 26px;
}
.welcome-avatar {
  width: 58px;
  height: 58px;
  border-radius: 18px;
  margin: 0 auto 14px;
  background: linear-gradient(140deg, var(--ai), #7c3aed);
  color: #fff;
  display: grid;
  place-items: center;
  font-size: 24px;
  font-weight: 700;
  box-shadow: 0 10px 26px rgba(67, 56, 202, 0.32);
}
.welcome-head h2 {
  margin: 0 0 8px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.015em;
}
.welcome-head p {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.7;
}
.starter-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 24px;
}
.starter-chip {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: 999px;
  padding: 7px 13px;
  cursor: pointer;
  font-size: 12.5px;
  color: var(--text-soft);
}
.starter-chip:hover {
  border-color: var(--ai);
  color: var(--ai);
  background: var(--ai-soft);
}
.agent-group {
  margin-bottom: 16px;
}
.agent-group-title {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}
.agent-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}
.agent-card {
  text-align: left;
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 11px 12px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.agent-card:hover {
  border-color: var(--ai);
  box-shadow: var(--shadow);
}
.agent-card.active {
  border-color: var(--ai);
  background: var(--ai-soft);
}
.agent-name {
  font-weight: 600;
  font-size: 13px;
}
.agent-tagline {
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.5;
}

.msg .thinking {
  margin-bottom: 8px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 12px;
  color: var(--text-soft);
  white-space: pre-wrap;
  line-height: 1.6;
}
details.thinking summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
}
.composer textarea {
  resize: none;
  min-height: 56px;
  max-height: 200px;
  overflow-y: auto;
  line-height: 1.6;
}

.tab-row {
  display: flex;
  gap: 8px;
  /*
   * 水平外边距必须是 auto：`.page > *` 给每个直接子元素都加了
   * max-width + margin auto（限宽居中），而这里的 `margin: 14px 0`
   * 会把左右外边距归零 —— 结果整行页签贴在最左边，
   * 与上方标题、下方卡片（都是居中的）对不齐。
   * 侧栏收起后主区变宽，这个错位被放大得尤其明显（后台「账号与积分」页签行）。
   */
  margin: 14px auto;
  flex-wrap: wrap;
}
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.data-table th,
.data-table td {
  padding: 7px 6px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
.data-table th {
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.data-table .num {
  text-align: right;
}
.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}
.row-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
}
/*
 * 「账号与积分」9 列宽表：窄视口（尤其收起侧栏后主区再让出 48px）下
 * 不再横向挤压整页布局 —— 表格在容器内横向滚动，页面其余部分纹丝不动。
 */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table-scroll .data-table {
  min-width: 880px;
}
/* 中国习惯：增为红、减为绿 */
.up {
  color: #dc2626;
  font-weight: 600;
}
.down {
  color: #059669;
  font-weight: 600;
}

.skill-admin-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.placeholder-note {
  margin-top: 16px;
  border-top: 1px dashed var(--border);
  padding-top: 12px;
}
.placeholder-note ul {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: 12.5px;
  color: var(--text-soft);
  line-height: 1.9;
}

/* ---------- memory list（空间记忆弹窗） ---------- */
.memory-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.memory-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.memory-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.memory-row:last-child {
  border-bottom: none;
}
.memory-main {
  min-width: 0;
  flex: 1;
}
.memory-title {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.memory-content {
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-soft);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------- responsive ---------- */
@media (max-width: 1080px) {
  .workbench {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(240px, 42%) minmax(0, 58%);
    overflow: auto;
  }
  .pane + .pane {
    border-left: 0;
    border-top: 1px solid var(--border);
  }
}
@media (max-width: 760px) {
  /* 窄屏：展开时改为全屏覆盖层（可见性规则与桌面一致，都由 .open 决定） */
  .sidebar.open {
    position: fixed;
    inset: 0 0 0 0;
    width: 100%;
    flex: none;
    z-index: 60;
  }
}


/* ---------- 场景页：开场白 / 要素 / 格式契约 ---------- */

/* 流水线导航：三个短剧 Agent 之间的上下游关系 */
.pipeline-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 10px;
  margin-bottom: 14px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.pipeline-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.04em;
  margin-right: 4px;
}
.pipe-step {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-soft);
  cursor: pointer;
  font-size: 13px;
  transition: all 0.15s ease;
}
.pipe-step:hover {
  border-color: var(--ai);
  color: var(--ai);
}
.pipe-step.done {
  border-color: var(--success);
  color: var(--success);
}
.pipe-step.active {
  background: var(--ai);
  border-color: var(--ai);
  color: #fff;
  font-weight: 600;
}
.pipe-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.08);
  font-size: 11px;
  font-weight: 700;
}
.pipe-step.active .pipe-num {
  background: rgba(255, 255, 255, 0.28);
}
.pipe-arrow {
  color: var(--muted);
  font-size: 13px;
}

/* 开场白：原文换行必须保留，它就是 Agent 的自我介绍 */
.scene-welcome {
  white-space: pre-line;
  color: var(--text-soft);
  margin: 8px 0 0;
  max-width: 640px;
}
.scene-stage-tag {
  display: inline-block;
  margin-top: 10px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--ai-soft);
  color: var(--ai);
  font-size: 12px;
}

/* 结构化要素卡 */
.composer-wrap {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/*
 * 专家模块条：左边是"这一轮挂着谁"，右边是「＋ 添加专家」。
 *
 * 专家用一枚具名 chip 表示而不是一个高亮的模式按钮 ——
 * 用户要回答的问题是"我在跟谁说话"，而名字正是这个问题的答案。
 * 「专家」二字比专家名小一号、浅一档：它是类别标签，不是内容。
 */
/*
 * 对话模式切换：自由对话 / 专家模式 / 超级智脑。
 *
 * 做成一组紧贴的分段按钮，而不是三个各自独立的 pill：
 * 这三者互斥，连续外形本身就在表达"只能选一个"。
 * 选中态用实底 + 主色，未选态透明 —— 用户扫一眼就知道自己现在在哪个模式里。
 *
 * 「超级智脑」这一项只在管理员配齐后才由视图层渲染出来，
 * 所以这里不需要"禁用态"的样式：不存在的按钮不需要长得灰。
 */
/*
 * 模式条：左边是「自由对话 / 专家模式」的胶囊，右边是**单独一个圆角矩形**的超级智脑。
 * 两者刻意不共用同一个胶囊（需求 2026-09-22）：前两者换的是「挂谁」，
 * 而超级智脑是另一种东西（一体化服务本体），视觉上要一眼分得开。
 */
.mode-bar {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/*
 * 独立的智脑按钮：自带边框、底色与**底纹**，不再依赖外面那枚胶囊。
 *
 * ⚠ 选择器必须写成 `.mode-bar .mode-btn.mode-btn-studio`（三票），不能只写 `.mode-btn-studio`。
 *
 * 原因（这就是用户报的「底纹怎么还没更新」的真因）：本文件里
 * `.mode-btn { border: 0; background: transparent }` 与 `.mode-btn.on { background: var(--surface) }`
 * 都排在**后面**，而同票数时后者胜 —— 于是这枚按钮一直是一枚"裸文字按钮"，
 * 边框与底色被静静吃掉。样式**写了**，只是被下一条同票数规则覆盖；
 * 这种"写法存在、效果为零"的缺陷光看源码是看不出来的。
 *
 * 底纹写法：圆点网纹 + 炫彩渐变必须写在**同一个 background-image** 里。
 * 若先用 background 简写铺渐变、再补一条 background-image 圆点，
 * 会把渐变的 image 槽位整个顶掉（只剩网纹）。
 */
.mode-bar .mode-btn.mode-btn-studio {
  border: 1px solid rgba(124, 92, 255, 0.5);
  background-color: transparent;
  background-image:
    radial-gradient(rgba(255, 255, 255, 0.22) 1px, transparent 1px),
    linear-gradient(
      135deg,
      rgba(124, 92, 255, 0.92),
      rgba(255, 92, 168, 0.82) 45%,
      rgba(64, 196, 255, 0.88)
    );
  background-size: 9px 9px, 100% 100%;
  padding: 5px 12px;
  color: #fff;
  box-shadow: 0 1px 6px rgba(124, 92, 255, 0.35);
}
.mode-bar .mode-btn.mode-btn-studio:hover {
  color: #fff;
  filter: brightness(1.08);
}
/* 选中态：亮一圈，与「自由对话/专家模式」的实底选中保持同一层语义 */
.mode-bar .mode-btn.mode-btn-studio.on {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.85);
  box-shadow:
    0 0 0 2px rgba(124, 92, 255, 0.3),
    0 2px 10px rgba(124, 92, 255, 0.45);
}
.mode-switch {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-alt);
}
.mode-btn {
  border: 0;
  background: transparent;
  color: var(--muted);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}
.mode-btn:hover {
  color: var(--text);
}
.mode-btn.on {
  background: var(--surface);
  color: var(--ai);
  box-shadow: 0 1px 2px rgba(16, 18, 22, 0.08);
}
/*
 * 对话已锁定在超级智脑时：另外两项弱化，但**仍然可点**。
 *
 * 2026-09-23 契约反转（用户报）：原先用 `disabled` 把按钮掐死，想的是"点不动"，
 * 实际把需求一起掐死了 —— disabled 元素不派发 click，于是"点下去要弹居中提示"
 * 永远弹不出来，hover 的 title 在多数浏览器里也不显示。
 * 现在只表达状态（弱化 + cursor: not-allowed），拦截交给动作层：
 * 点下去弹窗说明"需要切换模式请新建对话"。
 *
 * 用 not-allowed 而不是 default：它同时说清两件事 —— 现在不能做、但这里是有东西的。
 */
.mode-btn-locked {
  opacity: 0.45;
  cursor: not-allowed;
}
.mode-btn-locked:hover {
  color: var(--muted);
  opacity: 0.6;
}
.agent-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.agent-bar-hint {
  font-size: 12.5px;
  color: var(--muted);
}
.agent-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--ai-border);
  border-radius: 999px;
  background: var(--ai-soft);
  color: var(--ai);
  font-size: 12.5px;
  font-weight: 600;
}
.agent-chip-label {
  font-size: 11px;
  font-weight: 500;
  color: #6b6f9a;
  letter-spacing: 0.04em;
}
.agent-chip-x {
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 999px;
}
.agent-chip-x:hover {
  background: rgba(67, 56, 202, 0.14);
}
/* 添加专家：一行一个专家，名称 + 一句话说明 —— 原生下拉只能显示一行文本，看名字猜不出区别 */
.agent-pick-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
}
.agent-pick-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
}
.agent-pick-item:hover {
  border-color: var(--border-strong);
  background: var(--surface-alt);
}
.agent-pick-item.active {
  border-color: var(--ai);
  background: var(--ai-soft);
}
.agent-pick-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
}
.agent-pick-tagline {
  font-size: 12px;
  line-height: 1.5;
  color: var(--muted);
}
/*
 * 附件区（自由模式）。
 *
 * 这块此前完全没有样式 —— 类名在 views.js 里用了，styles.css 里一条都没有，
 * 于是它渲染成一堆裸按钮和裸文字。自由模式改成本轮的默认形态之后它就在首屏，
 * 必须给它一个明确的边界，让「这一块是附件、和上面的专家条不是一回事」一眼可分。
 */
.attach-panel {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  background: var(--surface-alt);
}
.attach-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.attach-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 8px;
}
.attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 6px 3px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-size: 12px;
  color: var(--text-soft);
}
.attach-x {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
  color: var(--muted);
}
.attach-x:hover {
  color: var(--danger);
}
.scene-fields {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-alt);
  padding: 10px 12px;
}
.scene-fields-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  font-size: 13px;
}
.scene-fields-head strong {
  color: var(--text);
}
.scene-fields-head .hint {
  flex: 1;
}
.scene-field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 8px;
}
.scene-field {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.scene-field-label {
  font-size: 12px;
  color: var(--text-soft);
  display: flex;
  align-items: center;
  gap: 5px;
}
.scene-field-label em {
  font-style: normal;
  color: var(--danger);
  font-size: 11px;
}
.scene-field .input {
  width: 100%;
}

/* 格式契约预览：发送前就能看见会得到什么结构 */
.contract-box {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 8px 12px;
  font-size: 13px;
}
.contract-box > summary {
  cursor: pointer;
  color: var(--text-soft);
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none;
}
.contract-box > summary::-webkit-details-marker {
  display: none;
}
.contract-box > summary::after {
  content: '展开';
  margin-left: auto;
  font-size: 12px;
  color: var(--ai);
}
.contract-box[open] > summary::after {
  content: '收起';
}
.contract-badge {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--ai-soft);
  color: var(--ai);
  font-size: 12px;
  font-weight: 600;
}
.contract-outline {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}
.contract-node {
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  color: var(--text-soft);
  font-size: 12px;
}
.contract-node.lv1 {
  border-color: var(--ai);
  color: var(--ai);
  font-weight: 600;
}
.contract-node.lv3 {
  color: var(--muted);
  font-size: 11px;
}
.contract-node.table {
  border-style: dashed;
  border-color: var(--success);
  color: var(--success);
}

/* 头部「快速模型 / 深度思考」开关 */
.chip-toggle {
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-soft);
  cursor: pointer;
  font-size: 12px;
  white-space: nowrap;
  transition: all 0.15s ease;
}
.chip-toggle:hover {
  border-color: var(--ai);
  color: var(--ai);
}
.chip-toggle.on {
  background: var(--ai);
  border-color: var(--ai);
  color: #fff;
  font-weight: 600;
}
.chip-toggle.muted {
  opacity: 0.55;
  border-style: dashed;
}

/* 流水线阶段标记与串联按钮 */
.stage-tag {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--ai-soft);
  color: var(--ai);
  font-size: 11px;
}

/*
 * 「↳ 续写」标识（只在续写回合渲染，见 views.js 的 msg-role）：
 * 用户点「继续生成」后，新消息开头与上文的重叠已被裁掉 —— 这个标识说明
 * "这条是接上一条写的"，否则用户会以为模型莫名其妙又起了一段。
 */
.continue-tag {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--surface-alt);
  color: var(--muted);
  border: 1px solid var(--border);
  font-size: 11px;
}

/* 后台表单分区标题 */
.form-section-title {
  margin: 14px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
}
.form-section-title:first-child {
  margin-top: 0;
}

/* 提示词预览与契约示例 */
.prompt-preview,
.contract-sample {
  max-height: 320px;
  overflow: auto;
  margin: 8px 0 0;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
}
.contract-sample {
  max-height: 220px;
  color: var(--text-soft);
}

@media (max-width: 760px) {
  .scene-field-grid {
    grid-template-columns: 1fr;
  }
}

/* ===================================================================
   新增：来源标识 / 用量分栏 / 生成提示语 / 技能资料上传 / 返回对话
   =================================================================== */

/* 「暂无配置」必须是警告色：它不是中性信息，而是下一次点击会失败的预告 */
.badge.warn {
  /* 警告色的**底**走变量（原来是硬编码 #fff7ed，深色下是一块亮斑） */
  background: var(--accent-soft);
  border-color: var(--accent-border);
  color: var(--warn);
  font-weight: 600;
}

/* 顶栏「返回对话」：任何模块下位置固定，用户不用找 */
.back-to-chat {
  color: #d8dde5 !important;
  border-color: rgba(255, 255, 255, 0.22) !important;
  background: rgba(255, 255, 255, 0.08);
  font-weight: 600;
}
.back-to-chat:hover {
  background: rgba(255, 255, 255, 0.16) !important;
  color: #fff !important;
}

/* ---------- 用量：平台 / 个人接入 分栏 ---------- */
.usage-split {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-bottom: 22px;
}
.usage-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 18px 20px;
  box-shadow: var(--shadow);
  position: relative;
  overflow: hidden;
}
.usage-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
}
.usage-card.is-platform::before {
  background: linear-gradient(90deg, #059669, #34d399);
}
.usage-card.is-personal::before {
  background: linear-gradient(90deg, var(--ai), #818cf8);
}
.usage-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  font-size: 14px;
}
.usage-card-body {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.usage-card-body > div {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.usage-card-body span {
  font-size: 11.5px;
  color: var(--muted);
}
.usage-card-body b {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* ---------- 生成期间的欢迎语（替代推理文案） ---------- */
/*
 * 推理过程默认不外露：那是模型的草稿，对用户是噪声。
 * 这里给一句有人味的话，让"空窗期"变成"它在忙"。
 */
.stream-greeting {
  margin-bottom: 10px;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: var(--ai-soft);
  border: 1px solid var(--ai-border);
  color: #4c4e8a;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.stream-greeting::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ai);
  animation: pulse 1.2s ease-in-out infinite;
  flex: 0 0 auto;
}

/* ---------- 后台：技能资料上传 ---------- */
.docs-upload {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface-alt);
  padding: 12px 14px;
  margin-bottom: 12px;
}
.docs-upload-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.docs-upload-row .hint {
  flex: 1;
  min-width: 200px;
  line-height: 1.6;
}
.docs-upload .textarea {
  font-size: 12px;
  line-height: 1.6;
}
.docs-summary {
  margin: 0 0 10px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 12.5px;
  line-height: 1.7;
  text-align: center;
}
.docs-summary.is-pending {
  background: rgba(245, 158, 11, 0.12);
  color: #b45309;
}
.docs-summary.is-error {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}
.docs-summary.is-success {
  background: rgba(5, 150, 105, 0.12);
  color: #047857;
}

/* 模型徽章在模型选择行里与下拉框对齐 */
.chat-head-tools .badge {
  white-space: nowrap;
}

/* 后台模型目录表格里的等宽标识列 */
.data-table code.mono {
  word-break: break-all;
}

@media (max-width: 760px) {
  .usage-card-body {
    grid-template-columns: 1fr 1fr;
  }
  .page {
    padding: 20px 16px 32px;
  }
  .chat-scroll {
    padding: 16px 12px 20px;
  }
  .composer-wrap {
    padding: 10px 12px 12px;
  }
}

/* ==================================================================
 * UI 重构（2026-09-21）：把纵向空间还给对话内容
 *
 * 三条贯穿全篇的原则，改动都从它们出发：
 *   1. **解释性文字不常驻** —— 只在出错或需要用户决策时才出现；
 *   2. **能合并成一行就合并** —— 顶栏、工具行、输入框各让出一行；
 *   3. **图标承担识别，文字承担确认** —— 高频动作给图标，低频/危险动作才给文字。
 * ================================================================== */

/* ---------- 图标 ---------- */

.icon {
  width: 16px;
  height: 16px;
  flex: none;
  display: block;
}
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: transparent;
  color: var(--text-soft);
  cursor: pointer;
  flex: none;
  transition: background .15s, color .15s, border-color .15s;
}
.icon-btn:hover {
  background: var(--surface-alt);
  border-color: var(--border);
  color: var(--text);
}
.icon-btn.on {
  background: var(--ai-soft);
  border-color: var(--ai-border);
  color: var(--ai);
}
.icon-btn.muted {
  color: var(--border-strong);
}
.icon-btn:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/*
 * 顶栏里**文字型**控件（开发者模式勾选框、「导出 SKILL」按钮）。
 *
 * 真实缺陷（2026-09-26 用户报「开发者按钮和导出 skill 按钮**完全重叠**」）：
 * 这两个控件都套了 `.icon-btn` —— 那是**固定 30×30 的图标按钮**类（`width:30px;height:30px`）。
 * 图标按钮装一个字没问题，但它们装的是**文字**（「开发」/「导出 SKILL」）：
 * 文字**溢出**各自的 30px 盒子，而两个盒子只隔 6px ⇒ 两段文字在视觉上**压在一起**，
 * 看起来就是"完全重叠"（不是定位问题 —— 两者都是正常流的 flex 项，谁也没 absolute）。
 *
 * 修法是**换基类语义**而不是挪像素：文字型控件宽度随内容、禁止压缩、禁止溢出。
 * 结构上不可能重叠：盒子 = 内容宽度，`flex:none` 保证不被压扁到 0，
 * 窄屏放不下时由父级 `.chat-head-tools` 的 `flex-wrap:wrap` **换行**（换行 ≠ 重叠）。
 */
.chat-head-tools .head-text-btn {
  width: auto;
  height: 30px;
  min-width: 0;
  padding: 0 8px;
  gap: 6px;
  white-space: nowrap;
  overflow: visible;
  flex: none;
  /* 明确禁止用绝对定位来"解决"排版 —— 一旦有人这么写，门禁会红（见 preflight 的结构性断言） */
  position: static;
}

/* ---------- 顶栏：只留开合侧栏与品牌 ---------- */

.topbar {
  height: 44px;
  padding: 0 10px;
  gap: 8px;
}
.topbar .brand {
  font-size: 13.5px;
}

/* ---------- 对话头部：标题 + 模式选择器 + 图标工具 ---------- */

.chat-head {
  padding: 8px 14px;
  gap: 10px;
  flex-wrap: nowrap;
}
.chat-head-main {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1;
}
.chat-title {
  font-size: 14.5px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 320px;
}
.chat-head-tools {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: none;
}

/* 模型入口：一枚图标 + 一枚来源状态点 */
.model-btn {
  position: relative;
}
.model-btn-dot {
  position: absolute;
  right: 3px;
  bottom: 3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  border: 1.5px solid var(--surface);
}
.model-btn-dot.ok { background: var(--success); }
.model-btn-dot.ai { background: var(--ai); }
.model-btn-dot.warn { background: var(--warn); }

/* ---------- 输入区：发送按钮进输入框、工具行在下 ---------- */

.composer-wrap {
  padding: 8px 16px 12px;
}
.composer {
  /*
   * ⚠️ **真正生效的是这一条**（上面那条同名 `.composer` 是早期规则，被本条覆盖 —— 改错地方会"看起来没改"）。
   *
   * 判据历史：2026-09-29 早先我按"不要那个很浅的直角框"把它**去盒**了；**同日用户更正**：
   * 「删错了 —— 我要保留这个**圆角灰底**，删除他的下一层（直角、半透明的底部带）」。
   * 那层带在 `.composer-wrap` 上，不在 `.composer` 上。⇒ 这里**恢复原值**
   * （取值来源：`git show b937e20^:web/styles.css`，未手写猜测）。
   */
  position: relative;
  display: block;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--shadow);
  transition: border-color .15s, box-shadow .15s;
}
.composer:focus-within {
  border-color: var(--ai);
  box-shadow: 0 0 0 3px var(--ai-soft);
}
.composer textarea {
  display: block;
  width: 100%;
  border: 0;
  outline: 0;
  resize: none;
  background: transparent;
  font-family: inherit;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--text);
  padding: 13px 52px 13px 15px;
  max-height: 220px;
  overflow-y: auto;
}
.composer textarea::placeholder {
  color: var(--muted);
}
/* 发送按钮落在输入框内部右下角：它跟着输入框走，不再单独占一行 */
.composer-send {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: 10px;
  background: var(--ai);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .15s, transform .1s;
}
.composer-send:hover { background: #372bb0; }
.composer-send:active { transform: scale(.96); }
.composer-send:disabled {
  background: var(--border-strong);
  cursor: not-allowed;
}
.composer-send.is-stop { background: var(--danger); }
.composer-send.is-stop:hover { background: #991b1b; }

/* 工具行：原来的「发送后自动创建新对话」那一行的位置 */
.composer-tools {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 4px 0;
  min-height: 34px;
}
.tool-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--surface);
  color: var(--text-soft);
  cursor: pointer;
  flex: none;
  transition: background .15s, color .15s, border-color .15s;
}
.tool-btn:hover {
  background: var(--surface-alt);
  color: var(--ai);
  border-color: var(--ai-border);
}
.tool-btn:disabled { opacity: .45; cursor: not-allowed; }
.tool-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 26px;
  padding: 0 4px 0 10px;
  border-radius: 999px;
  background: var(--ai-soft);
  border: 1px solid var(--ai-border);
  color: var(--ai);
  font-size: 12.5px;
  font-weight: 600;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tool-chip-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
  opacity: .6;
}
.tool-chip-x:hover { opacity: 1; background: var(--ai-border); }
.tool-chip-x .icon { width: 11px; height: 11px; }
.composer-status {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--muted);
}

/* ---------- 附件区：无附件时不占高度 ---------- */

.attach-panel.is-empty {
  height: 0;
  padding: 0;
  margin: 0;
  overflow: hidden;
}
.attach-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 8px;
}
.attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 9px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  font-size: 12px;
  color: var(--text-soft);
  max-width: 260px;
}
.attach-chip .icon {
  width: 13px;
  height: 13px;
  color: var(--muted);
  flex: none;
}
/*
 * 截断只作用在**文件名这一层**，绝不作用在整条 chip 上。
 *
 * 这里踩过一个把「删除」整块吃掉的坑：原先在 `.attach-chip` 上写了
 * `overflow: hidden`，而 ✕ 又是 chip 的最后一个子元素 —— 文件名一长，
 * ✕ 就被裁到可视区之外（几何位置仍在，但那一坐标上最上层的元素变成了父容器），
 * 于是**点 ✕ 什么也不会发生**，用户看到的就是"附件根本没有删除功能"。
 * 实测判据：长文件名下用 elementFromPoint 取 ✕ 中心，落点必须是按钮自身。
 */
.attach-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.attach-x {
  border: 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 11px;
  padding: 0 2px;
  /* 不能被文件名挤走，也不参与截断 */
  flex: 0 0 auto;
}
.attach-x:hover { color: var(--danger); }

/* ---------- 欢迎态：中央输入框 + 下方 agent ---------- */

.chat-view.is-empty .chat-scroll {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.welcome-hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 12px 0 18px;
}
/*
 * 自由对话（**不画插图**）时的欢迎区：那条 96px 的插图没了之后，标题会贴到顶边 ——
 * 补一点上间距让它仍然"站得住"，而不是看起来像被裁掉了半截（位置类核对见报告里的截图与断言）。
 */
.welcome-hero-plain {
  padding-top: 28px;
}
.hero-art {
  width: 96px;
  height: 96px;
}
.hero-art svg {
  width: 100%;
  height: 100%;
  display: block;
}
.hero-title {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: .2px;
  color: var(--text);
}
/* 欢迎态的输入框比对话态略宽、居中，与上面插图对齐 */
.composer-wrap.is-center {
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  padding: 0 16px;
}
.composer-wrap.is-center .composer {
  border-radius: 18px;
}
.composer-wrap.is-center .composer textarea {
  padding: 15px 54px 15px 17px;
  font-size: 15px;
}
/*
 * 欢迎态输入框不再挂在底部分区里，而是插在标题与卡片之间
 * （ChatGPT 新聊天页的排法）。slot 只负责居中与留白，
 * 输入框自身的样式仍由 .composer-wrap.is-center 负责。
 */
.composer-slot {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 8px 16px 0;
}

.welcome-agents {
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  padding: 18px 16px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.agent-group-title {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--muted);
  margin-bottom: 7px;
  text-transform: uppercase;
}
.agent-group-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px;
}
.agent-card {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 13px;
  background: var(--surface);
  cursor: pointer;
  font-family: inherit;
  transition: border-color .15s, box-shadow .15s, transform .1s;
}
.agent-card:hover {
  border-color: var(--ai-border);
  box-shadow: var(--shadow);
}
.agent-card:active { transform: scale(.99); }
.agent-card-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.agent-card-name {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.agent-card-sub {
  font-size: 12px;
  color: var(--muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 超级智脑单独一张卡：它有插图、并占据整行，因为它是管理员开通的一项服务 */
.agent-card.is-studio {
  grid-column: 1 / -1;
  border-color: var(--ai-border);
  background: var(--ai-soft);
}
.agent-card.is-studio .agent-card-name {
  font-size: 15px;
}
.agent-card-art {
  width: 40px;
  height: 40px;
  flex: none;
}
.agent-card-art svg { width: 100%; height: 100%; display: block; }

/* ---------- 侧栏底部：个人中心 ---------- */

.sidebar-foot {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
}
.account {
  border-top: 1px solid var(--border);
  padding-top: 8px;
  position: relative;
}
.account-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 8px;
  border: 1px solid transparent;
  border-radius: 11px;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: background .15s, border-color .15s;
}
.account-btn:hover {
  background: var(--surface-alt);
  border-color: var(--border);
}
.account-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  /* 头像默认用平台图标（与个人中心同一张图；这里小一圈，圆形裁切） */
  object-fit: cover;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  background: var(--surface-alt);
}
.account-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
.account-mail {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-sub {
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.account-balance {
  font-size: 12px;
  font-weight: 650;
  color: var(--text-soft);
  flex: none;
}
.account-chevron {
  width: 13px;
  height: 13px;
  color: var(--muted);
  flex: none;
}
/* 面板向上展开：它在页面最底部，向下会超出视口 */
.account-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  z-index: 40;
  display: flex;
  flex-direction: column;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}
.account-panel.is-hidden { display: none; }
.account-panel button {
  display: flex;
  align-items: center;
  gap: 8px;
  text-align: left;
  padding: 8px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text-soft);
  font-family: inherit;
  font-size: 13px;
  cursor: pointer;
}
.account-panel button:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.account-panel button .icon {
  width: 14px;
  height: 14px;
}
.account-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 10px;
  font-size: 12.5px;
  color: var(--muted);
}
.account-row strong {
  color: var(--text);
  font-weight: 650;
}
.account-row strong.is-warn { color: var(--warn); }

/* ---------- 拖动上传：只有输入区接得住高亮 ---------- */

.composer-wrap.is-dragover .composer {
  border-color: var(--ai);
  box-shadow: 0 0 0 3px var(--ai-soft);
}
.composer-wrap.is-dragover .composer-tools {
  position: relative;
}
.composer-wrap.is-dragover .composer-tools::after {
  content: '松开即可上传';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--ai);
  background: var(--ai-soft);
  border-radius: 8px;
}

/* ---------- 模型选择面板 ---------- */

.model-panel {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: 60vh;
  overflow-y: auto;
}
.model-section-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 8px;
}
.model-section-head strong {
  font-size: 13.5px;
  color: var(--text);
}
.model-section-head span {
  font-size: 11.5px;
  color: var(--muted);
}
.model-group + .model-group {
  margin-top: 10px;
}
.model-group-title {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--muted);
  margin-bottom: 5px;
}
.model-options {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.model-option {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 11px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  font-family: inherit;
  font-size: 13px;
  color: var(--text-soft);
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.model-option:hover {
  border-color: var(--ai-border);
  color: var(--text);
}
.model-option.on {
  border-color: var(--ai);
  background: var(--ai-soft);
  color: var(--ai);
  font-weight: 650;
}
.model-option-credits {
  font-size: 11px;
  color: var(--muted);
}
.model-empty {
  font-size: 12.5px;
  color: var(--muted);
  padding: 8px 10px;
  border: 1px dashed var(--border);
  border-radius: 10px;
}
.model-panel-foot {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--muted);
}


/* ==================================================================
 * 第 9 轮：品牌下移 / 标签进输入框 / 生成中可打断 / 上翻压缩
 * ================================================================== */

/* ---------- 侧栏品牌 ---------- */

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 12px 12px 10px;
}
.sidebar-brand-text {
  font-size: 13.5px;
  font-weight: 650;
  color: var(--text);
  letter-spacing: .2px;
}
/* 品牌与「新建对话」分开一点：顺序是"这是哪儿" → "从哪儿开始" */
.sidebar-section {
  margin-top: 2px;
}

/* ---------- 输入框：内部改成「正文 + 底部一行」 ---------- */

.composer {
  display: flex;
  flex-direction: column;
  position: relative;
}
.composer textarea {
  /* 右侧不再给发送键留位：它已经移到下面那一行里了 */
  padding: 12px 14px 4px;
  max-height: 200px;
  overflow-y: auto;
}
/* 底部一行：左是标签（附件 / 专家），右是动作。两者同排 —— 这就是"发送键左边" */
.composer-foot {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 0 8px 8px 10px;
  min-height: 42px;
}
.composer-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  margin-left: auto;
}
/* 发送按钮不再绝对定位：它现在是这一行里的一个普通元素 */
.composer-send {
  position: static;
  right: auto;
  bottom: auto;
}
.composer-send.is-interrupt {
  background: var(--text-soft);
}
.composer-send.is-interrupt:hover {
  background: var(--text);
}

/* 标签容器：空着时高度为 0，不占位置；有内容时自动换行 */
.composer-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  flex: 1;
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
}
.composer-tags .attach-chip {
  padding: 3px 5px 3px 8px;
  font-size: 11.5px;
  max-width: 200px;
  background: var(--surface-alt);
}
.composer-tags .attach-chip .icon {
  width: 12px;
  height: 12px;
}
.composer-tags .tool-chip {
  height: 24px;
  font-size: 12px;
  padding: 0 4px 0 9px;
}

/* ---------- 上翻历史时压缩输入框 ---------- */

.composer-wrap.is-compact .composer textarea {
  /* 压到一行：用户此刻在往回读，输入框里的内容不是他关心的东西 */
  height: 48px !important;
  overflow-y: hidden;
}
.composer-wrap.is-compact .composer-tags {
  /* 没有附件时这一行本来就是空的：收起它，给历史让出空间 */
  display: none;
}
/*
 * 但有附件时**必须留着**（真实反馈："文件拖入之后没有显示标识，但发送之后发现确实有文件"）：
 * 上翻历史会让输入框进入收起态，而附件的标签原本被一起藏了 ——
 * "这一条会带什么出去"恰恰是此刻最该看见的信息，藏起来等于让用户盲发。
 * 收起态里只藏"跟哪位专家"那枚，附件标签照常显示。
 */
.composer-wrap.is-compact .composer-tags.has-items {
  display: flex;
}
.composer-wrap.is-compact .composer-tags.has-items .tool-chip {
  display: none;
}

/* ---------- 模型面板：先来源、再厂商、再模型 ---------- */

.source-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.source-btn {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  font-family: inherit;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.source-btn strong {
  font-size: 13.5px;
  color: var(--text);
}
.source-btn span {
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.4;
}
.source-btn:hover {
  border-color: var(--ai-border);
}
.source-btn.on {
  border-color: var(--ai);
  background: var(--ai-soft);
}
.source-btn.on strong {
  color: var(--ai);
}

.provider-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.provider-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font-family: inherit;
  font-size: 12.5px;
  color: var(--text-soft);
  cursor: pointer;
  transition: border-color .15s, background .15s, color .15s;
}
.provider-chip:hover { border-color: var(--ai-border); color: var(--text); }
.provider-chip.on {
  border-color: var(--ai);
  background: var(--ai-soft);
  color: var(--ai);
  font-weight: 650;
}
/* 已配置用一枚小圆点表示：它只回答"这一项能不能用"，不需要占一行字 */
.provider-chip .ready-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--success);
  flex: none;
}

.model-option-flag {
  font-size: 10.5px;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.model-option-flag.ok {
  color: var(--success);
  background: rgba(22, 163, 74, .08);
}
.model-option-flag.warn {
  color: var(--warn);
  background: rgba(217, 119, 6, .1);
}

.panel-hint {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-alt);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-soft);
}

/* ==================================================================
 * 第 10 轮：顶栏删除 / 侧栏自持开合 / 欢迎页三卡片位（后端可排版）
 * ================================================================== */

/* ---------- 欢迎页卡片位：ChatGPT 新聊天页的排法 ---------- */

/*
 * 等宽三列、随容器换行（管理员配到 4-6 位时自动落到第二行）。
 * 卡片之间留足空隙：它们是这一屏真正要被点的东西。
 */
.welcome-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  width: 100%;
  /* 卡片高度由自身 aspect-ratio 决定，若用默认的 stretch，网格会把高度拉齐、aspect-ratio 就失效了 */
  align-items: start;
}
@media (max-width: 860px) {
  .welcome-cards {
    grid-template-columns: 1fr;
  }
}

.welcome-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 16px 16px 14px;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
  /*
   * 卡片高度固定成 --welcome-card-ratio（视图层按 WELCOME_CARD_RATIO 注入，见 welcomeAssets.js）。
   * 为什么必须固定：全铺卡（.has-custom-art）的图像就是整张卡，卡片比例**就是**图像渲染比例；
   * 卡片高度若随副标题行数浮动，用户在裁切器里"保留哪一块"的选择到界面上就对不上 ⇒ 裁切变摆设。
   * 🔴 刻意的**不写 fallback 比例**：一旦写死一个数字，CSS 里就出现第二份比例真相
   *（preflight 有规则钉住"CSS 不得出现比例字面量"）。变量缺省时该声明无效、卡片回落自适应高度。
   */
  aspect-ratio: var(--welcome-card-ratio);
}
.welcome-card:hover {
  border-color: var(--accent);
  box-shadow: 0 4px 16px rgba(15, 23, 42, .08);
  transform: translateY(-1px);
}
/* 选中态：与头部模式选择器互为映照，用 is-on 而不是 .on —— 后者是模式按钮的语义 */
.welcome-card.is-on {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset, 0 4px 16px rgba(15, 23, 42, .08);
}

/* 「试试」角标：与 ChatGPT 卡片同义 —— 点一下就能用，不是装饰 */
.welcome-try {
  position: absolute;
  top: 10px;
  right: 10px;
  font-size: 11px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 8px;
  background: var(--surface-alt);
}
.welcome-card:hover .welcome-try {
  color: var(--accent);
  border-color: var(--accent);
}

/*
 * 卡片「大背景」：插画不再是角落里的小图标，而是占满卡片上部的横幅 ——
 * 全宽、浅渐变衬底、大尺寸图形。它就是用户说的"背景"：一眼分清三张卡，
 * 同时不挤压下方标题与一句话说明的阅读。
 */
.welcome-card .agent-card-art {
  width: 100%;
  height: 88px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: linear-gradient(180deg, var(--surface-alt) 0%, transparent 100%);
}
/*
 * 三种卡片的大背景必须一眼分得开（需求：自由对话 / 专家 / 超级智脑各有各的背景）：
 * 插图本身区分类型，衬底再按类型 + 排位铺不同的浅色渐变 ——
 * 退化排版下连续多张同类型卡片（比如三位专家）也会因 tint 序号不同而分开。
 */
.welcome-card.is-kind-studio .agent-card-art {
  background: linear-gradient(180deg, rgba(139, 92, 246, 0.16) 0%, transparent 100%);
}
.welcome-card.is-kind-skill .agent-card-art {
  background: linear-gradient(180deg, rgba(14, 165, 233, 0.14) 0%, transparent 100%);
}
.welcome-card.is-kind-direct .agent-card-art {
  background: linear-gradient(180deg, rgba(16, 185, 129, 0.14) 0%, transparent 100%);
}
.welcome-card.tint-1 .agent-card-art {
  background: linear-gradient(180deg, rgba(236, 72, 153, 0.10) 0%, transparent 100%);
}
.welcome-card.tint-2 .agent-card-art {
  background: linear-gradient(180deg, rgba(245, 158, 11, 0.10) 0%, transparent 100%);
}
.welcome-card .agent-card-art svg {
  width: 68px;
  height: 68px;
}
/*
 * 管理员上传的自定义背景图（替换内置插画）。
 * 铺满整条 88px 横幅，用 object-fit: cover 裁掉溢出部分 —— 用户传的图比例各异，
 * 不留白边、也不拉伸变形。圆角与横幅一致，避免图角戳出卡片底框。
 */
.welcome-card .agent-card-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 10px;
  display: block;
}

/*
 * ── 全铺（full-bleed）：配了自定义背景图的卡片 ──────────────────────────────
 *
 * 用户原话：「这个卡片框的 UI 限制你还没告诉我呢，我要**全显示**的效果、
 * **不需要有白色安全粗框**」。
 *
 * 现状（要改掉的）：专家卡是「白底卡 + 内嵌 88px 横条图」，图外面裹着一圈白边 ——
 *   白边来自三层：`.welcome-card` 的 `background: var(--surface)`、`border: 1px`、
 *   以及 `padding: 16px 16px 14px`；再加 `.agent-card-art` 的 10px 圆角 ⇒
 *   图被裁成横条、还包着白框。
 *
 * 改法：**仅当**卡片配了自定义图（视图层按 `card.image` 打上 `.has-custom-art`）时，
 * 把图从"内嵌横条"提升为"整张卡的底"：卡片本身去背景色、去描边，
 * 图所在的 `.agent-card-art` 绝对定位铺满整卡，图 `object-fit: cover` 铺满不拉伸。
 * 没配图的卡片**不带这个类** ⇒ 下面每一条都不命中，外观逐字不变。
 *
 * 🔴 刻意**不加任何白色/浅色遮罩** —— 用户明确不要"白色安全框"，文字直接压在图上。
 *     （文字可读性由管理员选图时自控，不靠我们再盖一层白。）
 */
.welcome-card.has-custom-art {
  background: none;   /* 去掉白底：图要铺到卡边，白底会在图外露一圈 */
  border: none;       /* 去掉描边：那圈白框正是用户嫌的那道"安全粗框" */
  overflow: hidden;   /* 图不许戳出卡片的 14px 圆角 */
  /* padding 刻意**保留**：铺满的是"图"，文字仍要留白才读得清 */
}
.welcome-card.has-custom-art .agent-card-art {
  position: absolute; /* 脱离文档流 ⇒ 相对卡片铺满（inset:0 四边贴边） */
  inset: 0;
  height: auto;       /* 覆盖基线的 88px：让它等于卡片高，而不是固定横条 */
  border-radius: 0;   /* 圆角交给卡片的 overflow:hidden 裁，避免图角与卡角两层圆角 */
  background: none;   /* 去掉按 kind/tint 铺的浅色渐变衬底（有图就不需要了） */
  display: block;     /* 覆盖基线的 grid;place-items:center */
  z-index: 0;         /* 明确压在最底层，让文字提到它上面 */
}
.welcome-card.has-custom-art .agent-card-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;  /* 铺满整卡且不拉伸变形（比例不符时裁掉溢出部分） */
  border-radius: 0;
}
/*
 * 🔴 顺序敏感：`.agent-card-art` 在 DOM 里**排在** `.welcome-try` / `.agent-card-main` **之前**，
 * 它一旦 `position: absolute` 就会盖住后面的文字。所以把文字与角标抬到图的上面。
 *
 * 🔴 这两条**必须分开写**，不能合并成一条 `{ position: relative; z-index: 1 }`：
 *   · `.welcome-try` 基线**本就是 `position: absolute`**（钉在卡片右上角）。
 *     若在这里给它也写 `position: relative`，它会**掉回文档流**、窜到左上角，
 *     并把下面的标题一起往下挤 —— 实测复现过（截图里「试试」从右上跑到左上，
 *     全铺卡文字整体下移 ~30px）。它只需要补 `z-index`。
 *   · `.agent-card-main` 基线是 static，**必须**改成 relative 才受 `z-index` 支配。
 */
.welcome-card.has-custom-art .welcome-try {
  z-index: 1;
}
.welcome-card.has-custom-art .agent-card-main {
  position: relative;
  z-index: 1;
}

/*
 * ── 裁切器（web/js/cropEditor.js）────────────────────────────────────────────
 * 取景框的比例由 JS 用**常量** WELCOME_CARD_RATIO 内联设进 style.aspectRatio
 * （与卡片同源）；CSS 里刻意不写比例 —— 否则这里就多出第二份比例真相。
 * 框 overflow:hidden：图放大到超出框时被裁掉，正好呈现"最终会保留哪一块"。
 */
.crop-editor {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.crop-stage {
  display: flex;
  justify-content: center;
}
.crop-frame {
  position: relative;
  width: 100%;
  max-width: 340px;
  overflow: hidden;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  /* 触屏拖动不被页面的滚动 / 缩放抢走手势 */
  touch-action: none;
  cursor: grab;
}
.crop-frame.is-dragging {
  cursor: grabbing;
}
.crop-img {
  position: absolute;
  left: 0;
  top: 0;
  max-width: none; /* 覆盖可能的全局 img 约束，保证能放大到超出框 */
  user-select: none;
  -webkit-user-drag: none;
}
.crop-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: space-between;
}
.crop-zoom {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
}
.crop-zoom > span {
  font-size: 12px;
  color: var(--muted);
}
.crop-zoom input[type='range'] {
  flex: 1;
}

/*
 * ── 裁切器「文字安全区」示意层 ──────────────────────────────────────────────
 * 卡片是全铺的：图当整张卡的底、文字直接压在图上。用户在裁切器里看不到文字会盖在哪，
 * 很容易把主体正好裁到标题位置。所以按 welcomeAssets.js 的**比例常量**
 * （WELCOME_SAFE_*）在取景框里画两块虚线带：标题带 / 副标题带。
 *
 * 🔴 克制：**细虚线边界 + 极低不透明度的文字块**（深色、不是白色）—— 用户明确
 *    不要"白色安全框"；这里若铺一层白或高不透明度遮罩，等于把不要的白框又加回来。
 * 🔴 `pointer-events: none` 必须保留：示意层盖在图上，若吃掉鼠标事件就**拖不动图**了。
 * 位置（left/top/width/height）由 JS 按常量内联 —— CSS 里不写任何比例，避免第二份真相。
 */
.crop-safe {
  position: absolute;
  inset: 0;
  pointer-events: none; /* 示意层盖在图上，但绝不吃鼠标事件（否则拖动/缩放全失效） */
  z-index: 2; /* 在 .crop-img（position:absolute，z-index auto）之上 */
}
.crop-safe-band {
  position: absolute;
  border: 1px dashed rgba(255, 255, 255, .85);
  border-radius: 3px;
  padding: 2px 4px;
  font-size: 10px;
  line-height: 1;
  color: rgba(255, 255, 255, .92);
  /* 极低不透明度的**深色**文字块（不是白遮罩）：只为让字标可读，不改变底下图的观感 */
  background: rgba(0, 0, 0, .18);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
  overflow: hidden;
}
/* 一键隐藏：用户想干净看原图时（按钮在 cropEditor.js 里切换 is-safe-off） */
.crop-frame.is-safe-off .crop-safe {
  display: none;
}

/*
 * 卡片里的大背景是 88px 高的横幅（超级智脑那两张是 68px 的插图），
 * 所以字标在**卡片里**要放大到同一体量 —— 38px 那档是给别处小图标用的。
 */
.welcome-card .agent-card-char {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  font-size: 24px;
}
/* 专家卡片的字标：一枚圆底首字，与超级智脑的插图在体量上对齐 */
.agent-card-char {
  width: 38px;
  height: 38px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  font-size: 17px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(140deg, var(--accent), #0ea5e9);
}
/*
 * 专家主视觉（对话页顶部那块大的）：同步**当前所选专家**
 * （选了显示首字徽标 + 名称；没选显示「请选择专家」）。
 * 注意：欢迎页那三张卡**不能**用它 —— 卡片的图必须由卡片自己的 slug 决定，
 * 否则两张专家卡会画成同一个（2026-09-22 用户报的"背后图像都一样"）。
 */
.skill-art-empty,
.skill-art-picked {
  display: grid;
  place-items: center;
  gap: 4px;
  width: 100%;
}
.skill-art-empty svg {
  width: 50px;
  height: 50px;
  opacity: 0.5;
}
.skill-art-hint {
  font-size: 12px;
  color: var(--text-soft);
}
.skill-art-badge {
  width: 44px;
  height: 44px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  font-size: 19px;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(140deg, var(--accent), #0ea5e9);
}
.skill-art-name {
  font-size: 12px;
  color: var(--text-soft);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* WorkBuddy 平台积分的标识：与本站积分刻意不同色，避免两个口径被当成同一个数 */
.wb-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #0f766e;
  font-weight: 600;
}
.wb-flag::before {
  content: "WB";
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  height: 16px;
  padding: 0 4px;
  border-radius: 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #fff;
  background: linear-gradient(140deg, #0d9488, #0ea5e9);
}
.welcome-card .agent-card-name {
  font-size: 14px;
  font-weight: 650;
  color: var(--text);
}
.welcome-card .agent-card-sub {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- 管理后台「欢迎页排版」 ---------- */

.welcome-admin-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.welcome-admin-card {
  padding: 12px;
  border-bottom: 1px solid var(--border);
}
.welcome-admin-card:last-of-type {
  border-bottom: 0;
}
.welcome-admin-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.welcome-admin-head .hint {
  flex: 1;
}
.welcome-admin-ops {
  display: inline-flex;
  gap: 6px;
}
.welcome-admin-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 14px;
}
.welcome-admin-grid .field span {
  display: block;
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 4px;
}
.welcome-admin-check {
  display: flex;
  align-items: center;
  gap: 8px;
}
@media (max-width: 860px) {
  .welcome-admin-grid {
    grid-template-columns: 1fr;
  }
}
/* 卡片背景图的上传与预览（后台「欢迎页排版」每个卡片位一块） */
.welcome-admin-art {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  /* 隐藏的 file input 用绝对/裁剪方式藏，仍可被按钮 .click() 触发 */
  position: relative;
}
.welcome-admin-art-img {
  width: 132px;
  height: 74px;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
}
.welcome-admin-art-empty {
  width: 132px;
  height: 74px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  border: 1px dashed var(--border);
  color: var(--muted);
  font-size: 12px;
  background: var(--surface-alt);
}
.welcome-admin-art-ops {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
/*
 * 原生 file input 长得没法统一，界面上只保留触发它的按钮。
 * 用"裁到像素级 + 透明"而不是 display:none —— 后者在个别浏览器里
 * 会让程序化 .click() 打开不了文件选择框。
 */
.welcome-admin-art-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
}

/* ---------- 输入区常驻底部悬浮（ChatGPT 同款布局） ---------- */

/*
 * 输入区从滚动区里移出来，成为 chat-view 的第三个固定分区：
 * 头部 → 滚动区（消息）→ 悬浮输入区。无论滚到哪里，输入框始终在底部一行。
 */
.chat-view {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.chat-scroll {
  flex: 1 1 auto;
  min-height: 0;
}
.composer-dock {
  /*
   * ② 2026-09-29 用户报：「对话框背后还是有矩形框垫了一层背景」。
   * 先量再改（composer 子树测量见报告）：那一层就是这里的**硬编码浅色渐变**
   * `rgba(248,250,252,.92) → #ffffff` —— 深色下它是**一条与页面同宽、颜色不同的矩形带**，
   * 与用户描述逐字吻合。（`[data-theme='dark']` 只覆盖变量 ⇒ 硬编码渐变整条逃逸。）
   * 改法：渐变去掉、改用**与页面同底**（`--bg`）。
   *
   * ⚠️ 2026-09-29 追加「上分界线也不需要」：这条 `border-top` 与 `.composer-wrap` 那条是**同一种**
   * 分隔线（对话区到输入区的分界），按**同一意图**一并去掉 —— 否则聊天态仍会有一道线，
   * 用户会再报一次。（若只要 `.composer-wrap` 去、这里要留，说一声即可回滚这一行。）
   */
  flex: 0 0 auto;
  background: var(--bg);
}
/* 欢迎态：hero 与卡片在滚动区内垂直居中，输入框保持在底部悬浮 */
.chat-view.is-empty .chat-scroll {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* 悬浮态下输入区不再自带顶边（顶边由 dock 承担） */
.composer-dock .composer-wrap {
  border-top: 0;
  background: transparent;
}

/* 工具图标（附件 / 添加专家）进输入框内部左侧：学 ChatGPT 的「＋」 */
.composer-left {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: 1;
  min-width: 0;
  flex-wrap: wrap;
}
.composer-left .tool-btn {
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  border-radius: 8px;
}
.composer-left .tool-btn:hover {
  background: var(--surface-alt);
}

/* ---------- 思考过程块 ---------- */

.thinking {
  white-space: pre-wrap;
}
/*
 * 「简略显示深度推理」开启后，推理结束只剩这一行：
 * **必须是一行**（nowrap + 省略号），不许换行撑高 —— 需求原话「文本内容不要超出这一行」。
 * 它是 <div> 而不是 <details>，所以天然没有展开入口。
 */
.thinking.is-brief {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  user-select: none;
}
.thinking summary {
  user-select: none;
}
.thinking-body {
  margin-top: 6px;
  max-height: 260px;
  overflow-y: auto;
  line-height: 1.6;
}
/* 被折叠掉的那部分：说明还有多少字，并按需给出「展开全文」 */
.thinking-more {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 6px 0;
  padding-top: 6px;
  border-top: 1px dashed var(--border, rgba(127, 127, 127, 0.3));
  color: var(--text-soft);
  font-size: 12px;
}

/* ---------- 超长用户消息折叠（学 ChatGPT） ---------- */

/*
 * ⚠️ **`is-clamped` 现在只服务「用户长消息」**（2026-09-29 用户裁定）。
 *
 * 以前助手侧"有文件的完成态"也套这个类 —— 结果是**把四段正文的顶部裁掉**：
 * `mdFile` 一落地就折叠，而前 168px 基本被"思考总结 + 本次需求判断"两条 strip 占满
 * （实测：`.bubble-body` 顶部距气泡顶 150px、折叠线 168px ⇒ 桌面正文只剩 19% 可见、
 * **窄屏 `bodyVisibleRatio = 0`** ⇒ 正文整段在折线以下）。用户报的"（正文）没了"就是它。
 *
 * 现在助手侧走 **摘要态 `.is-briefed`**（见下）：只显示 交付 + 建议 + 展开全文，
 * 展开后仍是完整四段 + 正文。**别再把 `is-clamped` 当"通用折叠"**。
 */
.bubble.is-clamped {
  max-height: 168px;
  overflow: hidden;
  position: relative;
}
/* 底部渐隐：让"下面还有内容"可以被看见，而不是像被截断 */
.bubble.is-clamped::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 44px;
  background: linear-gradient(180deg, transparent, var(--surface));
  pointer-events: none;
}
/* ---------- 助手侧「有文件」的摘要态（2026-09-29 用户裁定）---------- */
/*
 * 折叠态**不裁高**：内容本身就是短的（本次交付 + 给您的下一步建议 + 展开全文），
 * 由渲染侧决定显示什么，**不再靠 CSS 裁掉正文顶部**。
 * 这里显式写 `max-height: none`，是为了让"将来有人以为折叠就该配 max-height"这件事
 * 在改这一行时必须先读这段注释 —— 那正是把正文裁没的那个错。
 */
.bubble.is-briefed {
  max-height: none;
}
.msg-clamp-toggle {
  margin-top: 4px;
}
.msg-clamp-toggle .btn {
  padding: 2px 10px;
  font-size: 12px;
}

/* ---------- 侧栏对话项：重命名 + 删除 ---------- */

.c-ops {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: none;
  opacity: 0;
  transition: opacity .12s;
}
.conv-item:hover .c-ops,
.conv-item.active .c-ops {
  opacity: 1;
}

/* ---------- WorkBuddy 绑定卡（设置 · 模型与密钥） ---------- */

.card.is-workbuddy {
  border-left: 3px solid var(--ai);
}
.card.is-workbuddy h4 {
  display: flex;
  align-items: center;
  gap: 8px;
}
.card.is-workbuddy h4 .icon {
  width: 17px;
  height: 17px;
  color: var(--ai);
}
.wb-form {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: flex-end;
}
.wb-form .field {
  margin: 0;
  min-width: 240px;
  flex: 1;
}
.wb-bound {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 8px 16px;
}
.wb-bound > div {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
}
.wb-bound span {
  color: var(--muted);
  font-size: 12.5px;
}
.wb-bill {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--surface-alt);
}
.wb-bill.is-ok {
  border-color: var(--success);
}
.wb-bill.is-bad {
  border-color: var(--danger);
}
.wb-bill-head {
  font-weight: 600;
  margin-bottom: 8px;
}
.wb-bill-body > div {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
}
.wb-bill-body span {
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.wb-bill-body .icon {
  width: 14px;
  height: 14px;
}

/* ---------- 用量页积分标识 ---------- */

.credit-flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/*
 * 两类积分并排：`✦x|⬡y`（四角星 = WorkBuddy 积分，六边形币 = 平台积分）。
 * 需求要求的形状就是"图标 + 数值，中间用 | 分隔"——
 * 用文字说"WorkBuddy 积分"会占掉半行，图标才能让两个数在同一眼里对比出来。
 */
.credit-pair {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.credit-pair-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.credit-pair-item .icon {
  width: 14px;
  height: 14px;
}
.credit-pair-item.is-wb {
  color: var(--ai);
}
.credit-pair-item.is-platform {
  color: var(--accent);
}
.credit-pair-sep {
  color: var(--border-strong);
}
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* ---------- 个人中心：账号绑定行 ---------- */

/* 未绑定的占位文字弱化：它不是要读的信息，是"这里还空着"的提示 */
.account-row .is-soft {
  font-style: normal;
  font-weight: 500;
  color: var(--muted);
  opacity: 0.75;
}
/* 绑定 / 修改小按钮：窄面板里只占必要宽度 */
.account-bind {
  flex: none;
  padding: 2px 10px;
  font-size: 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
}
.account-bind:hover {
  border-color: var(--ai);
  color: var(--ai);
}

/* ---------- 超长产出：.md 文件（消息上的文件条 + 右侧预览抽屉） ---------- */
/*
 * 一轮产出超过单次拉取上限（500 条分片）时改走文件：那个量级已经不适合塞在气泡里
 * （既难读、又拖页面）。文件条在消息流里只占一行，一眼看出"这轮出了个文件"；
 * 点它把整份内容放到**右侧**，阅读位置与消息流分开、互不遮挡。
 */
.msg-file {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.file-chip {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  max-width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.file-chip:hover {
  border-color: var(--ai);
  background: var(--ai-soft);
}
.file-chip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1px 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: #fff;
  background: var(--ai);
  border-radius: 6px;
}
.file-chip-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.file-chip-meta {
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}

/*
 * 「正在撰写文件…」提示条（超长产出在生成期间显示）。
 * 需求原话：「决定使用 .md 生成的情况下正文也要提示'正在撰写文件'之类的安抚话术」——
 * 没有它，用户面对一个字数在涨、却迟迟不出正文的界面只会以为卡住了。
 */
.doc-pending {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  padding: 7px 10px;
  border: 1px dashed var(--ai);
  border-radius: var(--radius-sm);
  background: var(--ai-soft);
  color: var(--ai);
  font-size: 12.5px;
}

/*
 * 抽屉是 .shell 的 flex 子项（不是遮罩弹窗）：出现时消息流**自动让位**，
 * 而不是盖在内容上面 —— 用户想两边对照着看都行。
 */
.doc-drawer {
  /* 分界线要绝对定位到左边缘上，所以抽屉需要一个定位上下文 */
  position: relative;
  flex: 0 0 min(560px, 46vw);
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-left: 1px solid var(--border);
  background: var(--surface);
  animation: doc-drawer-in 0.18s ease-out;
}
/*
 * 预览抽屉的分界线：按住左右拖可改宽度（逻辑在 dispatch 的 pointer 处理里）。
 *
 * 命中区刻意比视觉线宽（8px 对 1px 边框）：只给 1px 的话鼠标很难压准，
 * 而"抓不住"正是这类控件最常见的抱怨。平时透明，悬停或拖动中才显色。
 */
.doc-drawer-resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 8px;
  z-index: 6;
  cursor: col-resize;
  background: transparent;
}
.doc-drawer-resizer:hover {
  background: var(--border);
}
/*
 * 拖动期间锁住整页的光标与文本选中：否则鼠标一旦划出那 8px，
 * 光标就变回箭头（看着像"拖丢了"），而且会把正文里的字一路选中。
 */
body.is-resizing-doc {
  cursor: col-resize;
  user-select: none;
}
@keyframes doc-drawer-in {
  from {
    opacity: 0.4;
    transform: translateX(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.doc-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-alt);
}
.doc-drawer-title {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.doc-drawer-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.doc-drawer-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.doc-drawer-meta {
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}
.doc-drawer-body {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: 18px 22px 40px;
}

/* 窄屏：让位给消息流（抽屉改为覆盖式，仍可用） */
@media (max-width: 1080px) {
  .doc-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    width: min(92vw, 560px);
    box-shadow: var(--shadow-xl);
  }
}

/* 账单页板块小标题：说明该板块的**数据范围**（全平台 / 只有我），避免把两者读成同一份 */
.h4-sub {
  margin-left: 8px;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 400;
}
.card.is-platform-usage {
  border-left: 3px solid var(--warn);
}

/* ---------- 联网检索状态条（挂在助手气泡顶部） ---------- */
/*
 * 只报状态与条数，**不列来源链接**（2026-09-22 用户要求：检索引用不进气泡，
 * 命中内容只交给模型用）。四种状态各有自己的颜色与措辞 ——
 * 静默才是要避免的：别说"搜过了"，也别说"搜过了但没事"。
 */
.search-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  margin: 0 0 8px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-alt);
  font-size: 12.5px;
  color: var(--text-soft);
}
.search-strip .icon {
  width: 14px;
  height: 14px;
  flex: none;
}
.search-strip.is-pending { border-style: dashed; color: var(--muted); }
.search-strip.is-done { border-color: var(--success); }
.search-strip.is-empty { border-color: var(--warn); color: var(--warn); }
.search-strip.is-failed { border-color: var(--danger); color: var(--danger); }

/* ---------- 后台用户列表的搜索行 ---------- */
.admin-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 12px;
}
.admin-search .input {
  max-width: 320px;
}

/* ---------- 「接入你自己的 WorkBuddy」常驻入口（已撤下） ---------- */
/*
 * 这里原先有一条 `.wb-connect-bar`：输入区上方常驻提示"你还没接入 WorkBuddy"。
 * 用户反馈要求撤掉 —— 绑定是自愿的，没绑不该在聊天界面上被反复提醒。
 *
 * 样式一并删掉，不留死代码：元素已经不渲染，留着样式只会让下次有人搜到它、
 * 以为功能还在、然后把"入口消失"误判成 bug（就像曾经的反向故障那样）。
 * 入口本身移到两处**用户主动前往**的地方：设置页的 WorkBuddy 绑定卡、
 * 与模型面板里选中该通道时的「立即接入」。
 */

/* ---------- 「填模型那一栏到底填什么」的说明 ---------- */
/*
 * 方舟这一条是刚需：它的「模型」不是模型名，而是推理接入点 ID（ep-…）。
 * 不写在填写处的旁边，用户填了模型名、发出去被拒，只能猜是密钥错还是模型错 ——
 * 而这两种错误的修法完全不同。
 */
.model-endpoint-hint {
  margin: 6px 0 0;
  padding: 6px 10px;
  border-left: 2px solid var(--ai);
  background: var(--ai-soft);
  border-radius: var(--radius-sm);
  color: var(--text-soft);
  font-size: 12.5px;
  line-height: 1.6;
}

/* ---------- 生成出来的图片 ---------- */
/*
 * 约束是必须的：生成模型常给 1792x1024 这类大图，不加限制会把气泡撑破、
 * 把整页顶到横向滚动 —— 而用户只会觉得"页面坏了"。
 * 宽度跟随气泡、高度自适应，点击可看原图（浏览器默认行为）。
 */
.md-body .md-image {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 8px 0;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-alt);
}

/* ---------- 本轮思路摘要（只在前台显示，不写进 .md 文件） ---------- */
/*
 * 生成一份长稿时，用户最想知道两件事：它打算怎么做、现在走到哪了。
 * 这两句取自推理过程本身（首句 + 末句），不编造；文件里仍然只有正文。
 */
/*
 * 四段合一里的**正文**段（2026-09-29 用户定版：气泡 = 思考总结 + 需求判断 + 正文 + 建议）。
 *
 * 最小一组：**段间距 + 行距**，与气泡整体（`.bubble` 的 1.8）保持一致 ——
 * 让人一眼看出"这两块是同一段的两个层次"，而不靠自定义字体/颜色。
 *
 * ⚠️ **刻意不加 `max-height` / `overflow`**：正文的"折叠"已经**不再**靠裁高实现 ——
 * 助手侧「有文件」的完成态走的是**摘要态** `.bubble.is-briefed`（**只显示交付 + 建议 + 展开全文，
 * 根本不渲染 `.bubble-body`**，见 views.js 的 `isBriefed`），用户长消息那侧才是 `.bubble.is-clamped`。
 * 这里再加一层高度上限 = **两层折叠**，用户点开一次还被截，只会困惑"为什么展开了还是看不全"。
 */
.bubble-body {
  margin: 8px 0 4px;
  line-height: 1.8;
}
/* 正文自带标题时，避免与外层段间距叠成一道大缝（只抵消首尾，不动中间） */
.bubble-body > :first-child {
  margin-top: 0;
}
.bubble-body > :last-child {
  margin-bottom: 0;
}

.thinking-brief {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  margin: 8px 0 0;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--border);
  background: var(--surface-alt);
  font-size: 12.5px;
  color: var(--text-soft);
  line-height: 1.6;
}
.thinking-brief-tag {
  flex: none;
  font-weight: 600;
  color: var(--ai);
}
.thinking-brief-tag { flex: none; color: var(--accent); }
/*
 * **建议按条换行**（2026-09-29 用户：「1,2,3,4 条也需要格式换行方便阅读」）：
 * `pre-line` 让文本里**还没被切开**的 `\n` 也能换行（渲染侧已用 `<br>` 逐行切好，
 * 这里只是兜底；注意与别处无关，这条选择器只作用于四段气泡里的文本）。
 */
.thinking-brief-text { flex: 1 1 320px; white-space: pre-line; }
.thinking-brief-note { flex: none; color: var(--muted); }

/* ---------- 提示条带动作按钮（如「线上已更新，点此刷新」） ---------- */
/*
 * 提示条从"只说一句话"变成"能和它交互"：文字左、按钮右。
 * 按钮必须点得动且在窄屏也不被挤掉，所以文字部分允许换行、按钮不收缩。
 */
.composer-notice .notice-text {
  flex: 1 1 auto;
  min-width: 0;
}
.composer-notice > .btn {
  flex: none;
  margin-left: 10px;
  align-self: center;
}

/* ---------- 本轮技能资料的纳入情况（如实显示，避免"产出忽好忽坏"的错觉） ---------- */
.docs-usage {
  margin: 6px 0 0;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  border: 1px dashed var(--border);
  color: var(--text-soft);
  font-size: 12px;
  line-height: 1.6;
}

/* ============================================================================
 * 充值 / 会员（2026-09-22）
 * ==========================================================================*/
/* 超级会员徽章：与 ok/warn 同族但用暖金色调，一眼是"权益"而不是"状态" */
.badge.member {
  background: rgba(214, 158, 46, 0.14);
  color: #a4762a;
  border: 1px solid rgba(214, 158, 46, 0.4);
}
.member-card {
  background: linear-gradient(180deg, rgba(214, 158, 46, 0.05), transparent 70%);
}

/* 两条只显百分比的流量进度条 */
.member-progress {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
}
.member-meter-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 12.5px;
  color: var(--text-soft);
  margin-bottom: 5px;
}
.member-meter-head b {
  font-size: 14px;
  color: var(--text);
}
.member-meter-track {
  height: 7px;
  border-radius: 999px;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  overflow: hidden;
}
.member-meter-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--ai), #6f8cff);
  transition: width 0.4s ease;
}
.member-meter.is-hot .member-meter-fill {
  background: linear-gradient(90deg, #d69e2e, #e07b39);
}
.member-meter.is-full .member-meter-fill {
  background: linear-gradient(90deg, #c0563a, #a33b2a);
}

/* 充值卡片网格：一张卡 = 一个套餐 */
.pay-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 10px;
}
.pay-tier {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-alt);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.pay-tier:hover {
  border-color: var(--ai);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(16, 18, 22, 0.08);
}
.pay-tier:disabled {
  cursor: default;
  opacity: 0.55;
  transform: none;
  box-shadow: none;
}
.pay-tier-title {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-soft);
}
.pay-tier-price {
  font-size: 20px;
  font-weight: 700;
}
.pay-tier-sub {
  font-size: 12px;
  color: var(--muted);
}
.pay-tier-cta {
  margin-top: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ai);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.pay-tier.is-busy .pay-tier-cta {
  color: var(--muted);
}

/* ============================================================================
 * 个人中心（点击左下角头像进入，2026-09-22）
 * ==========================================================================*/
.profile-head {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.profile-avatar {
  width: 52px;
  height: 52px;
  border-radius: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 头像默认用平台图标（用户要求）；以后若要支持自传头像，这一条不用改 */
  object-fit: cover;
  background: var(--surface-alt);
  flex: 0 0 auto;
}
/* 昵称（主行）+「修改昵称」按钮：窄屏时按钮换行，不挤压昵称 */
.profile-name-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.profile-name {
  font-size: 16px;
  font-weight: 600;
  word-break: break-all;
}
.profile-id {
  flex: 1;
  min-width: 200px;
}
.profile-balance {
  text-align: right;
}
.profile-balance-value {
  font-size: 24px;
  font-weight: 700;
}
/* 绑定情况：一行一项（邮箱 / 手机号 / WorkBuddy），右侧是绑定/修改按钮 */
.bind-grid {
  margin-top: 14px;
  border-top: 1px solid var(--border);
}
.bind-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.bind-label {
  width: 92px;
  flex: 0 0 auto;
  font-size: 12.5px;
  color: var(--text-soft);
}
.bind-value {
  flex: 1;
  min-width: 140px;
  font-weight: 600;
  word-break: break-all;
}

/* ============================================================================
 * 收款方式：付款卡（用户侧）与收款码编辑（后台）
 * ==========================================================================*/
.pay-order-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.pay-order-title {
  font-size: 17px;
  font-weight: 600;
}
.pay-order-code {
  margin-top: 8px;
  font-size: 13px;
  color: var(--text-soft);
}
.pay-order-code code {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.4px;
  color: var(--text);
}
/* 两张收款码并排：图要够大能扫，标题在下 */
.pay-qr-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
.pay-qr {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.pay-qr img {
  /*
   * 收款码必须**扫得动**（2026-09-23 用户反馈："显示的图片太小了，扫码扫不出来"）。
   * 168px 在手机上等于 4 厘米出头，很多扫码器要凑很近才认得出；放大到 280px 之后
   * 微信/支付宝的默认扫一扫在正常握持距离就能识别。
   * 另外图片本身可点（data-act=zoom-pay-qr）打开原图，扫码失败时还有一条兜底路径。
   */
  width: 280px;
  height: 280px;
  object-fit: contain;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  padding: 8px;
  cursor: zoom-in;
}
/* 窄屏（手机）：给内容留边距，但仍然保持足够大的可扫尺寸 */
@media (max-width: 520px) {
  .pay-qr img {
    width: 248px;
    height: 248px;
  }
}
/* 用户消息里的附件：图片成图片，其余给一枚带名字的卡片 */
.msg-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 8px;
}
.msg-attach-img {
  /*
   * 气泡里的图片按缩略图尺寸显示（原图点一下可看）。
   * `object-fit: cover` + 固定盒子保证一屏里几张图不会把对话挤散；
   * 高度不锁死而是 max-height，竖图与横图都不至于变形。
   */
  display: block;
  width: auto;
  max-width: 240px;
  max-height: 240px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: zoom-in;
}
.msg-attach-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2, rgba(127, 127, 127, 0.08));
  font-size: 12.5px;
  color: var(--text-soft);
}
.msg-attach-chip em {
  font-style: normal;
  opacity: 0.75;
}

/* 点开原图时的大图：铺满可视宽度的 92%，上限 560px（再大也扫得动，且不会溢出屏幕） */
.pay-qr-zoom img {
  display: block;
  margin: 0 auto;
  width: 100%;
  max-width: min(92vw, 560px);
  height: auto;
  background: var(--surface);
  border-radius: 12px;
  padding: 8px;
}
.pay-qr figcaption {
  font-size: 12.5px;
  color: var(--text-soft);
}
/* 后台编辑区：两个缩略图 + 说明 */
.pay-qr-edit {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  align-items: flex-start;
}
.pay-qr-slot {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.pay-qr-thumb {
  width: 120px;
  height: 120px;
  object-fit: contain;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  padding: 4px;
}

/* 官方直连凭证的两栏编辑区（支付宝 / 微信） */
.mch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
}
.mch-slot {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.mch-slot textarea.input {
  resize: vertical;
  min-height: 52px;
  font-family: inherit;
}
.mch-slot .btn {
  align-self: flex-start;
}

/* ---------- 收款数据看板（后台「订单与收款」页） ---------- */
/*
 * 四格等宽、窄屏自动折行：看板的意义是一眼扫到数字，
 * 所以数字要大、标签要小，且数字不许换行折成两截。
 */
.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px;
}
.dash-cell {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
  background: var(--surface-alt);
}
.dash-value {
  font-size: 20px;
  font-weight: 700;
  margin: 2px 0;
  white-space: nowrap;
}

/* ---------------- 社区中心（2026-09-23） ---------------- */
.community-grid {
  display: grid;
  /* 左窄右宽：找人是一时的，聊天是长驻的 */
  grid-template-columns: minmax(260px, 340px) 1fr;
  gap: 14px;
  align-items: start;
}
@media (max-width: 860px) {
  /* 窄屏堆成一列：两栏挤在一起时，聊天区会被压到看不清 */
  .community-grid {
    grid-template-columns: 1fr;
  }
}
.community-side,
.community-chat {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface, transparent);
  padding: 12px;
}
.community-search {
  margin-bottom: 10px;
}
.community-block {
  margin-top: 12px;
}
.community-block h4 {
  margin: 0 0 8px;
}
.community-friend {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 4px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  color: inherit;
}
.community-friend:hover {
  background: rgba(127, 127, 127, 0.08);
}
.community-friend.active {
  border-color: var(--border);
  background: rgba(127, 127, 127, 0.12);
}
.community-friend-main {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.community-friend-name {
  font-weight: 600;
}
.community-friend-sub {
  font-size: 12px;
  color: var(--text-soft);
  margin-left: auto;
}
.community-result {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}
.community-result:last-child {
  border-bottom: none;
}
.community-result-main {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.community-chat {
  display: flex;
  flex-direction: column;
  min-height: 420px;
}
.community-chat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.community-chat-head .btn {
  margin-left: auto;
}
.community-thread {
  /* 固定高度 + 内部滚动：消息多了页面本身不该跟着无限变长 */
  flex: 1;
  min-height: 260px;
  max-height: 52vh;
  overflow-y: auto;
  padding: 10px 4px;
}
.community-day {
  text-align: center;
  font-size: 12px;
  color: var(--text-soft);
  margin: 10px 0 6px;
}
.community-msg {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-bottom: 8px;
}
.community-msg.mine {
  align-items: flex-end;
}
.community-bubble {
  max-width: 76%;
  padding: 8px 12px;
  border-radius: 14px;
  background: rgba(127, 127, 127, 0.12);
  /* 换行与超长链接都要能正常显示，否则用户的"经验分享"会被撑破布局 */
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.55;
}
.community-msg.mine .community-bubble {
  background: var(--accent-soft, rgba(99, 102, 241, 0.18));
}
.community-at {
  font-size: 11.5px;
  color: var(--text-soft);
}
.community-composer {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.community-composer .input {
  flex: 1;
  resize: vertical;
}

/* ── 后台「账号与积分」用户表：紧凑化 + 新增列（2026-09-23）─────────────────────
   用户要求「尽可能减少一个 UID 占据画面的高度」，同时把邮箱/手机号/昵称
   合并成一列三行、累计发放与累计消耗也合并两行。
   于是这里做两件事：把单元格内边距收窄（7px → 4px），并给多行单元格一个统一行距。
   收窄是安全的：这一页本来就是靠 .table-scroll 横向滚动、纵向扫描着读的，
   不像详情页那样需要松散的呼吸感。 */
.data-table.admin-users th,
.data-table.admin-users td {
  padding: 4px 6px;
  line-height: 1.32;
}
/* UID 不换行 —— 它是行首锚点，一旦折成两行整行就白高 8px */
.admin-users .uid-cell {
  white-space: nowrap;
}
.admin-users .uid-cell .badge {
  margin-left: 4px;
  padding: 0 4px;
  font-size: 10px;
}
/* 账号列：邮箱 / 手机号 / 昵称三行。截断而不换行 —— 换行会让行高随最长邮箱浮动 */
.acct-line {
  max-width: 220px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.acct-line + .acct-line {
  margin-top: 1px;
}
/* 累计列（发放 / 消耗）同样两行，与数字列一样右对齐 */
.stack-cell > div + div {
  margin-top: 1px;
}
/* 会员列下方的到期日 / 永久：小一号、弱一档，不与徽章抢注意力 */
.cell-sub {
  margin-top: 1px;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}
/* 每页条数（10/25/50/100）与翻页条 */
.admin-page-size {
  display: inline-flex;
  gap: 4px;
  margin-left: auto;
}
.admin-page-size .btn {
  min-width: 34px;
  padding: 2px 6px;
}
.admin-pager {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 4px;
}

/* ── 内测入口（顶部导航栏，模式切换那一行的右侧）─────────────────────────────
   位置变更（用户原话：「我的内测入口是要放在顶部导航栏，不是放在卡片导航栏」）：
   原先是欢迎页卡片区里的一张卡片（大块、带背景），搬到顶栏会撑坏版式 ——
   所以这里改成**紧凑胶囊**：与顶栏同高、单行、hover 有明显反馈。
   只有**内测用户且后台配了内容**才渲染（见 web/js/beta.js 的 visibleBetaEntries），
   所以这里的样式不需要考虑"隐藏"这一路。 */
.beta-topbar {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* 顶栏主区用 baseline 对齐，胶囊这里改成居中，避免与文字基线错位 */
  align-self: center;
  flex-wrap: wrap;
}
.beta-entry-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px;
  border-radius: 999px;
  border: 1px solid var(--ai);
  background: var(--ai-soft);
  color: inherit;
  text-decoration: none;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s, transform 0.15s;
}
.beta-entry-pill:hover {
  transform: translateY(-1px);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.beta-entry-pill-badge {
  padding: 0 5px;
  border-radius: 6px;
  background: var(--ai);
  color: #fff;
  font-size: 10px;
  letter-spacing: 0.4px;
}
.beta-entry-pill-name {
  font-size: 12.5px;
}

/* ── 后台「查看拦截机制」面板（2026-09-23）────────────────────────────────────
   八个闸门的表格 + 按族开关。被关掉的族整行弱化（划掉闸门名），
   这样一眼就能看出"这条当前不生效"—— 不必逐个去读开关按钮的文字。 */
.data-table.sec-families td {
  vertical-align: top;
}
.data-table.sec-families tr.is-off {
  opacity: 0.5;
}
.data-table.sec-families tr.is-off strong {
  text-decoration: line-through;
}
