/* =============================================================================
   设计系统 —— 一处定义，全站（含应用页）取用。
   调性是既定的：暖调暗墙、金色一个重音、全站无衬线（业主 2026-08-03/08-04）。
   这里补的是它上面缺的那层——尺度、层次、密度。三条规矩：
     1. 层级先靠字号和字重，颜色是最后一档，不是第一档；
     2. 表面分四级，卡片是「亮一档的面」而不是「描了一圈的框」；
     3. 强调是稀缺品——一屏至多一个实心金按钮。
   这里只放已经有第二、第三个使用者的东西。一个地方要用的样式写在它自己
   身上，等真的重复了再搬上来——先建框架再等人来用，建出来的是死代码。
   旧名（--card/--control/--field/--control-line）全部保留并指向新值：
   应用包的样式表在用它们，改名会静默地把它们变成未定义（初始值）。
   ========================================================================== */
:root {
  /* --- 表面：由深到浅四级。页底最深，卡片浮起来一档，控件再一档。
         旧的 bg/card 只差 9 个色阶，卡片在暗底上几乎显不出来。

         2026-09-19 整体抬了一档（业主：「现在侧栏和聊天框各处太过于黑了，稍微往
         灰色走一点」）。抬的是明度不是色相——暖调暗墙是 2026-08-03/08-04 定的，
         不动；页底从 L* 5 抬到 11，肉眼上是「从黑变成深灰」这一步。
         **判据是对比度不是眼睛**：文字四档跟着抬了一点点，于是每一对文字/底色
         跟抬之前相差都在 ±0.05 以内（muted 和 faint），fg / fg-soft 富余很多，
         降一点仍在 9 以上。算法和那张表在 `test_design_tokens_contrast`。 --- */
  --surface-0: #1e1c1a;
  --surface-1: #272421;
  --surface-2: #2f2b28;
  --surface-3: #37332f;
  --surface-4: #403b36;
  --surface-sunken: #171513;

  --bg: var(--surface-0);
  --card: var(--surface-1);
  --control: var(--surface-3);
  --field: var(--surface-sunken);

  /* --- 线条三档：分隔用最轻的，描边用常规的，只有需要被看见时用重的。
         2026-09-14 业主：少用边框线，层次尽量靠背景色，页面才是一整块。
         所以线只做「分隔」（页脚上那一道、表头下那一道），不做「描边」：
         按钮、输入框、卡片、标签、菜单、弹窗都是亮一档或暗一档的面，边留着
         但透明——宽度还在，布局一个像素不动。谁要描边，说明它要的其实是
         再亮一档的面。 --- */
  /* 面抬了一档，线跟着抬同样的一档——不然它们在新的底上就看不见了。 */
  --line-soft: #302c29;
  --line: #3d3833;
  --line-strong: #4f4942;
  --control-line: var(--line-strong);

  /* --- 文字四档。最弱的一档是给机器话（id、哈希、时间戳）留的位置：
         它们必须能查到，但不该跟人写的字争。
         最弱这档原来是 #6e6960：在页底上对比度 3.5、卡片上 3.2，低于读得清的
         4.5，而它偏偏用在 11 像素的时刻和说明上（业主 2026-09-15 设计审查）。
         提到 #8a847a：页底 5.1、卡片 4.7，跟 --muted 仍差出一档。
         2026-09-19 面抬了一档，这三档跟着抬——不抬的话 faint 在页底上从 5.1 掉到
         4.7、在卡片上掉到 4.3，正是当初把它提上来要躲的那条线。最亮的 --fg 不动：
         它富余太多，抬了只会把「最重的字」和「次重的字」压扁。 --- */
  --fg: #ece9e4;
  --fg-soft: #cdc7bd;
  --muted: #aba59d;
  --faint: #948e84;

  /* --- 金：全站唯一的重音。实心金只给「这一页要你做的那件事」。 --- */
  --gold: #d4b06a;
  --gold-bright: #e8cb8d;
  --gold-soft: rgba(212, 176, 106, 0.09);
  --gold-wash: rgba(212, 176, 106, 0.16);
  --gold-line: rgba(212, 176, 106, 0.38);
  /* 首页装饰系统：图纸角标的金、提亮一档的正文。 */
  --gold-mark: rgba(212, 176, 106, 0.42);
  /* 那张脸的眼睛（voice-face.js 里的 MUSTARD，改一处要一起改）：脸旁边替它说话
     的字用这个色——语音房「在说」的状态、对话页「在画图／在想」那一行（业主
     2026-09-14：「aimy 头像后面跟着的状态文字，使用 aimy 眼睛的那个颜色」）。
     它是脸的色不是页面的重音，所以不并进 --gold。 */
  --face-eye: #fd9d23;

  /* 出事了用这个红，全站三十来处都在用它（三个应用包也在用）；「在跑」用金。
     状态目前就这两个词，各自一个既有的颜色，没有第三个——不给还不存在的
     状态先建一张词表。 */
  --red: #cf8177;
  /* 红也需要「铺一层」那一档，跟金的 --gold-soft 同一个比例：整行报警的时候，
     实心红会把行里的字全盖掉。描边那一档还是用 --red 本身，红的边就该看得见。 */
  --red-soft: rgba(207, 129, 119, 0.1);
  /* 蘑菇头急停钮那一档：--red 是「出事了」的字色和描边，铺满一颗 108px 的圆钮
     就成了粉的。站台上那颗是深红加白字，所以这里是深的那一档，只有它一个用处
     ——2026-09-07 业主要「和地铁的急停按钮一样」的时候进来的。 */
  --red-deep: #a8241b;
  --red-deep-bright: #bd2c21;
  --green: #478263;
  /* 图表用的四个系列色（2026-09-08，能力页的上下文构成条）：金是 Aimy 自己的，
     其余三个只在读数图里出现，不做按钮、不做文字。相邻两色的区分度跑过校验
     （dataviz 校验器，最差一对 ΔE 16.4，色弱 9.8），顺序就是校验时的顺序，别调换。
     那次校验写的底是 #121110，2026-09-19 页底抬成了 #1e1c1a——**这一项不受影响**：
     量的是四个系列色两两之间的距离，不是它们和底的距离。 */
  --chart-1: var(--gold);
  --chart-2: #6f9be0;
  --chart-3: #cfc8bc;
  --chart-4: #5fb58c;
  --chart-track: var(--line-strong);

  --font-sans: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
  /* 全站无衬线（业主 2026-08-03）；标题靠字重不靠字形。 */
  --font-display: var(--font-sans);
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* --- 字号是一条音阶，不是一把散点。以 15px 正文为基准，约 1.22 一档。
         页面只从这里取，不写字面值——散点是层级读不出来的主因。 --- */
  /* 最小一档 12（业主 2026-09-15 设计审查：灰字太淡、字太小）。2xs 这个名字留着，
     三十来处在用；它现在就是 12，跟 xs 同号——比 12 小的字在中文里笔画糊成一团。 */
  --text-2xs: 12px;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 15px;
  --text-md: 17px;
  --text-lg: 20px;
  --text-xl: 25px;
  --text-2xl: 32px;
  --text-3xl: 40px;

  /* PingFang SC 只有 Regular/Medium/Semibold 三个真字重，写 650 会被
     舍进 Semibold——那就直接写 600，省得以为自己拿到了第四档。 */
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semi: 600;

  --leading-tight: 1.3;
  --leading-snug: 1.5;
  --leading-normal: 1.7;
  --leading-relaxed: 2;
  /* 中文不加字距；这一档只给全大写的西文小标签和字母数字。 */
  --tracking-label: 0.08em;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 56px;
  --space-16: 80px;

  --radius-xs: 5px;
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;
  /* 输入框这一档：一行高 58 的框配 16 的角还是方的，主流对话产品的框在 24–28。 */
  --radius-xl: 24px;
  --radius-pill: 99px;

  /* 暗底上阴影很弱，层次主要靠亮度差；阴影只用来说「这一层浮在上面」。 */
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.38);
  --shadow-3: 0 18px 48px rgba(0, 0, 0, 0.45);

  /* 动效（业主 2026-09-17：「打开的时候都加上一个舒适的动画，高级感一些」）：一条缓动
     曲线、三档时长，全站的「打开」都从这里取，别各写各的数。舒适＝短、位移小、只有出场
     没有入场式的弹跳；高级感来自一致，不来自更多花样。 */
  --ease-out: cubic-bezier(0.2, 0.7, 0.3, 1);
  --motion-quick: 0.16s;
  --motion-base: 0.24s;
  --motion-slow: 0.32s;

  --container-width: 1280px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: var(--text-base) / var(--leading-normal) var(--font-sans);
  /* 暗底上的细字会显得毛躁；抗锯齿调薄一档，字形边缘才干净。 */
  -webkit-font-smoothing: antialiased;
}

a {
  color: inherit;
  text-decoration: none;
}

/* 标题拉开真正的差距：以前 26/18 挨着正文 15，三档挤在一起谁也不像标题。 */
h1 {
  margin: 0 0 var(--space-3);
  font-size: var(--text-xl);
  font-weight: var(--weight-semi);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
}

h2 {
  margin: 0 0 var(--space-3);
  font-size: var(--text-md);
  font-weight: var(--weight-semi);
  line-height: var(--leading-snug);
}

h3 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-base);
  font-weight: var(--weight-semi);
  line-height: var(--leading-snug);
}

.display {
  font-family: var(--font-display);
  font-weight: var(--weight-semi);
  letter-spacing: -0.01em;
}

.sub,
.muted,
.hint {
  color: var(--muted);
  font-size: var(--text-sm);
}

/* 数字要能上下对齐：等宽数字。
   这里不列 .count——那个名字画廊已经用着，Core 认领它就把人家那条规则
   变成了「重定义 Core 的类」（架构闸门当场拦下）。 */
.stat-figure {
  font-variant-numeric: tabular-nums;
}

/* --- 按钮分级（业主看到的每一页都按这个分）：
       描边 = 默认，平级的动作长这样；
       安静 = 导航和退路，平时不占视觉重量；
       实心金 = 这一页要你做的那件事，一屏至多一个——目前只有网页私聊的
       「发送」是，就写在它自己身上，等出现第二个再提取成类。
       以前所有按钮长得一模一样、hover 一律描金，于是「克隆成副本」和
       「批准」在页面上一样重。 --- */
