/* 外壳样式：窗口铺满 + 左侧方块导航栏。配套 shell.js。
 *
 * 三条原则：
 *   1. 只摆位置，不给 app.js 里的东西改语义——它的 #content / #tabs / .on 全部原样。
 *   2. 窗口不跟着页面滚：整窗固定高，内容区自己滚。这才是软件，不是网页。
 *   3. 高度不够时缩方块，不缩到看不见——「不能让人找不着」是硬要求。
 */

body.shell-rail { height: 100vh; height: 100dvh; overflow: hidden; }

body.shell-rail #mainView {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr);
  height: 100vh; height: 100dvh;
  overflow: hidden;
}
/* 标题栏永远在最上面；提示条（断网、没进账的那几条）排在它下面。
   两者都在 DOM 里，grid-row 是写死的，所以顺序不受插入位置影响。
   `.notes` 是那几条横幅的容器（2026-11-02 T-049 ② 加的）：多一条「有新版本」
   就多一个块，不占新的行——直接再塞一个孩子进 #mainView 的话，
   它会自动流到左边导航栏那列去。 */
body.shell-rail .appbar { grid-column: 1 / -1; grid-row: 1; }
body.shell-rail .notes  { grid-column: 1 / -1; grid-row: 2; min-width: 0; }
/* 提示横幅也别贴着窗口边（2026-11-03 晚）。
   顶栏和左栏改成浮起的圆角块之后，中间夹一条左右的直角横幅很扎眼——
   它比上下两块都"硬"。给自己留 6px，`.note` 本来就有 8px 圆角，露出来就顺了。
   `:not(:empty)` 不能省：空容器也吃 padding 的话，顶上会多出一条 12px 的
   空白，平时没有横幅时那就是一块无来由的间隙。 */
body.shell-rail.glass .notes:not(:empty) {
  padding: var(--glass-gap) var(--glass-gap) 0;
}
body.shell-rail .tabs  { grid-column: 1; grid-row: 3; }
/* min-width:0 不能省：grid 子项默认按 min-content 撑，手机上会被表格顶出屏幕。 */
body.shell-rail .pagewrap {
  grid-column: 2; grid-row: 3;
  overflow-y: auto; overflow-x: hidden;
  min-height: 0; min-width: 0;
}
body.shell-rail main { grid-column: auto; grid-row: auto; overflow: visible; }

/* ---- 半透明外壳：只让「边缘」透（2026-11-03）----
 *
 * 微信那层半透，透出来的其实是桌面壁纸，而且主要透在顶栏和侧栏——
 * 聊天区本身是实打实的白。我们照着这个分寸做：
 *
 *   顶栏、左栏  真毛玻璃（backdrop-filter），壁纸的色块从这儿透进来
 *   卡片、表格  只是半透白（--card 已经改了），压住背景保证数字看得清
 *
 * 为什么不整窗都挂毛玻璃：这个软件一屏能有几十张卡片和上百个单元格，
 * 每个都单独对背后做一次模糊，低端办公机上滚表格会卡成幻灯片。
 * 「看着透」和「每帧都算一遍透」是两件事，只在前两处付这份显卡钱。
 */
/* ---- 一圈玻璃：顶栏、左栏、抽屉共用同一套参数（2026-11-03 晚）----
 *
 * 老板指着两张截图说：「上面那条跟外壳一样参数的透度。现在这样一条挺不好
 * 看的」、「也是整体一样的透度，跟抽屉一样，带圆角。字体颜色在调深一点」。
 *
 * 病根是这三块**各写各的数**：顶栏 `.34` + blur 22px，左栏 `.30` + blur 10px，
 * 抽屉干脆没有底色（它坐在左栏那层白上）。参数不同 → 同一块壁纸被糊成两种
 * 颗粒 → 接缝处能看出一条界线，读者眼里就是"贴了一条白胶带"，而不是
 * "这个窗口有一圈玻璃边"。
 *
 * 所以先把参数提成变量，三块都引用；再把顶栏和左栏从"贴边的通条"改成
 * "浮在壁纸上的圆角板"——**必须有缝圆角才看得见**，看得见圆角才像一块独立
 * 的料。抽屉本来就是圆角块，换成同一个 fill 就自然对齐了。
 *
 * 模糊取 10px（顶栏原来 22px 降到 10）：22 和 10 并排时，顶栏那块壁纸被糊成
 * 一片纯色、左栏还留着纹理，读者会以为顶栏是"另一张图"。同一套料必须同一个
 * 颗粒。字看不清的问题不用模糊来救，靠加深字色（见下面 .tabs 那条）。 */
body.shell-rail.glass {
  --glass-fill: rgba(255, 255, 255, .32);
  /* 没有 backdrop-filter 的老浏览器用这一档。那时透不出毛玻璃，只能靠
     白膜保可读性——真正在干活的不是这层白，是下面那层模糊，模糊没了
     白就得厚。 */
  --glass-fill-opaque: rgba(255, 255, 255, .55);
  --glass-blur: blur(10px) saturate(1.5);
  --glass-radius: 14px;
  --glass-ring: 0 0 0 1px rgba(255, 255, 255, .45);
  --glass-gap: 6px;
}

/* 没有 backdrop-filter 的老浏览器留在 opaque 那一档：那时透不出毛玻璃，
   只能靠白膜保可读性。有新 API 的才敢把白降下来 —— 这时候真正在干活的
   不是这层白，是下面那层模糊。

   **选择器必须带 `.shell-rail`**（2026-11-03 踩了）：下面 `body.shell-rail
   .tabs` 里有一行 `background: var(--card)`，跟 `body.glass .tabs` 是同一个
   优先级 (0,2,1)，而它写在后面，于是永远是它赢。表现出来是：这里把 .30
   改成 .12，截图一个像素都不变——因为左栏从始至终按 `--card` 的 88% 白在
   压，只透了 12%，跟这里写什么都没关系。差一层类名，调了半天空气。 */
