/* AGENTBOX — 深蓝黑机房控制台，琥珀磷光作为工作态强调色；
 * 浅色主题是同一套配方换纸：牛皮纸底 + 压深的琥珀墨。
 * base：设计令牌、基础元素、跨页面通用组件（按钮/状态灯/转圈/气泡/字标）。
 * 断点约定：全站移动端断点统一为 max-width: 760px，各文件就近写自己的窄屏规则。
 *
 * 主题有两个互不相干的维度，js/theme.js 都写在 <html> 上：
 *   data-skin  = 界面风格（amber 默认 / glass / cyberpunk / graphite / verdant / blueprint）
 *   data-theme = 明暗（dark / light；data-theme-mode 记用户选的 system|light|dark）
 * index.html 头部有一段内联脚本先行落这几个属性，避免首屏闪色。
 * 本文件的 :root 与 :root[data-theme="light"] 就是默认的琥珀风格；其余风格的
 * 整套令牌在 css/skins.css，风格专属的形态在 css/skin-effects.css。
 * 全站颜色、圆角一律走下面的令牌：新样式不要写死色值（浅色和其他风格会漏色），
 * 像素圆角写成 calc(Npx * var(--radius-scale))，直角风格才收得成 0。 */

:root {
  /* ---- 表面与文字 ---- */
  --bg: #12151c;
  --panel: #1a1f29;      /* 卡片 / 侧栏 / 弹窗等「一层」表面 */
  --panel-2: #222834;    /* 悬停填充、次级按钮、用户气泡等「抬起」填充 */
  --line: #2e3646;
  --line-soft: #1d2330;  /* 列表行分隔线，比 --line 更收敛 */
  --text: #d6dae3;
  --text-hi: #fff;       /* 悬停时的最亮文字 */
  --muted: #8a93a5;
  --scroll-thumb: #566174;
  --scroll-thumb-hover: #7b879b;
  --scroll-thumb-active: var(--amber);

  /* ---- 琥珀强调：墨（文字/描边）与填充（实心块）分开，
   * 浅色主题下墨要压深才有对比度，填充仍需保持亮以承载深色文字 ---- */
  --amber: #e8a33d;
  --amber-dim: #7a5c28;  /* 强调弱描边 */
  --accent: #e8a33d;     /* 主按钮 / 发送钮 / 字标光标块的底色 */
  --accent-hi: #f0b45a;
  --on-accent: #17130a;  /* 压在 --accent 上的文字 */

  /* ---- 语义色 ---- */
  --green: #57c78a;
  --green-line: #2c4636;
  --red: #e06c5f;
  --red-hi: #e98376;
  --red-line: #57352f;
  --on-red: #fff;
  /* 警告（额度将尽、休眠、限流预警）：独立于琥珀的黄。琥珀只表示「主操作 / 选中」，
   * 警告也用琥珀的话，用户分不清哪个是要点的、哪个是要注意的。 */
  --warn: #e2c55a;
  --warn-line: #574c26;

  /* ---- 代码片段（行内 code / 代码块 / 文件编辑器） ---- */
  --code-bg: #0d1016;
  --code-head: #151a24;
  --code-line: #242b3a;
  --syntax-keyword: #c5a0e5;
  --syntax-function: #7ebee6;

  /* ---- 终端：两个主题下都保持深色。
   * xterm 里跑的是 agent TUI，它的 ANSI 配色按深底设计，翻成浅底会有大片
   * 不可读的亮色文字，所以终端当成一块「设备屏幕」，不跟随主题。 ---- */
  --term-bg: #0d1016;
  --term-fg: #d6dae3;
  --term-cursor: #e8a33d;
  --term-sel: #2e3646;

  /* ---- 遮罩与投影 ---- */
  --scrim: rgba(6, 8, 12, 0.6);            /* 抽屉遮罩 */
  --backdrop: rgba(6, 8, 12, 0.7);         /* 弹窗背板 */
  --backdrop-strong: rgba(6, 8, 12, 0.82); /* 灯箱背板 */
  --overlay-chip: rgba(6, 8, 12, 0.72);    /* 压在图片上的角标/关闭钮，始终深色 */
  --shadow-sm: 0 6px 20px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 10px 32px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 16px 44px rgba(0, 0, 0, 0.55);
  --checker-a: #161a22;  /* 图片预览的透明棋盘格 */
  --checker-b: #12151c;

  /* ---- 其余固定配色：diff/徽章的亮绿亮蓝、工作台启停遮罩 ---- */
  --diff-add: #3fb950;
  --info: #6ea8fe;
  --busy-veil: rgba(11, 14, 20, 0.5);

  /* ---- 风格维度的形状与质感（琥珀取原值，其他风格在 skins.css 覆盖） ---- */
  --radius-scale: 1;     /* 全站像素圆角的倍数；直角风格取 0 */
  --radius-pill: 999px;  /* 胶囊（标签、进度条、滚动条） */
  --radius-round: 50%;   /* 大号圆形按钮/头像；小圆点、转圈固定写 50% 不跟它 */
  --field: var(--bg);    /* 输入框、搜索框、凹槽的底色 */
  --page: var(--bg);     /* 需要「铺一层页面底色」的元素（工作台头部等） */
  /* 压在别的内容上方的层：浮层、抽屉、吸顶表头、底部保存条。半透明风格的 --panel
   * 透得过文字，这些层叠在正文上会字压字，所以单列一个必须不透明的表面 */
  --panel-solid: var(--panel);
  --app-canvas: none;    /* 页面底下的背景层：玻璃色斑 / 蓝图网格 */

  /* ---- 动效：新写的过渡与动画一律取这几档，「减少动态效果」时在文件末尾统一归零。
   * 只动 opacity / transform / translate，不过渡会让周围重排的宽高与位置，长对话和终端旁
   * 也不掉帧（滑动指示条是绝对定位的小块，过渡它自己的宽高不牵动别人，是唯一的例外）。 ---- */
  --dur-fast: 120ms;  /* 悬停、按下、菜单与下拉 */
  --dur: 180ms;       /* 弹窗、页面切入、滑块 */
  --dur-slow: 260ms;  /* 抽屉、侧滑面板、展开收起 */
  --ease-out: cubic-bezier(.2, .8, .2, 1);    /* 进场：快出慢停 */
  --ease-in: cubic-bezier(.4, 0, 1, 1);       /* 退场：慢起快走 */
  --ease-move: cubic-bezier(.4, 0, .2, 1);    /* 原地换位置：指示条、滑块 */
  --enter-y: 6px;     /* 页面与弹窗进场的上移距离 */

  --claude: #d97757; /* Claude 官方陶土橙（simple-icons 品牌色） */
  --openai: #10a37f; /* ChatGPT 官方青绿 */
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: -apple-system, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;

  color-scheme: dark; /* 让原生滚动条 / 表单控件跟着主题走 */
}