/* display 故意不写。页面上有一批 <button> 是当容器用的（任务卡、筛选药丸），
   它们自己声明了 flex 布局；在这里给整个 button 家族加 inline-flex + 居中，
   会把那些卡片的内容挤出卡外——改完第一眼就看见了。

   按钮只有三种高（业主 2026-09-15 设计评审第二批「统一」：全站量出来 15、18、22、
   24、26、33、34、42、44 九种）：小 28（行内、卡片上的次要动作）、中 32（默认）、
   大 40（一页里要你做的那件事、输入框旁的主键）。高度由上下内边距 + 18 的行高 +
   上下各 1 的透明边凑出来，不写 height——当容器用的 <button> 自己定内边距，不被
   一个死高度压扁。只画图标的钮是同高的正方形（.button-icon）。 */
:root {
  --control-sm: 28px;
  --control-md: 32px;
  --control-lg: 40px;
  /* 顶栏有多高。几个页面要减掉它才算得出「剩下多少」（作图台的画布、讲述的
     播放画面、文件库那一屏），2026-09-18 把栏从 64 压到 48 时发现它们各自把
     栏高抄了一份写死在 calc() 里，抄的还是更早的 69——栏改了它们不会跟着改，
     多出来的空白也不会有人报错。一处定义，谁用谁减。 */
  --topbar-h: 48px;
  /* 左侧栏有多宽（业主 2026-09-19：导航挪进侧栏）。240 是对话页那份会话清单量
     出来的下限——一条不折行的标题在更窄的栏里只剩两三个字。收起来是 56，跟对话页
     那条窄轨同一个数：收起的其实是同一条栏。 */
  --rail-w: 240px;
  --rail-folded-w: 56px;
  /* 系统在这条栏的左端占掉多少（业主 2026-09-19 晚）。浏览器和 Windows 是 0：
     那里没有东西替我们画在栏上。macOS 的窗口是 92——红绿灯占掉的 68，加一个栏
     自己的边距（见 .is-desktop-mac .topbar）。
     它是个 token 而不是写死在那一条里，因为**两个地方要减它**：栏的左内衬，和
     「对话」那一颗标签的宽度（它要正好盖住左侧栏那一列，所以是「栏宽减掉这一段」）。
     全屏时红绿灯自己藏了，这个数归 0，那一颗于是自己往左顶到边缘。 */
  --bar-left: 0px;
  /* 毛玻璃那几块面有多不透明（见文件中段「毛玻璃」那一段）。这里给的是「完全
     不透明」，也就是没有毛玻璃这回事；桌面端的 shell.js 收到壳送来的数字时，
     把它写在 body 的行内样式上盖掉这一行。
     **默认值必须写在这儿**：不写它就是个未定义的变量，`rgba(…, var(--glass-alpha))`
     整条声明会被丢掉，那几块面反而变成全透明——设计令牌那道闸拦的正是这一类
     「静默变成初始值」，这一条就是被它拦下来才补的。 */
  --glass-alpha: 1;
}

/* 桌面端 macOS 的窗口：红绿灯那一段。挂在 body 上（.is-desktop-mac 也在 body
   上），所以栏和标签都继承得到。 */
.is-desktop-mac {
  --bar-left: 92px;
}

/* 全屏：灯藏起来了，那一段就不存在了。壳只报一句「我全屏了」（window.js 的
   enter/leave-full-screen → chrome.onChange），shell.js 只挂一个类，多宽多窄
   全在这一行里算——「壳报事实，页面定版面」。 */
.is-desktop-mac.is-desktop-fullscreen {
  --bar-left: 0px;
}

button,
.button {
  padding: 6px 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--control);
  color: var(--fg-soft);
  /* Form controls don't inherit the page font; without this a native
     <button> renders smaller than an <a class="button"> beside it. */
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: 18px;
  cursor: pointer;
  transition:
    color 0.15s ease,
    border-color 0.15s ease,
    background 0.15s ease;
}

button:hover,
.button:hover {
  border-color: transparent;
  background: var(--surface-4);
  color: var(--fg);
}

/* 退路、次级导航、行内小动作：平时只是一行字。 */
.button-quiet {
  border-color: transparent;
  background: transparent;
  color: var(--muted);
}

.button-quiet:hover {
  border-color: transparent;
  background: var(--surface-2);
  color: var(--fg);
}

/* 小 28：4 + 18 + 4 + 两道 1 的边。 */
.button-sm {
  padding: 4px 10px;
  font-size: var(--text-xs);
  line-height: 18px;
}

/* 大 40：10 + 18 + 10 + 两道 1 的边。 */
.button-lg {
  padding: 10px 16px;
  font-size: var(--text-base);
  line-height: 18px;
}

/* 只画图标的钮：跟同档的字钮一样高的正方形，图标用 flex 居中（图标是画的不是
   打的，见 chat-icons.js）。默认中档，配 .button-sm / .button-lg 换档。 */
.button-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-md);
  height: var(--control-md);
  padding: 0;
}

.button-icon.button-sm {
  width: var(--control-sm);
  height: var(--control-sm);
}

.button-icon.button-lg {
  width: var(--control-lg);
  height: var(--control-lg);
}

.button-icon[hidden] {
  display: none;
}

button:focus-visible,
.button:focus-visible,
a:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
}

button:disabled,
.button[aria-disabled="true"] {
  cursor: wait;
  opacity: 0.58;
}

/* textarea/select were missed here once and rendered as the browser's white
   boxes on this dark page (owner screenshot 2026-08-04) — style the family,
   not the one member a page happened to use first. */
/* 输入框是「亮一档的井」，不是描了边的洞：以前 --field 比页底只深 4 个色阶，
   全靠那一圈线才看得见。 */
input,
textarea,
select {
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--fg);
  font: inherit;
  transition:
    border-color 0.15s ease,
    background 0.15s ease;
}

::placeholder {
  color: var(--faint);
}

textarea {
  resize: vertical;
}

/* 勾选框和单选钮是仅有的两个没被管起来的控件：系统原生画法在暗底上是
   一块刺眼的白方块（作图首页「只看我的」实录）。自己画，勾号用 SVG
   而不是字符——字符自带基线边距，永远不居中。 */
input[type="checkbox"],
input[type="radio"] {
  -webkit-appearance: none;
  appearance: none;
  flex: none;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  background: var(--surface-3);
  vertical-align: -3px;
  cursor: pointer;
}

input[type="radio"] {
  border-radius: 50%;
}

input[type="checkbox"]:hover,
input[type="radio"]:hover {
  background: var(--surface-4);
}

input[type="checkbox"]:checked {
  border-color: var(--gold);
  background: var(--gold)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6.5' fill='none' stroke='%23121110' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% no-repeat;
}

input[type="radio"]:checked {
  border-color: var(--gold);
  background: radial-gradient(circle, var(--gold) 0 3.5px, var(--surface-3) 4.5px);
}

/* 卡片是「亮一档的面」，没有边。以前它是「描了一圈的框」，
   于是全站每样东西——读数、列表、正文、空状态——都长成同一个框。
   边留一像素透明的：页面上有一批规则在 hover 时改 border-color，宽度不在
   的话它们一亮就把布局挤一个像素。 */
.card {
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--card);
}

.badge {
  display: inline-flex;
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  padding: 2px 9px;
  font-size: var(--text-2xs);
  line-height: 1.5;
}

.pill {
  border-radius: var(--radius-pill);
  padding: 4px 9px;
  font-size: var(--text-xs);
}

/* 「副本」签：克隆的群、自测会话。记录页 2026-08-28 就有这一枚，任务页
   2026-09-04 也要——住在这里而不是各页一份，是因为两页上长得不一样的话，
   分辨它就成了新的困惑，而分辨正是它存在的理由。方括号不描粗边：它是
   注脚不是状态，状态那一列自有自己的记号。 */
.sandbox-tag {
  display: inline-block;
  margin-right: var(--space-2);
  padding: 0 0.4em;
  border: 1px solid transparent;
  border-radius: 3px;
  background: var(--surface-3);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--muted);
  vertical-align: middle;
}
/* `display` 写在类上，就压过了浏览器自带的 `[hidden] { display: none }`——
   自带样式表属于 user-agent 来源，任何作者规则都赢它。任务页详情抬头那一枚签
   是靠 `hidden` 开关的，少了这一条，真群的任务详情会顶着「副本」两个字，
   正好把要消除的困惑反过来造一遍（2026-09-04 探针量出来的）。 */
.sandbox-tag[hidden] { display: none; }

/* 一条栏，48 高（业主 2026-09-18）。桌面端原来在它上面还画了一条自己的标签栏，
   两条摞起来像「把网页装在盒子里」；标签搬进了这一条里（.topbar-tabs），所以
   网页端和桌面端从此是同一份模板、同一份样式，天然一致。

   2026-09-19 业主把导航挪进了左侧栏，顶栏于是只在三种情形下还是一条栏：
   手机（640 以下）、应用页、以及桌面端那条只装标签的窄条。收法在文件末尾
   min-width:641 那一段——这里定义的仍是「一条完整的顶栏长什么样」。 */
.topbar {
  min-height: 48px;
  padding: 0 var(--space-6);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  background: var(--surface-1);
  /* 钉在窗口顶上（业主 2026-09-19：「顶栏是用来方便切换的，不能随着页面滚动，
     要固定在顶部」）。会滚的页面——任务、能力、用量——原来一滚就把它带走了，
     桌面端连标签都跟着没影，而标签正是用来切换的那个东西。
     用 sticky 不用 fixed：sticky 留着自己那一格，不用在每种页上再补一截
     padding（对话页是 100vh 的 flex 列，补了就撑破）。老内核（AGENTS.md）不认
     sticky，那里它退回今天这样——跟着滚，是可以接受的退化，不是坏掉。
     z-index 比正文高一档，但低于管理菜单面板（20）和弹窗（80）。 */
  position: sticky;
  top: 0;
  z-index: 10;
}