body.shell-rail.glass .appbar,
body.shell-rail.glass .tabs {
  background: var(--glass-fill-opaque);
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  body.shell-rail.glass .appbar,
  body.shell-rail.glass .tabs {
    background: var(--glass-fill);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
  }
}

/* 圆角 + 缝。**只有跟窗口边缘留出 6px，圆角才看得见**——贴着边画的圆角
   等于没画，四周全被窗口自己的直角裁掉了。
   这一段不依赖 backdrop-filter，所以放在 @supports 外面：老浏览器也得是
   一块圆角板，不然同一份代码里它又是另一种形态。 */
body.shell-rail.glass .appbar {
  margin: var(--glass-gap) var(--glass-gap) 0;
  border-bottom: 0;
  border-radius: var(--glass-radius);
  box-shadow: var(--glass-ring);
}
body.shell-rail.glass .tabs {
  margin: var(--glass-gap) 0 var(--glass-gap) var(--glass-gap);
  border-right: 0;
  border-radius: var(--glass-radius);
  box-shadow: var(--glass-ring);
  /* 收起时列宽在过渡，margin 也得跟着收——不然列宽已经到 0 了，
     左边还留着 6px 的边，内容区永远顶不到窗口边缘（老板要的是"不占位"）。 */
  transition: opacity .16s ease, margin .22s cubic-bezier(.4, 0, .2, 1);
}
body.shell-rail.glass.rail-off .tabs { margin-left: 0; }
/* 左栏内容放不下时的滚动条（2026-11-03 晚）。
   系统那条灰条默认贴死在右边缘，圆角一加上去就被它切掉一口——
   真窗口截图里能明显看到圆角右边是直的。
   做法：8px 的槽 + 4px 的滑块，`background-clip: content-box` 把两边各让出
   2px，留出缝隙，圆角就保住了；颜色压到近乎无感，滚动这件事靠滚轮就够，
   不靠这条线提示。 */
body.shell-rail.glass .tabs::-webkit-scrollbar { width: 8px; }
body.shell-rail.glass .tabs::-webkit-scrollbar-track { background: transparent; }
body.shell-rail.glass .tabs::-webkit-scrollbar-thumb {
  background: rgba(60, 72, 88, .22);
  background-clip: content-box;
  border: 2px solid transparent;
  border-radius: 4px;
}
/* 字跟着底一起加深（2026-11-03，当晚又深了一档）。

   背景一透，原来的 #777 灰字在橙红的壁纸上等于消失——第一版降到 .12 时
   「数据与备份」整行都认不出来了。**透和看得清是一件事的两面，只做前面
   那半就是把左栏弄废。** 选中态那一项不在这里管：它是蓝底白字，本来就顶得住。

   当晚第二档（老板：「字体颜色在调深一点」）：底统一到 .32、模糊从 22 降到 10
   之后，背后的壁纸纹路比原来更清楚，原来那档 #353b44 在橙色岩壁上仍然发灰。
   再压到近黑（#1e242c），配 11px 的小字才立得住——小字要的是对比度，
   不是"高级灰"。

   `:not(.on)` 不能省：不加的话这条会把选中的蓝色一起盖成深灰，
   "当前在哪一页"就看不出来了。 */