/* 浅色主题：只覆盖会变的令牌，品牌色、字体、终端配色继承上面 */
:root[data-theme="light"] {
  --bg: #f5f2ea;
  --panel: #fffdf8;
  --panel-2: #eae5d8;
  --line: #dbd4c4;
  --line-soft: #e8e2d5;
  /* 中性色带一点暖：纸底配冷灰会发紫，和琥珀强调打架 */
  --text: #2b2822;
  --text-hi: #000;
  --muted: #6f6a5d;
  --scroll-thumb: #b7ae9d;
  --scroll-thumb-hover: #8c8270;

  --amber: #96620f;   /* 压深到能在纸底上当正文强调色 */
  --amber-dim: #d3b681;
  --accent: #dd9226;
  --accent-hi: #c97f14;

  --green: #2f7d55;
  --green-line: #b3d0bd;
  --red: #b8412f;
  --red-hi: #a63625;
  --red-line: #dfb0a7;
  --warn: #7f6a00;
  --warn-line: #d9cb8f;

  --code-bg: #f2efe4;
  --code-head: #e9e3d5;
  --code-line: #dcd5c5;
  --syntax-keyword: #80529a;
  --syntax-function: #286b94;

  --scrim: rgba(36, 32, 24, 0.3);
  --backdrop: rgba(36, 32, 24, 0.32);
  --backdrop-strong: rgba(28, 24, 16, 0.7);
  --shadow-sm: 0 4px 14px rgba(45, 38, 24, 0.08);
  --shadow-md: 0 8px 26px rgba(45, 38, 24, 0.11);
  --shadow-lg: 0 16px 40px rgba(45, 38, 24, 0.16);
  --checker-a: #e8e4d8;
  --checker-b: #f5f2ea;
  /* 纸底上的蓝字要压深才有对比度（diff 段头、缓存读取、输入框里的命令与 @ 引用） */
  --info: #2763c4;

  color-scheme: light;
}