/* 招牌拆成两半（业主 2026-08-29）：头像是「Aimy 是什么」的门，
   「Aimy」是回对话的页签。所以「Aimy」不再是一个更重的招牌，它就是
   页签——跟它右边那几个是同一类东西，长同一个样子最诚实。 */
.topbar-avatar-link {
  flex: none;
  display: inline-flex;
  border-radius: var(--radius-sm);
}

.topbar-avatar-link:hover {
  opacity: 0.85;
}

.topbar-avatar {
  width: 28px;
  height: 28px;
  display: block;
}

/* 页签里唯一一个字重高一档的：它是这套东西的名字，也是回家的路。 */
.topbar-nav-link.topbar-home {
  font-weight: var(--weight-medium);
}

.topbar-nav:empty {
  display: none;
}

.topbar-nav {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}

/* Aimy 自己的页签和管理菜单装在这一格里（业主 2026-09-19）。装进一格是为了
   应用页：那儿整格靠 hidden 收着，桌面端的 shell.js 把属性去掉就放出来——
   一份渲染、一个开关，不是两份模板各画一遍。撑满剩下的地方，好让管理菜单的
   margin-left:auto 仍然把自己顶到右端。 */
.topbar-nav-core {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}

/* 凡是给自己设了 display 的类，必须自带这一条：浏览器自己的 [hidden]{display:none}
   住在用户代理层，输给任何作者声明（2026-09-04「副本」签实录）。少了它，网页端的
   应用页顶上会挂着本该收起来的那一排。 */
.topbar-nav-core[hidden] {
  display: none;
}

/* 管理贴住右端；页签跟着品牌走左边。order 让它排在最后——它是这一格里唯一一个
   「不管后面再放什么都该在末尾」的东西。 */
.topbar-nav .topbar-menu {
  order: 1;
  margin-left: auto;
}

/* 竖线只分两段（业主 2026-08-14）：会话·任务·能力 都是 Aimy 核心的功能，
   跟右边的「应用」之间才是一道真的界线。竖线两侧多留一口气，让这道界线比
   页签之间的普通间距明显——分段靠的是距离，竖线只是把它点明。 */
.topbar-nav-divider {
  flex: none;
  width: 1px;
  height: 16px;
  margin: 0 var(--space-3);
  background: var(--control-line);
}

/* 托盘和它右边那一堆之间也有一道，长在 .topbar 上而不是 nav 里（业主
   2026-09-19 把托盘挪到了最前）。这里不自带留白：栏本身的 gap 就是 16px，
   nav 里那几道要 margin 只因为那边的 gap 只有 4px。 */
.topbar > .topbar-nav-divider {
  margin: 0;
}

/* --- 桌面端的一排标签（业主 2026-09-18） --------------------------------------
   网页端不长这个东西——浏览器自己有标签，所以模板把它 hidden 着送出去，只有
   桌面端的 shell.js 认出 window.aimyDesktop 之后才把标签画进去。

   它排在整条栏的最前面（业主 2026-09-19）。原来跟在导航后面：导航在对话页上
   有五项、在应用页上只有一句「← 回 Aimy」，于是同一排标签在两种页上差了四百
   像素，切一下就横着跳。标签是窗口的东西，不是这一页的东西，所以它的位置只跟
   窗口有关——左边只有 macOS 的红绿灯。

   各自独立、不共底（业主 2026-09-19：「这一排现在没有其他元素并列了，可以单独
   拆成明显的独立标签，不要挤在一起，宽一些，容易点击。这个原来用来统一的黑色
   背景也不要了」）。09-18 它们是嵌在一个深一档的槽里的一串，那时右边紧挨着导航，
   需要一道界线说清「这边是开着的东西，那边是去哪儿」；当天下午导航搬进了左侧栏，
   这条栏上只剩标签，界线就没有另一边可分——槽于是只剩下把几颗钮挤在一起这一个
   作用。 */
.topbar-tabs {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* 凡是给自己设了 display 的类，必须自带这一条：浏览器自己的 [hidden]{display:none}
   住在用户代理层，输给任何作者声明（理由同 .topbar-alarm[hidden]）。少了它，
   网页端每一页顶栏上都会挂着一个空槽。 */
.topbar-tabs[hidden] {
  display: none;
}

/* 一颗标签就是一颗钮，而且要一眼看得出能点（业主 2026-09-19：「宽度不够，样式也
   不够明显，没有选中的标签也要有背景色区分，不要完全融于背景。要做成人很喜欢点击
   的样子」）。三档底色：没选中 surface-2、鼠标停上去 surface-3、选中 surface-4，
   一档比一档亮，每一颗都浮在栏（surface-1）上面。

   `line-height: 1.4` 是那一点点裁切的解（业主同日：「顶部的标签有轻微裁切文字」）：
   名字那一段 `overflow: hidden`，行高写 1 时行盒只有字号那么高，中文的字形比这
   高出半成，上下就各被削掉一点——放大到字号 15 之后更看得出来。 */
.topbar-tab {
  flex: none;
  min-width: 120px;
  max-width: 260px;
  height: 36px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--fg-soft);
  font-family: inherit;
  font-size: var(--text-base);
  line-height: 1.4;
  cursor: pointer;
  transition:
    background var(--motion-quick) var(--ease-out),
    color var(--motion-quick) var(--ease-out);
}

.topbar-tab:hover {
  background: var(--surface-3);
  color: var(--fg);
}

.topbar-tab.is-active {
  background: var(--surface-4);
  color: var(--fg);
  font-weight: var(--weight-semi);
}

/* 「看一眼」那一颗是斜的：它装的是这一刻点开的那一个——应用列表、任务、一份文件
   ——下一个链接换的就是它。斜体说的正是「这一颗不常驻」。 */
.topbar-tab.is-aside .topbar-tab-title {
  font-style: italic;
}

/* --- 这一排的三种东西，各有一个样子（业主 2026-09-19 晚）---------------------
   业主的说法是位置：「对话页签宽度和左侧栏同宽（桌面端就减去红绿灯按钮），然后
   横向位置一跨过左侧栏的宽度就是应用按钮，打开了应用以后就往后排列应用的页签。
   然后用三种稍微有点区别的样式做出来。」位置本身就说清了三件东西是什么：

   **对话**是这条栏上唯一一个「一直在那儿」的地方，所以它有固定的一格——正好是
   左侧栏那一列，两条边对齐。关不掉，没有叉。
   **应用**是一扇门，不是一个地方，所以它是空心的：一圈描边，没有底。按下去出来
   的是菜单，不是一页。
   **应用／看一眼**那几颗是「你打开的东西」：实心的底、自己的宽度、右边一个叉。

   对话那一颗的宽度＝栏宽减掉系统占掉的那一段（--bar-left）。所以在浏览器里它
   从 0 起、到 240 收；在 macOS 的窗口里从 92 起、还是到 240 收；全屏时灯藏了，
   --bar-left 归 0，它自己往左长回 240——业主点名的那一条。
   老内核拿不到 calc 里的变量也有个能看的底（240 / 148 两行死数在前面）。
   **左侧栏收起来的时候（56）这一颗不跟着缩**：缩了名字就没地方写，而收起是这
   一页的开关，不是整条栏的事。那会儿两条边就不齐了，知道。 */
.topbar-tab.is-home {
  width: 240px;
  width: calc(var(--rail-w) - var(--bar-left));
  max-width: none;
  font-weight: var(--weight-medium);
}

/* 打开的那几颗：站在哪一个里，那一颗的记号是金的。对话那一颗不参加——它永远在，
   用不着再说一遍「你在这儿」，而且底色已经亮了一档。 */
.topbar-tab.is-app.is-active .topbar-tab-face,
.topbar-tab.is-aside.is-active .topbar-tab-face {
  color: var(--gold);
}

/* 一类一个记号（业主 2026-09-19）。三枚同一个尺寸、同一个颜色，颜色跟着标签走
   ——没选中的淡、选中的亮，记号和名字一起变，不另设一档。 */
.topbar-tab-face {
  flex: none;
  width: 16px;
  height: 16px;
  display: block;
  color: inherit;
}

/* 名字吃掉多出来的宽度，关闭钮才会贴着标签的右边缘。不这么写，一颗比 min-width
   还短的标签（「Aimy」「作图」）会把名字、数字、叉都堆在左半边，右半边空着，
   看上去像没画完。min-width:0 是让 ellipsis 在 flex 里生效的那一条。 */
.topbar-tab-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: left;
}

.topbar-tab-badge {
  flex: none;
  min-width: 16px;
  height: 16px;
  padding: 0 var(--space-1);
  border-radius: var(--radius-pill);
  background: var(--red);
  color: var(--surface-0);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semi);
  line-height: 16px;
  text-align: center;
}

/* 同上：没有未读的时候这枚数字靠 hidden 收着。 */
.topbar-tab-badge[hidden] {
  display: none;
}

/* 标签长大了，叉也跟着：20 见方的落点，图形还是 11。它自己 stopPropagation，
   所以这 20 像素是「关掉」，标签剩下的都是「切过去」——两个动作离得开。 */
.topbar-tab-close {
  flex: none;
  width: 20px;
  height: 20px;
  margin-right: -4px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xs);
  color: var(--muted);
}

.topbar-tab-close:hover {
  background: var(--surface-4);
  color: var(--fg);
}

.topbar-tab-close svg {
  width: 11px;
  height: 11px;
  display: block;
}

/* 「应用」那一枚（业主 2026-09-19 晚：「把『应用』放到会话按钮后面，hover 就能
   看到应用，点击就能打开应用」）。它由 shell.js 挪进托盘的第二位，排在「对话」
   之后——也就是横向一跨过左侧栏那一列就是它。
   **它是三种里唯一空心的那一个**：一圈描边、没有底。理由在上面那段长注释里——
   它是一扇门，不是一个地方，按下去出来的是菜单而不是一页。高和圆角跟标签一样，
   所以这一排还是一排；不一样的只有「实心／空心」这一件事。
   宽度不设 min-width：标签有，是因为里头是长短不一的名字；这一枚永远是两个字。
   选择器写全 `.topbar-menu > summary.topbar-apps-button`，不是图好看：下面那条
   给所有菜单写的 `.topbar-menu > summary` 权重比单个类高，又排在后面，光写类名
   的话 padding 会被它的 10px 顶掉。权重压过去，两条的先后就不用记了。
   三角由那条公共规则收（list-style + ::-webkit-details-marker 各一条，两个内核
   收法不一样），这里不再抄一遍。 */
