/* 通用工作台。三栏：会话 / 对话 / 文件。
 *
 * 文件那一栏原先是常驻的：这条泳道的价值就在文件进出，怕把它折进按钮里，页面
 * 就退回成一个聊天框。可常驻的代价是它一份文件都没有的时候也占着 300px，靠一条
 * 通天的竖线把版面切掉一块，屏幕上最显眼的东西是三行「还没有文件」的说明。
 * 现在改成：宽屏上它跟着文件走——有文件就在，没有就不出现，第一份文件进来它
 * 自己滑出来；老师自己开过或关过就以他那次为准（desk.js 的 syncFilesPanel）。
 * 任何宽度下都可以关掉，标题栏那颗「文件」扣子随时把它叫回来，并且带着份数，
 * 所以关掉之后也不会忘了它在。 */

.desk-workbench {
  height: 100vh;
  height: 100dvh;
}

.desk-layout {
  --rail-w: 268px;
  /* 收起来就是把这条轨道收到 0，不是把面板 display:none——轨道宽度是可以过渡的，
     整条栏因此是滑进滑出的，而不是啪地少一块。 */
  --files-w: 0px;
  height: 100%;
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr) var(--files-w);
  /* 这一行不能省。不写行轨，隐式行就是 auto，高度对里面那层是「不定」的，
     于是中栏的 minmax(0, 1fr) 消息区按 max-content 算——聊够几轮，输入框
     就被顶到视口下面去了，窗口越矮出现得越早。写死一行 1fr 才把高度定下来。 */
  grid-template-rows: minmax(0, 1fr);
  transition: grid-template-columns 180ms ease;
}

/* 收到 0，不是收到一条图标栏。理由写在 rail.css 顶上。 */
.desk-layout.is-rail-collapsed { --rail-w: 0px; }
.desk-layout.is-files-open { --files-w: 322px; }

/* 桌面端整页只有这一个滚动区（消息流），页头交给侧栏顶部的品牌，
   与工作台、案例库一致。
   条件从 ready 改成「不是 closed」：ready 之前还有一档 loading，顶栏在那零点
   几秒里撑出 50px，接口一回来又整条收掉，页面往上跳一次——而两条泳道各等各的
   接口，来回切就是一边跳一边不跳。closed 那档要留着它：名单之外的老师在那一屏
   上，顶栏是唯一能离开的路。 */
@media (min-width: 761px) {
  body:not([data-desk-state="closed"]) .app-header { display: none; }
}

.desk-rail { height: 100%; min-height: 0; }
.desk-session-list {
  min-height: 0;
  flex: 1 1 auto;
  display: grid;
  align-content: start;
  gap: 1px;
  margin-top: 2px;
  padding-bottom: 10px;
  overflow-y: auto;
}

/* 会话条跟 /agent/ 的对话条是同一种东西：一行标题、一列记号、一个短到不能再短
   的时间。两栏的写法必须对得上，不然同一个老师在两条泳道之间来回切的时候，
   要重新学一遍怎么读这张列表。那边的原件在 conversation.css 的 .thread-item，
   这边不能直接引——那份样式不在 min-width 媒体查询里，工作台也不加载它。 */
.desk-session-group {
  margin: 16px 10px 5px;
  color: #9a9491;
  font-size: var(--t-meta);
  font-weight: var(--w-medium);
}
.desk-session-group:first-child { margin-top: 6px; }

.desk-session {
  width: 100%;
  min-height: 36px;
  position: relative;
  display: grid;
  grid-template-columns: 4px minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  color: #585350;
  text-align: left;
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease;
}

.desk-session:hover { background: rgb(35 33 30 / 5%); color: var(--ink); }
/* 选中的一条靠一块实心底色，不再靠左边那条 2px 的红竖条。竖条是给一列
   等宽导航用的记号；这里每一行长短不一，一条贴在栏边的红线读起来像栏本身
   的一部分，而不像「你在这一条上」。 */