* { box-sizing: border-box; }
/* 共用原生滚动交互，仅定制外观；透明轨道避免圆角容器内出现系统白槽。
 * Firefox 使用标准属性；Chromium / Safari 使用伪元素以控制圆角与悬停。
 * 支持两套 API 的浏览器必须恢复 auto，否则标准属性会覆盖伪元素。 */
* { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }
@supports selector(::-webkit-scrollbar) {
  * { scrollbar-width: auto; scrollbar-color: auto; }
  ::-webkit-scrollbar { width: 10px; height: 10px; background: transparent; }
  ::-webkit-scrollbar-track { background: transparent; margin: 3px; }
  ::-webkit-scrollbar-thumb {
    background: var(--scroll-thumb); background-clip: padding-box;
    border: 2px solid transparent; border-radius: var(--radius-pill);
    min-height: 28px; min-width: 28px;
  }
  ::-webkit-scrollbar-thumb:hover { background-color: var(--scroll-thumb-hover); }
  ::-webkit-scrollbar-thumb:active { background-color: var(--scroll-thumb-active); }
  ::-webkit-scrollbar-corner { background: transparent; }
  ::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
}
/* 高对比模式交还系统配色。 */
@media (forced-colors: active) {
  * { scrollbar-width: auto; scrollbar-color: auto; }
  ::-webkit-scrollbar-thumb { background-color: ButtonText; }
  ::-webkit-scrollbar-thumb:hover, ::-webkit-scrollbar-thumb:active { background-color: Highlight; }
}
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.6;
}
/* 风格背景层（--app-canvas 为 none 时什么也不画）。z-index:-1 落在页面底色之上、
 * 全部内容之下；fixed 让它不随列表滚动，只有固定层的背景模糊才看得到它在动。 */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1;
  background: var(--app-canvas);
  pointer-events: none;
}
.hidden { display: none !important; }
.mono { font-family: var(--mono); }

button { font: inherit; cursor: pointer; }
input, textarea, select {
  font: inherit;
  color: var(--text);
  background: var(--field);
  border: 1px solid var(--line);
  border-radius: calc(6px * var(--radius-scale));
  padding: 8px 10px;
}
input:focus-visible, textarea:focus-visible, select:focus-visible, button:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 1px;
}
/* 手指点开弹窗 / 菜单后，iOS 也把程序挪过去的焦点算成 focus-visible（见 modality.ts），
 * 关闭按钮凭空套个框；按钮的焦点环只留给键盘。输入框照常显示，那是在告诉你往哪打字 */
:root[data-input="pointer"] button:focus-visible { outline: none; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); cursor: pointer; }
input[type="checkbox"]:disabled, input[type="radio"]:disabled { cursor: default; }

/* ---- 字标：logo 主形 + AGENTBOX + 呼吸光标 ---- */
.wordmark {
  display: flex; align-items: center;
  font-family: var(--mono);
  font-size: 22px;
  letter-spacing: 0.18em;
  color: var(--text);
}
.wordmark-sm { font-size: 15px; }
.wordmark .mark { flex: none; width: 23px; height: 23px; margin-right: 9px; }
.wordmark-sm .mark { width: 17px; height: 17px; margin-right: 7px; }
/* 空色块与字标共用文字基线；cap 按当前字体的大写字母高度取值，
 * 避免按行框居中时伸到字母下方。末尾 letter-spacing 已提供间距。 */