.topbar-menu > summary.topbar-apps-button {
  flex: none;
  height: 36px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  border: 1px solid var(--control-line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg-soft);
  font-size: var(--text-base);
  line-height: 1.4;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--motion-quick) var(--ease-out),
    border-color var(--motion-quick) var(--ease-out),
    color var(--motion-quick) var(--ease-out);
}

/* 手一过去、或者菜单正开着：填上底，描边让位。空心变实心就是「这扇门开了」。 */
.topbar-menu > summary.topbar-apps-button:hover,
.topbar-apps[open] > summary.topbar-apps-button {
  border-color: transparent;
  background: var(--surface-3);
  color: var(--fg);
}

.topbar-apps-face {
  flex: none;
  display: block;
  color: inherit;
}

/* details 默认是块级，在栏里（和被挪进托盘之后）都是一个 flex 项：不写 flex:none
   它会跟着伸缩，「应用」两个字就被压扁。 */
.topbar-apps {
  flex: none;
}

/* 手机顶栏上的「任务」（业主 2026-09-22 晚）。宽屏上它不出现——那儿每一页的侧栏上都有
   「任务」那一行；样子写在文件末尾 640 以下那一段，跟「应用」那一格同一路。 */
.topbar-tasks {
  display: none;
}

.topbar-tasks[hidden] {
  display: none;
}

/* 桌面壳给「画不出栏的页面」（PDF、图片的原始字节）戴的那一条就是这一页
   （templates/shell_strip.html，业主 2026-09-19：「顶部栏保持应该有的标签」）。
   它只有一条栏，没有正文——所以这里只管把页面本身收干净，栏的样子照旧走上面
   那几条，一个字都不抄。 */
.shell-strip {
  margin: 0;
  overflow: hidden;
  background: var(--surface-1);
}

/* --- 桌面端：这条栏就是标题栏 -------------------------------------------------
   `is-desktop` 由 shell.js 认出 window.aimyDesktop 之后挂到 body 上，所以这一整段
   在浏览器里一条都不生效。整条栏可以拖窗口，栏里每个能点的东西自己退出拖动区
   ——不退出的话，点它变成拖窗口。 */
.is-desktop .topbar {
  -webkit-app-region: drag;
}

.is-desktop .topbar-avatar-link,
.is-desktop .topbar-nav-link,
.is-desktop .topbar-menu,
.is-desktop .topbar-tabs,
.is-desktop .topbar-tasks,
.is-desktop .topbar-alarm {
  -webkit-app-region: no-drag;
}

/* macOS 的红绿灯画在栏的左端：窗口那边从 x=16 起画三颗 12px、间距 8px 的灯
   （window.js 的 TRAFFIC_LIGHTS），最后一颗到 68 结束。留白必须是「68 再加一个
   栏自己的边距」——第一版直接写了 68，等于让头像紧贴着最后一颗灯的边缘长出来，
   业主更新后第一眼就看见它俩糊在一起（2026-09-18）。68 是灯占掉的，不是留白。

   这个数现在叫 `--bar-left`（见 :root），因为**第二个人要用它**：「对话」那一颗
   标签要正好盖住左侧栏那一列，它的宽度就是「栏宽减掉这一段」。一处定义，两处减。
   **全屏时红绿灯自己藏起来**（业主 2026-09-19：「mac 上全屏的时候红绿灯会隐藏，
   会话按钮应该自动变宽往左顶住边缘」），那一刻这一段该是 0——壳发一句「我全屏了」，
   shell.js 给 body 挂上 is-desktop-fullscreen，下面那一条把它归零。 */
.is-desktop-mac .topbar {
  padding-left: 92px;
  padding-left: var(--bar-left);
}

/* Windows 的最小化／最大化／关闭画在栏的右端（titleBarOverlay）。先留一个够用的
   死数，认得 env() 的内核再按它实报的宽度收——旧写法自己站得住。 */
.is-desktop-win .topbar {
  padding-right: 148px;
  padding-right: calc(100% - env(titlebar-area-width, 100%) + var(--space-6));
}

/* 应用页那句「← 回 Aimy」在桌面端是重复的：托盘里就有 Aimy 那一颗。 */
.is-desktop .topbar-nav-exit {
  display: none;
}

/* --- 毛玻璃（业主 2026-09-19：「客户端加上整体毛玻璃透明效果，允许用户调整
   透明度」）---------------------------------------------------------------------
   三层缺一层就还是实底：系统画的材质、窗口和每一格视图让出自己的面（两样都在
   adapters/desktop/src/window.js），和这里——页面把自己那几块**大面积的底**刷成
   半透明。卡片、菜单、气泡一律不动，它们靠「亮一档的面」说话，半透明之后层次
   就没了。

   **一块地方只许铺一层**（业主 2026-09-19 报「开到强也没效果」「输入框背后的
   背景色露馅了，有一片更深的颜色」，探针定的案）。第一版每一块都自己 `rgba` 一
   遍，而它们是**套在一起**的：左侧栏套在整页里、输入框那一坨套在对话区里，
   两层叠起来 0.72 就成了 0.92——几乎不透，正是「没效果」；而同色的两层一叠又比
   旁边深一块，正是「露馅」。探针那次直接把名单打出来了：
   `nav.railbar 套在 body 里，都是 rgb(30,28,26)`、`div.chat-dock 套在 div.chat-shell
   里，都是 rgb(23,21,19)`。
   所以规矩是：**同色的那一层，里面那个不铺**（railbar、chat-dock 归透明，外面
   那层已经是它要的颜色）；**异色的那一层照铺**（顶栏比整页亮一档、对话区比整页
   深一档，那是设计要的深浅，叠出来更实也对——标题栏和正文区本来就该比别处实）。

   `--glass-alpha` 由 shell.js 从壳送来的数字写在 body 的行内样式上，`is-glass`
   同时挂上。两件事都由壳决定，所以：**浏览器里这一段一条都不生效**（没有
   `is-desktop`），关掉透明时也一条都不生效（没有 `is-glass`）。

   这里的 rgb 三元组是上面那几个 token 的同一个颜色，手抄了一份——`rgba()` 里
   放不进一个十六进制变量。为此把整套 token 改成「三个数字再拼回去」的写法也行，
   但那要付的代价大得多：老内核一旦解析不了那种拼法就整条声明丢掉、面直接变透明。
   抄一份的风险归 `tests/test_design_tokens.py` 看着：那条测把 token 的十六进制
   转成十进制，跟这里的数字逐一对，对不上就红。 */
.is-desktop.is-glass {
  background: rgba(30, 28, 26, var(--glass-alpha));
}

/* 栏底下要糊一层（业主 2026-09-19：「顶栏的背景色要有模糊效果，否则有内容穿过
   的时候会看不清楚」）。这条栏是 sticky 的，正文从它底下滚过去——半透明之前那
   是块实底，看不见；一透明，滚过去的字就直接透上来，跟栏上的标签叠在一起。
   窗口底下那层材质是系统糊的，管的是**桌面**；**页面自己的内容**得页面自己糊，
   这一条就是。只在毛玻璃下写：关掉时栏是实底，没有东西可糊。
   `backdrop-filter` 在这儿可以放心用——这一段整段挂在 `.is-desktop` 上，跑的是
   客户端里的 Chromium，不是 AGENTS.md 里那个老内核。 */
.is-desktop.is-glass .topbar {
  background: rgba(39, 36, 33, var(--glass-alpha));
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
}

.is-desktop.is-glass .chat-shell {
  background: rgba(23, 21, 19, var(--glass-alpha));
}

/* 这两块跟罩着它们的那一层同色，所以在毛玻璃下不铺第二遍：左侧栏跟整页同色，
   输入框那一坨跟对话区同色。铺了就是同一个颜色叠两遍，比旁边深一块。 */
.is-desktop.is-glass .railbar,
.is-desktop.is-glass .chat-dock {
  background: transparent;
}

/* 大影子在半透明上是一片乌云（业主 2026-09-19：「应用按钮 hover 出菜单以后
   背后有一片深色笼罩」）。`--shadow-2` 是 20 像素的黑色晕开，不透明的时候它
   只是一圈立体感，底下一透就成了一大块脏。面板自己是毛玻璃（backdrop-filter）
   加一圈描边，本来就分得开，不靠影子。 */
.is-desktop.is-glass .topbar-menu-panel {
  box-shadow: none;
}

/* --- 左侧栏 -----------------------------------------------------------------
   业主 2026-09-19：「把 aimy 头像、应用、任务，都放到左侧栏。取消顶部栏。」
   一份骨架，Aimy 的每一页共用（模板 aimy_base.html）：上面是头像和几项导航，
   中间是这一页自己的东西（{% block rail %}），底下是「你是谁」和管理菜单。

   只有 641 以上才有，跟对话页清单变抽屉的断点是同一个数（对话页那份样式表）——不另发明
   一个断点。640 以下这一条整条不出现：导航仍在顶栏，各页自己的抽屉仍归各页自己。
   应用页也没有：它保持 2026-08-14 定的最简头部（模板里连 .railbar 都不渲染）。

   position: fixed 而不是 sticky。栏要从窗口顶贴到窗口底；sticky 在老内核上掉了
   就是一条跟着长页面滚走的栏——整套导航在第二屏就不见了。占位交给 .shell-body
   的 padding-left，两个写法全内核都认。

   **z-index 必须写**（业主 2026-09-19 报「后台管理菜单被裁切」，探针定的案）。
   建这条栏时这里写着「不设 z-index，免得它自己开了层叠上下文把菜单面板关在栏里」
   ——那句话把规矩记反了：`position: fixed` **本来就无条件开一个层叠上下文**，
   z-index 写不写都一样。于是面板那个 z-index:20 只在栏内部排序，而整条栏以
   「z-index: auto 的定位元素」参与根上下文，按 DOM 顺序排在 `<main>` 前面——
   `.chat-shell`（position: relative）于是盖在面板上，菜单从栏的右边缘（240）
   起整块被遮住。实测：面板 x=196 宽 144，在 x=336 那一点 `elementsFromPoint`
   回来的第一个是 `.chat-shell`，第二个才是面板。
   取 50：压过正文和手机抽屉（30），让给灯箱（60）和弹窗（80）。 */
