/* 3D 侧边栏：DOM 伪 3D，近白玻璃（偏一点点灰）浮在纯白空间上。
   一条 --rail-e 通道（0 收起 / 1 展开）驱动全部形变，由 loop 每帧从 sidebar.expansion 写入。
   JS 还同时用它算主体的让位量，所以面板和盒子永远同步，不存在第二套时间线。 */

@property --rail-e {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* 必须注册：未注册属性在 :hover 之外没有值，calc 里的 var(--rail-f) 会整条失效。
   注册了也才能被 transition —— 但只 transition 它，别 transition transform，
   transform 每帧都在读 --rail-e，加了过渡会和状态机打架。 */
@property --rail-f {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

@property --cap-f {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

:root {
  /* 尺寸的唯一来源是 src/config/design.js 的 RAIL —— JS 要用它算主体的让位量。
     这里留一份同值默认，只为 main.js 尚未执行的那一帧不出现无样式闪烁。 */
  --rail-w: 236px;
  --rail-x: 20px;
  /* 白面板没有暗剪影卖深度，透视要给足，形变才读得出是"翻"而不是"缩"。 */
  --rail-persp: 880px;
  /* 近白玻璃：左上亮、右下压一点点灰，顶光斜射的方向感全靠这个差。 */
  --rail-glass-a: rgba(252, 253, 255, 0.86);
  --rail-glass-b: rgba(236, 240, 247, 0.93);
  /* 白底上的边界必须是暗线，用亮线会糊成一片白。 */
  --rail-edge: rgba(40, 52, 70, 0.16);
  --rail-edge-hi: rgba(24, 34, 48, 0.3);
  --rail-sheen: rgba(255, 255, 255, 0.95);
  --rail-ink: #1b2430;
  --rail-ink-2: rgba(24, 32, 44, 0.82);
  --rail-ink-3: rgba(26, 35, 47, 0.7);
  --rail-hair: rgba(28, 38, 52, 0.1);
  /* 白底上打不出"内发光"，改成强调色的软晕。 */
  --rail-glow: rgba(47, 109, 246, 0.3);
  /* backdrop-filter 单独一个令牌：sheets.css 的 --blur 语义是给边缘磨砂带的 9px，
     别混用。由 main.js 的 applyTier 在 mobile 档归零。 */
  --rail-blur: 20px;
  /* 玻璃感的真正来源。实测面板背后是绝对纯白（meanLum 255.00、非白像素 0），
     blur 一个常数白场输出还是白 —— 折射这条路在白底上根本不存在，
     所以表面感全靠这层程序生成的微噪点 + 下面的倒角与阴影。
     内联 SVG feTurbulence：零外部文件、零 JS、零运行时开销，
     stitchTiles 保证 120px 平铺无缝，rect 自身 opacity 控强度。 */
  --rail-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='rg'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23rg)' opacity='0.055'/%3E%3C/svg%3E");
}

.rail {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  z-index: 15;
  width: calc(var(--rail-w) + var(--rail-x) * 2);
  perspective: var(--rail-persp);
  /* 顶光从左上方来：透视灭点也放在那儿，展开时面板像是从胶囊里"翻"出来。 */
  perspective-origin: left top;
  pointer-events: none;
  transition: opacity 0.22s ease;
}

/* 内容态整条侧栏退出。inert 挡得住交互但挡不住视觉 —— .sheet 的 clip-path
   会把左侧一条带裁空，胶囊正好从那个缺口里露出来，所以必须靠 visibility。 */
.rail.rail-off {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* 延迟只加在"藏"的方向：展开时不能慢半拍才出现。 */
  transition: opacity 0.22s ease, visibility 0s 0.22s;
}

/* ---------- 收起态：左上角胶囊 ---------- */

.rail-cap {
  position: absolute;
  left: var(--rail-x);
  top: var(--rail-x);
  display: flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 14px 0 8px;
  border: 1px solid var(--rail-edge);
  border-radius: 999px;
  background:
    var(--rail-grain) repeat 0 0 / 120px 120px,
    linear-gradient(148deg, var(--rail-glass-a), var(--rail-glass-b));
  backdrop-filter: blur(var(--rail-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--rail-blur)) saturate(1.4);
  /* 倒角：顶左亮边 + 底右次亮边 + 一圈暗发丝收边 + 底部厚度；
     光从左上射，所以投影一律落向右下。 */
  box-shadow:
    inset 1px 1px 0 var(--rail-sheen),
    inset -1px -1px 0 rgba(255, 255, 255, 0.45),
    inset 0 0 0 1px rgba(40, 52, 70, 0.07),
    0 1px 1px rgba(28, 38, 52, 0.09),
    0 6px 14px -6px rgba(28, 38, 52, 0.18);
  color: var(--rail-ink);
  font: inherit;
  font-weight: 400;
  -webkit-font-smoothing: auto;
  cursor: pointer;
  pointer-events: auto;
  /* 展开时淡出；opacity 与 --rail-e 反相，由 JS 每帧驱动，CSS 不加 transition。 */
  opacity: calc(1 - var(--rail-e));
  /* 只有纯 2D 平移：translateZ 在 880px 透视下会把整块文字非整数倍放大重采样，
     那才是"看着发虚"的真凶。浮起感交给 translateY(-1px) 与阴影，都不改缩放。 */
  transform: translate3d(calc(var(--rail-e) * -14px), calc(var(--cap-f) * -1px), 0)
    scale(calc(1 - var(--rail-e) * 0.12));
  transition: --cap-f 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.rail-cap:hover,
.rail-cap:focus-visible {
  border-color: var(--rail-edge-hi);
  --cap-f: 1;
  box-shadow:
    inset 1px 1px 0 #fff,
    inset -1px -1px 0 rgba(255, 255, 255, 0.6),
    inset 0 0 0 1px rgba(40, 52, 70, 0.09),
    0 1px 2px rgba(28, 38, 52, 0.1),
    0 10px 22px -8px rgba(28, 38, 52, 0.24);
}

.cap-badge {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  flex: none;
  color: rgba(27, 36, 48, calc(0.55 + var(--cap-f) * 0.35));
}

.cap-mark {
  width: 20px;
  height: 20px;
  filter: drop-shadow(0 0 calc(2px + var(--cap-f) * 3px) var(--rail-glow));
}

/* 还没指向任何面时编号是空串：留着会凭空多出一截 gap。 */
.cap-no:empty {
  display: none;
}

.cap-no {
  font-size: 11px;
  letter-spacing: 0.16em;
  color: var(--rail-ink-3);
  font-variant-numeric: tabular-nums;
}

.cap-title {
  font-size: 13px;
  letter-spacing: 0.06em;
}

/* ---------- 展开态：面板 ---------- */

.rail-panel {
  position: absolute;
  left: var(--rail-x);
  top: var(--rail-x);
  width: var(--rail-w);
  padding: 16px 15px 15px;
  /* 矮屏或放大字号时内容会顶出视口：让面板自己滚，别把账号块裁掉。
     dvh 排在 vh 之后，移动端地址栏收起时才不会量错。 */
  max-height: calc(100vh - var(--rail-x) * 2);
  max-height: calc(100dvh - var(--rail-x) * 2);
  overflow: hidden auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(28, 38, 52, 0.22) transparent;
  border: 1px solid var(--rail-edge);
  border-radius: 18px;
  /* 三层：斜向高光带 → 微噪点表面 → 玻璃本体。
     全部走 background 而不是伪元素：background 永远画在内容之下。
     之前那层 ::before 白纱没有 z-index，按绘制顺序压住了所有非定位后代，
     品牌行和导航靠 position:relative 躲过去了，"公告""账号"两块被直接洗淡 ——
     用户说的"糊"里有一部分其实是"被白纱盖住的淡"。 */
  background-image:
    linear-gradient(104deg,
      rgb(255 255 255 / 0) 14%,
      rgb(255 255 255 / calc(0.5 + var(--rail-f) * 0.42)) 27%,
      rgb(255 255 255 / 0) 41%),
    var(--rail-grain),
    linear-gradient(152deg, var(--rail-glass-a), var(--rail-glass-b));
  background-repeat: no-repeat, repeat, no-repeat;
  background-size: 100% 100%, 120px 120px, 100% 100%;
  backdrop-filter: blur(var(--rail-blur)) saturate(1.45);
  -webkit-backdrop-filter: blur(var(--rail-blur)) saturate(1.45);
  /* 白底上玻璃感全靠这两组：inset 那三条把"厚度"画出来（顶左受光亮边、
     底右次亮边、一圈暗发丝收边、底部一道切口），外层阴影负责落影。
     光从左上射，投影必须落向右下 —— 原来写成 -18px 是反的。 */
  box-shadow:
    inset 1px 1px 0 var(--rail-sheen),
    inset -1px -1px 0 rgba(255, 255, 255, 0.5),
    inset 0 0 0 1px rgba(40, 52, 70, 0.08),
    inset 0 -3px 6px -3px rgba(112, 130, 156, 0.26),
    0 1px 1px rgba(28, 38, 52, 0.1),
    0 10px 22px -8px rgba(28, 38, 52, 0.16),
    0 28px 60px -18px rgba(44, 62, 92, 0.2);
  color: var(--rail-ink);
  font-weight: 400;
  -webkit-font-smoothing: auto;
  pointer-events: auto;
  transform-origin: left top;
  /* 收起量越大越朝盒内翻：绕左边缘转出去，像是被合回胶囊里。
     --rail-f 只推高光与浮起，绝不进 translateZ —— 那会重采样文字。 */
  --fold: calc((1 - var(--rail-e)) * (1 - var(--rail-f) * 0.35));
  transform: translate3d(calc(var(--fold) * -34px), calc(var(--fold) * -8px + var(--rail-f) * -1px), 0)
    rotateY(calc(var(--fold) * 30deg)) rotateX(calc(var(--fold) * -12deg))
    scale(calc(0.86 + var(--rail-e) * 0.14));
  opacity: var(--rail-e);
  transition: --rail-f 260ms cubic-bezier(0.22, 0.61, 0.36, 1), box-shadow 0.24s ease, border-color 0.24s ease;
}

.rail-panel:hover,
.rail-panel:focus-within {
  --rail-f: 1;
  border-color: var(--rail-edge-hi);
  box-shadow:
    inset 1px 1px 0 #fff,
    inset -1px -1px 0 rgba(255, 255, 255, 0.62),
    inset 0 0 0 1px rgba(40, 52, 70, 0.1),
    inset 0 -3px 7px -3px rgba(112, 130, 156, 0.3),
    0 1px 2px rgba(28, 38, 52, 0.11),
    0 14px 28px -8px rgba(28, 38, 52, 0.18),
    0 34px 70px -18px rgba(44, 62, 92, 0.24);
}

/* ---------- 品牌行 ---------- */

.rail-brand {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--rail-hair);
}

/* 面板第一行装的是账号：#rail-acct 吃掉除叉按钮以外的宽度，
   里面的 .acct-row 自己会省略号截断（.acct-name 有 min-width:0）。 */
#rail-acct {
  flex: 1 1 auto;
  min-width: 0;
}

.rail-brand.is-acct .acct-name {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.rail-x {
  margin-left: auto;
  flex: none;
  display: grid;
  place-items: center;
  /* 26 → 32：这颗叉是面板里唯一的"退出"，原来只有 26px，
     在 236px 宽的面板里读起来像个装饰点，指尖也按不准。 */
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 9px;
  background: transparent;
  color: var(--rail-ink-2);
  cursor: pointer;
  transition: color 0.18s, border-color 0.18s, background-color 0.18s;
}

.rail-x svg {
  width: 16px;
  height: 16px;
}

.rail-x:hover,
.rail-x:focus-visible {
  border-color: var(--rail-hair);
  background: rgba(28, 38, 52, 0.045);
  color: var(--rail-ink);
}

/* ---------- 小标题 ---------- */

.rail-h {
  margin: 15px 0 7px;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.22em;
  color: var(--rail-ink-3);
  text-transform: uppercase;
}

/* ---------- 导航 ---------- */

.rail-nav {
  display: grid;
  gap: 1px;
  margin-top: 10px;
}

.rail-nav button {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  /* 12 项了（八面体版 8 项）：行高再松一点，账号块就会被推到面板外只能靠滚。 */
  padding: 5px 9px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--rail-ink-2);
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, box-shadow 0.18s, color 0.18s, transform 0.18s;
}

.rail-nav button .ico {
  width: 17px;
  height: 17px;
  flex: none;
  color: rgba(24, 32, 44, 0.64);
  transition: color 0.18s, filter 0.18s;
}

.rail-nav button .no {
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--rail-ink-3);
  font-variant-numeric: tabular-nums;
}