.wordmark .cursor {
  display: inline-block; vertical-align: baseline;
  width: 0.55em; height: 0.75em; height: 1cap;
  background: var(--accent); color: transparent;
  animation: blink 1.1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

/* ---- 吉祥物「盒仔」：开箱纸箱里的小机器人（模板在 index.html，两处挂载） ----
 * 动画三件套：整体浮动 / 眨眼 / 天线呼吸光，reduced-motion 下全部静止。 */
.mascot svg { display: block; width: 100%; height: auto; }
.boxi-bob { animation: boxi-bob 3.4s ease-in-out infinite; }
.boxi-eye { transform-box: fill-box; transform-origin: center; animation: boxi-blink 4.6s infinite; }
.boxi-glow { animation: boxi-glow 2.6s ease-in-out infinite; }
@keyframes boxi-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3.5px); } }
@keyframes boxi-blink { 0%, 90.5%, 94.5%, 100% { transform: scaleY(1); } 92.5% { transform: scaleY(0.12); } }
@keyframes boxi-glow { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.45; } }

/* ---- 按钮 ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 38px;
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: calc(8px * var(--radius-scale));
  padding: 8px 13px;
  font-size: 13px; font-weight: 500; line-height: 20px;
  white-space: nowrap; text-decoration: none; vertical-align: middle;
  user-select: none; touch-action: manipulation;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease, box-shadow 140ms ease, transform 100ms ease;
}
.btn:hover:not(:disabled) { background: var(--panel-2); border-color: color-mix(in srgb, var(--muted) 60%, var(--line)); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:focus-visible { outline-offset: 3px; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.btn-primary:hover:not(:disabled) { background: var(--accent-hi); border-color: var(--accent-hi); box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 16%, transparent); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn-ghost:hover:not(:disabled) { background: var(--panel-2); border-color: transparent; color: var(--text); }
/* 危险操作三档：
 * - 页面与列表里带文字的（停止、删除、丢弃）：淡红底 + 红描边 + 红字，一眼与普通按钮分开。
 *   6% 的红底在暗色下与面板只差 0.5% 亮度、亮色下近乎白色，所以至少 12%；
 *   字用 --red-hi：12% 底上 --red 的字在部分亮色风格不足 4.5:1，--red-hi 亮色压深、暗色提亮。
 * - ghost 变体（列表行里一排的移除）：同样有淡红底，只去掉描边，比上一档轻一点。
 * - 确认弹窗里点下去就执行的那一个（.dlg-actions 内）：实心红，与主按钮同一级。
 * 行内纯图标（.action-icon）仍是平时红图标、悬停才有红底，见 actions.css。 */