body.shell-rail.glass .tabs button[data-tab]:not(.on),
body.shell-rail.glass .tabs button.more { color: #1e242c; }
body.shell-rail.glass .tabs button[data-tab]:not(.on) .tile { color: #232a33; }

/* 深色壁纸时三块一起回厚：底色越白，深色字越立得住。改变量而不是逐个
   选择器写——抽屉、浮层都引同一个变量，改一处全跟着走。

   **深色壁纸下不再把字改白**（2026-11-03 晚 11.27 修掉的）。
   原来这里有两条 `[data-desk="dark"]` 的规则，把字改成近白 #f0f2f5——
   写的时候想的是"背景暗、字要亮"。但背景根本不是暗的：这一层是 58% 的
   **白**膜，压在深色壁纸上算出来仍是接近白的浅底。白字压在白底上 = 字没了。
   老板截图里左栏那一堆"淡淡地浮着"的图标，就是这两条造成的，不是颜色
   调得不够深——是方向反了。
   判据：**底色永远是白膜，字就永远是深色**，跟壁纸深浅无关。 */
body.shell-rail.glass[data-desk="dark"] { --glass-fill: rgba(255, 255, 255, .58); }

/* 悬停那一格原来写死 #f6f8fb，在半透的左栏上会变成一块不透明的补丁 */
body.shell-rail.glass .tabs button[data-tab]:hover,
body.shell-rail.glass .tabs button.more:hover { background: rgba(255, 255, 255, .55); }
/* 未选中的图标方块也别做成一块实灰：左栏透下来了，中间却嵌着一个个
   不透明的方块，看着像贴纸。同样是优先级问题，必须多带一层 .shell-rail
   ——`body.shell-rail .tabs .tile` 写在后面，同级压不过它。 */
body.shell-rail.glass .tabs .tile { background: rgba(255, 255, 255, .42); }
/* 深色壁纸时图标方块要比底更白一档。
   `.30` 是照浅底配的，可深底那档的底是 .58 —— 方块比底还透，看起来是凹进去的
   一块，不是"贴在上面的格子"。（2026-11-03 晚统一透度时一起对出来的。） */
body.shell-rail.glass[data-desk="dark"] .tabs .tile { background: rgba(255, 255, 255, .78); }
body.shell-rail.glass .railpop {
  background: rgba(255, 255, 255, .82);
  backdrop-filter: blur(20px) saturate(1.3);
}

/* 表格里钉住的那一列**不跟着透**：它的用处就是挡住后面滚过去的字，
   半透就等于白钉。手机上本来没壁纸，这里一并兜住。 */
body.glass .card table:not(.kv) th:last-child,
body.glass .card table:not(.kv) td:last-child { background: #fff; }

/* ---- 标题栏 ---- */
.appbar {
  display: flex; align-items: center; gap: 2px;
  height: 46px; padding: 0 10px 0 14px;
  background: var(--card); border-bottom: 1px solid var(--line);
}
.appbar .appname { font-size: 14px; font-weight: 600; margin-right: auto; }
.appbar button { font-size: 13px; padding: 5px 10px; }
/* 出版方（谁做的）+ 版本号（哪一版）摆顶栏右侧（2026-11-02 老板提）。
   都是灰字、都别抢账号动作的位置；窗口窄到放不下就先把出版方收起来，
   **版本号不藏**——报错时客户第一句就是"我这哪版"。 */
.appbar .pubinfo { font-size: 12px; color: var(--muted); white-space: nowrap; margin-right: 10px;
  text-decoration: none; border-bottom: 1px dotted transparent; }
/* 是链接，但不要用默认的蓝加下划线——顶栏上那一串字是落款，不是按钮。
   鼠标划上去才轻轻提示一下「这里能点」。 */
.appbar .pubinfo:hover { color: var(--brand); border-bottom-color: var(--brand); }
.appbar .vertag { margin-right: 6px; }
@media (max-width: 980px) { .appbar .pubinfo { display: none; } }

/* ---- 导航栏：一列方块 ---- */
body.shell-rail .tabs {
  position: static; height: 100%;
  display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px;
  padding: 8px 6px;
  /* 只许竖着滚。**overflow-x 必须显式写 hidden**：只写 overflow-y:auto 时
     overflow-x 会被算成 auto，而这一列的内容比容器宽 1px（右边框吃掉的那 1px），
     于是导航栏底部永远画出一条横向滚动条——2026-11-02 老板截图里那条灰条就是它，
     看着像"整个窗口能左右拖"，其实什么都没有。 */
  overflow-y: auto; overflow-x: hidden;
  background: var(--card);
  border-bottom: 0; border-right: 1px solid var(--line);
}
body.shell-rail .tabs .spacer { display: none; }

/* 每一项：上面方块，下面小字。按钮本身不上色，只有方块上色。 */
body.shell-rail .tabs button[data-tab],
body.shell-rail .tabs button.more {
  display: grid; justify-items: center; gap: 3px;
  flex: 0 0 auto; width: 100%;
  padding: 5px 2px; border: 0; border-radius: 10px; background: none;
  color: var(--muted); font-size: 11px; line-height: 1.2;
}
body.shell-rail .tabs button[data-tab]:hover,
body.shell-rail .tabs button.more:hover { background: #f6f8fb; }

body.shell-rail .tabs .tile {
  width: 34px; height: 34px; border-radius: 10px;
  background: #eef1f5; color: var(--muted);
  display: grid; place-items: center;
  transition: background .15s, color .15s;
}
body.shell-rail .tabs .lbl { white-space: nowrap; }

/* 当前页：只让方块变蓝。原来那条「蓝底白字的整块按钮」在竖栏里会变成一根长条。 */
body.shell-rail .tabs button[data-tab].on {
  background: none; border: 0; color: var(--brand); font-weight: 500;
}
body.shell-rail .tabs button[data-tab].on .tile { background: var(--brand); color: #fff; }

/* 「全部功能」那一项：虚线方块，跟正经模块区分开 */
body.shell-rail .tabs button.more .tile {
  background: none; border: 1px dashed #c3cbd6; color: var(--muted);
}
body.shell-rail .tabs button.more { border: 0; }

/* ---- 导航栏自动收起（2026-11-03 老板要求）----

   原话「导航栏能做几秒钟后收起的动作么」。确认过的形态：整条滑出去、
   **不占位**——内容区顶到窗口左边，横向多出 92px 给表格。所以这里必须
   真的把 grid 第一列从 92px 过渡到 0，只把左栏 translform 走、原地留一条
   空白，那等于没做。

   为什么不用 transform 滑出：左栏是 grid 子项，`translateX(-100%)` 的百分比
   是元素自身宽度，而列宽同时在缩，宽度和位移互相抵消，最后看到的是"被压扁"
   而不是"滑出去"。改用列宽过渡 + 淡出，观感是左栏一边变窄一边退场，内容区
   同时补上来。

   挤压过程中的变形看不出来，靠的是 `.tabs` 本来就有 overflow-x: hidden
   （2026-11-02 为了那条横向滚动条加的）：容器变窄时 34px 的图标被横着裁掉，
   不会溢出到内容区去糊一片。 */

body.shell-rail #mainView {
  transition: grid-template-columns .22s cubic-bezier(.4, 0, .2, 1);
}
body.shell-rail.rail-off #mainView {
  grid-template-columns: 0px minmax(0, 1fr);
}
body.shell-rail .tabs { transition: opacity .16s ease; }
body.shell-rail.rail-off .tabs {
  opacity: 0;
  /* 收起之后不许再吃点击：它还压在内容区底下那 92px 里 */
  pointer-events: none;
}

/* 左边缘的感应条 + 一颗看得见的小尾巴，收起后唯一能把导航叫回来的东西。
   **只在收起时存在**——展开时它是块看不见的挡板，会把最左边那几像素的
   点击全吃掉（表格第一列正好在那儿）。

   为什么要有尾巴（2026-11-03 老板在导航栏边上画了条线：「加一个能看到的小尾巴」）：
   第一版只留了 8px 的透明感应区，功能是通的，但**客户不知道那儿有东西**。
   收起之后整条左边缘看起来就是"界面从这儿开始"，谁也不会把鼠标贴上去试。
   所以感应区留透明、铺满整条左边（容错：随便碰哪儿都能叫回来），
   尾巴是它的**可见标识**，告诉客户"这里能拉出来"。

   尾巴做成白色 + 阴影而不是深色：这个软件的窗口是浅色界面，深色小把手
   在浅色壁纸上像一滴污渍；白色带阴影在浅底上是"一块浮起来的塑料"，
   在深色壁纸上反而更显眼，两个方向都成立。

   sensor 宽 14px、尾巴视觉只有 8px：hover 时尾巴变宽到 12px，如果判定区
   只有 8px，鼠标跟着移到 10px 处就掉出 hover → 缩回 → 又 hover，抖个不停。 */
#railSensor { display: none; }
body.shell-rail.rail-off #railSensor {
  display: block; position: fixed;
  left: 0; top: 46px; bottom: 0; width: 26px;
  z-index: 60;
}
body.shell-rail.rail-off #railSensor::after {
  content: ""; position: absolute;
  left: 0; top: 50%; transform: translateY(-50%);
  width: 14px; height: 110px;
  border-radius: 0 12px 12px 0;
  /* 颜色最后定成品牌蓝，是被截图否掉两版之后的结论：
       第 1 版 纯白 + 浅灰内描边 → 融进内容区的浅灰底，等于没加
       第 2 版 白 + 中灰外描边 + 影子 → 放大图能看清了，整图里仍然要凑近找
     这个软件的界面本身就是浅灰白，**白色把手在这个底上先天不成立**，
     描边加多粗都只是"勉强"。蓝条在浅底上是实打实的一块异色，
     在深色壁纸上（玻璃模式）比背景亮，两个方向都跳得出来。

     宽度 14px（第一版 9px，老板看了说「再宽点」）：9px 在 1000px 宽的
     窗口里就是一根头发丝，得盯着找。14px 加上圆角才看得出是"一截把手"。

     圆角只给右边（0 12px 12px 0），左边贴着窗口边缘，看起来才是从边框里
     长出来的一截，而不是一块浮在界面上的贴纸。

     2026-11-03 06:29 老板截图问「也能做半透么」——改成半透明。
     这里能这么干是因为**它后面不是内容区，是已经模糊过的亚克力**：
     导航栏收起后那 92px 是空的，底下就是 DWM 每帧算出来的桌面。
     所以 56% 的蓝 + 透出来的模糊底色 = 一块蓝色玻璃，跟整窗的材质是
     同一套语言，而不是贴上去一块塑料。

     不能全透：底色一变（浅色桌面 / 深色壁纸）颜色就跟着漂，
     客户会以为那块东西坏了。56% 是"能看出透、但颜色仍然认得出是蓝"。
     另外加一圈很淡的白色内描边——半透的块没有硬边，靠这 1px 定形。 */
  background: rgba(26, 102, 214, .56);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .22),
              2px 0 10px rgba(26, 102, 214, .32);
  transition: width .15s ease, background .15s ease, box-shadow .15s ease;
}
/* 鼠标贴上去：尾巴长长一点、蓝得实一点，明确"松手就在这儿" */
body.shell-rail.rail-off #railSensor:hover::after {
  width: 20px;
  background: rgba(47, 123, 234, .80);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .30),
              2px 0 12px rgba(26, 102, 214, .48);
}