.shell-body {
  display: block;
}

.railbar {
  display: none;
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 50;
  width: var(--rail-w);
  flex-direction: column;
  padding: var(--space-4) 0 var(--space-3);
  background: var(--surface-0);
  border-right: 1px solid var(--line-soft);
}

/* 自己设了 display 的类自带这一条（2026-09-04「副本」签实录）。 */
.railbar[hidden] {
  display: none;
}

/* 招牌靠左，这一页自己的钮（对话页的「收起清单」）贴右端。 */
.railbar-head {
  flex: 0 0 auto;
  padding: 0 var(--space-3);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.railbar-head > .railbar-brand {
  flex: 1 1 auto;
  min-width: 0;
}

.railbar-tool {
  flex: none;
  display: flex;
  align-items: center;
}

/* 头像 + 名字，点开是「Aimy 是什么」——跟顶栏上那一枚同一扇门。 */
.railbar-brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--fg);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
}

.railbar-brand:hover {
  opacity: 0.85;
}

.railbar-brand-name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 栏上现在只剩底下那一颗管理菜单还是「一行一项」的样子（导航 2026-09-19 晚整批
   搬回顶栏）。留着这一条是因为那一颗仍要 hover 和「当前页」两种样子。 */
.railbar-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  line-height: 20px;
  transition:
    color 0.15s ease,
    background 0.15s ease;
}

.railbar-link[hidden] {
  display: none;
}

.railbar-link:hover {
  background: var(--surface-1);
  color: var(--fg);
}

.railbar-link.is-current {
  background: var(--surface-1);
  color: var(--gold);
  font-weight: var(--weight-semi);
}

.railbar-icon {
  flex: none;
  display: block;
}

/* 「去哪儿」那一组：技能库、任务（业主 2026-09-22 晚：任务从管理菜单「拿出来（并且加上
   任务状态的显示，比如有几个在跑）」）。每一张带侧栏的页都有它——用量、会话这些页左边
   原来是空的，去任务页得先回对话再点。模板在 _rail_go.html：没有会话清单的页画在栏头
   底下，带清单的页画在清单顶上（手机上清单是对话页的抽屉，这两行得跟着进去）。

   行的样子跟清单里「新会话」「搜索」那两行（对话页样式表的 .chat-side-action）是同一
   套数：同高、同内衬、同字号、同一档 hover——它们在对话页上排成一摞，得一样大。这里
   另抄一份，因为任务页、用量页不带那份样式表。容器的内衬和行距也照 .chat-side-actions
   写，两处画出来是同一个位置；底下多 2px，是跟下面那一摞之间照旧隔 2px。 */
.railbar-go {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  padding: 0 var(--space-2) 2px;
}

.railbar-go[hidden] {
  display: none;
}

/* 行距用外边距，不用 flex 的 gap——老内核不认后者（AGENTS.md）。 */
.railbar-go-link + .railbar-go-link {
  margin-top: 2px;
}

.railbar-go-link {
  display: flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--muted);
  font-size: var(--text-base);
  text-decoration: none;
}

.railbar-go-link > svg {
  flex: none;
  display: block;
  margin-right: 10px;
}

.railbar-go-link:hover {
  background: var(--surface-2);
  color: var(--fg);
  text-decoration: none;
}

/* 站在这一页上：跟会话清单里「当前这一间」同一种说法（对话页样式表的
   .chat-session.is-current）——铺一层淡金、字亮起来、重一档。写在 hover 后面，
   手一过去也不退成灰底。 */
.railbar-go-link[aria-current="page"] {
  background: var(--gold-soft);
  color: var(--fg);
  font-weight: var(--weight-medium);
}

.railbar-go-link > .live-count {
  margin-left: auto;
}

/* 「几件在跑」：转圈、金字、带数。跟会话清单每一行的派单记号（对话页样式表的
   .chat-session-tasks）是同一个样子，数的范围不同——那个是自己的一间会话，这个是
   任务板。栏上那一行和手机顶栏那一格用的是它。 */
.live-count {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--gold-wash);
  color: var(--gold);
  font-size: var(--text-2xs);
  font-weight: var(--weight-normal);
  line-height: 18px;
  font-variant-numeric: tabular-nums;
}

.live-count[hidden] {
  display: none;
}

.live-count-spin {
  flex: none;
  display: block;
  margin-right: 3px;
}

/* 中间这一格是这一页自己的（对话页放它的会话清单）。没人往里放东西时它就是
   一块会伸缩的空白，把底下那一格顶到栏底。 */
.railbar-page {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.railbar-foot {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-3) 0;
  border-top: 1px solid var(--line-soft);
}

/* 「你是谁」：这台机器认出来的名字，和管理菜单排在同一行——栏底这一行说的是
   「你」和「这台机器」，跟上面那几项「去哪儿」不是一回事。 */
.railbar-who {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.railbar-who-name {
  flex: 1 1 auto;
  min-width: 0;
  padding-left: var(--space-2);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--fg-soft);
  font-size: var(--text-sm);
}

/* 一格一个应用，小方块（业主 2026-09-19：「每一个应用做成一个小方块」）。
   三个一行；不用 grid 也不用 flex 的 gap——两样在老内核上都靠不住（AGENTS.md），
   间距改成每颗自己带 4 的外边距、容器倒扣 4，这个写法哪儿都认。 */
.apps-panel {
  min-width: 0;
  padding: var(--space-3);
}

.apps-panel-grid {
  width: 252px;
  margin: -4px;
  display: flex;
  flex-wrap: wrap;
}

.apps-tile {
  flex: none;
  width: 76px;
  margin: 4px;
  padding: 10px 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-radius: 10px;
  color: var(--fg-soft);
  text-align: center;
  text-decoration: none;
  transition: background var(--motion-quick) var(--ease-out);
}

.apps-tile:hover {
  background: var(--surface-4);
  color: var(--fg);
  text-decoration: none;
}

.apps-tile.is-current {
  background: var(--surface-4);
  color: var(--gold);
}

/* 方块本身。里面那一个字是应用名字的头一个字——`WebSurface` 没有图标这一项，
   与其给契约加一列，不如先用它自己的名字；至少五个应用的头一个字互不相同。 */
.apps-tile-face {
  flex: none;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;
  background: var(--surface-1);
  color: var(--gold);
  font-size: 19px;
  font-weight: var(--weight-semi);
  line-height: 1;
}

.apps-tile:hover .apps-tile-face,
.apps-tile.is-current .apps-tile-face {
  background: var(--surface-0);
}

/* 名字最多两行，高度写死——长短不一的名字不该把一行方块推得高低不齐。 */
.apps-tile-name {
  margin-top: 6px;
  height: 2.8em;
  overflow: hidden;
  font-size: var(--text-xs);
  line-height: 1.4;
}

.railbar-menu {
  flex: none;
  margin-left: auto;
}

/* 只画记号的那一颗，方的（.railbar-link 是一行，这一颗不是）。 */
.railbar-menu > summary.railbar-link {
  padding: var(--space-2);
}

/* 栏底那一枚急停铃。跟顶栏那一枚是同一个类、同一个读数、同一份开关（急停页
   一次拨两枚），这里只管它在栏里怎么站。 */
.railbar-alarm {
  justify-content: center;
}

/* 页头读数条（业主 2026-08-14：会话、任务、能力的顶部信息都按能力页那条来）。
   它是仪表不是内容，所以不穿卡片的外框——那会让它读成"又一栏内容"；只留一条
   细线把它和下面的主体分开。不写 display，各页自己决定里面怎么排。 */
.page-gauge {
  padding: 0 0 var(--space-5);
  margin-bottom: var(--space-6);
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  border-radius: 0;
  background: none;
}

/* --- 「诊断」开关（_diagnostics.html，2026-09-15 设计评审第二批）：工程读数折进这里，
       只给管理员。折着是一行小灰字，跟页面的人话不抢；展开后读数自己排。 --- */
.diag-panel {
  margin: var(--space-2) 0;
  color: var(--muted);
  font-size: var(--text-xs);
}

.diag-summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px var(--space-2) 2px 6px;
  border-radius: var(--radius-pill);
  color: var(--faint);
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.diag-summary::-webkit-details-marker {
  display: none;
}

.diag-summary:hover,
.diag-panel[open] > .diag-summary {
  background: var(--surface-2);
  color: var(--muted);
}

.diag-icon {
  display: block;
  flex: 0 0 auto;
}

.diag-body {
  margin-top: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--surface-1);
}

/* --- 页头：这一页叫什么，以及一句话说清它是干嘛的。几页原本一进来就是
       内容，没有任何东西先说一句「你在哪」。 --- */
.page-head {
  margin-bottom: var(--space-8);
}

.page-head h1 {
  margin: 0 0 var(--space-2);
}