.rail-nav button .sub {
  margin-left: auto;
  font-size: 10px;
  letter-spacing: 0.14em;
  color: rgba(26, 35, 47, 0.72);
}

.rail-nav button:hover,
.rail-nav button:focus-visible {
  border-color: var(--rail-hair);
  background: rgba(255, 255, 255, 0.72);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgba(28, 38, 52, 0.08);
  color: var(--rail-ink);
  transform: translateX(2px);
}

/* 图标"内发光"：白底上靠 drop-shadow 打不出光，改成强调色的软晕。 */
.rail-nav button:hover .ico,
.rail-nav button:focus-visible .ico {
  color: var(--accent, #2f6df6);
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--accent, #2f6df6) 30%, transparent));
}

/* 当前指向的那一面走压印而不是浮起：它已经被选中，不该再往上跳。 */
.rail-nav button[aria-current='true'] {
  border-color: var(--rail-hair);
  background: rgba(255, 255, 255, 0.86);
  box-shadow: inset 0 1px 2px rgba(28, 38, 52, 0.1);
  color: var(--rail-ink);
  font-weight: 400;
}

.rail-nav button[aria-current='true'] .ico,
.rail-nav button[aria-current='true'] .no {
  color: var(--accent, #2f6df6);
}

.rail-nav button[aria-current='true']::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 50%;
  width: 2px;
  height: 16px;
  border-radius: 2px;
  background: var(--accent, #2f6df6);
  transform: translateY(-50%);
}

/* ---------- 公告 ---------- */

.rail-notes {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rail-notes li {
  font-size: 12px;
  font-weight: 400;
  line-height: 1.62;
  color: var(--rail-ink-2);
}

.rail-notes li b {
  display: block;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  color: var(--rail-ink-3);
}

.acct-row {
  display: flex;
  align-items: center;
  gap: 9px;
}

.avatar {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid rgba(28, 38, 52, 0.12);
  border-radius: 9px;
  /* 色相由用户名哈希决定：代码生成，不引入任何位图素材 */
  background: var(--av-bg, #eef2f8);
  color: var(--av-fg, var(--rail-ink));
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: 0.02em;
}

.acct-name {
  /* 必须是块级：span 里的 .acct-sub 排不到第二行，用户名和角色会挤成 "demo站主"。 */
  display: block;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--rail-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.acct-sub {
  display: block;
  font-size: 10.5px;
  font-weight: 400;
  color: var(--rail-ink-3);
}

.acct-btn {
  margin-left: auto;
  flex: none;
  padding: 5px 11px;
  border: 1px solid rgba(28, 38, 52, 0.16);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.6);
  color: var(--rail-ink);
  font: inherit;
  font-size: 11.5px;
  font-weight: 400;
  cursor: pointer;
  transition: border-color 0.18s, background 0.18s;
}

.acct-btn:hover,
.acct-btn:focus-visible {
  border-color: var(--rail-edge-hi);
  background: #fff;
}

.acct-btn.solid {
  border-color: #1b2430;
  background: #1b2430;
  color: #f6f8fb;
  font-weight: 400;
}

.acct-form {
  display: grid;
  gap: 7px;
  margin-top: 9px;
}

.acct-form input {
  width: 100%;
  height: 31px;
  padding: 0 10px;
  border: 1px solid rgba(28, 38, 52, 0.16);
  border-radius: 9px;
  background: rgba(255, 255, 255, 0.78);
  color: var(--rail-ink);
  font: inherit;
  font-size: 12px;
  outline: none;
}

.acct-form input::placeholder {
  color: rgba(27, 36, 48, 0.38);
}

.acct-form input:focus {
  border-color: var(--rail-edge-hi);
  box-shadow: 0 0 0 3px rgba(47, 109, 246, 0.16);
}

.acct-form .row {
  display: flex;
  gap: 7px;
}

.acct-form .row .acct-btn {
  margin-left: 0;
}

.acct-msg {
  font-size: 11px;
  font-weight: 400;
  line-height: 1.6;
  color: var(--rail-ink-3);
}

.acct-msg[data-kind='err'] {
  color: #b3202f;
}

/* ---------- 窄屏与竖屏：不给主体让位，面板就地盖住 ---------- */

@media (max-width: 1023px), (orientation: portrait) {
  .rail {
    width: 100%;
    perspective: none;
  }

  /* 没有透视就没有纵深可谈，hover 浮起反而怪，把 --rail-f 钉死在 0。 */
  .rail-panel {
    --rail-f: 0;
    transform: translate3d(0, 0, 0) scale(calc(0.94 + var(--rail-e) * 0.06));
    max-height: calc(100vh - 100px);
    max-height: calc(100dvh - 100px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .rail-panel,
  .rail-cap,
  .rail-nav button,
  .acct-btn,
  .rail-x {
    transition: none;
  }

  /* 形变通道交给 JS（reduced 时展开时长只有 1ms），CSS 这边不再叠加旋转。 */
  .rail-panel,
  .rail-cap {
    transform: none;
  }
}

/* ---------- 身份行 = 跳到 01 面的按钮 ----------
 * 它长得还是"头像 + 用户名 + 角色"，但整行可点：登录表单不在侧栏里，
 * 侧栏只负责说清现在是谁、以及把人送到那一面去（定位不进入）。 */
.acct-row.is-jump {
  width: 100%;
  padding: 5px 7px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s;
}

.acct-row.is-jump:hover,
.acct-row.is-jump:focus-visible {
  border-color: var(--rail-edge-hi);
  background: rgba(255, 255, 255, 0.62);
}

.acct-row.is-jump .acct-name {
  flex: 1 1 auto;
}

.acct-go {
  flex: none;
  font-size: 10px;
  letter-spacing: 0.16em;
  color: var(--rail-ink-3);
}