/* ---- 按住拖动换顺序（2026-11-03 老板：「就跟手机一样，按住拖动」）----
 *
 * 做法是**让位**，不是画一条落点线。
 *
 * 第一版画了 2px 的蓝线标落点，截图一看全被手上那块挡住了——客户抓着
 * 图标往下拖，正下方就是"会插到哪儿"，偏偏被自己挡住，等于没标。
 * 改成手机桌面那一套：被跨过的那几项整格平移，空出一格。空出来的缺口
 * 本身就说明"会落在这儿"，而且它在手旁边，不会被挡。
 *
 *   .rail-src  被拖的那一项：visibility:hidden，**保留占位**。
 *              用 display:none 会让下面的项整体上移，缺口位置算错一格。
 */
body.rail-dragging { cursor: grabbing; user-select: none; }
/* 拖动中只给 transform 过渡：让位要滑过去，不能瞬移。
   背景色那种过渡这时候没用，一起关掉，省得 hover 残留。 */
body.rail-dragging .tabs button[data-tab] {
  transition: transform .16s ease;
}
body.shell-rail .tabs button[data-tab].rail-src { visibility: hidden; }

/* 跟着手走的那一块。方块的尺寸、圆角、间距必须跟左栏一模一样，
   否则拖起来会「变胖」，一落位又缩回去，看着像东西被捏了一下。
   左栏是 34px 方块 / 10px 圆角 / gap 3px。 */