.btn-danger { background: color-mix(in srgb, var(--red) 12%, var(--panel)); color: var(--red-hi); border-color: color-mix(in srgb, var(--red) 45%, var(--line)); }
.btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--red) 20%, var(--panel)); border-color: var(--red); color: var(--red-hi); }
.btn-ghost.btn-danger { background: color-mix(in srgb, var(--red) 10%, transparent); border-color: transparent; }
.btn-ghost.btn-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--red) 18%, transparent); border-color: transparent; }
.dlg-actions .btn-danger { background: var(--red); border-color: var(--red); color: var(--on-red); font-weight: 600; }
/* 悬停不换底色：--red-hi 在琥珀暗色里更亮，白字会掉到 3:1 以下；只加一圈红光 */
.dlg-actions .btn-danger:hover:not(:disabled) { background: var(--red); border-color: var(--red-hi); color: var(--on-red); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 32%, transparent); }
.btn-sm { min-height: 32px; padding: 5px 10px; font-size: 12.5px; }
.btn.action-icon { min-width: 32px; padding-inline: 7px; }
/* 图标按钮：图标随文字取色（danger 变体自动跟着变红），大小固定不被挤压 */
.btn-ico { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.btn-ico svg { flex: none; }
/* Explicit data-icon controls also include navigation, segmented controls and links. */
[data-icon] { display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.ui-icon { display: block; flex: none; pointer-events: none; }
.btn > svg { flex: none; width: 16px; height: 16px; pointer-events: none; }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
/* 按钮加载态：转圈 + 锁定点击 */
.btn.loading, .btn.loading:disabled {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  opacity: 0.85; cursor: progress; pointer-events: none;
}
.btn.loading .spin { width: 11px; height: 11px; }
.btn-primary .spin {
  border-color: color-mix(in srgb, var(--on-accent) 35%, transparent);
  border-top-color: var(--on-accent);
}
.dlg-actions .btn-danger .spin {
  border-color: color-mix(in srgb, var(--on-red) 35%, transparent);
  border-top-color: var(--on-red);
}

/* 触屏密度：普通按钮与桌面同高（38px），小按钮只补 2px。不要再统一拉到 44px：
 * 手机本来就窄，一排排 44px 的大块按钮把内容挤掉一半，看着比桌面笨重得多。
 * 图标按钮的点按区由 actions.css 的 --action-size 统一给。 */
@media (pointer: coarse) {
  .btn-sm { min-height: 34px; }
  .dlg-x { min-width: 36px; min-height: 36px; }
  .scope-btn, .thread-new { min-height: 34px; }
}

/* ---- Agent 品牌标识：官方 logo + 品牌色，跨侧栏/工作台/弹窗/设置复用 ----
 * .agent-claude / .agent-codex 设定 --brand，图标、文字、头像瓦片各自取用。 */
.agent-claude { --brand: var(--claude); }
.agent-codex { --brand: var(--openai); }
.agent-ico { display: block; flex: none; color: var(--brand); }
.agent-text { color: var(--brand); }
.agent-avatar {
  position: relative;
  width: 30px; height: 30px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: calc(8px * var(--radius-scale));
  background: color-mix(in srgb, var(--brand) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--brand) 32%, transparent);
}
/* 头像右下角状态灯：外圈颜色须与所在底色一致，默认页面底，卡片内在 shell.css 覆盖 */
.agent-avatar .led {
  position: absolute; right: -4px; bottom: -4px;
  width: 8px; height: 8px;
  border: 2px solid var(--bg);
}

/* ---- 状态灯 ---- */
.led {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--line);
  display: inline-block; flex: none;
  transition: background-color var(--dur-slow) var(--ease-out); /* 顶栏的灯是常驻节点，亮灭渐变 */
}
.led.on {
  background: var(--green);
  animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse { 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--green) 20%, transparent); } }

/* ---- 转圈 ---- */
.spin {
  display: inline-block; vertical-align: -2px;
  width: 12px; height: 12px; flex: none;
  border: 2px solid var(--amber-dim);
  border-top-color: var(--amber);
  border-radius: 50%;
  animation: rot 0.9s linear infinite;
}
@keyframes rot { to { transform: rotate(360deg); } }

/* ---- 区域加载态：加载动画大、居中在内容展示区，提示语小字排在下方。
 * 历史对话 / 文件列表 / 终端连接 / 文件预览共用。 ---- */
.loading-block {
  flex: 1;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 14px;
  padding: 36px 20px;
  color: var(--muted);
  font-size: 12.5px;
}
.loading-block .spin { width: 30px; height: 30px; border-width: 3px; }

/* ---- 操作反馈气泡 ---- */
/* 全局离线横幅：连续 ping 失败后常驻顶部，一眼看出「屏幕上的数据已不可信」 */
.offline-banner {
  position: fixed; top: 0; left: 0; right: 0; z-index: 120;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 7px 14px;
  background: var(--panel-solid);
  border-bottom: 1px solid var(--red-line);
  color: var(--red);
  font-size: 12.5px;
  box-shadow: var(--shadow-md);
}
.offline-banner.hidden { display: none; }

/* ---- 悬浮提示 ---- */
/* 全站单例气泡，js/tip.js 负责定位与显隐；页面里只写 data-tip="文本"。
 * [popover] 的 UA 样式会强塞 inset/margin/padding/border/overflow，这里逐条盖掉——
 * 尤其 overflow: auto 会把箭头裁没。 */