.page-head-sub {
  margin: 0;
  max-width: 58ch;
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* --- 读数：一个数，一个说明它是什么的词。数大、词小，两行错开——
       以前读数是「11 会话 0 群聊 24 条消息」一串同样大小的字，
       数字和标签在一行里彼此淹没。 --- */
.stat-row {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--space-8);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.stat-figure {
  color: var(--fg);
  font-size: var(--text-lg);
  font-weight: var(--weight-semi);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
}

.stat-label {
  color: var(--muted);
  font-size: var(--text-xs);
}

/* 主读数——这一页最该看的那个数，金色，比同伴大一档。 */
.stat-lead .stat-figure {
  color: var(--gold);
  font-size: var(--text-xl);
}

/* --- 读数格：小标签在上、大数字在中、一行注脚在下（2026-09-08）。
       .stat 是「一个数一个词」；读数格多一行注脚，把这个数的构成或来历写在它
       底下——「20 会话」底下写「群聊 8 · 私聊 12」，数就不用再拆成三个并排的
       数了。能力页先长成这个样子（业主看过三轮），会话页、任务页跟上，第三次
       出现就提到这里，三页一个形状。大数字用比例宽度：单个大数不需要对齐列，
       等宽反而让 1 和 0 一样肥；注脚里的数才用等宽。 --- */
.meter-row {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
}

.meter {
  display: block;
  min-width: 0;
}

.meter-label {
  display: block;
  color: var(--muted);
  font-size: var(--text-xs);
  white-space: nowrap;
}

.meter-figure {
  display: block;
  margin-top: 2px;
  color: var(--fg);
  font-size: var(--text-xl);
  font-weight: var(--weight-semi);
  letter-spacing: -0.02em;
  line-height: 1.2;
  white-space: nowrap;
}

.meter-figure small {
  margin-left: 2px;
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}

/* 主读数：这一排最该看的那个数，金色。一排只有一个。 */
.meter-lead .meter-figure {
  color: var(--gold);
}

.meter-sub {
  display: block;
  margin-top: var(--space-1);
  color: var(--fg-soft);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

/* 一排里两类数之间的竖线：现在有什么｜这几天出了什么。窄屏折行后线没有意义，藏掉。 */
.meter-divider {
  align-self: stretch;
  width: 1px;
  background: var(--line);
}

@media (max-width: 640px) {
  /* 手机上不出标签：一排标签在这个宽度里挤掉的正是导航本身。桌面壳不会跑到这个
     宽度来（窗口最小 420 但那是桌面），所以这一条只挡网页。
     **收的是标签，不是整个托盘**：应用菜单被 shell.js 挪进了托盘里，整条藏掉会
     连它一起藏，而 `/apps` 页删了之后它是手机上唯一的应用入口。第一版就是整条
     藏的，探针在 500 宽上量出「应用」那一格 0×0——这一条的代价看不见，所以只有
     量得出来。
     那道竖线跟标签一起走：它是「托盘｜其余」的界线，标签不在就没有一边可分。
     它靠 hidden 属性开关，而脚本现在在网页上也跑了，所以得在这儿明写。 */
  body:not(.is-desktop) .topbar-tabs > .topbar-tab,
  body:not(.is-desktop) .topbar > .topbar-nav-divider {
    display: none;
  }

  /* 手机上「应用」排在这条栏的最右端，不跟着导航那一段走。它 2026-09-19 之前
     是导航里的第一项（紧跟招牌），这一版换了位置：`/apps` 那一页删掉之后，它
     是手机上**唯一**的应用入口，而导航那一段放不下时是自己横着滑的——会滑走的
     地方不该放唯一的门。两条都写：脚本跑过了它在托盘里（按托盘排），没跑它还是
     栏的第二个孩子（按自己排）。 */
  body:not(.is-desktop) .topbar-tabs,
  .topbar-apps {
    order: 2;
  }

  .meter-divider {
    display: none;
  }
}

/* --- 分节标题。以前是一行几乎看不见的小灰字，于是「更早」「还没跑过」
       这些分组等于不存在。给它字重和一条延伸到底的细线。 --- */
.section-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin: var(--space-10) 0 var(--space-4);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
}

.section-head:first-child {
  margin-top: 0;
}

.section-head::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line-soft);
}

.section-head .muted,
.section-head .section-note {
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-normal);
}

/* --- 空状态。以前是一行灰字贴在左上角，页面看起来像没加载完。 --- */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-16) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--surface-1);
  color: var(--muted);
  font-size: var(--text-sm);
  text-align: center;
}

.empty-title {
  color: var(--fg-soft);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
}

/* --- 页脚。页面得有个收口——以前正文之后是一路空到底的黑，
       没有任何东西告诉你「到头了」。 --- */
.page-foot {
  margin-top: var(--space-16);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line-soft);
  color: var(--faint);
  font-size: var(--text-xs);
}

/* 页签是字，不是框（业主 2026-09-18）。原来当前页是一个带底色的圆角块，一排
   药丸让顶栏读成「一排待办的动作」；现在没选中的是灰字，当前页是金字加一档
   字重——层级先靠字重和颜色，不靠又多一个面。 */
.topbar-nav-link {
  padding: 6px var(--space-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  /* 正文那一档，不是小一档（业主 2026-08-29：顶栏的字偏小）。页签是一直在
     那儿要被认出来的东西，不是注解。 */
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  line-height: 1;
  transition: color 0.15s ease;
}

.topbar-nav-link:hover {
  color: var(--fg);
}

.topbar-nav-link.is-current {
  color: var(--gold);
  font-weight: var(--weight-semi);
}

/* 应用页顶栏上唯一那一条：回 Aimy 的出口，贴右端，安静一点——它是备用出口，
   不是这一页要你点的东西（业主 2026-08-14）。桌面端不显示它，托盘里就有
   Aimy 那一颗（见下面 .is-desktop 那一段）。 */
.topbar-nav-exit {
  margin-left: auto;
}

/* 急停铃：全体停手开着的时候，每一页顶栏右端挂的这一枚（业主 2026-09-07：
   「开关隐藏太深」）。顶栏上唯一一处红——正因为它只在出事时出现，才不必再喊得
   更大声。页签平时只是一行字；这一枚有红的底、红的字、一个点，跟它们
   一眼分得开。 */
.topbar-alarm {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px 11px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--red-soft);
  color: var(--red);
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  line-height: 1.2;
  white-space: nowrap;
}

/* 自己设了 display 的类必须自带这一条：浏览器的 [hidden]{display:none} 住在
   用户代理层，输给任何作者声明——不写这一句，关着的急停铃会一直挂在顶栏上，
   那是比没有铃更糟的一种谎（2026-09-04「副本」签实录）。 */
.topbar-alarm[hidden] {
  display: none;
}

.topbar-alarm:hover {
  background: var(--red);
  color: var(--surface-0);
}

.topbar-alarm-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
}

/* 铃在呼吸，慢到不烦人又不至于被当成一枚静止的标签。纯装饰：动画关掉之后
   它仍然是红的、仍然在那儿。 */
@keyframes aimy-alarm-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.4;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .topbar-alarm-dot {
    animation: aimy-alarm-pulse 1.8s ease-in-out infinite;
  }
}

/* The gallery lights its works on a dark wall; the front door is lit the same
   way. The wash sits behind everything (the page background paints on the
   canvas, below any negative layer) and is the page's only flourish. */
.home::before {
  content: "";
  position: fixed;
  /* 四边写全不用 inset 简写：旧内核不认，光晕会整个消失。 */
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: radial-gradient(
    58% 46% at 22% 40%,
    var(--gold-wash),
    var(--gold-soft) 42%,
    transparent 72%
  );
  pointer-events: none;
}

/* 四周一圈很轻的暗角：中央的光晕更聚，画更亮。 */
.home::after {
  content: "";
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  background: radial-gradient(120% 95% at 50% 30%, transparent 58%, rgba(0, 0, 0, 0.4));
  pointer-events: none;
}

.home-intro {
  max-width: 46ch;
}

/* The showcase is ONE repeating unit — a left-aligned paragraph with its
   drawing under it, 40rem wide, one unit per screenful (owner, 2026-08-04).
   No second composition exists on the page. */
.chapter {
  max-width: 44rem;
  min-height: 84vh;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* 装饰皆出自一个隐喻——图纸挂在暗墙上：标题前一枚斜方印（头像那张
   歪方脸的角度），画的四角是工程图纸的角标，页面四周一圈暗角聚光。 */
.chapter-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
  font-size: 22px;
  font-weight: 650;
  text-align: left;
}

.chapter-head::before {
  content: "";
  flex: none;
  width: 10px;
  height: 10px;
  background: var(--gold);
  border-radius: 3px;
  transform: rotate(-8.5deg);
}

.chapter-text {
  margin: 0;
  color: var(--fg-soft);
  font-size: 15px;
  line-height: 2;
  text-align: left;
}

.chapter .print {
  position: relative;
  margin-top: 48px;
  padding: var(--space-5);
}

/* 四角各一组 L 形角线，不描整框——图纸钉在墙上的记号。 */
.chapter .print::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  pointer-events: none;
  background:
    linear-gradient(var(--gold-mark), var(--gold-mark)) left top / 18px 1px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) left top / 1px 18px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) right top / 18px 1px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) right top / 1px 18px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) left bottom / 18px 1px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) left bottom / 1px 18px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) right bottom / 18px 1px,
    linear-gradient(var(--gold-mark), var(--gold-mark)) right bottom / 1px 18px;
  background-repeat: no-repeat;
}

.home-signin {
  margin: 96px auto 0;
  color: var(--muted);
  font-size: 13px;
  text-align: center;
}

/* The comics are transparent line art in a warm stroke — drawn straight on
   the dark wall, no white card under them (owner, 2026-08-04). */
.print {
  margin: 0;
}

.print img {
  display: block;
  width: 100%;
  height: auto;
}

.print figcaption {
  margin-top: var(--space-4);
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.04em;
  text-align: center;
}

.home-lead {
  margin: var(--space-3) 0 0;
  color: var(--muted);
  font-size: 16px;
}

/* A legend on a hairline; the capabilities page still sections itself with it. */
/* auto-fit 而不是 auto-fill：三个应用配 1280 的版心，auto-fill 会留出
   第四道空列、把三张卡挤到左上角，右边和下面全空——页面看着像没加载完。
   auto-fit 把空列收掉，剩下的卡把整行分完。 */
.home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}

.topbar-menu > summary {
  list-style: none;
  display: inline-flex;
  align-items: center;
  padding-inline: 10px;
  cursor: pointer;
}

.topbar-menu > summary svg {
  display: block;
}

.topbar-menu > summary::-webkit-details-marker {
  display: none;
}

.topbar-menu[open] > summary {
  color: var(--fg);
}