.rail-ghost {
  position: fixed; z-index: 90; pointer-events: none;
  display: grid; justify-items: center; gap: 3px;
  padding: 5px 2px; border-radius: 10px;
  background: var(--card);
  box-shadow: 0 12px 26px rgba(16, 26, 40, .26),
              0 2px 6px rgba(16, 26, 40, .14);
  /* 略微放大 + 1 度倾斜：手上的东西跟放着的东西要能分开。
     再多就像贴纸歪了。 */
  transform: scale(1.07) rotate(-1deg);
}
.rail-ghost .tile {
  width: 34px; height: 34px; border-radius: 10px;
  background: rgba(26, 102, 214, .12); color: var(--brand);
  display: grid; place-items: center;
}
.rail-ghost .lbl {
  font-size: 11px; line-height: 1.2; color: var(--brand);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- 抽屉：不用的模块收在这儿（2026-11-03）----
 *
 * 它**不在 #tabs 里**，而是 #mainView 左列里独立的一格、贴底。
 * 塞进 #tabs 的话只有两条路：排在十几个模块后面（客户滚不到），
 * 或者 sticky 贴底但要盖住滚过去的按钮。独立一格最省事。
 *
 * 四块小方块就是「里头有存货」——跟任务栏那个文件夹图标一个意思，
 * 不是画一个抽屉，是让人一眼看出这里能拉开。
 */
body.shell-rail #railDrawer {
  grid-column: 1; grid-row: 3;
  align-self: end; justify-self: stretch;
  position: relative; z-index: 6;
  margin: 0 6px 8px;
  display: grid; justify-items: center; gap: 3px;
  padding: 6px 2px 5px;
  border: 0; border-radius: 10px;
  /* **非玻璃模式不铺底色**。第一版给了一块 var(--card) 的实底，截图一看
     在浅灰界面上就是一块白补丁——旁边每一项都是透明的，只有它一个人是
     贴上去的。改成只留一圈细框：既跟其它项分得开（表示"这一格不是模块"），
     又跟整条栏是一个面。

     玻璃模式反过来——那里它要跟顶栏、左栏并排当同一块料，所以下面
     `body.shell-rail.glass #railDrawer` 一条会把它重新铺上同一份半透底。
     两个模式各自成立，不是重复定义。 */
  background: none;
  box-shadow: 0 0 0 1px rgba(203, 212, 224, .85);
  color: var(--muted); font-size: 11px; line-height: 1.2;
  cursor: pointer;
  transition: opacity .18s ease, box-shadow .15s ease, background .15s ease;
}
body.shell-rail #railDrawer:hover { background: rgba(255, 255, 255, .55); }

/* 空抽屉（2026-11-03）：一格虚线。**必须常驻**，不能"有东西才出现"——
 * 格子不在，就没地方往里拖，也就永远拖不进第一项。这是老板截图里
 * 「能单独拖动，没有抽屉」的根。
 *
 * 用 border 而不是 box-shadow 做虚线圈（dashed 画不出在 shadow 上），
 * padding 各减 1px 把 border 占掉的高度补回来，所以空/满两种状态
 * offsetHeight 一致，--drawer-h 不会跳，左栏也不会跟着抖一下。 */
body.shell-rail #railDrawer[data-empty="1"] {
  border: 1px dashed #c3cbd6;
  padding: 5px 1px 4px;
  color: #9aa4b2;
  box-shadow: none;
}
body.shell-rail #railDrawer[data-empty="1"] .tile {
  background: none; color: #9aa4b2;
}
body.shell-rail #railDrawer[data-empty="1"]:hover {
  border-color: #9aa4b2;
  background: rgba(255, 255, 255, .55);
}
/* 左栏收起时它跟着走。留着的话，左栏没了、左下角却挂着一个孤零零的小方块，
   比一起消失更奇怪。唤回还是靠那颗蓝尾巴。 */
body.shell-rail.rail-off #railDrawer { opacity: 0; pointer-events: none; }

/* 玻璃模式下抽屉跟左栏、顶栏是**同一块料**（老板：「跟抽屉一样，带圆角。
   透度跟抽屉一样」）——反过来做：让抽屉引用跟它们同一组变量，而不是自己
   再写一个数。三处一个 fill、一个 blur、一个圆角，看起来才是从一个模子里
   切出来的。

   margin-left 从 6px 改成 --glass-gap，右 margin 归 0：左栏自己已经有
   6px 的左边距、右边贴着 92px——抽屉再留 6px 右边距就比左栏窄一截，
   两块并排时右边缘错开 6px，圆角一大就看得出来。 */
body.shell-rail.glass #railDrawer {
  margin: 0 0 8px var(--glass-gap);
  border-radius: var(--glass-radius);
  background: var(--glass-fill);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-ring);
  color: #242b34;
}
body.shell-rail.glass #railDrawer:hover { background: rgba(255, 255, 255, .46); }
/* 空抽屉靠虚线说话，不能同时又套一圈实线——两条线叠在一起像没对齐的框 */
body.shell-rail.glass #railDrawer[data-empty="1"] { box-shadow: none; }

/* 方块尺寸跟左栏那堆一模一样，矮屏那两档压缩才带得走（见文件下面的媒体查询）。
   要是这里写死 34px，窗口一矮就它一个不缩，整栏看着参差不齐。 */
body.shell-rail #railDrawer .tile {
  width: 34px; height: 34px; border-radius: 10px;
  background: #eef1f5; color: var(--muted);
  display: grid; place-items: center;
  transition: background .15s, color .15s;
}
body.shell-rail #railDrawer .tile svg { width: 20px; height: 20px; }
body.shell-rail #railDrawer .lbl { white-space: nowrap; }
body.shell-rail.glass #railDrawer .tile { background: rgba(255, 255, 255, .42); }

/* 拖着东西的时候抽屉要"开口"：一整圈虚线框是最省事的说法，
   客户不用猜"能不能往这儿扔"。 */
body.rail-dragging #railDrawer {
  box-shadow: 0 0 0 2px var(--brand), 0 4px 14px rgba(26, 102, 214, .22);
  color: var(--brand);
}
/* 拖着东西时，空抽屉整格变蓝虚线——这是"往这儿扔"的唯一提示，
   客户手上正抓着图标，没空去找文字说明。 */