.tip {
  position: fixed; inset: auto; z-index: 200;
  display: none;
  margin: 0;
  width: max-content; max-width: min(300px, 78vw);
  padding: 6px 10px;
  background: var(--panel-solid);
  border: 1px solid var(--amber-dim); border-radius: calc(7px * var(--radius-scale));
  color: var(--text);
  font-size: 12px; line-height: 1.5;
  white-space: pre-line; /* 让文案里的 \n 生效，与原生 title 的行为一致 */
  overflow: visible;
  box-shadow: var(--shadow-md);
  pointer-events: none;  /* 永远不吃鼠标，免得气泡自己遮住锚点来回抖 */
  opacity: 0;
  transform: translateY(2px);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.tip.show { display: block; }
.tip.in { opacity: 1; transform: none; }

/* 箭头：旋转 45° 的小方块，画在气泡边框之上，底色正好把那截边框盖住。
 * --tip-ax / --tip-ay 由 tip.js 写，跟着锚点中心走，气泡被夹到视口边缘也指得对。 */
.tip::after {
  content: ""; position: absolute;
  width: 8px; height: 8px;
  background: var(--panel-solid);
  border: 1px solid var(--amber-dim);
  transform: rotate(45deg);
}
.tip[data-side="top"]::after    { bottom: -4px; left: var(--tip-ax); margin-left: -4px; border-top: 0; border-left: 0; }
.tip[data-side="bottom"]::after { top: -4px;    left: var(--tip-ax); margin-left: -4px; border-bottom: 0; border-right: 0; }
.tip[data-side="left"]::after   { right: -4px;  top: var(--tip-ay);  margin-top: -4px;  border-bottom: 0; border-left: 0; }
.tip[data-side="right"]::after  { left: -4px;   top: var(--tip-ay);  margin-top: -4px;  border-top: 0; border-right: 0; }

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:active:not(:disabled) { transform: none; }
  :root .scope-btn, :root .set-nav button { transition: none; }
  .wordmark .cursor { animation: none; }
  .boxi-bob, .boxi-eye, .boxi-glow { animation: none; }
  .led.on { animation: none; }
  .spin { animation: none; }
  .tip { transition: none; }
}

/* ============================================================================
   动效公共件。令牌在 :root；这里放跨页面复用的几样：
   页面切入、滑动指示条（ink.ts）、列表错落进场、骨架占位。
   ========================================================================= */

/* 视图、标签页、设置分区从 display:none 切到显示的那一帧，由 @starting-style 给出起点：
 * 切换逻辑仍旧只切 .hidden，不用改一行脚本。只做进场不做退场——退场得让两份内容
 * 同时在场，终端与长对话都经不起。
 * 祖先从隐藏变显示时，里面的子孙也算「第一次出现」，会一起播：所以外层（主视图、
 * 首页与工作台）只淡入，位移只给内层（标签页、设置分区），两层叠起来不会挪双份。
 * 终端、远程浏览器只淡入不位移：动画那一下若正好量尺寸、换算点击坐标，会差几个像素。 */
.view, #empty, #workbench { transition: opacity var(--dur) var(--ease-out); }
.pane, .set-section, .mdl-panel {
  transition: opacity var(--dur) var(--ease-out), translate var(--dur) var(--ease-out);
}
@starting-style {
  .view, #empty, #workbench { opacity: 0; }
  .pane, .set-section, .mdl-panel { opacity: 0; translate: 0 var(--enter-y); }
  #tab-term, #tab-browser { translate: none; }
}

/* 顶层弹层（popover）：下拉、分区菜单、工具菜单、版本菜单、日期面板、分步引导。
 * 进场淡入并从触发器一侧滑出 4px；退场同弹窗，只在支持 overlay 的浏览器上开（理由见 dialogs.css）。 */
:is(.select-panel, .section-menu, .action-popover, .version-menu, .date-range-popover, .workspace-guide):popover-open {
  transition: opacity var(--dur-fast) var(--ease-out), translate var(--dur-fast) var(--ease-out);
}
@starting-style {
  :is(.select-panel, .section-menu, .action-popover, .version-menu, .date-range-popover, .workspace-guide):popover-open {
    opacity: 0; translate: 0 var(--pop-from, -4px);
  }
}
@supports (overlay: auto) {
  :is(.select-panel, .section-menu, .action-popover, .version-menu, .date-range-popover, .workspace-guide) {
    transition: opacity var(--dur-fast) var(--ease-in), translate var(--dur-fast) var(--ease-in),
      overlay var(--dur-fast) allow-discrete, display var(--dur-fast) allow-discrete;
  }
  :is(.select-panel, .section-menu, .action-popover, .version-menu, .date-range-popover, .workspace-guide):not(:popover-open) {
    opacity: 0; translate: 0 var(--pop-from, -4px); pointer-events: none;
  }
}