/* 毛玻璃（业主 2026-09-19：「菜单本身做好看一些，毛玻璃效果」）。老内核先拿到
   一块实底——`backdrop-filter` 在 EdgeHTML 上整条丢掉，而一块半透明、底下什么
   都不糊的面板是看不清字的。所以顺序是：实底打底，认得的内核再用后面两行换成
   半透明＋背后模糊。`-webkit-` 那行给 Safari。 */
.topbar-menu-panel {
  position: fixed;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 9rem;
  padding: var(--space-2);
  border-radius: var(--radius);
  background: var(--surface-2);
  box-shadow: var(--shadow-2);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .topbar-menu-panel {
    border: 1px solid var(--line-soft);
    background: rgba(44, 41, 38, 0.72);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    backdrop-filter: blur(20px) saturate(150%);
  }
}

.topbar-menu-panel .topbar-nav-link {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

/* 面板里的那几项仍然是一行行可点的东西，底色是它们唯一的 hover 记号——
   栏上的页签去掉底色之后，这一条得自己写回来。 */
.topbar-menu-panel .topbar-nav-link:hover {
  background: var(--surface-3);
}

/* 管理菜单里「急停」那一项旁边的红字。菜单要打开才看得见，所以它不是警报本身
   ——顶栏那枚铃才是；这一行是「你翻到这里了，就是这一项」。不给自己设 display
   （行内元素本来就是行内的），hidden 才收得住；保险那一条仍然写上。 */
.topbar-menu-alarm {
  margin-left: var(--space-2);
  color: var(--red);
  font-weight: var(--weight-semi);
}

.topbar-menu-alarm[hidden] {
  display: none;
}

.home-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-height: 190px;
  padding: var(--space-6);
  overflow: hidden;
  box-shadow: var(--shadow-1);
  transition:
    border-color 0.18s ease,
    background 0.18s ease,
    box-shadow 0.18s ease,
    transform 0.18s ease;
}

/* The marker draws down the edge on hover, the way a plate lights up. */
.home-card::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--gold);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 0.22s ease;
}

/* The marker is the whole tell — a gold border on top of it would say the same
   thing twice, and every card on the page would read as one bright box. */
.home-card:hover,
.home-card:focus-visible {
  border-color: transparent;
  background: var(--surface-2);
  box-shadow: var(--shadow-2);
  /* 抬起一格——门是被推开的，不只是被照亮的。 */
  transform: translateY(-2px);
}

.home-card:hover::before,
.home-card:focus-visible::before {
  transform: scaleY(1);
}

.home-card h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-semi);
  font-size: var(--text-lg);
  letter-spacing: -0.01em;
}

.home-card p {
  margin: 0;
  max-width: 32ch;
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

.home-enter {
  align-self: flex-start;
  margin-top: auto;
  padding-top: var(--space-4);
  color: var(--faint);
  font-size: var(--text-md);
  line-height: 1;
  transition:
    color 0.18s ease,
    transform 0.18s ease;
}

.home-card:hover .home-enter,
.home-card:focus-visible .home-enter {
  color: var(--gold);
  transform: translateX(4px);
}

@media (prefers-reduced-motion: reduce) {
  .home-card,
  .home-card::before,
  .home-enter {
    transition: none;
  }

  .home-card:hover,
  .home-card:focus-visible,
  .home-card:hover .home-enter,
  .home-card:focus-visible .home-enter {
    transform: none;
  }
}

.data-table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--text-sm);
}

.data-table th,
.data-table td {
  border-top: 1px solid var(--line-soft);
  padding: var(--space-3);
  text-align: left;
  vertical-align: top;
}

/* 表头是标尺不是内容：小、轻、跟数据拉开一档，别跟第一行数据长得一样。 */
.data-table th {
  border-top: 0;
  padding-bottom: var(--space-2);
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
}

.data-table tbody tr:hover td {
  background: var(--surface-2);
}

/* 数字列上下对齐——一列金额里有一个不对齐，整列就读不成一列。 */
.data-table .num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.data-table tr.overdue td {
  background: rgba(207, 129, 119, 0.08);
}

.data-table tr.blocked {
  box-shadow: inset 3px 0 var(--red);
}

.badge-overdue {
  color: var(--red);
}

.badge-blocked {
  color: var(--gold);
}

/* 版心是版式主干，只用全内核都认的写法：min()/margin-inline 在旧内核里
   整条被丢，版心就静默失效、页面铺满全屏（2026-08-12 前端体检实录）。 */
.container {
  width: 100%;
  max-width: var(--container-width);
  margin-left: auto;
  margin-right: auto;
  padding: var(--space-10) var(--space-6) var(--space-16);
}

/* --- 宽屏：顶栏不在，导航在侧栏（业主 2026-09-19）---------------------------
   断点取 641，跟对话页清单变抽屉的那个断点是同一个数（对话页那份样式表）：一个站点只该
   有一个「这是宽屏了」的说法。 */
@media (min-width: 641px) {
  /* **栏必须排在左侧栏上面**（业主 2026-09-19 晚：「应用弹窗被压住了」——截图上
     应用菜单的左边一列不见了）。探针定的案：面板那两颗方块（x=161 和 x=237，都
     落在侧栏的 0–240 里）正中命中的是 `.chat-side-new` 和 `.chat-side-projects-row`
     ——**不是被裁，是被侧栏盖住了**。
     根因跟 09-19 早些时候那一次是同一条规矩的两面：`position: sticky` 加 z-index
     也开层叠上下文，所以面板那个 z-index:20 只在栏内部排序，整条栏以 10 参加根
     上下文，而侧栏是 50。上一次是面板在侧栏里、被正文盖住，给侧栏写了 50；这一次
     面板搬进了栏里，轮到栏不够高。
     取 55：压过侧栏（50）和正文，让给灯箱（60）和弹窗（80）。只在 641 以上写——
     手机上没有侧栏，那儿的会话抽屉是 30 而且要盖住整屏包括这条栏，栏要是 55 就
     会浮在抽屉上面。

     左内衬交给 `--bar-left`：浏览器和 Windows 是 0，于是「对话」那一颗从 x=0 起、
     到 240 收，跟左侧栏那一列严丝合缝（业主：「对话页签宽度和左侧栏同宽」）。
     macOS 的窗口由 `.is-desktop-mac .topbar` 以更高的权重盖过这一条。 */
  .topbar {
    padding-left: 0;
    z-index: 55;
  }

  /* 宽屏上这一条就是导航（业主 2026-09-19 晚：「web 端不再打开新页面，和桌面端
     一样，保持一个顶部栏管理页签，这样两者就同一了」）。栏上只剩三样：标签、
     应用菜单、急停铃——招牌和那一排页签是手机才要的，宽屏上招牌在侧栏头上，
     管理菜单在侧栏底下，再来一份就是同一件事说两遍。
     应用页同理，而且这一次连浏览器一起（业主同日上午只说了桌面端：「最右侧这个
     头像＋应用名称，不要显示了」，那时网页上还留着 08-14 那块门牌）。宽屏的网页
     现在也有那一排标签，上面已经写着「作图」而且是高亮的那一颗，门牌再来一次是
     同一件事说两遍。**手机上那块门牌一个字没动**：640 以下没有标签，同事从聊天
     里点进来看到的唯一一页就是它，那儿还得靠「← 回 Aimy」回去。
     急停铃在应用页上留着——它平时 hidden，停机时是唯一的告示，而应用页没有侧栏
     替它站岗（业主 2026-09-07）。 */
  .shell-rail > .topbar > .topbar-avatar-link,
  .shell-rail > .topbar > .topbar-nav,
  .shell-rail > .topbar > .topbar-nav-divider,
  .shell-rail > .topbar > .topbar-alarm,
  .shell-app > .topbar > .topbar-avatar-link,
  .shell-app > .topbar > .topbar-nav,
  .shell-app > .topbar > .topbar-nav-divider {
    display: none;
  }

  /* 栏从顶栏底下起，不是从窗口顶起——顶栏这一版留着了（业主 2026-09-19 晚把
     导航搬回了那条栏）。`--topbar-h` 是 :root 上那一个，不在这里再定义一遍：
     09-18 把栏从 64 压到 48 时，栈里好几处各自抄了一份栏高，栏改了它们不会跟着
     改，而多出来的一道空白不会有人报错。 */
  .shell-rail .railbar {
    display: flex;
    top: var(--topbar-h);
  }

  .shell-rail .shell-body {
    padding-left: var(--rail-w);
  }

  /* 收起来：整条栏缩成 56，每一项只剩记号。记号挂在 body 上而不是栏上——栏是
     fixed 的，占位的是 .shell-body，两处得一起改，而 CSS 读不到孩子的状态。
     目前只有对话页那颗「收起清单」会挂（chat-sessions.js）。 */
  .is-rail-folded .railbar {
    width: var(--rail-folded-w);
  }

  .is-rail-folded .shell-body {
    padding-left: var(--rail-folded-w);
  }

  .is-rail-folded .railbar-head,
  .is-rail-folded .railbar-foot {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
  }

  .is-rail-folded .railbar-brand,
  .is-rail-folded .railbar-link,
  .is-rail-folded .railbar-who {
    justify-content: center;
  }

  .is-rail-folded .railbar-brand-name,
  .is-rail-folded .railbar-who-name {
    display: none;
  }

  /* 「去哪儿」那两行是文字行，只在展开时有（窄轨上不另添一颗钮：窄轨上那颗「有任务
     在跑」说的是自己的会话，点了是展开清单，不是去任务页——两件事，别在 56 宽里挤
     出一颗长得像它的）。 */
  .is-rail-folded .railbar-go {
    display: none;
  }

  /* 收起来之后栏只有 56 宽，招牌那一行放不下第二样东西，所以这一页自己的钮
     （对话页的「展开」）排到头像正下方，自己一行——收和展仍在栏头，不再像
     09-19 早些时候那样一个在顶上一个在窄轨里差着两百像素。

     上一版是让「展开」盖住头像、鼠标一过才浮出来，业主当天就否了：「aimy 头像
     hover 变成展开不是个好主意，展开按钮独立，放在 aimy 头像下方就行」。一个
     东西同时是招牌和开关，不碰它的时候没人看得出它还是个开关。 */
  .is-rail-folded .railbar-head {
    flex-direction: column;
    gap: var(--space-1);
  }

  .is-rail-folded .railbar-tool {
    display: flex;
  }

  .is-rail-folded .railbar-menu {
    margin-left: 0;
  }

  .is-rail-folded .railbar-alarm {
    padding: 5px var(--space-2);
    font-size: var(--text-xs);
  }

  .is-rail-folded .railbar-alarm .topbar-alarm-dot {
    display: none;
  }
}