body.rail-dragging #railDrawer[data-empty="1"] {
  border-color: var(--brand);
  background: rgba(26, 102, 214, .07);
  color: var(--brand);
  box-shadow: none;
}
body.rail-dragging #railDrawer .tile { color: var(--brand); }
body.rail-dragging #railDrawer[data-empty="1"] .tile { color: var(--brand); }

/* 左栏要让出抽屉那一截，**而且是真的缩短**，不是加 padding-bottom。

   加 padding 只是让内容可滚，滚到底时最后一项仍然停在抽屉底下——
   2026-11-03 截图里「打印流水」就是被压掉一半的那个，滚到底也还压着。
   改成 height 直接减掉，两条东西就不重叠了。
   --drawer-h 由 shell.js 量出来写进 body（矮屏压缩后它会变小）。 */
body.shell-rail.has-drawer .tabs {
  align-self: start;
  height: calc(100% - var(--drawer-h, 69px));
}
/* 玻璃模式下左栏自己也占了上下各 6px 的缝（那是圆角能看见的前提），
   所以让给抽屉的那一截之外还要再扣掉这 12px，否则滚到底时最后一项
   仍旧被抽屉压住——11.25 刚修好的那个毛病会以另一个形式回来。 */
body.shell-rail.glass.has-drawer .tabs {
  height: calc(100% - var(--drawer-h, 69px) - 2 * var(--glass-gap));
}

/* 抽屉面板：三列，比左栏那个悬停浮层宽——里面放的是"暂时不用的"，
   客户是来找东西的，铺开比排一长条好找。 */
.railpop.drawerpop {
  display: grid;
  grid-template-columns: repeat(3, auto);
  gap: 4px;
  max-width: 330px;
}
/* 空抽屉点开时那一行说明。跨满三列，宽度卡住，别让一句话拉成一长条。 */
.railpop.drawerpop .drawer-tip {
  grid-column: 1 / -1;
  max-width: 216px;
  padding: 4px 6px;
  color: var(--muted);
  font-size: 12px; line-height: 1.6;
}

/* ---- 窄屏：导航栏变成顶上一条横排，能横向滑，内容照旧铺满 ---- */
@media (max-width: 640px) {
  body.shell-rail #mainView {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto minmax(0, 1fr);
  }
  /* 窄屏不收起（导航是横排，收了就是顶上一条空白）。这两条是保险：
     带 .rail-off 的桌面规则优先级更高，万一状态没及时清掉，列宽会被
     压成 0，整个界面看起来"没加载出来"。 */
  body.shell-rail.rail-off #mainView { grid-template-columns: minmax(0, 1fr); }
  body.shell-rail.rail-off .tabs { opacity: 1; pointer-events: auto; }
  #railSensor { display: none !important; }
  body.shell-rail .appbar { grid-row: 1; }
  body.shell-rail .notes { grid-row: 2; }
  body.shell-rail .tabs {
    grid-column: 1; grid-row: 3;
    flex-direction: row; align-items: flex-start;
    height: auto; overflow-x: auto; overflow-y: hidden;
    border-right: 0; border-bottom: 1px solid var(--line);
    padding: 6px 8px;
    /* 横排放不下 12 个模块，确实要能横滑；但浏览器那条灰条会横在导航下面，
       看着像"整个窗口能左右拖"（2026-11-02 实测 390 宽截图）。
       手机上横滑本来就不画条，桌面上照样能滚轮/拖动滑。 */
    scrollbar-width: none;
  }
  body.shell-rail .tabs::-webkit-scrollbar { display: none; }
  /* 窄屏导航是顶上一条横排，抽屉那一格会跟它抢 row 3 的位置，直接藏掉。
     同时把 has-drawer 给 .tabs 扣掉的高度还回去——那条规则优先级比
     下面这条高，不显式覆盖的话横排导航会被压成 100%-69px。 */
  body.shell-rail #railDrawer { display: none; }
  body.shell-rail.has-drawer .tabs { height: auto; align-self: auto; }
  body.shell-rail .tabs button[data-tab],
  body.shell-rail .tabs button.more { width: auto; min-width: 60px; }
  body.shell-rail .pagewrap { grid-column: 1; grid-row: 4; }
  .appbar { height: 42px; }
  /* 窄屏别学桌面那套"浮起的圆角板"：顶上一条横排本来就只有 60px 高，
     再留 6px 的缝、倒 14px 的角，等于把导航吃掉一截。贴边通条在这里是对的。
     这几条必须写在媒体查询里，而且带 .glass——不然上面那组
     `body.shell-rail.glass` 优先级更高，会把手机上的缝和圆角又加回来。 */
  body.shell-rail.glass .appbar {
    margin: 0; border-radius: 0; box-shadow: none;
    border-bottom: 1px solid var(--line);
  }
  body.shell-rail.glass .tabs {
    margin: 0; border-radius: 0; box-shadow: none;
    border-bottom: 1px solid var(--line);
  }
}

/* ---- 窗口矮的时候把方块收小，模块要一屏放得下 ----

   2026-11-03 实测过要不要提门槛（左栏改回全开之后）：760 视口下左栏可用
   680px，一项 52.6px，9 项 485、10 项 538、11 项 590 —— 装得下。所以门槛
   不动，方块保持原尺寸。真到 12 项（full 档全开）才需要滚，那时滚是对的。 */