/* 滑动指示条：ink.ts 量出选中项的位置写进 --ink-x/y/w/h，条本身只做 transform 与尺寸过渡。
 * 宿主带 data-ink 并 position: relative；条的外观（下划线还是底块）由各组件自己写。 */
[data-ink] { position: relative; }
.ink {
  position: absolute; left: 0; top: 0; z-index: 0;
  width: var(--ink-w, 0); height: var(--ink-h, 0);
  transform: translate(var(--ink-x, 0), var(--ink-y, 0));
  pointer-events: none;
  transition: transform var(--dur) var(--ease-move), width var(--dur) var(--ease-move), height var(--dur) var(--ease-move), opacity var(--dur-fast);
}
[data-ink-still] .ink { transition: none; }
[data-ink]:not([data-ink-ready]) .ink { opacity: 0; }
/* 选中项抬到条上面，文字不被底块盖住 */
[data-ink] > :not(.ink) { position: relative; z-index: 1; }
/* 差异/完整、认证方式这类分段按钮：各段原本自带底色，挂上滑块后底色交给宿主，
 * 选中段的底色交给滑块（脚本没挂上时仍是原来的样子） */
:is(.changes-view-tabs, .auth-modes)[data-ink] { background: var(--panel); }
:is(.changes-view-tabs, .auth-modes)[data-ink] .seg { background: transparent; }
:is(.changes-view-tabs, .auth-modes)[data-ink-ready] .seg.active { background: transparent; }
:is(.changes-view-tabs, .auth-modes) > .ink { background: var(--panel-2); }

/* 列表条目进场（首页卡片、使用记录行）：motion.ts 的 ListMotion 给新出现的条目加 .enter，
 * 并改写动画的开始时刻做错落与「重绘后接着播」，这里只管动画本身。 */
.enter { animation: list-enter var(--dur) var(--ease-out) both; }
@keyframes list-enter { from { opacity: 0; translate: 0 var(--enter-y); } }

/* 骨架占位：首次读取、换目录、换筛选时代替转圈，形状对上真实的行，数据到了整块替换。
 * 整块晚 150ms 才淡入：读得快时根本不出现，不会先闪一下骨架再换成内容。 */