@media (max-width: 820px) {
  .topbar {
    padding-inline: 18px;
  }
}

/* On a phone the nav pills are wider than the bar, which used to push the
   whole document sideways. Give them their own row and let that row scroll. */
@media (max-width: 640px) {
  /* 手机上顶栏是一行（业主 2026-09-15 设计审查）：头像和页签排在同一行，页签
     放不下时自己横着滑。原来是两行——头像一行、页签一行——量出来 107 高，
     对话页再加一行标题，第一句话要到 150 像素以下才开始。 */
  .topbar {
    min-height: 0;
    padding: var(--space-2) var(--space-3);
    gap: var(--space-2);
    flex-wrap: nowrap;
  }

  .topbar-avatar {
    width: 32px;
    height: 32px;
  }

  /* 「应用」那一枚在手机上跟头像同高。宽屏上它是 36，因为它排在 36 高的标签
     中间；这里没有标签，而这条栏是「上下各 8 的内衬 + 最高的那个孩子」——
     36 会把 48 顶成 52（探针在 500 宽上量到的）。 */
  body:not(.is-desktop) .topbar-menu > summary.topbar-apps-button {
    height: 32px;
  }

  /* 「任务」一格：跟「应用」同高、同圆角、同一档底色（空心一圈描边，手一过去填上
     surface-3），带着侧栏那一行同一个在跑的数。站在任务页上它就是填上的那个样子——
     跟「应用」菜单开着时同一种说法：这扇门此刻是开着的。
     order 1：排在招牌和管理菜单（order 0）后面、应用（order 2）前面，不进会横着滑的
     导航那一段。 */
  .topbar-tasks {
    order: 1;
    flex: none;
    height: 36px;
    display: flex;
    align-items: center;
    padding: 0 var(--space-4);
    border: 1px solid var(--control-line);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--fg-soft);
    font-size: var(--text-base);
    line-height: 1.4;
    white-space: nowrap;
    text-decoration: none;
  }

  body:not(.is-desktop) .topbar-tasks {
    height: 32px;
  }

  /* 字和数之间的空，用外边距：老内核不认 flex 的 gap（AGENTS.md）。 */
  .topbar-tasks > svg {
    flex: none;
    display: block;
    margin-right: var(--space-2);
  }

  .topbar-tasks > .live-count {
    margin-left: var(--space-2);
  }

  .topbar-tasks:hover,
  .topbar-tasks[aria-current="page"] {
    border-color: transparent;
    background: var(--surface-3);
    color: var(--fg);
    text-decoration: none;
  }

  /* 页签占住头像右边剩下的地方。挤是被压出来的：flex 子项默认可以收缩到比
     内容还窄，页签就被压成「会/话」竖着排（500px 实拍），所以子项不收缩、
     整排自己滑。 */
  .topbar-nav {
    flex: 1 1 auto;
    min-width: 0;
    gap: 2px;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }

  /* 里面那一格跟着一起不换行：滚动的是 .topbar-nav，它只管别把自己的孩子
     压扁——min-width:0 才允许它缩到比内容窄，让外面那条滑起来。 */
  .topbar-nav-core {
    min-width: 0;
    gap: 2px;
    flex-wrap: nowrap;
  }

  .topbar-nav-link {
    flex: none;
    padding: 7px 8px;
    white-space: nowrap;
  }

  .topbar-nav-divider {
    margin: 0 var(--space-1);
  }

  .topbar-nav::-webkit-scrollbar {
    display: none;
  }

  /* 急停铃开着时贴在同一行最右端，不被页签挤走——报警的东西不能滑出屏幕。 */
  .topbar-alarm {
    flex: none;
    margin-left: auto;
    padding: 4px 8px;
  }

  .home-card {
    min-height: 0;
  }

  .home-card p {
    max-width: none;
  }
}

/* --- motion ----------------------------------------------------------------
   Three layers, all opt-out via prefers-reduced-motion: a native cross-page
   fade (View Transitions, progressive — browsers without it just navigate),
   a staggered rise for card grids, and a pop for the topbar menu. */

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.18s;
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes card-in {
    from {
      opacity: 0;
      transform: translateY(10px);
    }
  }

  @keyframes menu-pop {
    from {
      opacity: 0;
      transform: translateY(-4px) scale(0.98);
    }
  }

  .home-grid > *,
  .chapter,
  .session-grid > *,
  .task-grid > * {
    animation: card-in 0.32s cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
  }

  .home-grid > :nth-child(2), .session-grid > :nth-child(2), .task-grid > :nth-child(2) { animation-delay: 0.04s; }
  .home-grid > :nth-child(3), .session-grid > :nth-child(3), .task-grid > :nth-child(3) { animation-delay: 0.08s; }
  .home-grid > :nth-child(4), .session-grid > :nth-child(4), .task-grid > :nth-child(4) { animation-delay: 0.12s; }
  .home-grid > :nth-child(5), .session-grid > :nth-child(5), .task-grid > :nth-child(5) { animation-delay: 0.16s; }
  .home-grid > :nth-child(6), .session-grid > :nth-child(6), .task-grid > :nth-child(6) { animation-delay: 0.2s; }
  .home-grid > :nth-child(7), .session-grid > :nth-child(7), .task-grid > :nth-child(7) { animation-delay: 0.24s; }
  .home-grid > :nth-child(n + 8), .session-grid > :nth-child(n + 8), .task-grid > :nth-child(n + 8) { animation-delay: 0.28s; }

  .topbar-menu[open] .topbar-menu-panel {
    animation: menu-pop 0.16s ease-out;
  }

  /* 「打开」的四个词（2026-09-17）：浮起（面板、折叠段、新出现的一行）、淡入（遮罩）、
     从右滑入（抽屉）、弹窗入场（略缩放的浮起）。只定义出场：关闭走 hidden／display 切换，
     瞬间消失比慢慢退场更轻。名字是 aimy- 前缀的全局关键帧，页面样式表直接引用。 */
  @keyframes aimy-rise {
    from {
      opacity: 0;
      transform: translateY(8px);
    }
  }

  @keyframes aimy-fade {
    from {
      opacity: 0;
    }
  }

  @keyframes aimy-slide-right {
    from {
      opacity: 0;
      transform: translateX(24px);
    }
  }

  /* 「几件在跑」那一圈（.live-count-spin）。纯装饰：动画关掉之后记号和数字都还在。 */
  @keyframes aimy-spin {
    to {
      transform: rotate(360deg);
    }
  }

  .live-count-spin {
    animation: aimy-spin 1.1s linear infinite;
  }

  @keyframes aimy-dialog-in {
    from {
      opacity: 0;
      transform: translateY(10px) scale(0.98);
    }
  }

  .aimy-dialog-back {
    animation: aimy-fade var(--motion-quick) ease-out;
  }

  .aimy-dialog {
    animation: aimy-dialog-in var(--motion-base) var(--ease-out);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* 弹窗：Core 的一套，页面和应用共用（业主 2026-08-23：浏览器自带的很难用）。
   .aimy-* 前缀属于 Core；应用只调 window.aimyDialog，不重定义这些类。 */
.aimy-dialog-back {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.62);
}
.aimy-dialog {
  width: 100%;
  max-width: 380px;
  box-sizing: border-box;
  padding: 18px 18px 14px;
  border-radius: var(--radius);
  background: var(--surface-2);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.45);
}
.aimy-dialog-title {
  margin: 0 0 8px;
  font-size: 16px;
  font-weight: 650;
}
.aimy-dialog-body {
  margin: 0 0 14px;
  color: var(--fg-soft);
  font-size: 14px;
  line-height: 1.6;
  word-break: break-word;
  white-space: pre-wrap;
}
.aimy-dialog-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 14px;
}
.aimy-dialog-textarea {
  min-height: 6em;
  resize: vertical;
  font: inherit;
  line-height: 1.5;
}
.aimy-dialog-row {
  display: flex;
  justify-content: flex-end;
}
.aimy-dialog-row > * + * {
  margin-left: 8px;
}

/* 桌面端的下载（shell.js「桌面端的下载」）：右上角一小叠，只在客户端里出现。
   不放右下：那儿是对话页的输入框，一张卡会压住正在打的字（2026-09-23 实测）。
   往下让 64 是让开对话页右上那颗「文件」。 */
.aimy-downloads {
  position: fixed;
  top: 112px;
  top: calc(var(--topbar-h) + 64px);
  right: 16px;
  z-index: 70;
  display: flex;
  flex-direction: column;
  width: 320px;
  max-width: 90%;
}
.aimy-downloads[hidden] {
  display: none;
}
.aimy-download {
  box-sizing: border-box;
  margin-bottom: 8px;
  padding: 12px 14px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
  box-shadow: var(--shadow-1);
}
.aimy-download-name {
  overflow: hidden;
  color: var(--fg);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  text-overflow: ellipsis;
}
.aimy-download-status {
  margin-top: 2px;
  color: var(--muted);
  font-size: var(--text-xs);
}
.aimy-download.is-interrupted .aimy-download-status {
  color: var(--red);
}
.aimy-download-track {
  height: 3px;
  margin-top: 8px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--chart-track);
}
.aimy-download-fill {
  height: 100%;
  background: var(--gold);
}
.aimy-download-acts {
  display: flex;
  flex-wrap: wrap;
  margin: 8px -4px 0;
}
.aimy-download-act {
  margin: 0 4px;
}
.aimy-dialog-ok {
  border-color: transparent;
  background: var(--gold-wash);
  color: var(--gold);
}
.aimy-dialog-ok.is-danger {
  border-color: transparent;
  background: var(--red-soft);
  color: var(--red);
}