@media (min-width: 641px) and (max-height: 700px) {
  body.shell-rail .tabs .tile { width: 30px; height: 30px; border-radius: 9px; }
  body.shell-rail .tabs button[data-tab],
  body.shell-rail .tabs button.more { font-size: 10.5px; padding: 3px 2px; gap: 2px; }
  /* 抽屉那一格不在 .tabs 里，得单独跟一档，不然它一个人不缩 */
  body.shell-rail #railDrawer .tile { width: 30px; height: 30px; border-radius: 9px; }
  body.shell-rail #railDrawer .tile svg { width: 18px; height: 18px; }
  body.shell-rail #railDrawer { font-size: 10.5px; padding: 3px 2px; gap: 2px; }
}
/* 再矮一档：十个模块全开时，靠这一档才不用滚 */
@media (min-width: 641px) and (max-height: 640px) {
  body.shell-rail .tabs { gap: 1px; padding: 6px 5px; }
  body.shell-rail .tabs .tile { width: 27px; height: 27px; border-radius: 8px; }
  body.shell-rail .tabs .tile svg { width: 16px; height: 16px; }
  body.shell-rail .tabs button[data-tab],
  body.shell-rail .tabs button.more { font-size: 10px; padding: 1px 2px; gap: 1px; }
  body.shell-rail #railDrawer .tile { width: 27px; height: 27px; border-radius: 8px; }
  body.shell-rail #railDrawer .tile svg { width: 16px; height: 16px; }
  body.shell-rail #railDrawer { font-size: 10px; padding: 1px 2px; gap: 1px; }
}

/* ---- 大屏：内容栏不跟着拉成一张巨表，也不用缩在中间一小条 ----
   2026-11-03：1040 → 1360。窗口按工作区开了（app.js 的 mainWindowSize），
   1920 的屏上窗口 1766 宽，再卡 1040 就是「窗口放大了、账本没放大」，
   右边一大条白——老板要的正是把这块用起来。表格和单据行都是弹性的，
   放宽只是列宽舒展，不会错位（下面冒烟截图核对过）。 */
@media (min-width: 1500px) {
  body.shell-rail main { max-width: 1360px; }
}

/* 左栏悬停浮出的子菜单（2026-11-02）。贴按钮右侧，不参与网格布局——
   放进 .tabs 里会被那一列的 overflow 裁掉，所以 fixed + 挂 body。

   2026-11-03 改成方块：老板「要换成和导航一样的方块形状」。原来是一行行
   纯文字（像一段说明），现在每项都跟左栏格子同宽同款——上面一个灰底圆角方块
   装图标，下面一行小字。**宽度写死 92px**，跟左栏格子一模一样，
   一排方块才对得齐；文案因此不许超过 7 个字（见 app.js 的 RAIL_MENU）。 */