.skeleton {
  display: block; height: 0.9em; max-width: 100%; border-radius: calc(4px * var(--radius-scale));
  /* 取文字色的半透明而不是面板色：放在抬起的底色上（余额卡、表头、磁贴）也看得出来 */
  --skel: color-mix(in srgb, var(--text) 10%, transparent);
  --skel-hi: color-mix(in srgb, var(--text) 4%, transparent);
  background: linear-gradient(90deg, var(--skel) 0, var(--skel-hi) 50%, var(--skel) 100%) 0 0 / 200% 100%;
  animation: skeleton-sweep 1.4s ease-in-out infinite;
}
@keyframes skeleton-sweep { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.skeleton-list, tbody:has(> .skeleton-row) { animation: skeleton-late var(--dur) var(--ease-out) 150ms both; }
@keyframes skeleton-late { from { opacity: 0; } }
.skeleton-row { pointer-events: none; }
/* skeleton.ts 拼出来的通用骨架：每条一根标题条加几根说明条。条目自身再挂真实行的类名
 * （.git-connection-row、.au-row…），内边距与分隔线就和读完后的行一致，高度不跳。 */
.skel-item { display: flex; flex-direction: column; gap: 8px; pointer-events: none; }
.skel-item > .skeleton:first-child { height: 1.05em; }
.skel-item > .skeleton + .skeleton { height: 0.8em; }
.skel-pad > .skel-item { padding: 10px 0; }
.skel-pad > .skel-item + .skel-item { border-top: 1px solid var(--line-soft); }
.skeleton.inline { display: inline-block; vertical-align: middle; }
/* 占满一行行高的条：放在文字的位置上，行高与读完后的文字一致（lh 不支持时退回 0.9em） */
.skeleton.text { box-sizing: border-box; height: 1lh; padding-block: calc((1lh - 0.9em) / 2); background-clip: content-box; }
/* 按钮位置的占位块：与小按钮同高，行高就和读完后带按钮的行一样 */
.skeleton.btn-like, .skel-item > .skeleton.btn-like { height: 32px; border-radius: calc(6px * var(--radius-scale)); }
.skel-item > .skeleton.btn-like { margin-top: 4px; }
@media (max-width: 760px) { .skeleton.btn-like, .skel-item > .skeleton.btn-like { height: 34px; } }
/* 正文/代码骨架：一行一根条，行距对上正文行高 */
.skel-lines { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; }
/* 已有内边距的容器里（diff、技能文档）不再加一层；代码行更密 */
.skel-lines.skel-code { padding: 6px 0; gap: 9px; }
.skel-lines.skel-doc { padding: 4px 0; }

/* ---- 进度条（progress.ts）：知道总量时画实心填充，不知道时一小段来回滑。
 * 填充用 translate 走位而不是改宽度：只动 transform，不引起重排，右端圆角也不被压扁。 ---- */
.progress {
  position: relative; height: 6px; overflow: hidden;
  border-radius: var(--radius-pill); background: var(--panel-2);
}
.progress-fill {
  position: absolute; inset: 0; border-radius: inherit; background: var(--accent);
  transform: translateX(calc((var(--progress, 0) - 1) * 100%));
  transition: transform var(--dur-slow) var(--ease-out), background-color var(--dur) var(--ease-out);
}
/* 进行中：填充上走一层斜纹，阶段卡在同一个百分比时也看得出还在动 */
.progress.active .progress-fill {
  background-image: linear-gradient(-45deg, transparent 0 30%, color-mix(in srgb, var(--panel-solid) 22%, transparent) 30% 50%, transparent 50% 80%, color-mix(in srgb, var(--panel-solid) 22%, transparent) 80%);
  background-size: 22px 22px;
  animation: progress-stripes 0.9s linear infinite;
}
@keyframes progress-stripes { to { background-position: 22px 0; } }
.progress.indeterminate .progress-fill {
  inset: 0 auto 0 0; width: 38%; transform: none; background-image: none;
  animation: progress-slide 1.4s var(--ease-move) infinite;
}
@keyframes progress-slide { from { translate: -105% 0; } to { translate: 270% 0; } }
.progress.ok .progress-fill { background-color: var(--green); }
.progress.error .progress-fill { background-color: var(--red); }

@media (prefers-reduced-motion: reduce) {
  /* 令牌归零：所有取令牌的过渡与动画一并静止，不必逐个组件再写一遍 */
  :root { --dur-fast: 0s; --dur: 0s; --dur-slow: 0s; --enter-y: 0px; }
  .enter { animation: none; }
  .skeleton, .skeleton-list, tbody:has(> .skeleton-row) { animation: none; }
  /* 不确定进度不再来回滑：整条铺成半透明，仍能看出「在进行、但不知道到哪」 */
  .progress .progress-fill { animation: none; }
  .progress.indeterminate .progress-fill { width: 100%; opacity: 0.45; translate: none; }
}

/* 展开收起：内容高度从 0 平滑过渡到 auto。要 interpolate-size 与 ::details-content，
 * 目前只有 Chromium 系，其余浏览器照旧瞬间展开。内容区得 overflow-y: clip，贴边输入框的
 * 焦点环会被切掉一截，所以只挂在纯文字/列表的折叠块上，带表单的（高级设置、剪贴板）不挂；
 * 窄屏的分区说明 .section-help 收起时仍显示正文（responsive.css），也不能挂。 */
@supports (interpolate-size: allow-keywords) and selector(::details-content) {
  :is(details.think, details.raw, .learn-more, .price-fold, .price-history, .home-setup, .update-notes) {
    interpolate-size: allow-keywords;
  }
  :is(details.think, details.raw, .learn-more, .price-fold, .price-history, .home-setup, .update-notes)::details-content {
    block-size: 0; overflow-y: clip;
    transition: block-size var(--dur-slow) var(--ease-move), content-visibility var(--dur-slow) allow-discrete;
  }
  :is(details.think, details.raw, .learn-more, .price-fold, .price-history, .home-setup, .update-notes)[open]::details-content {
    block-size: auto;
  }
}