.desk-session.is-active { background: #e8e3e0; color: var(--ink); font-weight: var(--w-medium); }
/* 「项目」原来是标题下面一行里的一个红胶囊。会话名才是选会话时要读的东西，
   长期与否只要一个记号：实心是长期项目，空心是临时会话。 */
.desk-session i { width: 4px; height: 4px; border-radius: 50%; background: transparent; }
.desk-session.is-project i { background: #b6b3af; }
.desk-session.is-active.is-project i { background: var(--crimson); }
.desk-session.is-running i {
  width: 10px;
  height: 10px;
  margin-left: -3px;
  border: 1.5px solid #d0cecb;
  border-top-color: var(--crimson);
  background: transparent;
  animation: desk-spin 800ms linear infinite;
}
.desk-session strong {
  min-width: 0;
  overflow: hidden;
  font-size: var(--t-label);
  font-weight: inherit;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 时间平时不出现。一列时间戳每行都在，读的却是标题——它把每一行的右端都占住
   了，标题因此更早被截断。鼠标停到哪一行，哪一行的时间才现身。 */
.desk-session time {
  color: #a8a29f;
  font-size: var(--t-meta);
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transition: opacity 120ms ease;
}
.desk-session:hover time,
.desk-session.is-active time { opacity: 1; }
.desk-session-empty { margin: 8px 10px 4px; color: #a8a29f; font-size: var(--t-label); line-height: 1.6; }

@keyframes desk-spin { to { transform: rotate(360deg); } }

/* ---------- 中栏 ---------- */

.desk-pane {
  min-width: 0;
  height: 100%;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  background: var(--paper);
}

/* 首屏那一档：在输入框下面再补一条 1fr 的空轨。上面消息区的 1fr 和它一样宽，
   于是「标题 + 胶囊 + 输入框」这一整块被两段等高的空白夹在正中——竖直居中，
   而不是贴底。原先它贴在页面最下沿，上方量出来近三分之一屏什么都没有，整屏
   的重心因此压在脚下；居中之后重心回到视线高度，页面才「敞」得起来。
   （对话一旦开始，空轨随 [data-empty-state] 一起消失，输入框回到底部。） */
.desk-pane:has(> .desk-messages > .desk-intro:not([hidden])) {
  grid-template-rows: auto minmax(0, 1fr) auto auto 0.82fr;
}
.desk-pane:has(> .desk-messages > .desk-intro:not([hidden]))::after { content: ""; }

/* 标题栏不再有下边框。那条线把中栏横切成「头」和「身」两块，可这一栏本来就是
   一整片纸——线一去，页面从上到下是通的。栏里的东西没变，只是不再被框住。 */
.desk-pane-head {
  min-width: 0;
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: 10px;
  /* 标题栏的边距跟内容列的 --gutter 脱钩，固定 14px。这一行是工具条，它的两颗
     扣子该贴着窗口的边；内容列是读的东西，它该居中。让扣子跟着内容列的留白走，
     宽屏上它们会离窗口边 56px——飘在半空，既不属于边也不属于列。 */
  padding: 10px 14px;
  border-bottom: 0;
}

/* 侧栏收起来之后，把它叫回来的入口。位置正是侧栏那颗折叠扣子原来所在的地方
   （左上角第一格），图标也是同一个——收起和展开是同一个动作的两面，不该长成
   两个样子。侧栏在的时候它不出现：那时候扣子在侧栏自己头上。 */
.desk-pane-open {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  display: none;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: #a09a97;
  cursor: pointer;
  transition: background-color 140ms ease, color 140ms ease;
}
.desk-pane-open:hover { background: rgb(35 33 30 / 6%); color: var(--ink); }
.desk-pane-open svg { width: 18px; height: 18px; display: block; fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linejoin: round; }
.desk-pane-open svg path { fill: currentColor; stroke: none; opacity: 0.42; }

@media (min-width: 761px) {
  .desk-layout.is-rail-collapsed .desk-pane-open { display: grid; }
}

/* 标题栏只有一行了：会话名。原先下面还挂一行「模型 · 12 轮 · …」，
   模型名在输入框下方说过一遍，轮次没人拿它做决定。 */
.desk-pane-title { min-width: 0; flex: 1 1 auto; }
.desk-pane-title > strong {
  overflow: hidden;
  color: #6b6663;
  font-size: var(--t-ui);
  font-weight: var(--w-medium);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 首屏上这一格是空的。会话还没有名字，标题栏里那句「AI 工作台」跟侧栏顶上的
   产品名是同一句话——同一屏上说两遍，第二遍就只是噪音。 */
.desk-pane:has(> .desk-messages > .desk-intro:not([hidden])) .desk-pane-title > strong { visibility: hidden; }

.desk-title-input {
  width: min(100%, 420px);
  padding: 3px 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: #fff;
  color: var(--ink);
  font-size: var(--t-ui);
  font-weight: var(--w-medium);
}

.desk-retention {
  justify-self: start;
  padding: 2px 10px 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: #fff;
  color: #85827e;
  font-size: var(--t-meta);
  cursor: pointer;
}
.desk-retention:hover { border-color: var(--line-strong); color: #55524f; }
.desk-retention:disabled { opacity: 0.5; cursor: default; }
.desk-retention[data-retention="project"] {
  border-color: rgba(184, 41, 52, 0.28);
  background: rgba(184, 41, 52, 0.06);
  color: var(--crimson-deep);
}

.desk-pane-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
.desk-pane-actions > button {
  min-height: 30px;
  padding: 0 11px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: #6b6663;
  font-size: var(--t-label);
  cursor: pointer;
}
.desk-pane-actions > button:hover { background: rgb(35 33 30 / 6%); color: var(--ink); }
.desk-pane-actions > button:disabled { opacity: 0.45; cursor: default; }

/* ---------- 会话操作菜单 ---------- */

.desk-menu { position: relative; flex: 0 0 auto; }
.desk-menu-trigger {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: #85827e;
  cursor: pointer;
}
.desk-menu-trigger:hover,
.desk-menu-trigger[aria-expanded="true"] { border-color: var(--line); background: #f6f5f4; color: var(--ink); }
.desk-menu-trigger svg { width: 17px; height: 17px; fill: currentColor; stroke: none; }

.desk-menu-list {
  position: absolute;
  z-index: 30;
  top: calc(100% + 5px);
  right: 0;
  min-width: 186px;
  display: grid;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: 0 12px 30px rgba(38, 34, 30, 0.13);
}
.desk-menu-list > button {
  display: grid;
  gap: 1px;
  padding: 7px 9px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: #4a4744;
  font-size: var(--t-label);
  text-align: left;
  cursor: pointer;
}
.desk-menu-list > button small { color: #a8a5a1; font-size: var(--t-meta); }
.desk-menu-list > button:hover { background: #f4f3f1; color: var(--ink); }
.desk-menu-list > button:disabled { opacity: 0.42; cursor: default; }
.desk-menu-list > button:disabled:hover { background: transparent; color: #4a4744; }
.desk-menu-list > button.is-danger { color: var(--crimson-deep); }
.desk-menu-list > button.is-danger:hover { background: rgba(184, 41, 52, 0.06); color: var(--crimson-deep); }

.desk-drawer-button {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  display: none;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: #6b6663;
  cursor: pointer;
}
.desk-drawer-button:hover { background: rgb(35 33 30 / 6%); color: var(--ink); }
.desk-drawer-button svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* 这两颗跟别的抽屉扣子不一样，任何宽度下都在：文件面板现在默认不出现，把它
   叫出来和收回去的两个入口要是也跟着宽度躲起来，就没人知道它还在了。 */
.desk-files-button,
.desk-files-actions [data-close-files] { display: inline-grid; }

.desk-files-button {
  width: auto;
  min-width: 30px;
  grid-auto-flow: column;
  gap: 5px;
  padding: 0 8px;
}

/* 份数印在扣子上。面板关着的时候，这是「这个会话里有东西」的唯一信号——
   没有它，收起来的面板跟「这个会话没有文件」看着一模一样。 */
.desk-files-button b { color: #8a8481; font-size: var(--t-meta); font-weight: var(--w-strong); }
.desk-files-button[aria-expanded="true"] { background: rgb(35 33 30 / 7%); }

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

/* 中栏可以有一千多像素宽，消息不能跟着一起长。所有东西——每一条消息、首屏的
   标题和胶囊、下面的输入框——都按 --column 定宽并居中，窗口再宽，读者的视线
   也只在这一列里上下走。原先每块各定各的宽（680 / 833 / 无上限），窗口一宽
   就散成一片，而且左边界还各差几十像素。 */
.desk-messages {
  min-height: 0;
  display: grid;
  align-content: start;
  justify-items: center;
  gap: 20px;
  padding: 24px var(--gutter) 8px;
  overflow-y: auto;
}

.desk-message { width: min(var(--column), 100%); min-width: 0; }
.desk-message p { margin: 0 0 10px; color: #34322f; font-size: var(--t-body); line-height: 1.78; }
.desk-message p:last-child { margin-bottom: 0; }
.desk-message time { display: block; margin-top: 6px; color: #b3b0ac; font-size: var(--t-meta); }
.desk-message.is-pending { opacity: 0.5; }

/* 时间和复制按钮同一行。复制按钮平时是隐的，鼠标进到这条回复上才浮出来——
   它是个偶尔用一次的动作，不该在每条回复下面一直亮着。 */
.desk-message-foot { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.desk-message-foot time { margin-top: 0; }
.desk-copy {
  padding: 2px 8px 3px;
  border: 1px solid transparent;
  border-radius: var(--r-xs);
  background: transparent;
  color: #a8a5a1;
  font-size: var(--t-meta);
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease;
}
/* 这里是 :focus 而不是 :focus-visible：按钮平时 opacity 0，
   键盘 Tab 过来时必须看得见，不能赌浏览器的 focus-visible 启发式。 */
.desk-message:hover .desk-copy,
.desk-copy:focus { opacity: 1; }
.desk-copy:hover { border-color: var(--line); background: #fff; color: #55524f; }

/* 自己发的那一条靠右，但靠的是这一列的右边界，不是中栏的右边界——两边都在
   同一列里，对话因此读起来是一条竖直的线，不是横跨整个屏幕的来回摆动。 */
.desk-message.is-user { display: flex; flex-direction: column; align-items: flex-end; }
.desk-message.is-user > div {
  max-width: 88%;
  padding: 11px 16px;
  border-radius: var(--r-lg);
  background: #eeeae7;
}
/* 自己敲的换行就是换行，原样留着。 */
.desk-message.is-user p { font-size: var(--t-body); line-height: 1.7; white-space: pre-wrap; }
.desk-message.is-user time { text-align: right; }

.desk-message.is-system {
  color: #93908c;
  text-align: center;
}
.desk-message.is-system p { color: #93908c; font-size: var(--t-label); line-height: 1.65; }

/* 新话题的分隔线。是对话里的一个时刻，所以画在流里，不在侧栏挂着。 */
.desk-topic-break {
  width: min(var(--column), 100%);
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 2px 0;
}
.desk-topic-break::before,
.desk-topic-break::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: #eae8e6;
}
.desk-topic-break span { flex: 0 1 auto; color: #b3b0ac; font-size: var(--t-meta); }

.desk-outcome {
  width: min(var(--column), 100%);
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #fff;
  color: #6c6965;
  font-size: var(--t-ui);
  line-height: 1.7;
}
.desk-outcome.is-failed {
  border-color: rgba(184, 41, 52, 0.3);
  background: rgba(184, 41, 52, 0.05);
  color: var(--crimson-deep);
}

/* ---------- 回复里的 Markdown ---------- */

/* 这几档比正文小半号、灰一档：一条回复里的小标题是给这段话分层的，
   不该看起来像页面自己的标题。 */
.desk-markdown h3,
.desk-markdown h4,
.desk-markdown h5 {
  margin: 18px 0 8px;
  color: var(--ink);
  font-weight: var(--w-strong);
  line-height: 1.5;
}
.desk-markdown h3 { font-size: var(--t-title); }
.desk-markdown h4 { font-size: var(--t-body); }
.desk-markdown h5 { font-size: var(--t-ui); color: #57544f; }
.desk-markdown > :first-child { margin-top: 0; }
.desk-markdown > :last-child { margin-bottom: 0; }

.desk-markdown ul,
.desk-markdown ol { margin: 0 0 10px; padding-left: 22px; color: #34322f; }
.desk-markdown li { margin: 3px 0; font-size: var(--t-body); line-height: 1.75; }
.desk-markdown li > ul,
.desk-markdown li > ol { margin: 3px 0 2px; }
.desk-markdown ul { list-style: disc; }
.desk-markdown ul ul { list-style: circle; }
.desk-markdown ol { list-style: decimal; }

.desk-markdown code {
  padding: 1px 5px 2px;
  border-radius: var(--r-xs);
  background: #f1efec;
  color: #4a4744;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--t-label);
}
/* 代码块自己横向滚，不把中栏撑破。min-width: 0 是关键：grid 里的项默认
   按内容算最小宽度，少了这一条，一行长命令能把整栏顶宽。 */
.desk-markdown pre {
  min-width: 0;
  max-width: 100%;
  margin: 0 0 12px;
  padding: 11px 13px;
  border: 1px solid #ecebe9;
  border-radius: var(--r-sm);
  background: #fbfaf8;
  overflow-x: auto;
}
.desk-markdown pre code {
  display: block;
  padding: 0;
  background: transparent;
  font-size: var(--t-label);
  line-height: 1.7;
  white-space: pre;
}

.desk-table { max-width: 100%; margin: 0 0 12px; overflow-x: auto; }
.desk-markdown table { border-collapse: collapse; font-size: var(--t-label); }
.desk-markdown th,
.desk-markdown td {
  padding: 6px 11px;
  border: 1px solid #ecebe9;
  color: #4a4744;
  line-height: 1.6;
  text-align: left;
}
.desk-markdown th { background: #f8f7f5; color: #57544f; font-weight: var(--w-strong); }
.desk-markdown td.is-right,
.desk-markdown th.is-right { text-align: right; }
.desk-markdown td.is-center,
.desk-markdown th.is-center { text-align: center; }

.desk-markdown blockquote {
  margin: 0 0 12px;
  padding: 2px 0 2px 13px;
  border-left: 2px solid var(--line);
  color: #6c6965;
}
.desk-markdown hr { margin: 18px 0; border: 0; border-top: 1px solid #ecebe9; }
.desk-markdown a { color: var(--crimson-deep); text-decoration: underline; text-underline-offset: 2px; }
.desk-markdown strong { font-weight: var(--w-strong); }

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

/* 一句话、一只输入框、一排入口，竖直居中在整片纸的正中。
   之前这一屏是「内容贴底、上方空着近三分之一屏」：那是在一个左对齐、满幅
   排版里能做到的最好结果——既然整块只能顶在左边界，至少让它离输入框近些。
   这一版把前提换掉了：所有东西改成按同一条 --column 居中，那么整块也就该
   居中。竖直方向由 .desk-pane 那条 0.82fr 的空轨负责（见上文），横向由
   justify-items: center 负责，一屏因此有四个方向的留白，而不是两个。
   撑杆留着：消息区是滚动容器，窗口很矮时 1fr 会缩到 0，内容仍从顶上开始，
   不会被推到滚不到的地方。 */
.desk-messages:has(> .desk-intro:not([hidden])) { grid-template-rows: 1fr auto; }
.desk-messages:has(> .desk-intro:not([hidden]))::before { content: ""; }

.desk-intro {
  width: min(var(--column), 100%);
  padding: 0 0 4px;
  text-align: center;
}

/* 标题前面原先还有一枚记号。删掉了：那句话本来就立得住，记号只是替它撑场子，
   而一个抽象符号在首屏最显眼的位置上，读者第一眼要花掉的那点注意力，是花在
   猜它是什么意思，不是花在读那句话。少一样东西要维护，也少一处要解释。 */

/* 整页最大的一句话，也是整页仅有的两处宋体之一（另一处是侧栏顶上的产品名）。
   字重从 600 退到 500：这么大的号数上，600 的宋体笔画粗得发闷，压不住的是
   它周围那一大片空白。号数跟着窗口走，但两头都封死——小屏上不能挤，大屏上
   也不能长成一张海报。 */
.desk-intro h1 {
  margin: 0 0 26px;
  color: var(--ink);
  font-family: var(--serif);
  font-size: clamp(28px, 3.4vw, 40px);
  font-weight: var(--w-medium);
  line-height: 1.35;
  letter-spacing: 0.01em;
}

.desk-intro > p { margin: 0; color: #6b6663; font-size: var(--t-body); line-height: 1.85; }

/* 拖放在整块中栏上都算数，所以画的是这一栏的边，不是光标下面那个元素。
   原先高亮的是首屏那个虚线框——框已经删了，而且它只在没有消息时才在，
   聊到一半再拖文件进来时没有任何东西亮起来。 */
.desk-layout.is-dropping .desk-pane { box-shadow: inset 0 0 0 2px rgba(184, 41, 52, 0.42); }
.desk-layout.is-dropping .desk-files { background: rgba(184, 41, 52, 0.04); }

/* 那只藏起来的 file input 是 position: absolute 但没给坐标，于是停在它的静态
   位置——整块布局的末尾，也就是视口下沿再往下 1px。整页因此一直挂着一根滚动
   条，一屏的界面滚一格又弹回来。它被 clip 掉了，也 aria-hidden 了，钉在哪儿都
   一样，钉在原点就不再撑高文档。 */
.desk-layout ~ .sr-only,
main > .sr-only { top: 0; left: 0; }

/* 四个入口从「2×2 的卡片墙」改成「一行胶囊」。
   卡片那一版每张有 14px 的内边距、一条描边、两行字，四张摞起来 190px 高，
   在首屏上比输入框还重——可它们只是四句备选的开场白，不是四件要挑的东西。
   收成一行胶囊之后，它们退到输入框下面当一排提示，首屏的主角重新变成
   「那句话 + 那只输入框」。放不下就换行，不再横向滚。 */
/* 位置在输入框「下面」，不在它上面：这一排是「不知道写什么的话，可以写这个」，
   得先看见那只空输入框，这句话才成立。所以它在 DOM 里是 form 的兄弟，跟着
   首屏一起来去——判据用的是首屏那块的 hidden，跟 app.js 用的是同一个信号，
   不需要再多一行 JS 去同步第二个开关。 */
.desk-scenarios {
  width: min(var(--column), calc(100% - 2 * var(--gutter)));
  display: none;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin: 12px auto 24px;
}
.desk-pane:has(> .desk-messages > .desk-intro:not([hidden])) .desk-scenarios { display: flex; }

.desk-scenarios button {
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 15px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: rgb(255 255 255 / 55%);
  color: #575350;
  cursor: pointer;
  /* 悬停不抬起来：四颗排一行，光标扫过去就是四次抖动。动的只有底色和边线，
     位置一个像素都不挪。 */
  transition: border-color 160ms ease, background-color 160ms ease, color 160ms ease;
}
.desk-scenarios button:hover { border-color: var(--line-strong); background: #fff; color: var(--ink); }

/* 后缀用宋体，跟标题同一副嗓子：系统本来就知道的那一格用宋体，
   读者自己写的、点的用黑体。 */
.desk-scenario-kind {
  font-family: var(--serif);
  color: #a8a29f;
  font-size: var(--t-label);
  font-weight: var(--w-normal);
  transition: color 160ms ease;
}
.desk-scenarios button:hover .desk-scenario-kind { color: var(--crimson); }
.desk-scenarios strong { font-size: var(--t-ui); font-weight: var(--w-normal); white-space: nowrap; }

/* ---------- 进行中 ---------- */

.desk-run {
  width: min(var(--column), calc(100% - 2 * var(--gutter)));
  margin: 0 auto;
  padding: 11px 0 0;
}
.desk-run-head { display: flex; align-items: center; gap: 9px; }
.desk-run-pulse {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--crimson);
  animation: desk-pulse 1400ms ease-in-out infinite;
}
.desk-run-head strong { min-width: 0; overflow: hidden; color: #57544f; font-size: var(--t-label); font-weight: var(--w-medium); text-overflow: ellipsis; white-space: nowrap; }
.desk-run-head small { flex: 0 0 auto; color: #a8a5a1; font-size: var(--t-meta); font-variant-numeric: tabular-nums; }
.desk-run-head > button {
  min-height: 26px;
  flex: 0 0 auto;
  margin-left: auto;
  padding: 0 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: #85827e;
  font-size: var(--t-label);
  cursor: pointer;
}
.desk-run-head > button:hover { border-color: rgba(184, 41, 52, 0.42); color: var(--crimson-deep); }
.desk-run-head > button:disabled { color: #c2bfbb; cursor: default; }

.desk-run-log { margin-top: 7px; }
.desk-run-log summary { color: #a8a5a1; font-size: var(--t-label); cursor: pointer; list-style: none; }
.desk-run-log summary::-webkit-details-marker { display: none; }
.desk-run-log summary:hover { color: #6c6965; }
.desk-run-log ol {
  max-height: 190px;
  display: grid;
  gap: 4px;
  margin: 7px 0 0;
  padding: 0 0 0 2px;
  overflow-y: auto;
  list-style: none;
}
.desk-run-log li { display: flex; align-items: baseline; gap: 10px; color: #85827e; font-size: var(--t-meta); line-height: 1.6; }
.desk-run-log li span { min-width: 0; flex: 1 1 auto; }
.desk-run-log li time { flex: 0 0 auto; color: #c2bfbb; font-size: var(--t-meta); font-variant-numeric: tabular-nums; }
.desk-run-log li.is-activity span { color: #6c6965; }

/* 轮次结束后同一批记录挂在回复上。默认折起来：老师大多数时候只想看结论，
   想追究经过时再展开，而不是每条回复都先被一段日志挡住。 */
.desk-run-log.is-past { margin: 0 0 9px; }
.desk-run-log.is-past ol { max-height: 260px; }

@keyframes desk-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

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

/* 整页的重心。文字行在上，动作行在下。
   跟上一版的区别全在四个数上：宽度从「满幅减 22px」收到跟消息同一条
   --column，圆角从 10px 放到 26px，内边距从 14 放到 16/18，阴影从一层压扁的
   投影换成两层很浅的落地感。前三样负责「不硬」——一只近乎方角、横贯整屏的
   长条，是这一屏之前最像「控件」而最不像「纸」的东西；最后一样负责「敞」：
   它得看起来是放在纸上的，不是嵌在版面里的。 */
.desk-composer {
  width: min(var(--column), calc(100% - 2 * var(--gutter)));
  margin: 12px auto 0;
  padding: 16px 16px 10px 18px;
  border: 1px solid rgb(58 52 48 / 9%);
  border-radius: var(--r-xl);
  background: #fff;
  box-shadow: var(--shadow-soft);
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
/* 光标进来时抬一档阴影，不换边色也不换背景：这是「你在这儿写」，
   不是一个状态变化。 */
.desk-composer:focus-within { border-color: rgb(58 52 48 / 16%); box-shadow: var(--shadow-lift); }

/* 有对话的时候，输入框和页面底边之间留一口气。首屏那一档不用——那时候
   它下面还有一排胶囊，间距由胶囊那条 margin 负责。 */
.desk-pane:not(:has(> .desk-messages > .desk-intro:not([hidden]))) .desk-composer { margin-bottom: 22px; }
.desk-composer textarea {
  width: 100%;
  /* 六行封顶（24px 一行）。原先是 220px，快十行——老师写一段完整的任务描述
     就能把输入框撑到中栏的三分之一，把上面的对话挤没。交互工具那条泳道封在五行，
     这里宽一些、老师写得也长一些，给到六行。这两个数是 autoGrow 的唯一来源。
     行高写成整数而不是倍数：一行 24px，六行正好 144px，autoGrow 停在整行上，
     不会在最后半行的位置上留一道切口。 */
  min-height: 24px;
  max-height: 144px;
  display: block;
  overflow-y: hidden;
  border: 0;
  background: transparent;
  color: var(--ink);
  /* 读和写同一个字号。原先 13.5px 写、14px 读，差半个像素——读不出层级，
     只读得出没对齐。 */
  font-size: var(--t-body);
  line-height: 24px;
  resize: none;
}
.desk-composer textarea:focus { outline: none; }
.desk-composer textarea::placeholder { color: #a8a29f; }

.desk-composer-foot { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.desk-composer-foot > span { min-width: 0; flex: 1 1 auto; overflow: hidden; color: #a8a29f; font-size: var(--t-meta); text-overflow: ellipsis; white-space: nowrap; }
.desk-composer-foot > span.is-error { color: var(--crimson-deep); }

/* 回形针。跟发送键同一行、各在一头：一头是「带上这个」，一头是「发出去」。
   做成一颗描边的圆钮而不是一个裸图标——它是这一行左端唯一可按的东西，
   跟右端那颗实心的发送键成一对：一个空心一个实心，一个添一个发。 */
.desk-attach {
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: #85807d;
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease;
}
.desk-attach:hover { border-color: var(--line-strong); color: var(--ink); }
.desk-attach:disabled { opacity: 0.4; cursor: default; }
.desk-attach:disabled:hover { border-color: var(--line); color: #85807d; }
.desk-attach svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.desk-model { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; color: #a8a5a1; font-size: var(--t-meta); }
.desk-model select {
  max-width: 190px;
  padding: 3px 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  background: #fff;
  color: #55524f;
  font-size: var(--t-meta);
}
/* 得连上 .desk-composer-foot > span 一起写：那条规则给状态文字发了 flex: 1 1 auto，
   它的特异度比单个类高，模型名会跟着抢一半空槽，飘在输入框正中间而不是贴着发送键。 */
.desk-composer-foot > span.desk-model-fixed {
  flex: 0 0 auto;
  padding: 3px 10px 4px;
  border-radius: var(--r-pill);
  color: #85807d;
  font-size: var(--t-meta);
}

.desk-composer-foot > button[type="submit"] {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ink-fill);
  color: #fff;
  cursor: pointer;
  transition: background-color 140ms ease;
}
.desk-composer-foot > button[type="submit"]:hover { background: var(--night); }
.desk-composer-foot > button[type="submit"]:disabled { background: #ddd8d5; cursor: default; }
.desk-composer-foot > button[type="submit"] svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- 文件面板 ---------- */

.desk-files {
  min-width: 0;
  height: 100%;
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  /* 轨道收到 0 的时候，里面 300px 宽的一列内容得自己收住，否则它会顶着
     0 宽的盒子往外撑。 */
  overflow: hidden;
  border-left: 1px solid var(--line);
  background: var(--sand);
}

/* 宽屏上它不再是靠一条通天竖线切出来的一栏，而是一张浮在纸面上的卡片：留出
   边距和圆角，中栏因此有了一个收边，而不是被劈成两半。
   关掉时轨道宽度归零，边框要跟着撤——0 宽的元素留着 1px 边框，会在版面最右边
   留下一条谁也解释不了的竖线；顺手让它淡出，跟宽度一起收，不是突然消失。 */
@media (min-width: 1101px) {
  .desk-files {
    margin: 12px 12px 12px 0;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: #fff;
    opacity: 1;
    visibility: visible;
    /* visibility 也放进过渡里，收起时它会等淡出走完才生效，展开时立刻生效。
       光靠 opacity: 0 的话，这一栏虽然看不见，Tab 还是会走进去——上传、删除
       都还在焦点序列里，而屏幕上什么都没有。 */
    transition: opacity 160ms ease, visibility 160ms ease;
  }

  .desk-layout:not(.is-files-open) .desk-files {
    border-width: 0;
    opacity: 0;
    visibility: hidden;
  }
}

/* 段与段之间只有一条线，不做卡片：这一栏是参考信息，不该比中间的对话更抢眼。
   现在只剩「用量」还用得上这个小标题——文件那段的标题已经上提到面板头部。 */
.desk-files h2 {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: #a8a5a1;
  font-size: var(--t-meta);
  font-weight: var(--w-strong);
  letter-spacing: 0.04em;
}

/* 行数固定成四行，跟子元素的数量脱钩：保留期胶囊在没有会话时整行 hidden，
   靠自动排布的话文件列表会顶到第二行去，把 1fr 让给页脚。 */
.desk-files > header { grid-row: 1; }
.desk-side-keep { grid-row: 2; }
.desk-side-files { grid-row: 3; }
.desk-files > footer { grid-row: 4; }

.desk-side-keep {
  display: grid;
  padding: 11px 16px 12px;
  border-bottom: 1px solid #ecebe9;
}

.desk-side-files { min-height: 0; display: grid; grid-template-rows: minmax(0, 1fr); }

.desk-files > header {
  min-height: 58px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid #ecebe9;
}
.desk-files > header > div:first-child { display: flex; align-items: baseline; gap: 7px; }
.desk-files > header strong { color: var(--ink); font-size: var(--t-ui); font-weight: var(--w-strong); }
.desk-files > header span { color: #b8b5b1; font-size: var(--t-meta); }
.desk-files-actions { display: flex; align-items: center; gap: 6px; }
.desk-upload {
  min-height: 24px;
  padding: 0 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: #fff;
  color: #5e5b57;
  font-size: var(--t-label);
  cursor: pointer;
}
.desk-upload:hover { border-color: var(--ink-fill); background: var(--ink-fill); color: #fff; }
.desk-upload:disabled { opacity: 0.45; cursor: default; }
.desk-files.is-uploading { opacity: 0.7; }

.desk-file-list { min-height: 0; display: grid; align-content: start; gap: 2px; padding: 10px; overflow-y: auto; }

/* 一份文件都没有的时候，这一栏有六百多像素高，那句说明贴在最上面，读起来像
   一张被截断的列表——像是本来该有东西，只是没加载出来。居中之后它才是一句
   说明。只在空的时候居中：有文件了就是列表，列表从上往下长。
   居中放在 :has 里而不是给 .desk-file-empty 加 margin，是因为这一格同时是
   滚动容器，align-content 在内容超高时会把顶部推到滚不到的地方——但空态只有
   一行，不可能超高，所以这里安全，而给列表本身用就不安全。 */
.desk-file-list:has(> .desk-file-empty) { align-content: center; }
.desk-file { display: flex; align-items: center; gap: 4px; border-radius: var(--r-sm); }
.desk-file:hover { background: #f4f3f1; }
.desk-file > a {
  min-width: 0;
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  color: inherit;
  text-decoration: none;
}
.desk-file > a > span { min-width: 0; flex: 1 1 auto; display: grid; gap: 2px; }

/* 扩展名分组用颜色区分，图形只区分「哪一类」，不追求认出每一种后缀。 */
.desk-file-icon {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  fill: none;
  stroke: #b3b0ac;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.desk-file-icon.is-doc { stroke: #6f8fb0; }
.desk-file-icon.is-sheet { stroke: #5f9070; }
.desk-file-icon.is-slide { stroke: #c08a4e; }
.desk-file-icon.is-pdf { stroke: #b96a63; }
.desk-file-icon.is-image { stroke: #9483b4; }
.desk-file-icon.is-archive { stroke: #a09789; }
.desk-file-icon.is-code { stroke: #7f8b96; }
.desk-file strong {
  overflow: hidden;
  color: #4a4744;
  font-size: var(--t-label);
  font-weight: var(--w-medium);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.desk-file:hover strong { color: var(--crimson-deep); }
.desk-file small { color: #a8a5a1; font-size: var(--t-meta); }

.desk-file-remove {
  width: 24px;
  height: 24px;
  flex: 0 0 auto;
  margin-right: 6px;
  padding: 0;
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: #c2bfbb;
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
}
.desk-file:hover .desk-file-remove,
.desk-file-remove:focus-visible { opacity: 1; }
.desk-file-remove:hover { background: #eae8e6; color: var(--crimson-deep); }
.desk-file-remove:disabled { opacity: 0; cursor: default; }
.desk-file-empty { margin: 6px 9px; color: #a8a5a1; font-size: var(--t-label); line-height: 1.7; }

.desk-files > footer { display: grid; gap: 7px; padding: 12px 16px 16px; border-top: 1px solid #ecebe9; }
.desk-usage { height: 3px; border-radius: 999px; background: #e8e6e3; overflow: hidden; }
.desk-usage span { height: 100%; display: block; width: 0; border-radius: 999px; background: #a8a5a1; transition: width 240ms ease; }
.desk-usage.is-full span { background: var(--crimson); }
.desk-files > footer small { color: #a8a5a1; font-size: var(--t-meta); line-height: 1.6; }
/* 后缀名是拉丁字母，中文的 12px 下限对它不适用；这一行是查表用的，不是读的。
   压到 11px 让它明显退到「容量」和「隐私」这两句话后面去。 */
.desk-formats { margin: 0; color: #b3b0ac; font-size: 11px; line-height: 1.7; word-break: break-word; }
.desk-privacy { margin: 2px 0 0; color: #b3b0ac; font-size: var(--t-meta); line-height: 1.65; }

/* ---------- 名单之外 ---------- */

.desk-closed {
  min-height: calc(100vh - 58px);
  min-height: calc(100dvh - 58px);
  display: grid;
  place-content: center;
  padding: 40px var(--page-inline);
}
.desk-closed > div { max-width: 520px; }
.desk-closed span {
  display: inline-block;
  margin-bottom: 14px;
  padding: 2px 10px 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: #a3a09c;
  font-size: var(--t-meta);
  letter-spacing: 0.06em;
}
.desk-closed h1 { margin: 0 0 12px; color: var(--ink); font-family: var(--serif); font-size: 25px; font-weight: var(--w-strong); line-height: 1.45; }
.desk-closed p { margin: 0 0 22px; color: #77746f; font-size: var(--t-body); line-height: 1.85; }
.desk-closed-back {
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  padding: 0 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: #4a4744;
  font-size: var(--t-ui);
  text-decoration: none;
}
.desk-closed-back:hover { border-color: var(--ink-fill); background: var(--ink-fill); color: #fff; }

/* ---------- 浮层 ---------- */

.desk-scrim { display: none; }

.desk-toast {
  position: fixed;
  z-index: 300;
  bottom: 22px;
  left: 50%;
  max-width: min(520px, calc(100vw - 32px));
  padding: 9px 16px;
  border-radius: var(--r-sm);
  background: rgba(37, 36, 35, 0.94);
  color: #fff;
  font-size: var(--t-label);
  line-height: 1.6;
  transform: translateX(-50%);
}

/* ---------- 窄一些的桌面：文件面板收进抽屉 ---------- */

@media (min-width: 761px) and (max-width: 1100px) {
  .desk-layout { grid-template-columns: var(--rail-w) minmax(0, 1fr); }
  .desk-files {
    position: fixed;
    z-index: 60;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(88vw, 340px);
    transform: translateX(105%);
    transition: transform 180ms ease;
  }
  .desk-layout.is-files-open .desk-files { transform: translateX(0); }
  .desk-scrim { position: fixed; z-index: 55; inset: 0; display: block; border: 0; background: rgb(34 33 31 / 28%); }
}

@media (min-width: 761px) and (max-width: 980px) {
  .desk-layout { --rail-w: 232px; }
  .desk-layout.is-rail-collapsed { --rail-w: 0px; }
}

/* ---------- 手机：两侧都是抽屉 ---------- */

@media (max-width: 760px) {
  .desk-workbench { height: calc(100dvh - 50px); }
  .desk-layout { display: block; height: 100%; }

  /* 抽屉里只放「新建」和会话列表。品牌、导航、账号在页头里已经有了，
     rail.css 在这个宽度下也不给它们样式。 */
  .desk-rail {
    position: fixed;
    z-index: 60;
    top: 50px;
    bottom: 0;
    left: 0;
    /* 跟文件抽屉一个毛病：基础规则上的 height: 100% 会盖过 bottom: 0，抽屉从页头
       下方 50px 起算却仍要满屏高，于是最下面 50px 掉到屏幕外——会话多的时候，
       列表滚到底也够不着最后一条。 */
    width: min(86vw, 310px);
    height: auto;
    display: flex;
    flex-direction: column;
    padding: 14px 12px;
    border-right: 1px solid #e4e4e3;
    background: #fff;
    transform: translateX(-105%);
    transition: transform 180ms ease;
  }
  .desk-layout.is-sessions-open .desk-rail { transform: translateX(0); }
  .desk-rail .rail-head,
  .desk-rail .rail-account { display: none; }
  .desk-rail .rail-section { margin: 18px 4px 2px; color: #9a9491; font-size: var(--t-meta); font-weight: var(--w-medium); }
  .desk-rail .rail-new {
    min-height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 0 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: #fff;
    color: var(--ink);
    cursor: pointer;
  }
  .desk-rail .rail-new span { display: grid; place-items: center; }
  .desk-rail .rail-new .rail-icon {
    width: 16px;
    height: 16px;
    display: block;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  .desk-rail .rail-new b { font-size: var(--t-ui); font-weight: var(--w-medium); }
  .desk-session-list { margin-top: 4px; }
  /* 桌面上 32px 的行是拿光标点的，手指点不了那么准。抽屉不缺纵向空间，
     行高和字号都放回够按的尺寸。 */
  .desk-session { min-height: 44px; padding: 0 10px; }
  .desk-session strong { font-size: var(--t-ui); }
  .desk-session time { font-size: var(--t-meta); }
  .desk-session-group { margin: 16px 10px 4px; font-size: var(--t-meta); }

  .desk-pane { height: 100%; }
  .desk-pane-head { padding: 8px 14px; }
  .desk-drawer-button { display: grid; }
  .desk-pane-open { display: none; }

  .desk-messages { padding: 18px 16px 6px; }
  /* 手机上没有 hover，时间戳和复制按钮都改成常驻但很淡。 */
  .desk-session time { opacity: 1; }
  .desk-copy { opacity: 0.6; }
  .desk-composer { border-radius: var(--r-lg); }
  .desk-composer:last-child { margin-bottom: max(14px, env(safe-area-inset-bottom)); }
  .desk-scenarios { margin-bottom: max(14px, env(safe-area-inset-bottom)); }
  /* 一行放不下就换行，屏幕越窄换得越多——四颗胶囊竖着排也只有 170px 高，
     不像四张卡那样会把标题顶出屏幕，所以这一屏在手机上仍然是居中的。 */
  .desk-intro h1 { margin-bottom: 20px; font-size: clamp(24px, 7vw, 30px); }
  /* iOS Safari 聚焦到小于 16px 的输入框会把整页放大，而且不会自己退回来。 */
  .desk-composer textarea { font-size: 16px; }

  /* height 得手动收掉：基础规则上的 height: 100% 会盖过 bottom: 0，
     抽屉从页头下方 50px 起算却仍要满屏高，底部的隐私说明正好被挤出屏幕。 */
  .desk-files {
    position: fixed;
    z-index: 60;
    top: 50px;
    right: 0;
    bottom: 0;
    height: auto;
    width: min(88vw, 330px);
    transform: translateX(105%);
    transition: transform 180ms ease;
  }
  .desk-layout.is-files-open .desk-files { transform: translateX(0); }
  .desk-scrim { position: fixed; z-index: 55; inset: 50px 0 0; display: block; border: 0; background: rgba(34, 33, 31, 0.3); }
}

@media (prefers-reduced-motion: reduce) {
  .desk-layout,
  .desk-rail,
  .desk-files,
  .desk-usage span,
  .desk-scenarios button { transition: none; }
  /* 退回没有关键帧的样子：一个不转的圈、一个不闪的点。仍然读得出「在跑」。 */
  .desk-run-pulse,
  .desk-session.is-running i { animation: none; }
  .desk-run-pulse { opacity: 1; }
}