.railpop {
  position: fixed; z-index: 60;
  padding: 8px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--card); box-shadow: 0 8px 26px rgba(0, 0, 0, .14);
  display: flex; gap: 4px;
}
.railpop-item {
  width: 92px; border: 0; background: none; border-radius: 10px;
  padding: 7px 2px 8px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  font-size: 11px; line-height: 1.2; color: var(--ink);
  text-align: center;
}
.railpop-item .tile {
  width: 34px; height: 34px; border-radius: 10px;
  background: #eef1f5; color: var(--muted);
  display: grid; place-items: center;
  transition: background .15s, color .15s;
}
.railpop-item .lbl { white-space: nowrap; }
.railpop-item:hover { background: #eef4ff; color: var(--brand); }
.railpop-item:hover .tile { background: var(--brand); color: #fff; }

/* ==================== 导航搬到窗口底部（2026-11-04） ====================
 *
 * 老板原话：「做到下面去。跟mac一样，这回抽屉放左面。」
 *
 * 为什么这一节全是"覆盖"而不是把上面那条左栏删掉：
 * 上面那套（92px 竖栏、收起、感应条、拖拽让位）每一条后面都挂着一个踩过的坑。
 * 只把**布局**这一层换成底部横排，行为和 DOM 一概不动。
 *
 * ★ 选择器必须带 `.glass`：玻璃模式下左栏那套是 `body.shell-rail.glass .tabs`
 *   （三个类），比我一开始写的 `body.shell-rail .tabs`（两个类）优先级高。
 *   第一版就是这么栽的：margin 还是 6px 0 6px 6px、高度还是旧的，
 *   整条竖着排到屏幕中间去——**改了没生效，但 CSS 确实加载了**，
 *   查了半天才发现是优先级。
 */

body.shell-rail #mainView {
  grid-template-columns: minmax(0, 1fr);
  /* 内容区**伸到窗口底**，导航浮在它上面。
     分一行给导航的话，那一条永远坐在一块底上，玻璃后面没东西可透——
     看着就是"一块白板"，跟老板说的"导航不透"是同一件事。 */
  grid-template-rows: auto auto minmax(0, 1fr);
}
body.shell-rail .pagewrap {
  grid-column: 1; grid-row: 3;
  /* 末尾留出导航的高度，不然最后一行永远压在它底下、滚也滚不出来 */
  padding-bottom: 92px;
}

/* 收起那套在底部形态下不适用 */
body.shell-rail.rail-off #mainView,
body.shell-rail.glass.rail-off #mainView { grid-template-columns: minmax(0, 1fr); }
body.shell-rail.rail-off .tabs,
body.shell-rail.glass.rail-off .tabs { opacity: 1; pointer-events: auto; }
body.shell-rail #railSensor { display: none !important; }

/* ---- 一条横着的玻璃胶囊 ---- */
body.shell-rail .tabs,
body.shell-rail.glass .tabs {
  position: fixed;
  /* top 必须显式写回 auto：某个祖先（或 grid 的静态位置）把它算成了 0，
     于是 top:0 和 bottom:10 同时成立 → 整条被拉成满屏高，
     height 写多少都没用。这是第一版"导航跑到屏幕中间"的真正原因。 */
  top: auto;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  z-index: 50;
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: 2px;
  /* 固定高度。**必须 !important**：`style.css` 里那条通用的 `.tabs`
     （sticky 那套）和 shell.css 里左栏那条 `height:100%` 都在抢这个属性，
     光靠"后写赢"压不住——第一版就是这样：top 改对了、高度还是 625，
     整条变成一根从顶上垂到底下的柱子。 */
  height: 68px !important;
  width: fit-content;
  max-width: calc(100% - 16px);
  margin: 0;
  padding: 6px 10px;
  border: 0;
  border-radius: 22px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
body.shell-rail .tabs::-webkit-scrollbar { height: 0; }
body.shell-rail .tabs .spacer { display: none; }

/* 每一项：图标在上、小字在下 */
body.shell-rail .tabs button[data-tab],
body.shell-rail.glass .tabs button[data-tab],
body.shell-rail .tabs button.more,
body.shell-rail.glass .tabs button.more {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 2px;
  width: auto;
  min-width: 48px;
  padding: 2px 4px 1px;
  border: 0;
  border-radius: 9px;
  background: none;
  font-size: 9.5px;
  line-height: 1.2;
}
body.shell-rail .tabs .tile,
body.shell-rail.glass .tabs .tile {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  transition: background .15s, color .15s, transform .12s;
}
body.shell-rail .tabs .lbl {
  white-space: nowrap;
  max-width: 58px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 悬停轻微放大：Dock 的招牌动作。
   只放大不位移——真要"旁边让位"得给整条算布局，鼠标一扫就抖成一片。 */
body.shell-rail .tabs button[data-tab]:hover .tile,
body.shell-rail.glass .tabs button[data-tab]:hover .tile,
body.shell-rail .tabs button.more:hover .tile,
body.shell-rail.glass .tabs button.more:hover .tile {
  transform: scale(1.08);
}

/* 当前页：图标那一格变蓝 + 下面一个小点。两个一起给——只变蓝在十几个图标里
   不够跳；只有点没有色，又看不出"点的是哪个"。 */
body.shell-rail .tabs button[data-tab].on,
body.shell-rail.glass .tabs button[data-tab].on {
  position: relative;
  color: var(--brand);
  font-weight: 600;
  background: none;
}
body.shell-rail .tabs button[data-tab].on .tile,
body.shell-rail.glass .tabs button[data-tab].on .tile {
  background: var(--brand);
  color: #fff;
}
body.shell-rail .tabs button[data-tab].on::after,
body.shell-rail.glass .tabs button[data-tab].on::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -1px;
  width: 3px;
  height: 3px;
  margin-left: -1.5px;
  border-radius: 50%;
  background: var(--brand);
}

/* ---- 抽屉入口挪到左下角 ---- */
body.shell-rail #railDrawer,
body.shell-rail.glass #railDrawer {
  position: fixed;
  left: 14px;
  bottom: 14px;
  z-index: 50;
  width: 54px;
  margin: 0;
  padding: 6px 2px 5px;
  /* 三样都跟着顶栏/导航那组变量走，不再自己写死一份透度——
     写死一份，下次改一边另一边不动，接缝就出来了（2026-11-03 踩过一次）。 */
  border-radius: var(--glass-radius, 14px);
  background: var(--glass-fill, var(--card));
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-ring, none), 0 4px 14px rgba(30, 40, 70, .16);
}
/* 悬停不再换一种白，直接提亮：换色就又回到"写死一份透度"那条老路上去了 */
body.shell-rail #railDrawer:hover,
body.shell-rail.glass #railDrawer:hover { filter: brightness(1.08); }

/* ---- 窄窗口：先缩字，再藏字 ---- */
@media (max-width: 1180px) {
  body.shell-rail .tabs button[data-tab],
  body.shell-rail.glass .tabs button[data-tab] { min-width: 42px; font-size: 9px; }
  body.shell-rail .tabs .tile,
  body.shell-rail.glass .tabs .tile { width: 29px; height: 29px; }
}
@media (max-width: 980px) {
  /* 只留图标。名字靠悬停那条浮层看（railpop 本来就有） */
  body.shell-rail .tabs .lbl { display: none; }
  body.shell-rail .tabs button[data-tab],
  body.shell-rail.glass .tabs button[data-tab] { min-width: 38px; padding: 2px 3px; }
}

/* ---- 浮层背景必须不透明（2026-11-04 老板：「重影」） --------------------
 *
 * 现象：鼠标划过导航，浮出来的那几格和底下的图标叠在一起，像印刷重影。
 *
 * 原因：`.railpop` 的背景走的是 `var(--card)`，而玻璃模式里 --card 是
 * `rgba(255,255,255,.88)`——半透明。以前浮层贴在左栏右边，底下是页面空白，
 * 透过去看不出来；导航搬到窗口底部之后，它浮在导航条上方，
 * 底下正好是图标和文字，透上来就是两块内容叠着。
 *
 * 玻璃感靠阴影和圆角就够，**内容不能透**：浮层是拿来读的，不是拿来透的。
 * 三条一起改（普通浮层、抽屉浮层），`!important` 不用——
 * 选择器比上面 `.glass` 那条多一层，写在后面就赢。
 */
body.shell-rail .railpop,
body.shell-rail.glass .railpop,
body.shell-rail .railpop.drawerpop,
body.shell-rail.glass .railpop.drawerpop {
  background: #fff;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* 描边也做实：半透明描边在深色壁纸上会消失，浮层边界就靠阴影撑着。
     不再叠一圈 ring——原来的 border 还在，两条边会成双线。 */
  border-color: #e3e6ea;
  box-shadow: 0 12px 32px rgba(18, 26, 44, .26);
}
