/* ==========================================================================
   登录页专用视觉层（六套版式 · 控制台语言）
   定稿原型：outputs/登录页原型_B_左右式_20261005.html（版式①）
             后续各版式原型均存于 outputs/「登录页原型_*」目录
   --------------------------------------------------------------------------
   版式清单（#1059 起）：① split 左右 54:46（默认）/ ② center 居中式 /
     ③ split73 左右 7:3 / ④ aurora 极光式 / ⑤ ticker 行情式 / ⑥ cosmos 星穹式。
     全部集中在文末「登录页版式层」；①②③④⑤是「左右分栏」语言，⑥是
     「全域沉浸 + 悬浮登乘舱」（叠放）—— 它们在结构上互斥，切换只靠 <html> 类名。
     本文正文（版式层之前）的所有规则都服务于 ① split，其余版式一律只做加法。

   引用方：**只有** templates/login.html。登录页同时加载 login.css，分工是：
     · login.css      —— 注册/找回**弹窗**（.auth-modal*）、提示框（.error-tip /
                          .ok-tip）、WebAuthn 区块（.passkey-*）与三主题 --lg-* 令牌。
                          register.html / forgot.html 也在用它，**不得改动**。
     · login-hero.css —— 本页版式、左右分栏、装饰层与卡片外观。加载在 login.css
                          之后，同特异性时本文件胜出。

   ⚠️ 类名隔离（两条规则都不重复）：
     login.css 用 .login-card / .input-group / .input-field / .submit-btn / .captcha-row …；
     本文件用 .split / .side / .main / .box / .field / .submit / .console …。
     唯一刻意避开的同名类是验证码行 —— login.css 已有 .captcha-row（flex/gap），
     本文件改名 .lg-cap-row。同名共存时两处 flex 属性会按加载顺序互相覆盖，
     表现为「看着能跑、布局被悄悄改掉」。

   ⚠️ 主题令牌：default 写在下方 :root；gold / red 用 html.theme-* (0,1,1) 覆盖，
     特异性高于 theme-*.css 里的 :root(0,1,0) ⇒ **不受 theme 文件加载顺序影响**。
     但 body 底色必须用 html.theme-* 显式声明，原因见文末「生产适配层」①。

   ⚠️ 本文件的选择器与 templates/login.html 的类名成对；改一处须同改另一处。
   ========================================================================== */

/* ==========================================================================
   原型 B · 左右式登录页 —— 科技感强化版（v4）
   左侧：多层叠加视觉（粒子网络 / 雷达扫描 / 透视网格 / 数据流 / 扫描线 /
        经纬刻度 / HUD 角标 / 轨道环系统 / 实时读数），全部走主题令牌。
   右侧：控制台终端面板（纵深装饰 + 面板框架 + 终端标题栏 + 表单 + 底部信息行）；
        卡片 min-height:100% 撑满右栏，与左侧面板视觉等高（炽红浅底下尤其明显）。
   三主题：default 深蓝（base.css :root）/ gold 黑金 / red 炽红。
   ⚠️ theme-default.css 不存在 —— 深蓝即 :root，不加载任何主题文件。
   本文件为独立原型：不依赖项目 static/，可直接双击打开预览。
   ========================================================================== */

/* ---------- 主题 1：深蓝（默认） ---------- */
:root, html.theme-default {
  --bg-2: #0c1429;
  --panel: rgba(14, 30, 56, .55);
  --card: rgba(16, 34, 62, .68);
  --text: #e0e8f0;
  --text-dim: #8ea2c4;
  --text-faint: rgba(142, 162, 196, .55);
  --input: rgba(6, 14, 30, .72);
  --border: rgba(51, 119, 187, .34);
  --accent: #00a8ff;
  --accent-2: #4fd0ff;
  --accent-rgb: 0, 168, 255;
  --side-1: #030c1c;
  --side-2: #0a2c54;
  --side-glow: rgba(0, 168, 255, .34);
  --particle: 0, 168, 255;
  --grid: rgba(0, 168, 255, .09);
  --shadow: 0 26px 70px rgba(0, 0, 0, .5);
  --ok: #4fe08a;
  /* 科技层专用令牌 */
  --hud-rgb: 110, 198, 255;      /* HUD 线 / 刻度 / 数据流 */
  --hud: rgba(110, 198, 255, .5);
  --hud-dim: rgba(110, 198, 255, .22);
  --scan: rgba(190, 232, 255, .5);
  --vignette: rgba(0, 4, 12, .55);
  /* 黄金点缀令牌（品牌级，跨主题保留；深底用亮金） */
  --gold-rgb: 214, 178, 86;
  --gold-1: #f6e6a8;
  --gold-2: #d4a84b;
  --floor-k: 1;
  /* 右侧控制台专用令牌：炽红为浅底，若复用 --hud-rgb(亮色) 会整片不可见 */
  --rhud-rgb: 110, 198, 255;
  --rhud: rgba(110, 198, 255, .5);
  --rhud-dim: rgba(110, 198, 255, .2);
  --rgrid: rgba(0, 168, 255, .09);
  --rvig: rgba(2, 6, 16, .55);            /* 右栏暗角：深底主题走深色 */
}
/* ---------- 主题 2：黑金 ---------- */
html.theme-gold {
  --bg-2: #0a0805;
  --panel: rgba(26, 24, 18, .55);
  --card: rgba(26, 22, 14, .7);
  --text: #f0ece0;
  --text-dim: #c9b896;
  --text-faint: rgba(201, 184, 150, .5);
  --input: rgba(16, 13, 8, .75);
  --border: rgba(140, 111, 58, .42);
  --accent: #d4a84b;
  --accent-2: #ffd88a;
  --accent-rgb: 212, 168, 75;
  --side-1: #0a0803;
  --side-2: #33270f;
  --side-glow: rgba(212, 168, 75, .32);
  --particle: 212, 168, 75;
  --grid: rgba(212, 168, 75, .08);
  --shadow: 0 26px 70px rgba(0, 0, 0, .6);
  --ok: #7be0a4;
  --hud-rgb: 232, 197, 122;
  --hud: rgba(232, 197, 122, .45);
  --hud-dim: rgba(232, 197, 122, .2);
  --scan: rgba(255, 235, 190, .45);
  --vignette: rgba(6, 4, 0, .58);
  /* 黑金主题：金更亮更暖（与主题主色同族，观感最协调） */
  --gold-rgb: 234, 199, 122;
  --gold-1: #fff2c8;
  --gold-2: #e0b968;
  --floor-k: 1;
  --rhud-rgb: 232, 197, 122;
  --rhud: rgba(232, 197, 122, .45);
  --rhud-dim: rgba(232, 197, 122, .19);
  --rgrid: rgba(212, 168, 75, .075);
  --rvig: rgba(8, 5, 1, .6);
}
/* ---------- 主题 3：炽红（亮色） ---------- */
html.theme-red {
  --bg-2: #f2f3f6;
  --panel: rgba(255, 255, 255, .7);
  --card: rgba(255, 255, 255, .9);
  --text: #1a1a1a;
  --text-dim: #666666;
  --text-faint: rgba(102, 102, 102, .6);
  --input: #ffffff;
  --border: rgba(193, 39, 45, .2);
  --accent: #c1272d;
  --accent-2: #e8565c;
  --accent-rgb: 193, 39, 45;
  --side-1: #7d1014;
  --side-2: #c1272d;
  --side-glow: rgba(255, 190, 190, .3);
  --particle: 255, 255, 255;
  --grid: rgba(255, 255, 255, .1);
  --shadow: 0 22px 56px rgba(30, 20, 22, .14);
  --ok: #15803d;
  --hud-rgb: 255, 226, 226;
  --hud: rgba(255, 226, 226, .48);
  --hud-dim: rgba(255, 226, 226, .2);
  --scan: rgba(255, 255, 255, .5);
  --vignette: rgba(70, 4, 8, .5);
  /* 炽红主题：金只出现在左侧深红面板上，故可保持暖亮金 */
  --gold-rgb: 212, 168, 84;
  --gold-1: #f0d089;
  --gold-2: #d4a84b;
  --floor-k: 1.9;                      /* 亮红底上网格对比不足 ⇒ 线条加重 */
  /* 炽红是浅底 ⇒ 右侧改用深红系 HUD 色，否则亮色线在 #f2f3f6 上完全消失 */
  --rhud-rgb: 193, 39, 45;
  --rhud: rgba(193, 39, 45, .34);
  --rhud-dim: rgba(193, 39, 45, .15);
  --rgrid: rgba(193, 39, 45, .085);
  --rvig: rgba(122, 118, 128, .17);       /* 炽红是浅底 ⇒ 暗角必须走浅灰，否则发脏 */
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
  font-family: -apple-system, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
  background: var(--bg-2); color: var(--text);
  overflow: hidden; -webkit-font-smoothing: antialiased;
  transition: background .45s ease, color .45s ease;
}
.mono { font-family: ui-monospace, "SF Mono", Consolas, "Liberation Mono", monospace; }

/* ---------- 左右分栏 ---------- */
/* 左右比例 1.19 : 1（左 54.3%）—— 左栏承载品牌视觉与主张，略宽更稳；
   ≤1280 回落到 1.10 : 1，避免右栏被压到装不下卡片。 */
/* ⚠️ --air：卡片竖向「呼吸量」。
   卡片是为了「与左栏等高」而被撑满的（见 .box 的 min-height:100%），视口越高、富余越多；
   固定 px 的间距吃不下它 ⇒ 顶部与底部会各空出一大片（实测 1440x900 各 113/91px）。
   这里用一条与视口高度**线性**的量统一驱动卡片内全部竖向间距：
     768px 高 → 0（各处数值与改动前完全一致，短屏不受影响）
     900px 高 → ≈1.0    1080px 高 → ≈2.4（上限 2.6）
   ⚠️ calc 里「长度 ÷ 数」得到的仍是**长度**，拿不到无单位数 ⇒ --air 是长度，
      各处一律写成 `calc(基准 + var(--air) * 系数)`。 */
.split { display: grid; grid-template-columns: 1.19fr 1fr; height: 100vh;
  --air: clamp(0px, calc((100vh - 768px) / 130), 2.6px);
  /* ⚠️ --card-in（#1064）：右卡在右栏内的**上下内缩量**。它决定卡片的上/下边缘落在哪，
     左栏内容块要与之对齐（见下方 #1064 段）。来源是右侧两个既有令牌之和：
       .main 的 --mpy = calc(20px + var(--air) * 8)
     + .console 的 --cpy = 16px（≤1080 时 12px）—— 卡片是 .stage 内被 .console 的
       padding 再内缩一层得到的，实测 1440×900 卡片上沿 = 28 + 16 = 44px。
     ⚠️ 这里是**按公式复算一份**而不是直接引用那两个变量：--mpy/--cpy 都定义在 .main 上
       （兄弟节点），左栏取不到。既有它们的常量（16/12），漂移风险由探针断言兜底 ——
       断言「左栏内容块上下沿 == 右卡上下沿」，任何一边改动对不上就会 FAIL。
     ⚠️ 已知边界：.stage 有 max-height:1040px 兜底（防超高超宽屏把卡片拉空）。
       视口高 >≈1122px 时卡片会封顶并在右栏内居中，此时左栏内容块按本令牌对齐的是
       **未封顶**的边界，两者会有 (剩余高度/2) 的偏差。1122px 以下（含 1080p）无偏差。 */
  --card-in: calc(20px + var(--air) * 8 + 16px); }
@media (max-width: 1280px) { .split { grid-template-columns: 1.10fr 1fr; } }
/* ≤1080：.main 把 --cpy 收到 12px（见下方响应式段）⇒ 此处同步，否则左栏会比卡片多缩 4px */
@media (max-width: 1080px) { .split { --card-in: calc(20px + var(--air) * 8 + 12px); } }

/* ==========================================================================
   左：品牌视觉区（多层科技视觉）
   ========================================================================== */
.side {
  /* 左栏内边距令牌：.side-inner 与顶部控制条 .side-top 共用，避免两处漂移 */
  --spx: 58px;
  --spy: 56px;
  position: relative; overflow: hidden; color: #fff;
  background:
    radial-gradient(760px 520px at 22% 16%, var(--side-glow), transparent 62%),
    linear-gradient(155deg, var(--side-1) 0%, var(--side-2) 100%);
  transition: background .45s ease;
}
.side::after {                       /* 斜向分隔光带 */
  content: ""; position: absolute; top: 0; bottom: 0; right: 0; width: 1px; z-index: 20;
  background: linear-gradient(180deg, transparent, rgba(255,255,255,.34), transparent);
}

/* ---- 装饰层通用 ---- */
.side .lay { position: absolute; inset: 0; pointer-events: none; }

/* ① 平面网格（随鼠标微位移） */
.mesh {
  z-index: 0; opacity: .85;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 44px 44px, 44px 44px;
  -webkit-mask-image: radial-gradient(115% 88% at 30% 38%, #000 22%, transparent 76%);
          mask-image: radial-gradient(115% 88% at 30% 38%, #000 22%, transparent 76%);
  transform: translate3d(calc(var(--mx, 0) * -10px), calc(var(--my, 0) * -10px), 0);
  transition: transform .5s cubic-bezier(.2,.7,.3,1);
}
/* ② 底部透视网格：改由 canvas 透视投影绘制（见脚本 drawFloor）。
   CSS 3D 变换实测会把网格压扁到面板中部并被内容盖住，故不用 .floor 元素。 */
/* ②b 地平线：canvas 绘制 */
/* ③ 斜向晶格（电路质感）—— 保持克制，过密会糊成灰雾 */
.lattice {
  z-index: 1; opacity: .42;
  background-image:
    repeating-linear-gradient(60deg, var(--hud-dim) 0 1px, transparent 1px 30px),
    repeating-linear-gradient(-60deg, var(--hud-dim) 0 1px, transparent 1px 30px);
  -webkit-mask-image: radial-gradient(66% 56% at 32% 38%, #000 4%, transparent 70%);
          mask-image: radial-gradient(66% 56% at 32% 38%, #000 4%, transparent 70%);
  transform: translate3d(calc(var(--mx, 0) * 8px), calc(var(--my, 0) * 8px), 0);
  transition: transform .5s cubic-bezier(.2,.7,.3,1);
}
/* ④ 粒子网络（canvas） */
#sideBg { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 2; }
/* ⑤ 雷达扫描扇面 */
.sweep {
  z-index: 3; left: -30%; top: -30%; right: -30%; bottom: -30%; inset: -30%;
  background: conic-gradient(from 0deg,
    transparent 0deg,
    rgba(var(--hud-rgb), .05) 14deg,
    rgba(var(--hud-rgb), .22) 46deg,
    rgba(var(--hud-rgb), .04) 74deg,
    transparent 108deg,
    transparent 360deg);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 0 52%, transparent 76%);
          mask-image: radial-gradient(circle at 50% 50%, #000 0 52%, transparent 76%);
  animation: sweepGo 8s linear infinite;
  opacity: .85;
}
@keyframes sweepGo { to { transform: rotate(360deg); } }
/* 炽红主题底色亮 ⇒ 雷达扇面会变成一大块浅色楔形，压弱 */
html.theme-red .sweep { opacity: .42; }
/* ⑥ 纵向数据流 */
.streams { z-index: 3; overflow: hidden; }
.streams i {
  position: absolute; top: -40%; left: var(--x); width: 1.6px; height: 32%;
  background: linear-gradient(180deg, transparent, rgba(var(--hud-rgb), .85) 55%, transparent);
  animation: streamGo var(--dur) linear infinite var(--del);
  opacity: var(--op, .5);
}
.streams i::after {                  /* 流头亮点 */
  content: ""; position: absolute; left: -.7px; bottom: 4px; width: 3px; height: 3px;
  border-radius: 50%; background: #fff; box-shadow: 0 0 10px 2px rgba(var(--hud-rgb), .9);
}
/* 金脉：从数据流中挑几条改金（黄金科技元素的"流通"语义） */
.streams i.gold {
  background: linear-gradient(180deg, transparent, rgba(var(--gold-rgb), .95) 55%, transparent);
}
.streams i.gold::after { box-shadow: 0 0 10px 2px rgba(var(--gold-rgb), .95); }
@keyframes streamGo { from { transform: translateY(0); } to { transform: translateY(360%); } }
/* ⑦ 扫描线（纵向掠扫）—— 必须"细"，宽了就读成雾带而不是扫描线 */
.scanline {
  z-index: 6; top: 0; height: 3.4%;
  background: linear-gradient(180deg,
    transparent, rgba(var(--hud-rgb), .18) 40%,
    var(--scan) 50%, rgba(var(--hud-rgb), .18) 60%, transparent);
  animation: scanGo 7s cubic-bezier(.55,.05,.45,.95) infinite;
  opacity: .8;
}
@keyframes scanGo { from { transform: translateY(-105%); } to { transform: translateY(3120%); } }
/* ⑧ 经纬刻度尺 */
.ruler-y {
  z-index: 7; left: 0; width: 8px; opacity: .5;
  background-image: repeating-linear-gradient(180deg, var(--hud) 0 1px, transparent 1px 22px);
  -webkit-mask-image: linear-gradient(180deg, #000 0 4%, #000 96%, transparent);
          mask-image: linear-gradient(180deg, #000 0 4%, #000 96%, transparent);
}
.ruler-x {
  z-index: 7; bottom: 0; height: 8px; opacity: .45;
  background-image: repeating-linear-gradient(90deg, var(--hud) 0 1px, transparent 1px 22px);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
/* ⑨ HUD 四角角标 */
.hud { z-index: 7; }
.hud span {
  position: absolute; width: 24px; height: 24px;
  border: 1.5px solid var(--hud); opacity: .8;
}
.hud span::after {
  content: ""; position: absolute; width: 3px; height: 3px; background: #fff;
  box-shadow: 0 0 8px 1px rgba(var(--hud-rgb), .9);
}
.hud .tl { top: 16px; left: 16px; border-right: 0; border-bottom: 0; }
.hud .tl::after { left: 3px; top: 3px; }
.hud .tr { top: 16px; right: 16px; border-left: 0; border-bottom: 0; }
.hud .tr::after { right: 3px; top: 3px; }
.hud .bl { bottom: 16px; left: 16px; border-right: 0; border-top: 0; }
.hud .bl::after { left: 3px; bottom: 3px; }
.hud .br { bottom: 16px; right: 16px; border-left: 0; border-top: 0; }
.hud .br::after { right: 3px; bottom: 3px; }
/* 四角中取两角改金：黄金点缀不必铺满，两处足够提"贵" */
.hud .tr, .hud .bl { border-color: rgba(var(--gold-rgb), .85); }
/* ⑩ 暗角 + 颗粒 —— 克制的暗角：过重会把底部透视网格整片吃掉 */
.vignette {
  z-index: 8;
  background:
    radial-gradient(122% 98% at 32% 34%, transparent 46%, var(--vignette) 100%),
    linear-gradient(90deg, transparent 74%, rgba(0,0,0,.28) 100%);
}
.grain {
  z-index: 9; opacity: .04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ⑪ 黄金点缀层：金箔扫光（斜向缓掠） */
.gold-sweep {
  z-index: 5; left: -45%; top: -60%; right: auto; bottom: auto;
  width: 62%; height: 220%;
  background: linear-gradient(90deg,
    transparent, rgba(var(--gold-rgb), .05) 42%,
    rgba(var(--gold-rgb), .16) 50%, rgba(var(--gold-rgb), .05) 58%, transparent);
  animation: goldGo 9.5s ease-in-out infinite;
  opacity: 0;
}
@keyframes goldGo {
  0%   { transform: translateX(0) rotate(16deg); opacity: 0; }
  22%  { opacity: 1; }
  70%  { opacity: 1; }
  100% { transform: translateX(300%) rotate(16deg); opacity: 0; }
}
/* ⑫ 黄金微粒（星尘）由 #sideBg 的 gold 通道绘制，见文末脚本 */

/* ---- 内容容器 ---- */
.side-inner {
  position: relative; z-index: 12; height: 100%;
  display: flex; flex-direction: column; justify-content: space-between;
  padding: var(--spy) var(--spx) 34px;
}
/* 顶部控制条：左＝节点读数，右＝三主题切换（按钮回到页面上方）
   —— 右栏顶部已被终端标题栏（含时钟）占满，横排放那里必然重叠，故落在左栏顶部。 */
.side-top {
  position: absolute; z-index: 13; top: 16px; left: var(--spx); right: var(--spx);
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  pointer-events: none;
}
.side-top > * { pointer-events: auto; }
.coords {
  font-size: 9.5px; letter-spacing: .22em; opacity: .5; white-space: nowrap;
  color: rgba(255, 255, 255, .85);
}
.coords b { color: #fff; font-weight: 500; }

/* 品牌头 */
.side-brand { display: flex; align-items: center; gap: 13px; }
.side-brand .mk {
  position: relative;
  width: 44px; height: 44px; border-radius: 12px; flex: none;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .1);
  border: 1px solid rgba(255, 255, 255, .3);
  box-shadow: 0 0 24px rgba(255, 255, 255, .16);
}
.side-brand .mk::after {             /* 呼吸光环 */
  content: ""; position: absolute; inset: -5px; border-radius: 16px;
  border: 1px solid rgba(var(--hud-rgb), .5);
  animation: mkPulse 3.2s ease-out infinite;
}
/* 品牌标记的黄金描边（旋转金环 —— 黄金科技元素之一） */
.side-brand .mk::before {
  content: ""; position: absolute; inset: -3px; border-radius: 15px; padding: 1.5px;
  background: conic-gradient(from 0deg,
    transparent 0deg 28deg,
    rgba(var(--gold-rgb), .95) 88deg,
    rgba(var(--gold-rgb), 0) 152deg 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: spin 5.5s linear infinite;
  pointer-events: none;
}
@keyframes mkPulse {
  0% { transform: scale(.9); opacity: .8; }
  100% { transform: scale(1.28); opacity: 0; }
}
.side-brand .mk svg { width: 25px; height: 25px; color: #fff; position: relative; }
/* #1073：系统标题字号由「信息配置 → 系统标题字号」(sys_title_size) 驱动。
   锚 = 16（= 该配置的默认值）⇒ 默认状态下 --st-ts 恰为 1，与改动前逐值等价。
   桌面基准由 15.5px 收敛为 16px（整数刻度，差 0.5px 不可辨），换取「填 16 ⇒ 恰好 16px」的语义自洽。
   ⚠️ 基准值必须与登录页模板里 `(_sts / 16)` 的除数一致，改一处必改另一处。 */
.side-brand h2 { font-size: calc(16px * var(--st-ts, 1)); font-weight: 600; letter-spacing: .06em; }
.side-brand p { font-size: 10.5px; letter-spacing: .24em; opacity: .68; margin-top: 3px; text-transform: uppercase; }

/* ---------- 中部：轨道环系统 + 主张 ---------- */
.hero { padding: 6px 0; }

.orbit {
  /* 直径是本组件唯一缩放锚点：内部所有同心元素都按 --o 的比例推导，
     这样超宽屏放大 / 窄屏缩小都只需改一个数，不会出现「环变了、核心没变」的错位。 */
  --o: 190px;
  position: relative; width: var(--o); height: var(--o); margin-bottom: 40px;
}
.orbit .rail { position: absolute; inset: 0; border-radius: 50%; }
.orbit .rail::before, .orbit .rail::after {  /* 十字准星 */
  content: ""; position: absolute; background: var(--hud-dim);
}
.orbit .rail::before { left: 50%; top: -12px; bottom: -12px; width: 1px; margin-left: -.5px; }
.orbit .rail::after  { top: 50%; left: -12px; right: -12px; height: 1px; margin-top: -.5px; }

/* #1065 准星刻度轴 .axis 与轨道伴星 .sat：只在左右式 ≥961px 现身（规则在文件末尾 #1065 段）。
   ⚠️ 必须在这里先声明 display:none 兜底 —— 它们长在 .orbit 里，而 .orbit 在 ≤960 堆叠式
      里同样存在（那时环只有 82px、顶部没有空档，画一根 170px 的标尺会直接插进品牌行）。 */
.orbit .axis, .orbit .sat, .orbit .sat-path { display: none; }

.orbit .ring { position: absolute; border-radius: 50%; border: 1px solid rgba(255,255,255,.22); }
.orbit .ring.r1 { inset: 0; border-top-color: rgba(255,255,255,.9); animation: spin 20s linear infinite; }
.orbit .ring.r2 { inset: calc(var(--o) * .1368);   /* 26/190 */ border-right-color: rgba(255,255,255,.8); animation: spin 14s linear infinite reverse; }
.orbit .ring.r3 { inset: calc(var(--o) * .2737);   /* 52/190 */ border-bottom-color: rgba(255,255,255,.95); animation: spin 8s linear infinite; }
.orbit .ring.r3::after {             /* 环上第二段高亮弧 */
  content: ""; position: absolute; inset: -1px; border-radius: 50%;
  border: 1px solid transparent; border-left-color: rgba(var(--hud-rgb), .9);
  animation: spin 5s linear infinite reverse;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* 倾斜椭圆轨道 + 卫星 */
.orbit .ell { position: absolute; inset: calc(var(--o) * .0316); border-radius: 50%; border: 1px solid var(--hud-dim); }
.orbit .ell i {
  position: absolute; top: -3.5px; left: 50%; margin-left: -3.5px;
  width: 7px; height: 7px; border-radius: 50%; background: #fff;
  box-shadow: 0 0 12px 2px rgba(var(--hud-rgb), .95);
}
.orbit .ell.e1 { animation: ellA 15s linear infinite; }
.orbit .ell.e2 { inset: calc(var(--o) * .1158); animation: ellB 10s linear infinite reverse; }
@keyframes ellA {
  from { transform: rotate(-26deg) scaleY(.34) rotate(0deg); }
  to   { transform: rotate(-26deg) scaleY(.34) rotate(360deg); }
}
@keyframes ellB {
  from { transform: rotate(48deg) scaleY(.4) rotate(0deg); }
  to   { transform: rotate(48deg) scaleY(.4) rotate(360deg); }
}

/* 向外扩散的能量波纹 */
.orbit .wave {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid rgba(var(--hud-rgb), .75);
  animation: waveGo 3.6s ease-out infinite;
}
.orbit .wave.w2 { animation-delay: 1.8s; }
@keyframes waveGo {
  0% { transform: scale(.44); opacity: .75; }
  100% { transform: scale(1.22); opacity: 0; }
}

/* 黄金弧环（绕核心巡游的金色能量环：靠 mask 只留最外一圈 1~2px） */
.orbit .gold-arc {
  position: absolute; border-radius: 50%; pointer-events: none;
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 96.2%, #000 96.9% 100%);
          mask-image: radial-gradient(closest-side, transparent 0 96.2%, #000 96.9% 100%);
}
.orbit .gold-arc.ga1 {
  inset: calc(var(--o) * -.0368);
  background: conic-gradient(from 0deg,
    rgba(var(--gold-rgb), 0) 0deg,
    var(--gold-1) 58deg,
    rgba(var(--gold-rgb), .9) 86deg,
    rgba(var(--gold-rgb), .18) 122deg,
    rgba(var(--gold-rgb), 0) 158deg,
    rgba(var(--gold-rgb), 0) 360deg);
  animation: spin 9s linear infinite;
  filter: drop-shadow(0 0 8px rgba(var(--gold-rgb), .75));
}
.orbit .gold-arc.ga2 {
  inset: calc(var(--o) * -.1263);
  background: conic-gradient(from 180deg,
    rgba(var(--gold-rgb), 0) 0deg,
    rgba(var(--gold-rgb), .6) 44deg,
    rgba(var(--gold-rgb), 0) 96deg,
    rgba(var(--gold-rgb), 0) 360deg);
  animation: spin 15s linear infinite reverse;
  opacity: .75;
}
/* 沿金弧滑行的光点（元素"在动"的关键：有实体在轨道上跑） */
.orbit .spark {
  position: absolute; inset: calc(var(--o) * -.0368); border-radius: 50%;
  animation: spin 9s linear infinite;
}
.orbit .spark b {
  position: absolute; top: -2.5px; left: 50%; margin-left: -2.5px;
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--gold-1);
  box-shadow: 0 0 10px 3px rgba(var(--gold-rgb), .95), 0 0 22px 6px rgba(var(--gold-rgb), .45);
}

/* 核心 */
.orbit .core {
  position: absolute; inset: calc(var(--o) * .3895);   /* 74/190 */ border-radius: 50%;
  background: radial-gradient(circle at 38% 34%, rgba(255,255,255,.42), rgba(255,255,255,.1) 62%, rgba(0,0,0,.2));
  border: 1px solid rgba(255,255,255,.5);
  display: grid; place-items: center;
  box-shadow: 0 0 38px rgba(255,255,255,.34), inset 0 0 22px rgba(255,255,255,.2),
              0 0 0 3px rgba(var(--gold-rgb), .16), 0 0 26px 4px rgba(var(--gold-rgb), .3);
  animation: breathe 3.4s ease-in-out infinite;
}
.orbit .core b {
  width: calc(var(--o) * .0632); height: calc(var(--o) * .0632); border-radius: 50%; background: #fff;
  box-shadow: 0 0 18px 3px rgba(255,255,255,.95);
}
@keyframes breathe { 0%,100% { opacity: .82; transform: scale(.97); } 50% { opacity: 1; transform: scale(1.04); } }

/* 轨道数据标签（HUD 读数感） */
.orbit .tag {
  position: absolute; display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 7px; border-radius: 4px; white-space: nowrap;
  font-size: 8.5px; letter-spacing: .16em;
  color: rgba(255,255,255,.88);
  background: rgba(var(--hud-rgb), .1);
  border: 1px solid var(--hud-dim);
  animation: floaty 5s ease-in-out infinite;
}
.orbit .tag::before {
  content: ""; width: 3px; height: 3px; border-radius: 50%;
  background: rgb(var(--hud-rgb)); box-shadow: 0 0 6px rgba(var(--hud-rgb), .95);
}
.orbit .tag.t1 { top: -10px; left: -26px; animation-delay: -.2s; }
.orbit .tag.t2 { bottom: 20%; right: -34px; animation-delay: -3.1s; }
@keyframes floaty { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }

/* 主张 */
.kicker {
  display: inline-flex; align-items: center; gap: 9px; margin-bottom: 14px;
  font-size: 10px; letter-spacing: .26em; text-transform: uppercase;
  color: rgba(255,255,255,.62);
}
.kicker::before {
  content: "//"; color: rgb(var(--hud-rgb)); letter-spacing: 0; font-weight: 700;
}
.kicker em {
  font-style: normal; padding: 2px 7px; border-radius: 4px;
  background: rgba(var(--hud-rgb), .12); border: 1px solid var(--hud-dim);
  color: rgba(255,255,255,.9); letter-spacing: .16em;
}
/* 标题字号锚定「左栏内容宽」，而不是整页 vw。
   原写法 clamp(24px, 2.6vw, 36px) 用视口宽推栏内字号，而标题只活在占 54% 宽的左栏里：
     · 1280~1385px 段最饱（约 67% 宽），到 ≥1600 却封顶 40px ⇒ 2560 屏只剩 36%，缩成一小块；
     · ≤1080 断点把它硬写成 24px，而 1081px 时是 28.1px ⇒ 相邻 1px 跳 17%；
     · ≥1600 断点又从 36px 跳到 40px。
   实测（outputs/_probe_login_hero_fit.py）：占比在 36%~70% 之间震荡，无稳定区间。
   改为 container query：.hero 声明 container-type 后，5.5cqw 恒为其内容宽的 5.5%
     ⇒ 各级视口下标题都稳定占内容宽约 62%，且 1080 / 1600 两处跳变自然消失
       （原来那两条断点字号必须一并撤掉，否则会盖住本规则）。
   clamp 上限 52px 仅在 >2020px 视口生效，避免超宽屏把标题推到失衡的绝对尺寸。
   回退：不支持容器查询的浏览器保留上面那条 vw 表达式（@supports 内才会被覆盖）。

   ⚠️ #1057 字号可配置：下面**四档字号**（桌面回退 / 桌面容器查询 / ≤900 / ≤520）
      全部乘同一个无单位系数 --hero-ts（由 templates/login.html 按
      sys_config.login_hero_title_size 注入 :root，默认 1）。
        · 系数默认 1 ⇒ 与未做配置前**逐值等价**，不存在隐性视觉回归；
        · 「每一档都乘」是硬要求：只改其中一档，会让另外几档视口「改了配置没反应」；
        · 用系数而非注入固定 px，是为了让小屏跟着等比缩小 —— 否则小屏配上大字号
          会把标题撑到折行 / 破版。
      兜底 var(--hero-ts, 1)：万一注入的 <style> 缺失（模板异常），退回未缩放的原值。 */
.hero h1 {
  font-size: clamp(calc(24px * var(--hero-ts, 1)), calc(2.6vw * var(--hero-ts, 1)), calc(36px * var(--hero-ts, 1)));
  line-height: 1.3;
  font-weight: 600; letter-spacing: .02em; margin-bottom: 14px;
}
@supports (font-size: 1cqw) {
  .hero { container-type: inline-size; }
  .hero h1 { font-size: clamp(calc(24px * var(--hero-ts, 1)), calc(5.5cqw * var(--hero-ts, 1)), calc(52px * var(--hero-ts, 1))); }
}
.hero h1 em {
  font-style: normal;
  /* 金色流光掠过标题（元素自身在动，而非仅背景动） */
  background: linear-gradient(100deg,
    #fff 6%, rgba(255,255,255,.62) 30%,
    var(--gold-1) 46%, rgba(var(--gold-rgb), .9) 52%,
    rgba(255,255,255,.62) 70%, #fff 94%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: titleShine 8s ease-in-out infinite;
}
@keyframes titleShine {
  0%, 100% { background-position: 130% 0; }
  50%      { background-position: -30% 0; }
}
.hero .sub { font-size: 13px; line-height: 1.85; opacity: .74; max-width: 430px; }

/* 两列网格（#1057）：特性条目从固定 3 条改为可配置、最多 6 条。
   单列铺 6 条会把左栏竖向拉长、破坏与右栏卡片的等高关系；两列后 6 条恰好 3 行，
   占用高度与原来的「3 条单列」一致（实测 1440x900 下 feats 高度仍约 90px）。
   minmax(0,1fr) + 子项 min-width:0 是网格不溢出的前提：长条目在列内折行，
   而不是把列撑宽、把 .side 的 overflow:hidden 裁到看不见。 */
.feats { margin-top: 30px; display: grid; gap: 13px 24px;
         grid-template-columns: repeat(2, minmax(0, 1fr)); }
.feat { display: flex; align-items: flex-start; gap: 11px; font-size: 12.5px; opacity: .92;
        min-width: 0; line-height: 1.5; }
/* 条目文案：flex 里裸文本节点是匿名 flex item，min-width 默认 auto ⇒ 内容不会收缩，
   超长条目会撑破列宽并把整块左栏顶出横向滚动。包一层后显式允许收缩 + 断词。 */
.feat .tx { min-width: 0; overflow-wrap: anywhere; }
.feat .no {
  font-size: 9.5px; letter-spacing: .06em; opacity: .55; width: 16px; flex: none;
  color: rgb(var(--hud-rgb));
}
.feat .tick {
  width: 20px; height: 20px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  background: rgba(255, 255, 255, .13); border: 1px solid rgba(255, 255, 255, .26);
  animation: tickGlow 3.6s ease-in-out infinite;
}
.feat:nth-child(2) .tick { animation-delay: .7s; }
.feat:nth-child(3) .tick { animation-delay: 1.4s; }
@keyframes tickGlow {
  0%, 100% { box-shadow: inset 0 0 10px rgba(var(--hud-rgb), .18); border-color: rgba(255,255,255,.26); }
  50%      { box-shadow: inset 0 0 12px rgba(var(--gold-rgb), .3), 0 0 14px rgba(var(--gold-rgb), .3); border-color: rgba(var(--gold-rgb), .62); }
}
.feat .tick svg { width: 11px; height: 11px; color: #fff; }

/* 侧栏底部状态 + 实时指标 */
.side-foot {
  display: flex; align-items: center; gap: 13px; flex-wrap: wrap;
  font-size: 10.5px; letter-spacing: .14em; opacity: .62;
}
.side-foot .live { display: flex; align-items: center; gap: 7px; }
.side-foot .live b {
  width: 6px; height: 6px; border-radius: 50%; background: #fff;
  box-shadow: 0 0 10px rgba(255,255,255,.95); animation: blink 2.2s ease-in-out infinite;
}
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: .28; } }
.side-foot .meter {
  display: inline-flex; align-items: center; gap: 6px;
}
.side-foot .meter span {
  display: inline-block; width: 26px; height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, rgb(var(--hud-rgb)) var(--v, 60%), rgba(255,255,255,.18) var(--v, 60%));
}
.side-foot .sep { opacity: .4; }

/* ---- 入场序列：内容元素依次"亮起" ---- */
@keyframes riseIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}
.side-brand { animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .05s both; }
.coords     { animation: riseIn .7s ease .18s both; }
.hero .orbit { animation: riseIn .85s cubic-bezier(.2,.8,.3,1) .22s both; }
.kicker  { animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .34s both; }
.hero h1 { animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .42s both; }
.hero .sub { animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .5s both; }
.feats .feat:nth-child(1) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .6s both; }
.feats .feat:nth-child(2) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .68s both; }
.feats .feat:nth-child(3) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .76s both; }
.side-foot { animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .84s both; }

/* ==========================================================================
   右：控制台表单区（v3 · 终端化重构）
   纵深层次（自下而上）：
     0  .main::before 主色辉光
     1  .main::after  网格底纹
     2  .dl           装饰层（流动光带 → 同心 HUD 圆环 → 数据点 → 刻度尺）
     3  .stage        3D 倾斜层（JS 驱动，无 transform 动画以免冲突）
     4  .console      控制台框架（虚线框 + 金角标 + 金环巡游 + 竖排读数）
     4b .d-halo       卡片背后的光柱（让卡片「发光」，而不是漂在网格上）
     4c .d-dial       左下角仪表刻度盘（缓慢自转 + 亮弧巡游）
     4d .d-reticle    卡片外圈四角瞄准框（与 .console 金角标形成双层框 = 纵深）
     5  .box          终端窗口（标题栏 + 表单 + 底部信息行；min-height:100% 与左栏等高）
   ⚠️ 主题切换已移出右栏，见左栏「顶部控制条 .side-top」
   ⚠️ 右侧一律用 --rhud-rgb / --rgrid，不得复用 --hud-rgb：
      炽红主题下 --hud-rgb 是亮色(255,226,226)，落在浅灰底上完全不可见。
   ========================================================================== */
.main {
  /* 右栏几何令牌：.stage / .console / .d-halo / .d-reticle 共用，改一处即可整体缩放 */
  --cardw: 460px;                 /* 卡片最大宽度 */
  --mpy: calc(20px + var(--air) * 8); --mpx: 40px;   /* 右栏上下 / 左右留白（上下随 --air 略增） */
  --cpy: 16px; --cpx: 24px;       /* 控制台框架上下 / 左右留白 */
  position: relative; display: flex; align-items: center; justify-content: center;
  /* 上下留白刻意压小：右栏卡片要撑到与左侧面板「等高」，不能让卡片又矮又飘 */
  padding: var(--mpy) var(--mpx);
  /* ⚠️ overflow-x 必须**显式**写 hidden（#1062）：只声明 overflow-y 时，按 CSS 规范
     另一方向的 visible 会被计算为 auto ⇒ 右栏悄悄变成一个「可横向滚动」的容器。
     而右栏的「卡片 3D 倾斜 + 装饰层视差」是随鼠标位置实时位移的
     （.stage translate3d(±4px, ±3px)、.dl translate3d(--dx ±8px, --dy ±6px)），
     在右栏余量不足时（左右 7:3 版式、窄视口 —— 卡片正好铺满内容宽）这些位移
     会把内容撑出容器 ⇒ 表现为「鼠标在登录卡片上一动，底下就冒出横向滑动条」。
     实测（#1062 诊断）：split73 @1600 静止 scrollWidth=480，鼠标在卡片上滚动后 484。
     它们是纯视觉效果，裁掉即可：卡片位移始终落在 40px 的 padding 内、不会被裁，
     只有装饰层的渐变边缘会被裁掉几像素（本就淡出，无观感影响）。 */
  overflow-y: auto; overflow-x: hidden;
  perspective: 1500px; perspective-origin: 50% 46%;
}
.main::before {                      /* 双向辉光 + 四周暗角（暗角让卡片「浮」起来，而非平贴） */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(620px 430px at 78% 6%, rgba(var(--accent-rgb), .11), transparent 68%),
    radial-gradient(520px 400px at 8% 94%, rgba(var(--rhud-rgb), .075), transparent 70%),
    radial-gradient(142% 124% at 50% 46%, transparent 42%, var(--rvig) 100%);
}
.main::after {                       /* 点阵 + 稀疏主线（走 --rgrid：炽红浅底同样可见） */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    radial-gradient(circle, var(--rgrid) 1px, transparent 1.4px),
    linear-gradient(var(--rgrid) 1px, transparent 1px),
    linear-gradient(90deg, var(--rgrid) 1px, transparent 1px);
  background-size: 17px 17px, 68px 68px, 68px 68px;
  -webkit-mask-image: radial-gradient(98% 80% at 62% 32%, #000 0 30%, transparent 78%);
          mask-image: radial-gradient(98% 80% at 62% 32%, #000 0 30%, transparent 78%);
  opacity: .92;
}
.main > * { position: relative; z-index: 3; }

/* ---- ④ 卡片背后的光柱：让卡片「发光」，而不是漂在网格上（本轮核心） ---- */
.d-halo {
  position: absolute; top: -6%; bottom: -6%; left: 50%; transform: translateX(-50%);
  width: 100%; max-width: calc(var(--cardw) + 240px);
  background:
    radial-gradient(46% 32% at 50% 46%, rgba(var(--rhud-rgb), .17), transparent 74%),
    linear-gradient(90deg, transparent,
      rgba(var(--rhud-rgb), .09) 18%, rgba(var(--rhud-rgb), .09) 82%, transparent);
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent);
          mask-image: linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent);
  filter: blur(5px);
}

/* ---- ⑤ 仪表刻度盘：左下角、部分出画 ⇒ 读作「面板外的仪器」而不是图案 ---- */
.d-dial {
  position: absolute; left: -212px; bottom: -236px;
  width: 640px; height: 640px; border-radius: 50%;
}
.d-dial::before {                    /* 外圈长刻度 */
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: repeating-conic-gradient(from 0deg, var(--rhud) 0 .32deg, transparent .32deg 3deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 90%, #000 91% 100%);
          mask-image: radial-gradient(closest-side, transparent 0 90%, #000 91% 100%);
  opacity: .5; animation: spin 140s linear infinite;
}
.d-dial::after {                     /* 内圈细环 */
  content: ""; position: absolute; inset: 86px; border-radius: 50%;
  border: 1px dashed var(--rhud-dim); opacity: .8;
}
.d-dial i {                          /* 沿盘面巡游的亮弧 */
  position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0deg 288deg,
    rgba(var(--rhud-rgb), .5) 326deg, transparent 352deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 95.4%, #000 96.2% 100%);
          mask-image: radial-gradient(closest-side, transparent 0 95.4%, #000 96.2% 100%);
  animation: spin 52s linear infinite;
}

/* ---- ⑥ 卡片外圈瞄准框：比 .console 虚线框再退 15px ⇒ 双层框制造纵深 ---- */
.d-reticle {
  position: absolute; width: auto; margin: 0 auto;
  top: calc(var(--mpy) + var(--cpy)); bottom: calc(var(--mpy) + var(--cpy));
  left: var(--mpx); right: var(--mpx);
  max-width: calc(var(--cardw) - var(--cpx) * 2);
}
.d-reticle span {
  position: absolute; width: 34px; height: 34px;
  border: 0 solid rgba(var(--rhud-rgb), .4);
}
.d-reticle .y1 { top: -15px; left: -15px; border-left-width: 1px; border-top-width: 1px; border-top-left-radius: 9px; }
.d-reticle .y2 { top: -15px; right: -15px; border-right-width: 1px; border-top-width: 1px; border-top-right-radius: 9px; }
.d-reticle .y3 { bottom: -15px; left: -15px; border-left-width: 1px; border-bottom-width: 1px; border-bottom-left-radius: 9px; }
.d-reticle .y4 { bottom: -15px; right: -15px; border-right-width: 1px; border-bottom-width: 1px; border-bottom-right-radius: 9px; }

/* ---- 装饰层（纯背景，不接收指针；整体随鼠标轻微视差） ---- */
.main > .dl {
  position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden;
  transform: translate3d(calc(var(--dx, 0) * 1px), calc(var(--dy, 0) * 1px), 0);
  transition: transform .55s cubic-bezier(.2, .7, .3, 1);
}

/* ① 斜向流动光带 ×2：主色一条 + 金色一条（呼应左侧金脉） */
.d-beam b {
  position: absolute; top: -45%; bottom: -45%; left: 0; width: 170px;
  background: linear-gradient(90deg, transparent, rgba(var(--rhud-rgb), .055), transparent);
  animation: beamGo var(--bd, 18s) linear infinite var(--bdel, 0s);
}
.d-beam b.g { background: linear-gradient(90deg, transparent, rgba(var(--gold-rgb), .05), transparent); }
/* ⚠️ 炽红是浅底 ⇒ 5% 金叠在浅灰上会读成「脏黄抹痕」；浅底主题改用主色系，金只留在左栏深红面板上 */
html.theme-red .d-beam b.g {
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .055), transparent);
}
@keyframes beamGo {
  from { transform: translateX(-420px) rotate(17deg); }
  to   { transform: translateX(1180px) rotate(17deg); }
}

/* ② 同心 HUD 圆环：以卡片中心为圆心向外辐射，慢速反向旋转 + 脉冲波 */
.d-rings i {
  position: absolute; left: 50%; top: 50%; border-radius: 50%;
  border: 1px solid var(--rhud-dim); pointer-events: none;
}
.d-rings i.r1 { width: 660px; height: 660px; border-style: dashed; border-color: rgba(var(--rhud-rgb), .26); animation: ringA 64s linear infinite; }
.d-rings i.r2 { width: 880px; height: 880px; border-color: rgba(var(--rhud-rgb), .085); }
.d-rings i.r3 { width: 1080px; height: 1080px; border-style: dashed; border-color: rgba(var(--rhud-rgb), .06); animation: ringB 128s linear infinite; }
.d-rings i.pw { width: 620px; height: 620px; border-color: rgba(var(--rhud-rgb), .45); animation: ringPulse 5.6s ease-out infinite; }
@keyframes ringA { from { transform: translate(-50%,-50%) rotate(0deg); } to { transform: translate(-50%,-50%) rotate(360deg); } }
@keyframes ringB { from { transform: translate(-50%,-50%) rotate(360deg); } to { transform: translate(-50%,-50%) rotate(0deg); } }
@keyframes ringPulse {
  0%   { transform: translate(-50%,-50%) scale(.66); opacity: .5; }
  100% { transform: translate(-50%,-50%) scale(1.06); opacity: 0; }
}

/* ③ 悬浮数据点 ×6（两枚金） */
.d-dots i {
  position: absolute; width: 3px; height: 3px; border-radius: 50%;
  background: rgb(var(--rhud-rgb)); box-shadow: 0 0 9px 1.5px rgba(var(--rhud-rgb), .75);
  animation: floaty var(--fd, 6s) ease-in-out infinite var(--fdel, 0s);
}
.d-dots i.g { background: rgb(var(--gold-rgb)); box-shadow: 0 0 10px 2px rgba(var(--gold-rgb), .8); }

/* ④ 上下刻度尺 */
.d-ruler { position: absolute; left: 7%; right: 7%; height: 5px; opacity: .55; }
.d-ruler.t { top: 22px; background-image: repeating-linear-gradient(90deg, var(--rhud-dim) 0 1px, transparent 1px 16px); }
.d-ruler.b {
  bottom: 22px; background-image: repeating-linear-gradient(90deg, var(--rhud-dim) 0 1px, transparent 1px 16px);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 20%, #000 80%, transparent);
}

/* ---- .stage：3D 倾斜层（入场动画一律放在 .console，避免 transform 互相覆盖） ----
   height:100% 让右侧卡片吃满右栏高度，与左侧面板视觉等高；max-height 防高屏被拉到空旷。 */
.stage {
  position: relative; z-index: 4;
  width: 100%; max-width: var(--cardw);
  /* 兜底 1040：1080 高屏上卡片仍占左栏 93%（900 兜底时只有 89.6%，又回到「矮一截」） */
  height: 100%; max-height: 1040px;
  transform-style: preserve-3d;
  transition: transform .45s cubic-bezier(.2,.7,.3,1);
}

/* ---- .console：控制台框架 ---- */
.console {
  position: relative; width: 100%; height: 100%; padding: var(--cpy) var(--cpx);
  animation: boxIn .75s cubic-bezier(.2,.8,.25,1) both;
}
.console::before {                   /* 虚线 HUD 外框 */
  content: ""; position: absolute; inset: 0; border-radius: 24px; pointer-events: none;
  border: 1px dashed var(--rhud-dim);
}
@property --ang2 { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
.console::after {                    /* 金色巡游光（外环，比卡片内环更慢更淡） */
  content: ""; position: absolute; inset: -1px; border-radius: 25px; pointer-events: none;
  padding: 1.2px;
  background: conic-gradient(from var(--ang2),
    transparent 0deg 58deg,
    rgba(var(--gold-rgb), .8) 92deg,
    transparent 130deg 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: angGo2 12s linear infinite;
}
@keyframes angGo2 { to { --ang2: 360deg; } }
/* 面板四角金角标（与左侧「两角金」呼应，此处为独立层级故四角统一） */
.console .cf {
  position: absolute; width: 19px; height: 19px; pointer-events: none;
  border: 1.6px solid rgba(var(--gold-rgb), .78);
}
.console .cf.a { top: -1px; left: -1px; border-right: 0; border-bottom: 0; border-radius: 7px 0 0 0; }
.console .cf.b { top: -1px; right: -1px; border-left: 0; border-bottom: 0; border-radius: 0 7px 0 0; }
.console .cf.c { bottom: -1px; left: -1px; border-right: 0; border-top: 0; border-radius: 0 0 0 7px; }
.console .cf.d { bottom: -1px; right: -1px; border-left: 0; border-top: 0; border-radius: 0 0 7px 0; }
/* 竖排读数（面板左右内侧，极淡） */
.console .cl {
  position: absolute; top: 50%; transform: translateY(-50%);
  writing-mode: vertical-rl; font-size: 8px; letter-spacing: .3em;
  color: var(--text-faint); pointer-events: none; white-space: nowrap;
}
.console .cl.l { left: 6px; }
.console .cl.r { right: 6px; }

/* 主题切换：随顶部控制条落在左栏右上角（玻璃胶囊 + 三色圆点）
   左栏三种主题下恒为深底 ⇒ 只用白色透明层即可，不必引入主题令牌。 */
.theme-switch {
  display: flex; flex-direction: row; align-items: center; gap: 7px;
  padding: 4px 6px; border-radius: 999px;
  background: rgba(255, 255, 255, .09); border: 1px solid rgba(255, 255, 255, .2);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .22);
  animation: riseIn .7s ease .12s both;
}
.ts-btn {
  width: 22px; height: 22px; border-radius: 50%; cursor: pointer;
  border: 1.5px solid transparent; padding: 0;
  transition: transform .2s ease, border-color .2s ease;
}
.ts-btn:hover { transform: scale(1.12); }
/* ⚠️ 现在落在左栏深底上 ⇒ 选中环不能用 --accent：炽红主题下 --accent 是深红，压在深红底上不可见 */
.ts-btn.active { border-color: rgba(255, 255, 255, .92); box-shadow: 0 0 0 2px rgba(255, 255, 255, .26); }
.ts-btn[data-theme="default"] { background: linear-gradient(135deg, #0a1428, #00a8ff); }
.ts-btn[data-theme="gold"]    { background: linear-gradient(135deg, #1a1812, #d4a84b); }
.ts-btn[data-theme="red"]     { background: linear-gradient(135deg, #ffffff, #c1272d); }

/* ---- 终端窗口（原「表单卡」） ----
   min-height:100% 撑满右栏 ⇒ 与左侧面板视觉等高（炽红浅底上这一条尤其明显）；
   内部 flex column + 两处 margin-top:auto ⇒ 表单在窗口内垂直居中，而非挤在顶部。 */
.box {
  position: relative; z-index: 3; width: 100%; min-height: 100%;
  display: flex; flex-direction: column;
  /* 控件高度：48px 起步、随 --air 长到 ~74px（1080 屏）。卡片撑高后控件偏小会让留白更显空。 */
  --fh: calc(48px + var(--air) * 11);
  /* 底部留白略大于顶部：把富余空间偏一点到卡片下沿，顶部不至于空成一片
     （.box-head 与 .box-note 的 auto 上下平分剩余量）。 */
  padding: 0 32px calc(24px + var(--air) * 6); border-radius: 18px;
  background: var(--card); border: 1px solid var(--border);
  box-shadow: var(--shadow);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  animation: boxIn .75s cubic-bezier(.2, .8, .25, 1) .14s both;
  transition: background .45s ease, border-color .45s ease, box-shadow .45s ease;
}
@keyframes boxIn { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
.box::before {                       /* 顶部光条 */
  content: ""; position: absolute; top: -1px; left: 18%; right: 18%; height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), var(--accent-2), transparent);
  box-shadow: 0 0 16px rgba(var(--accent-rgb), .6);
}

/* ---- 终端标题栏：把「网页表单」变成「控制台窗口」的关键一笔 ---- */
.box-bar {
  display: flex; align-items: center; gap: 10px;
  height: 40px; margin: 0 -32px 22px; padding: 0 16px;
  border-radius: 18px 18px 0 0; border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, rgba(var(--rhud-rgb), .13), rgba(var(--rhud-rgb), .02));
  animation: barIn .5s ease .34s both;
}
.box-bar .dots { display: flex; gap: 5px; flex: none; }
.box-bar .dots i { width: 7px; height: 7px; border-radius: 50%; background: rgba(var(--rhud-rgb), .32); }
.box-bar .dots i:first-child {
  background: rgb(var(--rhud-rgb)); box-shadow: 0 0 8px rgba(var(--rhud-rgb), .85);
  animation: blink 2.4s ease-in-out infinite;
}
.box-bar .dots i:last-child { background: rgba(var(--gold-rgb), .85); box-shadow: 0 0 8px rgba(var(--gold-rgb), .7); }
.box-bar .ttl { flex: 1; font-size: 9.5px; letter-spacing: .2em; color: var(--text-dim); white-space: nowrap; }
.box-bar .tm { flex: none; font-size: 9.5px; letter-spacing: .1em; color: var(--text-faint); }
@keyframes barIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* 环绕光弧（沿卡片描边巡游） */
@property --ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
.box::after {
  content: ""; position: absolute; inset: -1px; border-radius: 19px; pointer-events: none;
  padding: 1px;
  background: conic-gradient(from var(--ang),
    transparent 0deg 54deg,
    rgba(var(--accent-rgb), .85) 82deg,
    transparent 116deg 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: angGo 7s linear infinite;
}
@keyframes angGo { to { --ang: 360deg; } }
/* 卡片四角标已由「终端标题栏 + 控制台金角标」取代，避免三层四角同时出现。 */

/* ---- 卡片头部徽标带 ----
   卡片被撑到与左栏等高后，「终端标题栏 → 欢迎回来」之间会空出一大片（实测约 130px）。
   解决分两步：① 这条头带自身消耗掉一部分高度；② 剩余量由它和 .box-note 的 auto 上下平分。
   ⚠️ 徽标与左栏品牌**同一枚**（同源识别），但**不重复系统名称** —— 否则文字密度回升，
      与上一轮「精简文案」的方向相反。
   ⚠️ ≤960 收起：窄屏卡片高度由内容决定、没有留白要填，且品牌就在正上方。 */
.box-head {
  display: flex; align-items: center; gap: 14px;
  margin-top: auto; margin-bottom: calc(20px + var(--air) * 7);
  animation: barIn .5s ease .40s both;
}
.box-head .bh-mk {
  position: relative; flex: none;
  width: calc(44px + var(--air) * 4); height: calc(44px + var(--air) * 4); border-radius: 15px;
  display: grid; place-items: center; color: var(--accent);
  border: 1px solid var(--border);
  background: linear-gradient(150deg, rgba(var(--accent-rgb), .16), rgba(var(--accent-rgb), .03));
  box-shadow: inset 0 0 18px rgba(var(--accent-rgb), .16), 0 6px 18px rgba(0, 0, 0, .16);
  transition: background .45s ease, border-color .45s ease, box-shadow .45s ease;
}
.box-head .bh-mk::after {            /* 右上角金色状态点：与左栏金元素同源 */
  content: ""; position: absolute; right: -3px; top: -3px;
  width: 8px; height: 8px; border-radius: 50%; background: rgb(var(--gold-rgb));
  box-shadow: 0 0 9px rgba(var(--gold-rgb), .9); animation: blink 2.8s ease-in-out infinite;
}
.box-head .bh-mk svg { width: calc(22px + var(--air) * 2); height: calc(22px + var(--air) * 2);
  position: relative; z-index: 1; }
.box-head .bh-line {                 /* 由徽标向右淡出的引导线：把空带读成「有内容」而不是空白 */
  flex: 1 1 auto; min-width: 0; height: 1px;
  background: linear-gradient(90deg, rgba(var(--accent-rgb), .55),
                              rgba(var(--accent-rgb), .08) 62%, transparent);
}

.box h3 {
  font-size: calc(20px + var(--air) * 1); font-weight: 600; letter-spacing: .04em;
  margin-top: 0;                    /* 顶部 auto 已交给 .box-head，这里必须归零 */
  animation: barIn .5s ease .42s both;
}
.box .cap {
  margin: 10px 0 calc(30px + var(--air) * 18); font-size: 12px;
  color: var(--text-dim); letter-spacing: .05em;
  animation: barIn .5s ease .48s both;
}
.box form { animation: barIn .55s ease .54s both; }
.box .sep-or { animation: barIn .5s ease .62s both; }
.box .passkey { animation: barIn .5s ease .66s both; }

.field { position: relative; margin-bottom: calc(19px + var(--air) * 12); }
.field > .ico {
  position: absolute; left: 15px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--text-faint); pointer-events: none;
  transition: color .25s ease, filter .25s ease;
}
.field input {
  width: 100%; height: var(--fh); padding: 0 15px 0 43px;
  font-size: 14px; color: var(--text); background: var(--input);
  border: 1px solid var(--border); border-radius: 11px; outline: none;
  caret-color: var(--accent);
  /* 内阴影制造「仪器插槽」的凹陷感，而非一张平贴的表单 */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), inset 0 0 20px rgba(0, 0, 0, .16);
  transition: border-color .25s ease, box-shadow .25s ease, background .45s ease;
}
html.theme-red .field input { box-shadow: inset 0 1px 2px rgba(30, 20, 22, .07); }
.field input::placeholder { color: var(--text-faint); }
.field input:focus {
  border-color: var(--accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), inset 0 0 20px rgba(0, 0, 0, .16),
              0 0 0 3px rgba(var(--accent-rgb), .14), 0 0 20px rgba(var(--accent-rgb), .16);
}
html.theme-red .field input:focus {
  box-shadow: inset 0 1px 2px rgba(30, 20, 22, .07),
              0 0 0 3px rgba(var(--accent-rgb), .14), 0 0 20px rgba(var(--accent-rgb), .16);
}
.field input:focus ~ .ico { color: var(--accent); filter: drop-shadow(0 0 6px rgba(var(--accent-rgb), .85)); }
.field::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  transform: scaleX(0); transform-origin: 50%; transition: transform .35s ease;
}
.field:focus-within::after { transform: scaleX(1); }

.captcha-row { display: flex; gap: 10px; }
.captcha-row input { flex: 1; letter-spacing: .3em; }
.captcha-box {
  position: relative; width: 104px; flex: none; height: var(--fh); border-radius: 11px; cursor: pointer;
  border: 1px solid var(--border); background: var(--input);
  display: grid; place-items: center; user-select: none; overflow: hidden;
  font-family: ui-monospace, Consolas, monospace; font-size: 19px; letter-spacing: .16em;
  color: var(--accent); transition: border-color .25s ease;
}
.captcha-box:hover { border-color: var(--accent); }
.captcha-box::after {                /* 干扰斜线 */
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .35;
  background: repeating-linear-gradient(115deg, rgba(var(--accent-rgb), .35) 0 1px, transparent 1px 9px);
}

.row-between { display: flex; align-items: center; justify-content: space-between;
  margin: calc(8px + var(--air) * 2) 0 calc(24px + var(--air) * 10); font-size: 12.5px; }
.chk { display: flex; align-items: center; gap: 7px; cursor: pointer; color: var(--text-dim); }
.chk input { appearance: none; width: 15px; height: 15px; border-radius: 4px; cursor: pointer;
  border: 1.5px solid var(--border); background: var(--input); position: relative; }
.chk input:checked { background: var(--accent); border-color: var(--accent); }
.chk input:checked::after {
  content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
  border: solid #fff; border-width: 0 1.6px 1.6px 0; transform: rotate(42deg);
}
/* appearance:none 的自定义勾选框不会自带可见焦点 ⇒ 键盘用户按 Tab 到它时看不出选中位置 */
.chk input:focus-visible { outline: 2px solid rgba(var(--accent-rgb), .6); outline-offset: 2px; }
.chk:hover { color: var(--text); }
.link { color: var(--text-dim); text-decoration: none; transition: color .2s ease; }
.link:hover { color: var(--accent); }

.submit {
  position: relative; width: 100%; height: var(--fh); cursor: pointer; overflow: hidden;
  border: 0; border-radius: 11px;
  font-size: 14.5px; font-weight: 600; letter-spacing: .1em; color: #fff;
  background: linear-gradient(120deg, var(--accent), var(--accent-2));
  box-shadow: 0 8px 24px rgba(var(--accent-rgb), .3);
  transition: transform .18s ease, box-shadow .25s ease, filter .25s ease;
}
.submit:hover { transform: translateY(-1.5px); box-shadow: 0 12px 32px rgba(var(--accent-rgb), .42); filter: brightness(1.06); }
.submit:active { transform: translateY(0) scale(.994); }
.submit .txt { position: relative; z-index: 2; }
/* HUD 四角：让按钮读起来像「执行指令」而不是普通表单按钮 */
.submit .sc {
  position: absolute; width: 12px; height: 12px; z-index: 3; pointer-events: none;
  border: 0 solid rgba(255, 255, 255, .8);
}
.submit .sc.tl { top: 6px; left: 6px; border-left-width: 1.5px; border-top-width: 1.5px; }
.submit .sc.tr { top: 6px; right: 6px; border-right-width: 1.5px; border-top-width: 1.5px; }
.submit .sc.bl { bottom: 6px; left: 6px; border-left-width: 1.5px; border-bottom-width: 1.5px; }
.submit .sc.br { bottom: 6px; right: 6px; border-right-width: 1.5px; border-bottom-width: 1.5px; }
/* 认证进度条（点击后演示「指令执行中」） */
.submit .bar {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 0; z-index: 3;
  background: rgba(255, 255, 255, .92); box-shadow: 0 0 10px rgba(255, 255, 255, .9);
}
.submit.loading { pointer-events: none; filter: saturate(1.2) brightness(1.04); }
.submit.loading .bar { animation: authBar 1.35s cubic-bezier(.5, .05, .4, 1) forwards; }
.submit.loading::after { animation-duration: 1.1s; }
@keyframes authBar { from { width: 0; } to { width: 100%; } }
.submit::after {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 46%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.42), transparent);
  transform: translateX(-160%) skewX(-18deg);
  animation: flow 3.4s ease-in-out infinite;
}
@keyframes flow { 0% { transform: translateX(-160%) skewX(-18deg); } 55%,100% { transform: translateX(320%) skewX(-18deg); } }

.sep-or { display: flex; align-items: center; gap: 11px; margin: 20px 0 14px; color: var(--text-faint); font-size: 11.5px; }
.sep-or::before, .sep-or::after { content: ""; flex: 1; height: 1px; background: var(--border); }
.passkey {
  width: 100%; height: 43px; cursor: pointer; border-radius: 11px;
  background: transparent; color: var(--text-dim); border: 1px solid var(--border);
  font-size: 13px; display: flex; align-items: center; justify-content: center; gap: 9px;
  transition: border-color .22s ease, color .22s ease, background .22s ease;
}
.passkey:hover { border-color: var(--accent); color: var(--accent); background: rgba(var(--accent-rgb), .07); }
.passkey svg { width: 17px; height: 17px; }

.row-links { display: inline-flex; gap: 16px; font-size: 12.5px; }

/* 卡片底部信息行：把原先漂在页面底部的版权 / 安全通道行并入窗口内部，
   让卡片收敛成一个完整的整幅面板，右栏不再有游离的散件。 */
.box-note {
  margin-top: auto; padding-top: calc(24px + var(--air) * 6);
  display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap;
  font-size: 10.5px; letter-spacing: .06em; color: var(--text-faint);
  animation: barIn .6s ease .8s both;
}
.conn { display: inline-flex; align-items: center; gap: 6px; }
.conn i {
  width: 5px; height: 5px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 8px var(--ok); animation: blink 2.6s ease-in-out infinite;
}

/* ---------- 响应式 ---------- */
/* 超宽屏（≥1600：常见 1920 / 2560）：左栏变宽后若内容仍按 1440 的尺寸画，
   就会出现「大面板 + 小内容」的空旷感 ⇒ 主张字号、说明行宽、轨道环同步放大。 */
@media (min-width: 1600px) {
  .side { --spx: 76px; --spy: 60px; }
  .orbit { --o: 226px; margin-bottom: 48px; }
  .kicker { font-size: 10.5px; }
  .hero .sub { font-size: 14px; max-width: 520px; }
  .feats { gap: 15px 28px; }
  .feat { font-size: 13px; }
  .side-foot { font-size: 11px; }
}
@media (max-width: 1080px) {
  .side { --spx: 38px; --spy: 40px; }
  .side-inner { padding-bottom: 30px; }
  .side-top { top: 14px; }
  .orbit { --o: 160px; margin-bottom: 30px; }
  /* 右侧：收窄时只改几何令牌，.stage/.console/.d-reticle 自动跟随；同时收起竖排读数 */
  .main { --cardw: 408px; --mpx: 30px; --cpy: 12px; --cpx: 20px; }
  .console .cl { display: none; }
}
@media (max-width: 960px) {
  body { overflow: auto; }
  .split { grid-template-columns: 1fr; grid-template-rows: auto 1fr; height: auto; min-height: 100vh; }
  .side::after { top: auto; bottom: 0; left: 0; right: 0; width: auto; height: 1px;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent); }
  /* 顶部让出控制条高度（胶囊 38px + 12px 顶距）⇒ 品牌行独立成行，任何宽度都不会与胶囊相撞 */
  .side { --spx: 26px; --spy: 54px; }
  .hero { padding: 0; }
  /* 窄屏：轨道环缩小为徽标级，重装饰层降载（省电、避免视觉噪音） */
  .orbit { --o: 82px; margin-bottom: 16px; }
  .orbit .core b { width: 8px; height: 8px; }
  .orbit .rail, .orbit .wave { display: none; }
  /* 窄屏降载：大尺度金弧 / 标签 / 金箔扫光等比缩到 82px 轨道上会互相压叠，直接收起；
     金色观感由 canvas 金微尘承担（仍在）。 */
  .orbit .gold-arc, .orbit .spark, .orbit .tag, .gold-sweep { display: none; }
  /* ⚠️ #1057：与桌面两档乘**同一个** --hero-ts，四档必须同源（漏一处＝该视口改了没反应）。 */
  .hero h1 { font-size: calc(21px * var(--hero-ts, 1)); margin-bottom: 8px; }
  .hero .sub { font-size: 12px; line-height: 1.7; }
  .kicker { font-size: 9px; letter-spacing: .2em; margin-bottom: 9px; }
  .feats { display: none; }
  .side-foot { display: none; }
  .coords { display: none; }
  .ruler-x, .ruler-y { display: none; }
  /* 窄屏（上下堆叠）：右侧变为整幅，同心环/流动光带/刻度会大面积溢出且失去意义 ⇒ 整体降载，
     仅保留轻量数据点；金色观感由左侧面板承担。控制台外框一并收起，卡片直接呈现。 */
  .d-beam, .d-rings, .d-ruler, .d-dial, .d-reticle { display: none; }
  /* 上下堆叠时右栏高度由内容决定 ⇒ 必须解除撑满，否则卡片会被拉到整屏高。 */
  .stage { height: auto; max-height: none; transform: none !important; }
  .console { padding: 0; height: auto; }
  .box { min-height: 0; padding-bottom: 20px; }
  .box h3 { margin-top: 0; }
  /* 窄屏没有需要填的留白（卡片高度随内容），且品牌就在正上方 ⇒ 头带整条收起 */
  .box-head { display: none; }
  .box-note { margin-top: 18px; padding-top: 0; }
  .console::before, .console::after, .console .cf, .console .cl { display: none; }
  /* 窄屏：左栏变顶部通栏 ⇒ 控制条贴右（right:46px 避开右上角 HUD 角标），读数收起 */
  .side-top { top: 12px; right: 46px; justify-content: flex-end; }
  .main { padding: 28px 18px 40px; }
}
/* ---------- 移动端适配 ----------
   ≤960 已由上方断点切换为「上下堆叠、高度自适应」；此处补移动端专属细节：
   触控等待/高亮、iOS 聚焦自动放大、触控目标尺寸、小屏密度与安全区。 */
@media (max-width: 960px) {
  html { -webkit-tap-highlight-color: transparent; }
  .ts-btn, .submit, .passkey, .captcha-box, .chk, .link { touch-action: manipulation; }
  /* ⚠️ iOS 会因 font-size<16px 的输入框在聚焦时强制放大整页 ⇒ 移动端输入框必须 16px */
  .field input { font-size: 16px; }
  .field input::placeholder { font-size: 13.5px; }
  .box h3 { font-size: 18px; }
  .box .cap { font-size: 12.5px; margin: 6px 0 20px; }
  .box-note { font-size: 10px; gap: 5px 10px; }
  /* 触控目标：主题圆点 22px 对手指偏小 ⇒ 28px */
  .ts-btn { width: 28px; height: 28px; }
  .theme-switch { gap: 8px; padding: 5px 7px; }
}
/* 小屏（≤520：iPhone 12~15 竖屏 390~430）进一步降密，保证首屏能看全登录卡 */
@media (max-width: 520px) {
  .side { --spx: 20px; --spy: 52px; }
  .side-inner { padding-bottom: 26px; }
  .side-brand { gap: 10px; }
  .side-brand .mk { width: 38px; height: 38px; border-radius: 10px; }
  .side-brand .mk svg { width: 21px; height: 21px; }
  .side-brand h2 { font-size: calc(14px * var(--st-ts, 1)); }   /* #1073 随系统标题字号配置等比缩放 */
  .side-brand p { font-size: 9.5px; letter-spacing: .16em; }
  .orbit { --o: 66px; margin-bottom: 12px; }
  .orbit .core b { width: 7px; height: 7px; }
  .hero h1 { font-size: calc(19px * var(--hero-ts, 1)); margin-bottom: 7px; }
  .hero .sub { font-size: 11.5px; line-height: 1.7; }
  /* 小屏：kicker 与 sub 属营销层，窄屏上只会把登录卡挤出首屏 ⇒ 收起（h1 已表达同一主张） */
  .kicker, .hero .sub { display: none; }
  .main { padding: 24px 14px 34px; }
  .box { padding: 0 22px 20px; border-radius: 16px; }
  .box-bar { height: 36px; margin: 0 -22px 18px; padding: 0 13px; border-radius: 16px 16px 0 0; }
  .box-bar .ttl { letter-spacing: .12em; }
  .field input { height: 44px; }
  .captcha-box { width: 92px; height: 44px; font-size: 17px; }
  .submit { height: 44px; font-size: 14px; }
  .passkey { height: 41px; }
  .sep-or { margin: 16px 0 12px; }
}
/* 超矮视口（横屏手机、SE 类老小屏）：首屏高度不够时连轨道环一起收起，让登录卡先可见 */
@media (max-width: 960px) and (max-height: 620px) {
  .orbit { display: none; }
  .side { --spy: 46px; }
}
/* 移动端浏览器地址栏会吃掉 100vh ⇒ 支持 dvh 时改用动态视口高度 */
@supports (height: 100dvh) {
  @media (max-width: 960px) { .split { min-height: 100dvh; } }
}
/* iPhone 底部横条（Home Indicator）：给右侧区留出安全区 */
@supports (padding: env(safe-area-inset-bottom)) {
  @media (max-width: 960px) { .main { padding-bottom: calc(34px + env(safe-area-inset-bottom)); } }
}
/* 尊重「减少动态效果」系统偏好：全量停用 CSS 动画（canvas 侧见脚本 REDUCE 分支） */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}


/* ==========================================================================
   生产适配层（最后一段，特异性最高；只写「原型交付后对接生产」必需的覆盖）
   ========================================================================== */

/* ---- ① body 底色的最终裁决权 ----
   login.css 与 theme-{gold,red}.css 里存在 `.theme-red body{background:#f5f5f5}`
   这类 (0,1,1) 规则，上一行的 body(0,0,1) 盖不住 ⇒ 切到炽红后 body 底色会被改写成
   #f5f5f5，与左右两栏自身的底色之间在窄屏堆叠的接缝处露出色差。此处用 (0,2,1) 钉住。 */
html.theme-gold body,
html.theme-red body {
  background-color: var(--bg-2);
  color: var(--text);
}

/* ---- ② 提示框：复用 login.css 的 .error-tip / .ok-tip（三主题令牌驱动），
        只补卡片内的上下间距，避免出现第二套提示样式副本 ---- */
.box .error-tip { margin: 0 0 18px; }
.box .ok-tip { margin: 0 0 18px; padding: 9px 12px; border-radius: 10px; }

/* ---- ③ 验证码：把原型里的假字符块换成真 <img src="/login/captcha">
        （仅在失败满阈值、need_captcha=True 时渲染）
        ⚠️ 类名用 .lg-cap-row 而非 .captcha-row：后者在 login.css 里已有同名的
           flex 规则，同名共存会互相覆盖，必须隔离。 */
.lg-cap-row { display: flex; gap: 10px; }
.lg-cap-row input { flex: 1 1 auto; min-width: 0; letter-spacing: .3em; }
.lg-cap-img {
  width: 104px; flex: none; height: var(--fh); border-radius: 11px; cursor: pointer;
  border: 1px solid var(--border); background: var(--input);
  object-fit: cover; display: block;
  -webkit-user-select: none; user-select: none;
  transition: border-color .25s ease, filter .25s ease;
}
.lg-cap-img:hover { border-color: var(--accent); filter: brightness(1.05); }
.lg-cap-img:active { filter: brightness(.95); }

/* ---- ④ 功能入口行：左「记住账号」+ 右「注册 / 找回」，与定稿原型一致。
   ⚠️ 首版曾按「假控件必删」删掉这个勾选框 —— 当时它确实不落任何地方，删得对。
      本轮补齐了**真实行为**（localStorage['hg_login_account']，见 login.html 的
      「记住账号」脚本段）之后才恢复：**只存账号名，绝不存密码**。
   ⚠️ 该行**恒渲染**，不受注册/找回开关影响 —— 记住账号是纯客户端偏好，
      两个开关都关时只是右侧入口收起，左侧勾选框仍在。
   ⚠️ 勾选框**不带 name**：它不是提交字段。带 name 会混进 POST /login 的载荷，
      与后端 login_submit 的入参口径形成无谓耦合。
   ⚠️ 不再覆盖 justify-content：原型就是 space-between（左勾选 / 右入口），
      首版为「只剩入口」才改成 flex-end，现在恢复。 ---- */
.box .row-links { gap: 18px; }

/* ---- ⑤ WebAuthn（指纹 / 面容）区块 ----
   类名沿用 login.css 的 .passkey-box / .passkey-sep / .passkey-btn / .passkey-ico /
   .passkey-tip：它们是 --lg-* 令牌驱动、三主题自动适配的现成实现，避免第二套副本。
   此处只把外上间距交给卡片节奏。
   ⚠️ [hidden] 与 .passkey-box 同为 (0,1,0)：若样式表里该元素出现 display 声明，
      就会盖掉浏览器默认的 [hidden]{display:none}，按钮将以「恒显示、点了必失败」
      的形态露出。用 (0,2,0) 钉死，防止将来给 .passkey-box 加 display 时静默失效。 */
.passkey-box { margin-top: calc(14px + var(--air) * 8); }
.passkey-box[hidden] { display: none; }

/* ---- ⑥ 无障碍：仅屏幕阅读器可见的表单标签 ----
   原型的输入框靠图标表达字段含义，视觉上无 <label>。为保证读屏与表单自动填充
   仍能正确识别，模板中为每个输入框配了 .sr-only 的 <label for>。 */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---- ⑦ 移动端：验证码图片随控件一起收窄（与 .field input 的 44px 档对齐） ---- */
@media (max-width: 520px) {
  .lg-cap-img { width: 92px; height: 44px; }
}

/* ---- ⑧ 移动端：竖向节奏钉回紧凑值 ----
   ≤960 时卡片高度已改为「随内容」（上面的 .box { min-height:0 }），没有富余需要填，
   而手机上 100vh 动辄 780~900 ⇒ --air 会误判为「高屏」把间距撑开。
   本段位于**文件最末**，必定覆盖上面的 --air 计算。 */
@media (max-width: 960px) {
  .box { --fh: 47px; padding-bottom: 20px; }
  .field { margin-bottom: 15px; }
  .box .cap { margin: 6px 0 20px; }
  .row-between { margin: 4px 0 18px; }
  .passkey-box { margin-top: 14px; }
  .box-note { padding-top: 0; }
  /* 勾选框手指点按：整行 label 可点，但仍补一点上下内边距，避免 17px 高的细条难点 */
  .chk { padding: 6px 0; }
}

/* ---- ⑨ 浅底主题下的勾选框可见性 ----
   ⚠️ 炽红主题的 --input 与 --card 都是白色，且 --border 只有 20% 透明度 ⇒
      未勾选态整个控件只剩一道极淡的粉线（实测量到的亮度差仅 ≈0.07，几乎看不见）。
      这里把框线换成主题主色（42%）并给一点浅底；深蓝 / 黑金本身对比足够，不做改动。 */
html.theme-red .chk input {
  border-color: rgba(var(--accent-rgb), .42);
  background: rgba(var(--accent-rgb), .05);
}

/* ==========================================================================
   登录页版式层（#1059 起，现共六套）
   split 左右 54:46（默认）/ center 居中 / split73 左右 7:3 /
   aurora 极光式 / ticker 行情式 / cosmos 星穹式（#1080，全域沉浸 + 悬浮登乘舱）
   ★ #1081 起：split / split73 的**骨架**改为「左栏铺满整屏 + 登录窗悬浮靠右」
     —— 列比例与卡片几何一字未改，只把"并排两栏"改成"叠放两层"，见文件末尾 ⑦ 段。
   --------------------------------------------------------------------------
   · 六套版式共用**同一份 DOM 与装饰层**，唯一开关是 <html> 上的 layout-* 类名
     （服务端由「信息配置 → 登录页样式」的 login_layout 注入，见 templates/login.html）。
     ⚠️ 唯一例外：cosmos 额外用到一个**专属场景层** `.cosmos`（默认 DOM 里就在，
        但只有 layout-cosmos 下才可见）—— 它不需要改任何既有元素，见 ⑥ 段头。
   · 原则：**只做加法**。上面所有既有规则都是为 split 写的，本段一行都不改它们；
     居中式新增的三个容器（.crown / .c-feats / .c-foot）默认 display:none，
     因此「加了版式」这件事对 split 的表现是**零影响**（不依赖样式表顺序）。
   · ★ 例外（#1081）：split / split73 的**骨架**本身被改了（见文件末尾 ⑦ 段）——
     两列网格压成一格 ⇒ .side 铺满整屏、.main 变成"靠右、宽度=原右栏宽"的一列，
     登录卡成为浮在铺满视觉之上的窗口。**列比例与卡片几何一字未改**，所以上面
     那些为 split 写的 `.main / .stage / .console / .box / --cardw / --mpx` 全部照旧生效；
     只有"分栏语言"的四件（两栏接缝光带 / 右栏点阵 / .main > .dl 装饰组 / 面板暗角）
     在叠放下失去意义，已由 ⑦-2 显式收起。
   · ⚠️ 一律用 html.layout-* 前缀（特异性 (0,2,x)）抬到既有规则之上：
     绝不能靠「写在文件末尾」取胜 —— 上面有多条 @media 里同名的 .split/.main 规则，
     顺序在这些断点下并不可靠。
     ⚠️ 但 ⑦-0b 那三条**断点阶梯**恰恰必须靠顺序（选择器相同、特异性相同）——
     这是本文件里唯一有意依赖"后来居上"的地方，已在该处注明。
   ========================================================================== */

/* 居中式专用容器：默认版式下不参与任何布局（不占位、不进无障碍树） */
.crown, .c-feats, .c-foot { display: none; }

/* ★ 版式⑥（星穹式）专属场景层：同上口径 —— 其余五套版式下**完全不参与布局**。
   为什么这条是必须的（不是防御性加固）：.cosmos 及其全部子元素（.cos-sky /
   .cos-stars / .cos-grid / .cos-scene / .cos-flux / .cos-meteor）的样式**只在
   `html.layout-cosmos` 前缀下存在**。场景层要常驻 DOM（服务端渲染，不做 JS 注入），
   于是在 split / center / split73 / aurora / ticker 里它就是一组**无样式的块级元素**
   —— 会被当成正常内容撑开 .side，把五套版式一起改形（且不报任何错）。
   ⚠️ 反向那一半同样必须：`html.layout-cosmos .cosmos` 那条规则**没有** display
   声明，若不在这里补 `display: block`，本版式下场景会被这条 (0,1,0) 永久隐藏
   —— 表现为"版式 ⑥ 只有底色、没有任何太空实体"。 */
.cosmos { display: none; }

/* 冠内高亮色：深底主题走亮金，浅底主题（炽红）必须换深金 ——
   冠的底色是 --card，炽红主题里 --card 是近白，亮金 #f0d089 在上面几乎看不见。
   ⚠️ 定义在 :root 上即随主题的 --gold-1 走（var 在同一元素上按层叠后的值解析）。 */
:root { --crown-hi: var(--gold-1); }
html.theme-red { --crown-hi: #9a6b12; }   /* 白底深金，对比度 ≈ 4.6:1 */

/* --------------------------------------------------------------------------
   版式 ①：左右 7:3
   --------------------------------------------------------------------------
   只想让主张区更宽、卡片更窄，因此**只动两处**：网格比例 + 右栏几何令牌。
   卡片宽度不写死像素而用 100%（撑满收缩后的右栏），否则窄屏下卡片会横向溢出。
   -------------------------------------------------------------------------- */
html.layout-split73 .split { grid-template-columns: 7fr 3fr; }
html.layout-split73 .side { --spx: 50px; --spy: 52px; }
html.layout-split73 .main { --cardw: 100%; --mpx: 26px; --cpx: 22px; --cpy: 14px; }
/* 7:3 在窄屏会把右栏压到装不下卡片（1280 时右栏仅 384px）⇒ 逐级回收比例。
   ⚠️ 三档都必须带 html.layout-split73 前缀，否则盖不过上面的基础规则。 */
@media (max-width: 1440px) { html.layout-split73 .split { grid-template-columns: 2.05fr 1fr; } }
@media (max-width: 1200px) { html.layout-split73 .split { grid-template-columns: 1.62fr 1fr; } }
@media (max-width: 960px) {
  /* ≤960 已经是上下堆叠（见上方断点），7:3 不再有意义，回到单列并把比例还原 */
  html.layout-split73 .split { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
}

/* --------------------------------------------------------------------------
   版式 ②：居中式
   --------------------------------------------------------------------------
   .side 从「左半屏」变成「整屏装饰层」，.main 从「右半屏」变成「整屏纵向中轴」：
     品牌/主张（.crown）→ 终端卡片（.stage）→ 特性网格（.c-feats）→ 读数条（.c-foot）
   左栏原有的 .side-inner（品牌 + 轨道环 + 特性 + 读数）整块隐藏 ——
   它的内容由 .crown / .c-feats / .c-foot 以**同一份配置文案**重新排布，
   而不是把 .hero 的 CSS 硬套到窄卡片上（那是按半屏宽栏写的，必然走形）。
   -------------------------------------------------------------------------- */
html.layout-center .split {
  display: block; position: relative; height: 100vh; overflow: hidden;
}
/* ⚠️ 这里必须用 absolute，**既不能**用 fixed、**也不能**加 z-index ——
   两件事都会把 .side 变成层叠上下文，从而把 .side-top 的 z-index:13 连同自家装饰
   一起封印在里面；而居中式 .main 是 z-index:10 且铺满整屏（透明，但照样吃指针事件）
   ⇒ 右上角那三个主题按钮「看得见、点不动」（#1059 实测踩到，最初还误以为只是 z-index 的锅）：
     · fixed 的坑：按规范 position:fixed **无条件**创建层叠上下文，写不写 z-index 都一样；
     · z-index 的坑：absolute + z-index:0 同样会创建。
   改成 absolute 且不写 z-index（z-index:auto 不创建层叠上下文）之后：
     · .side 仍铺满整屏 —— 包含块是 .split，居中式下它就是 100vh 全屏且自身不滚动，观感零变化；
     · 装饰层 z-index 最高才 9（mesh 0 / sideBg 2 / streams 3 / scanline 6 /
       ruler & hud 7 / vignette 8 / grain 9）⇒ 全部沉在 .main(10) 之下，不抢卡片；
     · .side-top 的 13 直接参与根层叠 ⇒ 压过 .main，三个主题按钮恢复可点；
     · 唯一超过 10 的 .side::after（分隔光带，z-index:20）在居中式下已 display:none。 */
html.layout-center .side { position: absolute; inset: 0; }
html.layout-center .side::after { display: none; }   /* 左右分隔光带在整屏层上没有意义 */
html.layout-center .side-inner { display: none; }
html.layout-center body { overflow: hidden; }

html.layout-center .main {
  position: relative; z-index: 10; height: 100vh;
  /* 纵向中轴：justify-content 刻意不用 center —— 内容高于容器时 center 会把顶部
     裁掉且无法滚动；改用「首尾 auto 外边距」居中，溢出时自动退化为可滚动。 */
  flex-direction: column; justify-content: flex-start; align-items: center;
  gap: 0; padding: 34px 22px; overflow-y: auto; overflow-x: hidden;
  background: none;            /* 整屏底色由 .side 装饰层提供 */
  perspective: none;           /* 关闭右栏的 3D 透视，避免整屏内容被推歪 */
}
/* 右栏的环境层（辉光 / 点阵 / 暗角）在整屏上有两份（.side 的网格 + 这里的点阵）会打架 ⇒ 收起 */
html.layout-center .main::before,
html.layout-center .main::after { display: none; }
html.layout-center .main > .dl { opacity: .55; }   /* 保留光柱/同心环，压一档不抢戏 */

/* ---- 主标题冠：坐在登录窗口上沿 ---- */
html.layout-center .crown {
  display: block; position: relative; margin-top: auto; flex: none;
  /* 与卡片**同宽共轴**：卡片内容宽 = --cardw − .console 左右 padding(×2)，
     冠再各让 10px，保证两条竖边在视觉上对齐（冠的描边落在卡片描边延长线上）。 */
  width: 100%; max-width: calc(var(--cardw) - var(--cpx) * 2);
  /* h1 的尺寸容器（#1059 追改）：主标题字号用 cqw 锚定「冠的实际内容宽」。
     inline-size containment 只压内联轴，而冠宽由 width/max-width 定死、不依赖内容 ⇒ 无副作用。 */
  container-type: inline-size;
  padding: 16px 12px 13px;
  border-radius: 18px 18px 0 0;
  border: 1px solid var(--border); border-bottom: 0;
  background:
    radial-gradient(130% 160% at 50% 0%, rgba(var(--accent-rgb), .17), transparent 64%),
    linear-gradient(180deg, rgba(var(--accent-rgb), .10), rgba(var(--accent-rgb), .02)),
    var(--card);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px);
  box-shadow: 0 -24px 48px -32px rgba(var(--accent-rgb), .6);
  text-align: center;
  animation: riseIn .7s cubic-bezier(.2,.8,.3,1) .30s both;
  transition: background .45s ease, border-color .45s ease;
}
/* 冠与卡片之间的接缝金线（共边的视觉封口） */
html.layout-center .crown::after {
  content: ""; position: absolute; left: 14px; right: 14px; bottom: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--gold-rgb), .6), transparent);
}
/* 共边的三个必要条件（缺一个就会在两个圆角之间露出月牙缝）：
   ① 冠去下边框（上面）；② 卡片去上圆角 + 上边框；③ 标题栏上圆角一并归零。
   ⚠️ 冠与卡片之间还隔着 .stage/.console，所以用 .crown + .stage 而不是 .crown + .box。 */
html.layout-center .crown + .stage .console { padding-top: 0; }
html.layout-center .crown + .stage .box { border-radius: 0 0 18px 18px; border-top: 0; }
html.layout-center .crown + .stage .box .box-bar { border-radius: 0; }

/* 眉标：徽标 + 系统名 + 英文名 + 版本号压成一行 —— 明确是「眉」，不与主标题抢层级 */
html.layout-center .cr-eye {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 8px; margin-bottom: 10px;
  font-size: 10.5px; letter-spacing: .18em; color: var(--text-dim);
}
html.layout-center .cr-eye .mk {
  width: 21px; height: 21px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  background: rgba(var(--accent-rgb), .14);
  border: 1px solid rgba(var(--accent-rgb), .38);
  color: var(--accent-2);
}
html.layout-center .cr-eye .mk svg { width: 12px; height: 12px; }
/* ⚠️ 该规则当前**没有对应元素**：#1059 追改把系统名从眉标升格成下方 h1 主标题，
   若仍留在眉标里就会在同一行连出两个系统名。样式刻意保留 —— 日后要还原眉标形态时不必重写。 */
html.layout-center .cr-eye .nm { font-weight: 500; color: var(--text); letter-spacing: .2em; }
html.layout-center .cr-eye .div {
  width: 4px; height: 4px; flex: none; transform: rotate(45deg);
  background: rgba(var(--gold-rgb), .9); box-shadow: 0 0 6px rgba(var(--gold-rgb), .8);
}
html.layout-center .cr-eye .en { font-size: 9px; letter-spacing: .3em; text-transform: uppercase; color: var(--text-faint); }
html.layout-center .cr-eye .ver em {
  font-style: normal; padding: 1px 6px; border-radius: 4px;
  font-size: 9px; letter-spacing: .16em;
  color: var(--text-dim); background: rgba(var(--accent-rgb), .12);
  border: 1px solid var(--border);
}
/* 主标题：居中式的绝对主角，内容是**系统名称**（#1059 追改，见 templates/login.html）。
   ⚠️ 字号锚定**冠的实际内容宽**（cqw），而不是整页视口宽（vw）：
      冠宽在 ≥960 视口下恒为「--cardw − padding」，根本不随视口变 ——
      原来的 2.6vw 在宽屏上一路顶到上限、在窄屏又缩得过小，属于「看着像响应式」。
      系数 8.8 是**反推**的：11 个汉字 × 字号 × (1 + letter-spacing .02em) ≈ 内容宽
      ⇒ 字号 ≈ 内容宽 × 8.9% ⇒ 取 8.8 留余量，出厂文案恰好排满一行不折。
      于是字号天然随可用宽度等比缩放：窄屏自动收小、宽屏到 34px 封顶。
   ⚠️ 上限 34px 不是拍的：它是「11 汉字刚好一行」的安全线，再大就会折行把卡片往下顶。
   ⚠️ 下限 17px 保可读；运营把系统名配长了必然折行，配 text-wrap: balance 让折点落在语义中间。
   ⚠️ 乘 --hero-ts（「信息配置 → 主张标题字号」注入的无单位系数）—— 居中式的冠主标题与
      左右式左栏主张标题**共用同一个字号配置**，避免该配置在居中式下静默失效（调了没反应）。 */
html.layout-center .crown h1 {
  font-size: clamp(17px, calc(8.8cqw * var(--hero-ts, 1)), calc(34px * var(--hero-ts, 1)));
  line-height: 1.26; font-weight: 600; letter-spacing: .02em; margin: 0;
  text-wrap: balance;
}
/* ⚠️ 渐变端点必须用**主题令牌**显式取值，绝不能写 currentColor：
   本元素为了 background-clip:text 把 color 设成了 transparent，
   currentColor 会跟着解析为透明 ⇒ 标题首尾笔画整段隐形（#1058 实测踩到）。 */
html.layout-center .crown h1 em {
  font-style: normal;
  color: var(--crown-hi, var(--gold-1));      /* 兜底：渐变失效时=实心高亮色而非透明 */
  background: linear-gradient(100deg,
    var(--text) 6%,
    var(--text-dim) 30%,
    var(--crown-hi, var(--gold-1)) 46%,
    var(--crown-hi, var(--gold-1)) 52%,
    var(--text-dim) 70%,
    var(--text) 94%);
  background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: titleShine 8s ease-in-out infinite;
}
html.layout-center .crown .sub {
  font-size: 12px; line-height: 1.8; opacity: .72; margin: 9px auto 0; max-width: 100%;
}
html.layout-center .cr-rule { display: block; position: relative; margin: 11px auto 0; width: 148px; height: 5px; }
html.layout-center .cr-rule::before,
html.layout-center .cr-rule::after { content: ""; position: absolute; top: 2px; width: 58px; height: 1px; }
html.layout-center .cr-rule::before { left: 0; background: linear-gradient(90deg, transparent, rgba(var(--gold-rgb), .8)); }
html.layout-center .cr-rule::after  { right: 0; background: linear-gradient(270deg, transparent, rgba(var(--gold-rgb), .8)); }
html.layout-center .cr-rule i {
  position: absolute; left: 50%; top: 0; width: 5px; height: 5px; margin-left: -2.5px;
  background: var(--gold-1); transform: rotate(45deg);
  box-shadow: 0 0 9px rgba(var(--gold-rgb), .85);
  animation: crPulse 3.4s ease-in-out infinite;
}
@keyframes crPulse { 0%, 100% { opacity: .5; } 50% { opacity: 1; } }

/* 卡片：居中式**不**与左栏等高 ⇒ 解除撑满，高度随内容（否则矮视口下会被拉到整屏高） */
html.layout-center .stage { height: auto; max-height: none; margin-top: 0; }
html.layout-center .box { min-height: 0; }

/* 特性网格：居中式没有左栏可放 ⇒ 移到卡片下方通栏（三列两行），与左右式 .feats 同源 */
html.layout-center .c-feats {
  display: grid; position: relative; margin-top: 24px; flex: none;
  width: 100%; max-width: 740px;
  grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 26px;
  text-align: left;
}
html.layout-center .c-feats .feat { font-size: 12px; }
html.layout-center .c-feats .feat:nth-child(1) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .58s both; }
html.layout-center .c-feats .feat:nth-child(2) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .64s both; }
html.layout-center .c-feats .feat:nth-child(3) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .70s both; }
html.layout-center .c-feats .feat:nth-child(4) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .76s both; }
html.layout-center .c-feats .feat:nth-child(5) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .82s both; }
html.layout-center .c-feats .feat:nth-child(6) { animation: riseIn .6s cubic-bezier(.2,.8,.3,1) .88s both; }

/* 底部读数条：对应左右式左栏底部的 .side-foot（同一份数据、同一个脚本按 class 更新）
   ⚠️ 颜色一律走主题令牌，不写 rgba(255,255,255,…)：炽红主题是浅底，白字会消失。 */
html.layout-center .c-foot {
  display: flex; position: relative; margin-top: 22px; margin-bottom: auto; flex: none;
  align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap;
  font-size: 9.5px; letter-spacing: .2em; color: var(--text-dim);
  animation: riseIn .7s ease .98s both;
}
html.layout-center .c-foot span { white-space: nowrap; }
html.layout-center .c-foot b { font-weight: 500; color: var(--text); }
html.layout-center .c-foot .sep { opacity: .4; }
html.layout-center .c-foot .live { display: inline-flex; align-items: center; gap: 6px; }
html.layout-center .c-foot .live i {
  width: 5px; height: 5px; border-radius: 50%; background: var(--ok);
  box-shadow: 0 0 8px var(--ok); animation: blink 2.2s ease-in-out infinite;
}
/* 读数条里的负载条：与 .side-foot .meter span 同一套机制（--v 由脚本改写） */
html.layout-center .c-foot .meter { display: inline-flex; align-items: center; gap: 6px; }
html.layout-center .c-foot .meter span {
  position: relative; width: 46px; height: 4px; border-radius: 2px; overflow: hidden;
  background: rgba(var(--rhud-rgb), .22);
}
html.layout-center .c-foot .meter span::after {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--v, 42%);
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  transition: width .9s ease;
}

/* 竖向预算不足时逐级降载（与左右式同一思路：卡片与主标题永远保留） */
@media (max-height: 940px) { html.layout-center .c-foot { display: none; } }
@media (max-height: 860px) { html.layout-center .crown .sub { display: none; } }
@media (max-height: 780px) { html.layout-center .c-feats { display: none; } }
@media (max-width: 960px) {
  /* ⚠️ ≤960 的既有断点把 .console 的 padding 归零（.console{padding:0}）⇒ 卡片宽度
     就等于 --cardw 本身，冠若仍按「--cardw − padding×2」取宽会比卡片窄 40px 而失去共边。
     这里把冠的宽度口径跟到同一处：与 .stage 同宽。 */
  html.layout-center .crown { max-width: var(--cardw); padding: 15px 12px 12px; }
  /* ⚠️ 此处**不再**覆盖 h1 字号（#1059 追改）：cqw 方案下冠宽一变、字号自动跟随；
     再手写一个固定 px 只会把它钉死，反而毁掉适配 —— 这正是「字体全局适配」要解决的毛病。 */
  html.layout-center .c-feats { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 560px; margin-top: 20px; }
}
@media (max-width: 560px) {
  html.layout-center .cr-eye .en, html.layout-center .cr-eye .ver { display: none; }
  html.layout-center .c-feats { display: none; }
}

/* ==========================================================================
   居中式「单卡化」（#1060）
   --------------------------------------------------------------------------
   改法：① 模板层把 .crown 整块从 .main 移进 .box 内部（DOM 位移，
            见 templates/login.html 里 .box-bar 之后那段）；
         ② 本段把 .crown 的「卡片外观」剥净，只留内容排版。
   · 为什么必须动 DOM 而不是纯 CSS：纯 CSS 只能在同一父容器内重排（order/flex），
     无法把元素搬进另一个元素内部；而「主标题与『欢迎回来』同处一张卡片」在语义上
     就是同一个父级 ⇒ 只能真的搬。
   · 连带自动消失的东西（**不需要**写覆盖）：原来「冠与卡片共边」的三条规则
     （.crown + .stage .console / .box / .box-bar）因 .crown 不再是 .stage 的前兄弟
     而自然失配 ⇒ .box 与 .box-bar 各自回落到基础圆角，卡片恢复成一张完整卡片。
   · 本段写在文件末尾 + 与 1478 行那条同特异性（html.layout-center .crown h1），
     按「同特异性后者胜出」覆盖其字号 —— 冠的宽度口径变了（从独立卡片的
     --cardw − --cpx×2 变成卡片内容宽），上限必须跟着重算，见下方 h1 注释。
   ========================================================================== */
html.layout-center .crown {
  display: block; position: static; flex: none;
  width: auto; max-width: none; margin: 0; padding: 0;
  border: 0; border-radius: 0; background: none; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  text-align: center;
  container-type: inline-size;          /* h1 仍按「可用内容宽」定字号 */
  animation: barIn .5s ease .40s both;
}
html.layout-center .crown::after { display: none; }   /* 共边金线取消：已经没有接缝了 */

/* 44px 徽标带是「卡片与左栏等高」时代的填空件 ——
   居中式卡片高度随内容、没有留白要填，且品牌识别已由上方主标题承担 ⇒ 收起。
   顺带省出约 90px 竖向空间，矮屏更从容。 */
html.layout-center .box-head { display: none; }

/* 品牌头内部节奏（卡片内比原来的冠更紧凑） */
html.layout-center .crown .cr-eye { margin-bottom: 9px; }
html.layout-center .crown h1 {
  /* ⚠️ 字号锚「卡片内容宽」而不是视口宽：卡片 460 − console(24×2) − box padding(32×2)
     = 348px。上限 30px 是算出来的：11 个汉字 × 30 × (1 + letter-spacing .02em) ≈ 337px
     < 348px，再大会折行、把「欢迎回来」与整个表单往下顶。 */
  font-size: clamp(18px, calc(8.6cqw * var(--hero-ts, 1)), calc(30px * var(--hero-ts, 1)));
}
html.layout-center .crown .sub { margin-top: 8px; }
html.layout-center .crown .cr-rule { margin-top: 12px; }

/* 「欢迎回来 / 请使用企业账号登录」在居中式下**取消**：
   卡片顶部的主标题已经把「这是哪个系统」说清楚了，再顶一句问候语属重复信息。
   ⚠️ 只作用于居中式 —— 左右式的卡片里没有主标题，那两行是必要的，照旧保留。
   ⚠️ 用 CSS 收起而不是模板条件渲染：这两行在 DOM 里仍是真实内容，
      将来要还原只需删掉这两行规则；也让左右式与居中式共用同一份 DOM。 */
html.layout-center .box h3,
html.layout-center .box .cap { display: none; }
/* ⚠️ 必须补回呼吸：h3(约 28px) + cap(约 12px + 上下 margin ≈ 50px) 让出后，
   .crown 的金线会直接贴住第一个输入框。对可能插在其间的提示条同样生效
   —— .box .error-tip 原本只有下边距，不补就会紧贴金线。 */
html.layout-center .box form,
html.layout-center .box > .error-tip,
html.layout-center .box > .ok-tip { margin-top: calc(22px + var(--air) * 8); }

/* ==========================================================================
   居中式的卡片下方不再挂特性列表（#1061）
   --------------------------------------------------------------------------
   诉求：居中式登录框下方**取消**三列特性清单 ⇒ 单卡页到此为止
         「品牌头 → 登录卡片 → 读数条」，纵向更干净。
   · 用 CSS 收起而不是删模板：DOM 仍在（与左右式左栏 .feats 共用同一份 _feats 配置），
     要还原只删掉下面这一条即可 —— 上方「特性网格」那段居中三列排版规则
     （`html.layout-center .c-feats { display:grid … }` 及逐条入场动画）**原样保留**，
     恢复时直接复用，不必重写。
   · ⚠️ 为什么写在文件**末尾**而不是那段原处：两条选择器特异性完全相同
     （都是 html.layout-center .c-feats ⇒ 0,2,1），CSS 按「同特异性后者胜出」裁决 ——
     只有写在 1521 行**之后**才能覆盖它的 display:grid。写在原处会被它反压。
   · ⚠️ 连带口径：`login_feats` 配置项从此**只作用于左右式左栏**（信息配置页说明已同步注明），
     不再出现「在居中式下改了它却没反应」的静默失效。
   · 读数条（.c-foot）不受影响：它不是特性列表，仍按高度预算在 ≥940px 高时显示。
   ========================================================================== */
html.layout-center .c-feats { display: none; }

/* ==========================================================================
   居中式：登录窗口垂直居中修复（#1063）
   --------------------------------------------------------------------------
   现象：居中式的内容贴在顶部，没有垂直居中。

   根因（#1060 单卡化引入的连带回归）：居中原本靠「首尾 auto 外边距」——
     .crown { margin-top: auto } 与 .c-foot { margin-bottom: auto } 分居两端，
     在 .main（flex column + justify-content: flex-start）里把中间那坨夹在正中。
     而 #1060 把 .crown 整块**搬进了卡片内部**（DOM 位移）⇒ .main 里再没有
     margin-top:auto 的元素 ⇒ 上端那一半失效，内容被 .c-foot 的下端 auto 推着贴顶。
     雪上加霜的是 .c-foot 在 ≤940px 高时本身会被 display:none 收起，
     那时连下端 auto 也没了，内容彻底贴顶。

   改法：不再依赖「两端 auto 外边距」这种要两个元素配合的机制，直接用
     justify-content 居中；同时把 .c-foot 的 auto 下边距归零 ——
     它在 justify-content:center 下会吃掉全部剩余空间、把内容重新推回顶部。

   ⚠️ 用 safe center：内容一旦高于容器（矮屏 / 小屏），纯 center 会把顶部内容
      顶到可视区之外**且无法滚动到**；safe 关键字让它在那种情况下自动退化为
      「从顶部开始」，内容仍可滚动可达。两条声明并存充当老浏览器回退
      （不认 safe 的那条时保留前一条）。
   ⚠️ 没有改用 .stage { margin: auto }：.c-foot 显示与否会改变 auto 份额的分配
      （上下两个 auto 分剩余空间 ⇒ 内容不在正中），不如 justify-content 稳定。
   ========================================================================== */
html.layout-center .main {
  justify-content: center;
  justify-content: safe center;
}
html.layout-center .c-foot { margin-bottom: 0; }

/* ==========================================================================
   #1064 左右式「呼吸重排」：左栏纵向节奏（方案 B）+ 右卡黄金分割留白
   --------------------------------------------------------------------------
   两个诉求放在同一条注释里，因为它们是同一件事的两面：
   左右栏都**必须随视口等高**（左栏 100vh、右卡 min-height:100%），
   于是两边都会把「内容装不满」的富余量摊成留白 —— 摊得不好就是「大片空白」。

   ⚠️ 本段全部规则**只作用于左右式**（layout-split / layout-split73），
      并加 `@media (min-width: 961px)` 闸门，两条都不是可选项：
      · ≤960 时页面已切成上下堆叠、高度自适应，卡片与左栏都不再有富余量；
      · 更关键的是 ≤960 那批降载规则特异性只有 (0,1,0) ——
        `.orbit{--o:82px}` / `.feats{display:none}` / `.side-foot{display:none}`。
        本段是 (0,2,1)，不设闸门会**静默压掉**它们（最典型：手机上的轨道环
        会被从 82px 拉回 136px）。设计稿阶段只渲染了 1440，漏掉的正是这一层。
      · 居中式（layout-center）另有 .box{min-height:0} 与 .box-head/.box h3 收起，
        本段的选择器不含它 ⇒ 居中式零影响。
   ========================================================================== */
@media (min-width: 961px) {

  /* ========================================================================
     一、左栏：纵向节奏重排（#1063 设计稿的方案 B）+ 与右卡**等高对齐**（#1064 追改）
     ========================================================================
     第一轮（方案 A/B）把 .side-inner 改成 safe center：三段作为一个内容块整体居中。
     实测 1440×900 三段的块间空隙从 155/161 收到 48/59，确实紧凑了；
     但代价是**内容块与右卡不再同高** —— 左栏内容占 181~741（560px），
     右卡占 44~856（812px），差了 250px。两张「卡片」一高一矮并排站着，
     视觉上左栏像一张缩在中间的小卡，反而更不平衡。
     ⇒ 本轮改回**两端撑开**（space-between），并把左栏内容的上下内缩量
       换成与右卡同一个令牌 --card-in ⇒ 两份内容的**上沿、下沿分别对齐**，
       左右两栏因此严格等高（1440×900 均为 44~856）。

     保留方案 B 的全部内部改造（这才是「别扭」的真正来源）：
       · 轨道环 190px → 136px 并压成背景层（那是 230px 的空装饰）
       · 主张与特性之间加极淡分隔线，建立两级层次
       · 特性序号列 16px → 24px，两列文字起点对齐（原来 12.5px 条目紧贴 37px 标题，
         层级断崖式跳跃，这才是「文字布局很别扭」的主因）
     ⚠️ 不再用方案 A 的两个 `margin-top: clamp(...)`：space-between 已经在分配
        两段空隙，再叠加上边距只会把空隙挤歪（且无法解释）。
     ⚠️ 为什么不是 safe center：safe 解决的是「内容高于容器时顶部被裁」，
        而 space-between 天然不裁顶（内容超出时只会从底部溢出，与改动前一致）；
        更要紧的是它做不到「与右卡同高」——那是本轮的硬要求。
       .side 仍是 overflow:hidden，故超矮视口下底部内容会被裁：
       这是**改动前就存在**的行为（原 space-between 同理），不是本轮引入。
     ======================================================================== */
  html.layout-split .side-inner,
  html.layout-split73 .side-inner {
    justify-content: space-between;
    padding-top: var(--card-in);
    padding-bottom: var(--card-in);
  }

  /* ========================================================================
     一之补：左栏内容块锚在**上黄金线**上（#1065）
     ========================================================================
     诉求：轨道环「没放在黄金分割位置」，看着别扭。

     量化（1440×900 实测）：左卡区间 44~856（高 812，与右卡严格等高）。
     环心原本在 y=315 ⇒ (315-44)/812 = **33.4%**，落在上黄金线 44+812×.382=354
     的**上方 39px**。也就是说环被顶得偏高、与下方「SYS v4.2 + 主张」之间
     只剩 43px 而头顶空出 159px —— 视觉重心偏上，这就是「别扭」的来源。

     改法：把 `.hero` 整块**脱流**，用 `top` 直接钉在上黄金线上；
     环心再靠轨道环自身的 `margin-top: calc(var(--o) * -.5)` 上移半个直径
     ⇒ **环心 = 上黄金线**，数学上与视口高度、与内容多少都无关：

         .hero.top = card_in + 0.382 × (卡高 - 2×card_in)
         环心      = .hero.top + 0 (padding) + (-o/2) + o/2 = .hero.top   ✓

     ⚠️ 为什么必须脱流、而不能只把轨道环往下推 39px：
        · 环的 `margin-bottom` 只有 43px（它下面是「SYS v4.2」眉标）——
          单推 39px 会把这段呼吸压到 4px，环直接贴上文字；
        · 那就得把**文字一起**往下带（环与主张是一组）。但在 `space-between`
          下改 `.hero` 的 padding/margin，环只会移动「所加空间的一半」
          （另一半被两侧空隙各分走）⇒ 想移 39px 得加 78px，
          而这段空间**取决于左栏内容自身的高度**（特性条数、说明文字都在配置里）
          ⇒ 任何写死的「加多少」都会随运营改文案而失准。
        · 绝对定位把「环的位置」与「内容的多少」彻底解耦：位置由卡高唯一决定。

     ⚠️ 左上/右三个偏移都用令牌，不复写常量：
        · `left/right: var(--spx)` —— `.side-inner` 的定位包含块是它的 padding box
          （没有 border ⇒ 就是外框），`left: 0` 会贴到**面板**左缘而不是内容左缘，
          必须补上 `--spx` 才能与改动前的正文左缘逐像素重合；
        · `top` 的 `100%` 按规范解析为**包含块高度**（= .side-inner = 100vh），
          不是宽度 —— 这里正是要它按高算。`--card-in` 由 .split 继承而来，
          自定义属性是**用到时才展开**，故在 .side-inner 上引用它没有问题。
     ⚠️ 脱流后 .side-inner 的在流子元素只剩 .side-brand 与 .side-foot，
        `space-between` 恰好把它们留在上下两端 ⇒ #1064 的「左栏内容上下沿与
        右卡等高对齐」**原样保持**（44 / 856），本轮没有推翻它。
     ⚠️ 内容过高时（运营把说明文字或特性条数加长）底部会被 .side 的
        overflow:hidden 裁掉 —— 与改动前同因同果（此前也是靠裁切兜底），
        不是本轮引入；探针在 1440×900 / 1366×720 两档盯住余量。
     ⚠️ 只在 ≥961px 的左右式内生效：≤960 已切成上下堆叠、卡片高度随内容，
        没有「黄金线」可言，`.hero` 仍在流内（原行为逐值不变）。
     ======================================================================== */
  html.layout-split .hero,
  html.layout-split73 .hero {
    position: absolute;                        /* 脱流：位置由黄金线决定，不再由内容决定 */
    top: calc(var(--card-in) + 0.382 * (100% - 2 * var(--card-in)));
    left: var(--spx);
    right: var(--spx);
    padding: 0;                                /* 原为 calc(var(--air) * 16) 0 0 —— 位置已由 top 定，不需要它再吃富余量 */
    margin-top: 0;
  }

  /* 主张标题 / 眉标的下边距也随屏高拉开：把空出来的高度落在**文字之间**，
     而不是让它堆成「品牌 → 主张」那段大空洞。 */
  html.layout-split .hero h1,
  html.layout-split73 .hero h1 {
    margin-bottom: calc(14px + var(--air) * 11);
  }

  html.layout-split .kicker,
  html.layout-split73 .kicker {
    margin-bottom: calc(14px + var(--air) * 9);
  }

  html.layout-split .side-foot,
  html.layout-split73 .side-foot {
    margin-top: 0;
  }

  /* ---- 底部状态条右端收口（#1066）----
     原本「● ONLINE · LOAD ▁ 42% · 12ms」整条挤在左端，面板右下全是空的 ——
     「元素都靠左」的观感有一半来自这里。把末位读数用 `margin-left:auto` 顶到
     内容右缘（= 顶部控制条的右缘、两列特性网格的右缘），面板上下两条横带就都
     横贯到同一个右边界，整块左侧栏被「框」住，不再是清一色左对齐。
     ⚠️ 同时收起第二个分隔点：它还留在原处会成为「42% ·」后面吊着的孤点。
        分隔点纯装饰（.sep 只是 "·"），隐藏它不影响任何语义或脚本
        （读数由 class 驱动更新：js-mload / js-mloadv / js-mlat）。 */
  html.layout-split .side-foot .sep ~ .sep,
  html.layout-split73 .side-foot .sep ~ .sep { display: none; }
  html.layout-split .side-foot > span:last-child,
  html.layout-split73 .side-foot > span:last-child { margin-left: auto; }

  /* 方案 B ①：轨道环 190px → 136px，并把不透明度压到 .5。
     它是左栏最占竖向的一块（190 + 40 边距 = 230px）而信息量为零 ⇒
     缩小后把约 95px 还给文字，同时保留「科技感底纹」的观感。
     ⚠️ --o 是本组件**唯一**缩放锚点（环内所有同心元素都按它推导），改它即整体缩放。
     ⚠️ 必须同步覆盖 ≥1600 那一档（见本段之后的媒体查询）：那处是 `.orbit`(0,1,0)，
        压不过本段 (0,2,1) —— 不覆盖的话超宽屏的轨道环会被钉死在 136px、
        失去原本「超宽屏同步放大」的行为。≤1080 那档同理，但差异只有 160→136，
        属方案 B 有意收窄，不再单独放回。 */
  html.layout-split .orbit,
  html.layout-split73 .orbit {
    --o: 136px;
    opacity: .5;
    /* #1065：环心对齐上黄金线 —— .hero 的 top 定的是「环所在那一行的起点」，
       环自身再上提半个直径，环心才落在线上。
       ⚠️ 用 `calc(var(--o) * -.5)` 而不是写死 68px：--o 是环的唯一缩放锚点，
          ≥1600 那档会变成 162px（即 -81px）。写死会让超宽屏的环心又偏下 13px，
          ——而这正是「改了尺寸、位置跟着漂」的老毛病。用 --o 则各档自动成立。
       ⚠️ 负上外边距会不会与 .hero 的 padding 折叠？不会：.hero 是绝对定位元素，
         按规范本身就是一个独立格式化上下文（且它还有 container-type: inline-size
         带来的 layout containment），子元素外边距不向外折叠。 */
    margin-top: calc(var(--o) * -.5);
    /* 与右卡同一手法：把「内容装不满」的富余量吃进节奏，而不是让它堆成块间的空洞。
       ⚠️ 左栏的富余量**随视口高度线性增长**（左栏自身没有像右卡 --air 那样的间距系统），
          所以 --air 系数必须够大：1440×900 吸约 85px、1920×1080 吸约 263px，
          实测两档的块间空隙都能稳定在 ~130px，不会一端挤死、另一端空成洞。 */
    margin-bottom: calc(26px + var(--air) * 17);
  }

  /* ========================================================================
     一之补二：给左栏顶部空档补位 —— 准星刻度轴 + 轨道伴星（#1065）
     ========================================================================
     背景：轨道环按上黄金线落位后（见上一条），它与品牌行之间让出一条约 200px 的
     空档（1440×900：品牌行 44~88，环 286~422）。空档是**算出来的**、不是失误，
     但一片空白也确实「单调」。这里补两件同族元件，都长在 .orbit 里、
     尺寸一律按 --o 推导 ⇒ 主环放大/缩小，它们同步，不会出现「环变了、伴星没变」。

     ① .axis —— 把环上那根竖向准星（.rail::before，原本只探出 12px）
        向上延长成一根**带刻度的标尺**：它让环看起来「挂在一根轴上」，
        而不是孤零零浮着；同时把品牌行与环在视觉上串成一列。
        长度 = 空档高 − 28px 呼吸（空档 = 0.382×(100vh−2×card_in) − --o/2 − 44px 品牌行高）
             ⇒ 0.382×(100vh−2×card_in) − --o/2 − 72px
        各档实测：1440×900 → 170px、1440×768 → 126px、1920×1080 → 218px，
        三者离品牌行都约 28px。clamp 下限 0：超矮视口（卡片本身已在溢出）轴自动收没。
        ⚠️ 用 vh 公式而不是「--o 的倍数」：空档是 vh 的函数，倍数在矮屏上会捅进品牌行
           （1440×768 用 1.05×--o 会顶到 y=91，而品牌行下沿在 80）。
        ⚠️ 掩膜向上淡出：刻度从环边长出来、朝上化开，不会在品牌行下方「断头」。

     ② .sat —— 一枚与主环同族的伴星节点（虚线轨道圈 + 细环 + 金色核心），
        落在空档右上，补掉横向的空。它是「星际」主题下最自然的配对物：
        主环是行星、它是卫星，两者共用同一套环形语言；再由 .sat-path 那条横贯面板的
        链路把它挂在主环的竖轴上，与刻度轴正交成一套「坐标架」（不这么做，
        它只是一枚与主环无关的小环，空档反而更散）。
        ⚠️ 落点按**中心点**给（1.55·--o, −0.65·--o），尺寸 --so 独立 ⇒ 改大小不会
           把伴星心挪出轨道弧。实测各档虚线轨道圈离品牌行：900 → 44px、
           768 → 15px、1080 → 73px，都不侵入品牌行。

     ⚠️ 两件默认 display:none（见本文件上方 .orbit 基础段），只在左右式 ≥961px 现身：
        ≤960 堆叠式环只有 82px、没有空档可言；居中式根本没有轨道环。
     ======================================================================== */
  html.layout-split .orbit > .axis,
  html.layout-split73 .orbit > .axis {
    display: block;
    position: absolute;
    left: 50%;
    bottom: 100%;                              /* 贴着环的上沿往上长 */
    width: 13px;                               /* = 刻度长度；主线在最左侧 1px */
    margin-left: -.5px;                        /* 让那 1px 主线正压准星轴心 */
    height: clamp(0px, calc(0.382 * (100vh - 2 * var(--card-in)) - var(--o) * .5 - 72px), 340px);
    background: repeating-linear-gradient(180deg,
      rgba(var(--hud-rgb), .56) 0 1px, transparent 1px 12px);
    -webkit-mask-image: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.18) 24%, #000 62%, #000 100%);
            mask-image: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.18) 24%, #000 62%, #000 100%);
  }
  /* 轴心的那条主线（与上方刻度的左端重合，形成「尺子」的边） */
  html.layout-split .orbit > .axis::before,
  html.layout-split73 .orbit > .axis::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 1px;
    background: linear-gradient(180deg, rgba(var(--hud-rgb), .15), rgba(var(--hud-rgb), .62));
  }
  /* 轴根的金色节点：标尺「从环上长出来」的起点标记 */
  html.layout-split .orbit > .axis::after,
  html.layout-split73 .orbit > .axis::after {
    content: ""; position: absolute; left: 50%; bottom: -2px;
    width: 3px; height: 3px; margin-left: -1.5px; border-radius: 50%;
    background: var(--gold-1);
    box-shadow: 0 0 8px 1.5px rgba(var(--gold-rgb), .9);
  }

  /* 伴星与链路同一高度：提成令牌，避免两处各写一遍 -0.65×--o 而漂移。
     （.sat 与 .sat-path 都是 .orbit 的子元素 ⇒ 挂在 .orbit 上两边都取得到） */
  html.layout-split .orbit,
  html.layout-split73 .orbit { --sy: calc(var(--o) * -.65); }

  html.layout-split .orbit > .sat,
  html.layout-split73 .orbit > .sat {
    --so: calc(var(--o) * .44);                /* 伴星直径：主环的 44%（136 → 60px） */
    display: block;
    position: absolute;
    /* 按**中心点**定位（2.6·--o, --sy），再用 translate(-50%,-50%) 抵消自身尺寸：
       若改成「给 left/top 再配 --so 的一半」，改 --so 就会连带挪动伴星心 ——
       尺寸与位置必须解耦（链路要穿过它，位置一漂就脱线）。 */
    left: calc(var(--o) * 3);
    top: var(--sy);
    transform: translate(-50%, -50%);
    width: var(--so); height: var(--so);
  }
  /* ---- .sat-path：横贯面板的**链路**（#1066）----
     诉求：左栏「元素都基本靠左」—— 空档里只有环+轴+伴星挤在 x 58~330，
     右侧 x 330~724 整片空着；而下方文案（尤其两列特性）铺到 724 ⇒ 上窄下宽、
     右边界犬牙交错，看着不协调。补一条从**环的竖轴**出发、横贯面板的虚线链路：
       · 它与上面的竖向刻度轴在 (轴心, 链路高) 处正交，形成一套「坐标架」，
         而不是两个各自漂着的装饰；
       · 伴星正好落在链路上 ⇒ 从「又放了一个小环」变成「链路上的一个节点」。
     ⚠️ 右端**不画终点**：宽度给到远大于任何真实面板宽（超宽屏左栏可到 2000px+），
        由 .side 的 overflow:hidden 在面板右缘裁掉 ⇒ 读作「链路继续伸向面板外」，
        而不是「画到一半停了」。也正因如此，它**不能**用来对齐文案右缘 ——
        文案右缘随视口变化，而 .sat-path 在 .orbit 内，只能用 --o 的相对量，
        取不到 .hero 的内容宽。
     ⚠️ 起点软收：链路起点与刻度轴重叠，硬起画会与轴形成生硬的十字；淡入后
        读作「从轴心引出的信号」。 */
  html.layout-split .orbit > .sat-path,
  html.layout-split73 .orbit > .sat-path {
    display: block;
    position: absolute;
    left: 50%;                                 /* 从环的竖轴出发（与 .axis 同轴） */
    top: var(--sy);
    width: 3000px;
    height: 1px;
    background: repeating-linear-gradient(90deg,
      rgba(var(--hud-rgb), .55) 0 5px, transparent 5px 10px);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, rgba(0,0,0,.4) 9%, #000 24%);
            mask-image: linear-gradient(90deg, transparent 0, rgba(0,0,0,.4) 9%, #000 24%);
  }
  /* 伴星自己的虚线轨道圈（自转 ⇒ 轨道在「跑」） */
  html.layout-split .orbit > .sat > .s-orbit,
  html.layout-split73 .orbit > .sat > .s-orbit {
    position: absolute; inset: calc(var(--so) * -.34); border-radius: 50%;
    border: 1px dashed rgba(var(--hud-rgb), .55);
    animation: spin 26s linear infinite;
  }
  /* 伴星本体：细环 + 径向高光（与主环 .core 同一手法，只是缩到 40%） */
  html.layout-split .orbit > .sat > .s-core,
  html.layout-split73 .orbit > .sat > .s-core {
    position: absolute; inset: 0; border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .5);
    background: radial-gradient(circle at 36% 32%, rgba(255,255,255,.22), rgba(255,255,255,.04) 58%, transparent);
    box-shadow: 0 0 20px rgba(var(--hud-rgb), .4), inset 0 0 12px rgba(255, 255, 255, .12);
  }
  html.layout-split .orbit > .sat > .s-core::after,
  html.layout-split73 .orbit > .sat > .s-core::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 26%; height: 26%; margin: -13% 0 0 -13%; border-radius: 50%;
    background: var(--gold-1);
    box-shadow: 0 0 10px 2px rgba(var(--gold-rgb), .85);
    animation: breathe 3.4s ease-in-out infinite;
  }

  /* ---- .sat2：主环的**第二枚伴星**（#1074 用户诉求「把卫星素材复制一份放到箭头方向」）----
     为什么只写 left/top：本元素沿用 .sat 的**全部**类与子结构（.s-orbit / .s-core），
     上面那批规则（尺寸 --so、虚线轨道圈、细环、金色核心、往返自转）全部照旧命中 ⇒
     外观是同一套「环形语言」，不是第二种子元件。这里只做两件事：**报坐标**。
     ⚠️ 这正是「唯一实现」原则的落地：尺寸/描边/动画都只有一份定义，
        将来调伴星观感时改一处即可，不存在「.sat 改了、.sat2 没改」的漂移口。
     ⚠️ 特异性：(0,4,1) > 上面 .sat 那条 (0,3,1) ⇒ 顺序不影响胜负，
        但仍写在它**之后**，便于连读（两者是同一族）。
     落点：纵向 dy = 1.8·--o（箭头方向上的「下」）；横向 **不按 --o 倍数**，
       而是用 cqw 锚定「距面板内容右缘 90px」—— 原因见下。
       · 方向对准用户箭头：截图里那条箭头斜率约 41°，本版式实测约 2.1:1.8 ≈ 42° 基本重合。
       · ⚠️ **横向为什么不用 --o 倍数**（首版就是这么写的，被探针 ②b 抓出）：
         `--o` 在 split 与 split73 里**是同一个值**（136 / 162），但两套左右式的
         **左栏宽度差很多** —— 1440 档 split73 左栏 968px、split 只有 ~782px；
         1600 档 --o 跳到 162 而 split 的面板只有 869px。
         ⇒ 同一个 `--o` 倍数在 split73 是「留白」、在 split 就是「出界被 .side 的
           overflow:hidden 裁掉」。实测：--o 倍数取 5.1 时 split@1366 右余量 −60px（裁没了）。
         改用 cqw（`.hero` 是 inline-size 容器，100cqw = 其内容宽）锚定右缘留白后，
         **两种左右式、各档视口一律 140px 左右**，一套公式自适应，且不再需要
         按版式写两个魔数。
       · 纵向幅度受**下方文案**约束，按**最坏情况**标定 —— 线上「主张标题」为空
         （左栏比配了标题时矮一整行 ≈37px、特性列表顶边上移），此时竖向空档最紧。
         线上真页实测（客户端注入候选值，零写入）：
             视口        split73 gap    split gap
             1366×720    27px           27px
             1366×768    26px           26px
             1440×800    36px           36px
             1440×900    67px           67px
             1600×900    55px           55px
             1920×1080   110px          110px
         取 1.8 后全档 ≥26px 呼吸；再上一档 1.9 会在 1366 档压住「多模块业务闭环」那一行。
         **刻意不做矮屏媒体查询**：实测 1.8 已在所有高度安全，加档只会让窗口缩放时
         伴星「跳一下」，徒增一个会失效的断点。
       · 与 .kicker / 主张标题 / 说明文字 / 特性条目**逐档实测交叠均为 0**。
         ⚠️ 判「与标题是否相交」必须按**文字墨迹**（Range.getBoundingClientRect）而非元素盒：
            `.hero h1` 是 display:block、宽度吃满内容宽，文字只占左半
            ⇒ 用元素盒会得到「必然相交」的假阳性（标定脚本首版即踩此坑）。
     ⚠️ 兜底必须先给：`cqw` 依赖 `.hero { container-type: inline-size }`（声明在
        `@supports (font-size: 1cqw)` 内）。万一浏览器不认容器查询单位，cqw 会掉回
        **视口宽** ⇒ `100cqw − 90px` 变成「视口宽 − 90」，伴星直接飞出面板被裁。
        故先给一个 --o 倍数兜底（3.9 在 split / split73 下均安全，只是更靠内），
        再在 @supports 里升级为 cqw 版。
     ⚠️ 默认隐藏由上面 `.orbit .sat { display: none; }` 基础段承担
        （.sat2 带 .sat 类 ⇒ 天然被覆盖）；≥961px 显示由 `.orbit > .sat` 那条承担。
        本段**不要**再写 display —— 写死 display:block 会把 ≤960 与居中式一起点亮。 */
  html.layout-split .orbit > .sat.sat2,
  html.layout-split73 .orbit > .sat.sat2 {
    left: calc(var(--o) * 3.9);
    top: calc(var(--sy) + var(--o) * 1.8);
  }
  @supports (font-size: 1cqw) {
    html.layout-split .orbit > .sat.sat2,
    html.layout-split73 .orbit > .sat.sat2 {
      /* ⚠️ 外层 max() 是**方向语义的保底**，不是安全余量：
         面板极窄时（实测 1000×1000，左右式仍生效但左栏仅 ~490~600px）
         `100cqw − 90px − 0.37·--o` 会小到 3.0·--o 以下 ⇒ 副本落到**原件左侧**，
         与「放到箭头方向（右下）」的诉求相反。取 3.3·--o 作下限 ⇒ dx ≥ 0.3·--o（≈41px），
         方向恒为右下。3.3 是**实测**选出来的：比 split@1366 的 cqw 值（3.57·--o）小，
         故正常视口一律走 cqw、完全不受影响。 */
      left: max(calc(var(--o) * 3.3), calc(100cqw - 90px - var(--o) * .37));
    }
  }
  /* 高屏档（≥1000px 高）：竖向空档随屏高线性变宽，1.8 档在 1080 下还余 ~110px，
     而用户箭头指向的落点更低 ⇒ 补一档把余量用掉（环下沿~特性列表顶仍有 29px）。
     ⚠️ 只加这一档、只为「更贴合箭头」而不是为了安全：
        1.8 在**所有**高度都已安全（最紧 1366 档 26px）；此档只影响 ≥1000px 的视口。
     ⚠️ 仍不做矮屏档：矮屏的 1.8 已是实测上限，再收只会让伴星缩到原件脚下。 */
  @media (min-width: 961px) and (min-height: 1000px) {
    html.layout-split .orbit > .sat.sat2,
    html.layout-split73 .orbit > .sat.sat2 {
      top: calc(var(--sy) + var(--o) * 2.3);
    }
  }

  /* 方案 B ②：主张与特性之间建立**两级层次**（间距加大 + 一条极淡分隔线）。
     现状 h1(37px) 下面直接跟 12.5px 的特性条目，层级断崖式跳跃；
     分隔线把「一句主张」与「一组支撑点」明确切成两块。
     ⚠️ 分隔线用白色低透明而非主题令牌：左栏三主题下恒为深底（见本文件头部说明），
        引入 --hud-rgb 会在炽红主题下变成亮线、反而抢戏。 */
  html.layout-split .feats,
  html.layout-split73 .feats {
    margin-top: calc(34px + var(--air) * 14);
    padding-top: calc(28px + var(--air) * 16);
    border-top: 1px solid rgba(255, 255, 255, .12);
  }

  /* 方案 B ③：序号列 16px → 24px —— 序号 / 勾选框 / 文字三段对齐，
     两列的**文字起点**也随之一致（现状序号列过窄，长序号会挤压文字）。 */
  html.layout-split .feat .no,
  html.layout-split73 .feat .no {
    width: 24px;
  }

  /* ========================================================================
     二、右卡：用黄金分割重排竖向留白
     ========================================================================
     现状（1440×900 实测）：.box 高 812px、内容带（头带 → 按钮）471px，
     富余 227px；而它被 .box-head / .box-note 的两个 `margin-top:auto`
     **五五开**分成 123 / 101 两坨 —— 上下一样空 ⇒ 内容块像「漂」在卡片正中，
     上端那颗徽标尤其显得孤零零（截图确认）。

     改法两层，缺一不可：
       ① **先把富余吃进内容自己的节奏**（控件更高、间距更松）——
          这一步才是真正「解决大片空白」：留白是靠**减**，不是靠挪；
       ② **剩下的零头按黄金分割分配**：上 : 下 = 1 : φ(1.618)。
          内容块因此略高于几何中心 —— 这是排版学的「视觉中心」做法，
          纯几何居中的文字块看起来会偏低。

     ⚠️ 为什么新开真元素 .box-sp，而不是把 flex-grow 挂在 .box-head/.box-note 上：
        那两个是**有内容的**元素，让它们长成空白块、再靠 align-items:flex-end
        把内容压到底，隐式耦合太多（头带的引导线、入场动画都会跟着变形）；
        而 auto 外边距**做不到** 1:1.618 —— 规范规定多个 auto 平分剩余空间，
        想拿精确比例只能换成 flex-grow。
     ⚠️ 两端固定的「额外空白」必须清零，否则比例被污染：
        .box-bar 的 22px 下边距、.box-note 的 padding-top(24+air*6)
        都会叠加到间隔块之外，让实测比例偏离 1:1.618。
     ⚠️ .box-sp 默认无 flex 规则（flex:0 1 auto、空 div 高度 0）⇒
        居中式与窄屏下它恒为 0，天然不参与排版，无需额外兜底。
     ======================================================================== */
  html.layout-split .box-sp,
  html.layout-split73 .box-sp {
    flex: 1 1 0;
    min-height: 0;
  }
  /* 下间隔取 φ 倍 ⇒ 上 38.2% / 下 61.8% */
  html.layout-split .box-sp-b,
  html.layout-split73 .box-sp-b {
    flex: 1.618 1 0;
  }

  html.layout-split .box-bar,
  html.layout-split73 .box-bar {
    margin-bottom: 0;
  }

  html.layout-split .box-head,
  html.layout-split73 .box-head {
    margin-top: 0;                            /* 原为 auto（与 .box-note 平分）—— 交给间隔块 */
    margin-bottom: calc(27px + var(--air) * 7);    /* 基准 20 → 27：头带与其下标题的咬合 */
  }

  html.layout-split .box-note,
  html.layout-split73 .box-note {
    margin-top: 0;                            /* 原为 auto，同上 */
    padding-top: 0;                           /* 空白已由 .box-sp-b 承担 */
  }

  /* ---- ① 吃进节奏：控件更高、间距更松 ----
     ⚠️ 写法是「在**原公式的基准**上加一个**常量**，--air 系数原样保留」：
        基准 +9 / +18 / +13 / +14 / +7，合计约 +94px，**与视口高度无关**。
        为什么不改成放大 --air 系数：实测各视口的富余量几乎是常数
        （1440×900 实测 252、1920×1080 约 267、1440×768 约 243 —— 因为 --air
        本身已经把「视口变高」换算成了间距）；
        再乘 --air 会在高屏上**过度吸收**（1080 屏要多吸 160px），把顶部间隔压到
        40px 以下，反而变成「挤」。
     ⚠️ 常量吸收必须确认矮屏有余量：1440×768 / 1366×720 实测富余 243 / 195px，
        吃掉 94px 后仍剩约 150 / 101px，黄金分割后上间隔仍有 57 / 39px，不会顶死。
     ⚠️ 全部经 `html.layout-split/.layout-split73` 限定 ⇒ 居中式（本就紧凑）
        与窄屏（卡片高度随内容）逐值不变。 */
  html.layout-split .box,
  html.layout-split73 .box {
    --fh: calc(57px + var(--air) * 11);            /* 基准 48 → 57：三控件各 +9px */
  }
  html.layout-split .box .cap,
  html.layout-split73 .box .cap {
    margin-bottom: calc(48px + var(--air) * 18);   /* 基准 30 → 48：「主张 → 表单」的沉降台 */
  }
  html.layout-split .field,
  html.layout-split73 .field {
    margin-bottom: calc(32px + var(--air) * 12);   /* 基准 19 → 32 */
  }
  html.layout-split .row-between,
  html.layout-split73 .row-between {
    margin-top: calc(10px + var(--air) * 2);       /* 基准 8 → 10 */
    margin-bottom: calc(38px + var(--air) * 10);   /* 基准 24 → 38 */
  }
}

/* --------------------------------------------------------------------------
   ② 左右 7:3 专属紧凑档（#1071）—— 因服务端启用「指纹 / 面容登录」而追加
   --------------------------------------------------------------------------
   背景（用户反馈原话）：「服务器端加载了使用指纹登陆的按钮，导致卡片超载了」。
   实测（outputs/_measure_split73_card.py，四档视口，只读）：

     视口        无指纹 spA/spB   有指纹 spA/spB   内容需求   卡片可用
     1440×900       64 / 103         16 /  25         775        816
     1280×800       51 /  82          5 /   9         714        728
     1440×768       59 /  96         15 /  23         662        700
     1366×720       41 /  66          0 /   0         662        662   ← 顶死

   `.passkey-box` 实测占 103px（分隔线 17 + 间距 12 + 按钮 50 + 提示位 24 + 上外边距 16）。
   两个弹性间隔块是**黄金分割留白**（.box-sp-a : .box-sp-b = 1 : 1.618）——
   它们被压到 0 就等于「卡片没有呼吸」，这正是「超载」的量化形态。

   ⇒ 处置：**只收 7:3 的竖向节奏**，把约 77~100px 还给间隔块。
      · 账号↔密码、密码↔功能行：32 → 18 起（公式结构「基准 + --air×k」保持不变）
      · .cap 下边距（「请使用企业账号登录」→ 账号框）：48 → 24 ⇒ 账号框**整体上移**
      · 头带下边距：27 → 20
      · 主按钮 ↔ 指纹块：16 → 12（该值来自 login.css 的全局 .passkey-box）
      · 标题两行**居中**（诉求③）；左栏主张在 7:3 下本就居中，两栏因此呼应

   ⚠️ 必须写在上面「吃进节奏」**之后**：两者选择器特异性完全相同（均为 (0,2,1)），
      只有文件顺序能决出胜负（本文件既定的口径：不用 !important）。
   ⚠️ 一律用 longhand（margin-bottom / margin-top）：.box-head 的 `margin-top:auto`、
      `.cap` 的 `margin-top:10px` 都**不能**被 shorthand 重置，否则顶端对齐会塌。
   ⚠️ 只加 html.layout-split73 前缀 ⇒ split（默认左右式）/ center / aurora / ticker
      四套版式逐值不变；≤960 的降载档（(0,1,0)）也不会被静默压掉。
   ⚠️ 已知边界（不处理，显式留档）：若同时开启图形验证码（.field.lg-cap-row，
      异常路径：同源失败满阈值才出现），内容需求再增约 75px，1366×720 下会再度顶死。
      不为此继续压缩——那会牺牲常态间距；届时靠「换更高视口」或后续单独收紧验证码行。
   ========================================================================== */
@media (min-width: 961px) {
  html.layout-split73 .box h3,
  html.layout-split73 .box .cap { text-align: center; }

  html.layout-split73 .field {
    margin-bottom: calc(18px + var(--air) * 8);      /* 基准 19 → 18；共用档为 32 */
  }
  html.layout-split73 .box .cap {
    margin-bottom: calc(24px + var(--air) * 8);      /* 基准 30 → 24；共用档为 48 */
  }
  html.layout-split73 .row-between {
    margin-bottom: calc(24px + var(--air) * 6);      /* 基准 24 不变；共用档为 38 */
  }
  html.layout-split73 .box-head {
    margin-bottom: calc(20px + var(--air) * 6);      /* 基准 20 不变；共用档为 27 */
  }
  html.layout-split73 .passkey-box { margin-top: 12px; }   /* login.css 全局 16 → 12 */
}

/* ≥1600 超宽屏：轨道环与边距按同一比例放回。
   136/190 ≈ 0.716，226 × 0.716 ≈ 162；边距 26/40 ≈ 0.65，48 × 0.65 ≈ 31 → 取 34 与
   上一档留出可见差异。缺失这一档，超宽屏的轨道环会被 @media(min-width:961px) 里的
   136px 钉死（(0,2,1) 压过本文件上方 @media(min-width:1600px) 的 .orbit(0,1,0)）。 */
/* ⚠️ 下边距**不在此处覆盖**：它已由上面那条 `calc(26px + var(--air) * 17)` 承担；
   这里再写一个固定值会把这档的「随屏高伸缩」截断（原先的 48px 同理，故一并收掉）。 */
@media (min-width: 1600px) {
  html.layout-split .orbit,
  html.layout-split73 .orbit {
    --o: 162px;
  }
}

/* ==========================================================================
   版式 ④：极光式（aurora）
   --------------------------------------------------------------------------
   诉求（用户原话）：「科技感十足，页面元素不要太多太杂，有动感」。
   四套版式的分工 ——
     · split / split73 —— 信息量优先：左栏主张 + 轨道环 + 特性网格 + 读数条
     · center          —— 装饰铺满 + 严格中轴，仪式感最强
     · aurora（本段）  —— **做减法**：收起左栏信息层与全部 HUD 装饰，只留
                          「一层会漂移的极光」+「一张玻璃卡片」。
                          动感只集中在两处：极光带缓慢游移、卡片描边循环流转。
                          元素总量是四套里最少的 —— 这正是「不要太多太杂」的落地。
   ⚠️ 与既有三套的关系：**只做加法**。.aurora 层默认 display:none，
      上面所有规则一行不改 ⇒ split / center / split73 逐像素不变。
   ⚠️ 层叠坑（与版式②同源）：.side 必须 absolute 且**不写 z-index** ——
      fixed 或带 z-index 都会创建层叠上下文，把 .side-top 的 z-index:13 封在里面，
      右上角的主题切换按钮就「看得见、点不动」（#1059 实测踩到）。
   ⚠️ 本段**不设宽度闸门**：极光式在所有视口都成立（≤960 也是同一形态），
      与 #1064 那种「只对左右式 ≥961 生效」的规则性质不同。
   ⚠️ 全部动画只改 transform / opacity ⇒ 走合成器，不触发重排重绘（低端机也不掉帧）。
   ========================================================================== */
.aurora { display: none; }   /* 兜底：非极光式一律不渲染（与 .crown/.c-feats/.c-foot 同口径） */

html.layout-aurora .split {
  display: block; position: relative; overflow: hidden;
  height: 100vh; height: 100dvh;
}
html.layout-aurora body { overflow: hidden; }

/* ---- 背景层：.side 从「左半屏」退成「整屏」，且只留星尘 ---- */
/* ⚠️ 底色必须压到**近黑**：极光是「暗底上的亮带」，底色一旦偏亮
   （.side 原本是深蓝渐变），光带就混进背景、完全看不出在流动 —— 实测第一版正是这样。
   三主题下登录页左栏本来就恒为深底（见本文件头部说明），故此处用固定深色是合理的，
   不需要为 gold / red 各配一套。 */
html.layout-aurora .side {
  position: absolute; inset: 0;
  background: radial-gradient(124% 104% at 50% -6%, #0b1428 0%, #05090f 58%, #010206 100%);
}
/* canvas 星尘层（#sideBg）**保留**：它是本版式唯一的「纵深感」来源（透视网格 + 星点），
   而且只有一张画布、不是多元素堆叠 ⇒ 不违背「元素不要太多太杂」。
   ⚠️ 但要压一档透明度：它画的是亮线，在近黑底上比原来抢眼，会盖过极光。 */
html.layout-aurora #sideBg { opacity: .5; }
html.layout-aurora .side-inner { display: none; }   /* 品牌 / 轨道环 / 特性网格 / 底部读数条 */
html.layout-aurora .side .lay { display: none; }    /* 网格 / 点阵 / 流光柱 / 扫描线 / 刻度尺 / HUD / 暗角 / 颗粒 */
html.layout-aurora .side .coords { display: none; } /* NODE ID 读数：极简下不留 */
/* 主题切换是**功能**不是装饰，必须保留；只把它从「左上角与读数并排」挪到右上角独占。
   ⚠️ .side-top 原本 left/right 双撑 + space-between（读数在左、按钮在右）；
      读数收起后按钮会被顶到左侧 ⇒ 必须把 left 收掉才回得到右上角。 */
html.layout-aurora .side-top { left: auto; right: var(--spx); }
/* 整屏叠加层：径向暗角（四角压暗，卡片才「浮」得起来）。
   ⚠️ 复用 .side::after（它在左右式下是「左右分隔光带」，整屏层上没有意义）。
   ⚠️ z-index 必须小于 .main 的 10，否则会把卡片一起压暗。 */
html.layout-aurora .side::after {
  content: ""; display: block;
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(132% 96% at 50% 42%, transparent 44%, rgba(0, 0, 0, .42) 100%);
}

/* ---- 极光层：三片大尺度径向渐变缓慢漂移 ----
   ⚠️ 柔边靠 filter: blur()，**不是**把 radial-gradient 的半径调大 ——
      渐变的边缘是硬弧，模糊过才有「极光」的质感；且 blur 只光栅化一次。
   ⚠️ 容器 inset:-20% 让光带能游出屏幕再回来，否则会看到「边缘被切断」的硬边。
   ⚠️ 配色一律走主题令牌（accent + gold），三主题各自协调；层次感由尺寸 / 透明度 /
      漂移速度的差异给出，不引入固定色（固定色会在炽红主题下突兀）。 */
html.layout-aurora .aurora {
  display: block; position: absolute; inset: -20%;
  z-index: 1; pointer-events: none;
}
html.layout-aurora .aurora i {
  position: absolute; display: block; border-radius: 50%;
  filter: blur(62px); will-change: transform;
}
html.layout-aurora .aurora i:nth-child(1) {          /* 主光：主题色，最大最亮 */
  left: 0%; top: 0%; width: 74%; height: 42%; opacity: .72;
  background: radial-gradient(circle, rgba(var(--accent-rgb), 1), transparent 68%);
  animation: auDrift1 27s ease-in-out infinite alternate;
}
html.layout-aurora .aurora i:nth-child(2) {          /* 补光：金，暖色对冲冷色主光 */
  right: -6%; top: 22%; width: 64%; height: 36%; opacity: .50;
  background: radial-gradient(circle, rgba(var(--gold-rgb), .95), transparent 70%);
  animation: auDrift2 34s ease-in-out infinite alternate;
}
html.layout-aurora .aurora i:nth-child(3) {          /* 底光：主题色，淡而广，压出纵深 */
  left: 12%; bottom: -2%; width: 72%; height: 40%; opacity: .42;
  background: radial-gradient(circle, rgba(var(--accent-rgb), .9), transparent 72%);
  animation: auDrift3 41s ease-in-out infinite alternate;
}
/* ⚠️ 每帧都带一个**固定** rotate：三个光斑都是扁椭圆，旋转后才读作「斜向光带」，
   那才是极光的形态；不转就只是三个圆形光晕（实测第一版观感偏「光晕」而非「极光」）。
   三个角度各不相同 ⇒ 三条带交叉，画面的方向感不单调。
   ⚠️ rotate 写在 translate3d **之前**：位移因此沿「旋转后的坐标系」发生，
      光带是沿着自身长度方向滑动的（写在后面会变成横平竖直地平移，很假）。 */
@keyframes auDrift1 { from { transform: rotate(-16deg) translate3d(0, 0, 0) scale(1); }    to { transform: rotate(-16deg) translate3d(13%, 8%, 0) scale(1.16); } }
@keyframes auDrift2 { from { transform: rotate(15deg) translate3d(0, 0, 0) scale(1.08); }  to { transform: rotate(15deg) translate3d(-15%, -7%, 0) scale(.95); } }
@keyframes auDrift3 { from { transform: rotate(-7deg) translate3d(0, 0, 0) scale(1.04); }  to { transform: rotate(-7deg) translate3d(10%, -12%, 0) scale(1.18); } }

/* ---- 卡片层：整屏居中，只有一张玻璃卡 ---- */
html.layout-aurora .main {
  position: relative; z-index: 10;
  height: 100vh; height: 100dvh;
  flex-direction: column; align-items: center; gap: 0;
  /* ⚠️ 与居中式的理由相同：内容高于容器（矮屏）时纯 center 会把顶部裁掉且滚不回去；
      safe 让它在那种情况下退化成「从顶部开始」，内容仍可滚动可达。两条并存兼作回退。 */
  justify-content: center;
  justify-content: safe center;
  padding: 28px 22px;
  background: none; perspective: none;
  overflow-y: auto; overflow-x: hidden;
}
html.layout-aurora .main::before,
html.layout-aurora .main::after { display: none; }   /* 辉光 / 暗角：整屏层已由 .aurora 与 .side::after 承担 */
html.layout-aurora .main > .dl { display: none; }    /* 光柱 / 同心环 / 点阵 / 刻度 / 准星：极简下全收 */

html.layout-aurora .stage { width: 100%; max-width: 430px; height: auto; max-height: none; }
html.layout-aurora .console { padding: 0; animation: none; }
html.layout-aurora .console::before,
html.layout-aurora .console::after { display: none; } /* 虚线 HUD 外框 / 金色巡游光外环 */
html.layout-aurora .console .cf,
html.layout-aurora .console .cl { display: none; }    /* 四角金标 / 竖排读数 */
/* 玻璃卡：半透明 + 背景模糊 ⇒ 极光透过来，卡片与背景是「一体」的，而不是贴在背景上的白块。 */
html.layout-aurora .box {
  padding: 30px 26px 24px;
  border-radius: 20px;
  border: 1px solid rgba(var(--accent-rgb), .22);
  background:
    linear-gradient(180deg, rgba(var(--accent-rgb), .07), transparent 44%),
    rgba(9, 15, 30, .72);
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
          backdrop-filter: blur(22px) saturate(1.35);
  box-shadow:
    0 34px 80px -34px rgba(0, 0, 0, .85),
    inset 0 0 0 1px rgba(255, 255, 255, .03),
    0 0 70px -24px rgba(var(--accent-rgb), .5);
  animation: auRise .85s cubic-bezier(.22, .9, .3, 1) both;
}
@keyframes auRise {
  from { opacity: 0; transform: translateY(18px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
html.layout-aurora .box::before { display: none; }   /* 顶部光条：与下面的描边流光语义重复 */
/* 卡片描边流光：复用既有的 .box::after（@property --ang + angGo），
   只把拖尾拉长、速度调快 ⇒ 同样是「一圈在跑的光」，观感更锐、更抓眼。 */
html.layout-aurora .box::after {
  border-radius: 21px;
  padding: 1.3px;
  background: conic-gradient(from var(--ang),
    transparent 0deg 252deg,
    rgba(var(--accent-rgb), .95) 300deg,
    rgba(255, 255, 255, .9) 322deg,
    transparent 344deg 360deg);
  animation-duration: 5.4s;
}

/* ---- 卡内：只留「这是哪个系统」与「怎么登录」，其余全部收起 ---- */
html.layout-aurora .box-bar,
html.layout-aurora .box-head { display: none; }       /* 终端标题栏 / 徽标带 */
/* 极简品牌头：复用 .crown（DOM 本就在卡内，居中式也用它）——
   ⚠️ 不能复用居中式的样式（那是 html.layout-center 前缀，不作用于本版式），
      必须在这里完整定义；且它是「卡内一块文字」⇒ 边框 / 圆角 / 底纹 / 阴影 /
      内边距全部归零，只留排版。
   ⚠️ 文案与居中式的冠**同源**（系统实名 + 说明文字），口径与之保持一致。 */
html.layout-aurora .crown {
  display: block; position: static;
  /* 下边距 32px 是**实测**定的，不是拍脑袋：#1069 裁掉 h3/.cap 后，
     标题底到表单顶一度只剩 20px，比表单内部「字段↔字段」的 31px 还紧 ⇒
     标题读起来像「表单的第一行」而不是「表单的标题」。取 32px（略大于 31px）
     把「标题块 / 表单」重新分成两组。⚠️ 改这里要同步跑版式探针 ⑤c 的节奏断言。 */
  width: auto; max-width: none; margin: 0 0 32px; padding: 0;
  border: 0; border-radius: 0; background: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: none; text-align: center; animation: none;
  container-type: inline-size;      /* h1 的 cqw 锚点（与居中式的冠同一手法） */
}
html.layout-aurora .crown::after { display: none; }   /* 共边金线：卡片内部没有接缝 */
html.layout-aurora .cr-rule { display: none; }        /* 冠下三段金线：极简下不要 */
/* ⚠️ 乘 --hero-ts（「信息配置 → 主张标题字号」注入的无单位系数）：居中式的冠主标题与
   左右式左栏主张**共用同一个字号配置**，本版式沿用同一口径，
   否则该配置在极光式下会静默失效（调了没反应）。 */
html.layout-aurora .crown h1 {
  font-size: clamp(16px, calc(7.2cqw * var(--hero-ts, 1)), calc(30px * var(--hero-ts, 1)));
  line-height: 1.28; font-weight: 600; letter-spacing: .02em; margin: 0;
  text-wrap: balance;
}
/* ⚠️ .cr-eye（眉标行）与 .mk（徽标）的**尺寸只在居中式的版式层里定义过**；
   本版式不写就会掉进两处陷阱，实测第一版正是这样：
     · .mk 没有宽高 ⇒ SVG 走自己的默认尺寸（≈200px），把卡片撑到超出视口、
       底部按钮被裁掉半截；
     · .en / .div / .ver 没有版式样式 ⇒ 英文名与版本号按正文排版，与眉标口径不一致。
   故这里按居中式的同一组数值补一份（数字有意保持一致：两套版式的眉标观感应统一）。 */
html.layout-aurora .crown .cr-eye {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 8px; margin-bottom: 10px;
  font-size: 10.5px; letter-spacing: .18em; color: var(--text-dim);
}
html.layout-aurora .cr-eye .mk {
  width: 21px; height: 21px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  background: rgba(var(--accent-rgb), .14);
  border: 1px solid rgba(var(--accent-rgb), .38);
  color: var(--accent-2);
}
html.layout-aurora .cr-eye .mk svg { width: 12px; height: 12px; }
html.layout-aurora .cr-eye .div { width: 1px; height: 9px; background: rgba(255, 255, 255, .16); }
html.layout-aurora .cr-eye .en { font-size: 9px; letter-spacing: .3em; text-transform: uppercase; color: var(--text-faint); }
html.layout-aurora .cr-eye .ver em {
  font-style: normal; font-size: 8.5px; letter-spacing: .16em;
  color: var(--accent-2);
}
html.layout-aurora .crown .sub { font-size: 12px; line-height: 1.75; opacity: .72; margin: 8px auto 0; }

/* 「欢迎回来 / 请使用企业账号登录」在极光式下**取消**（与居中式同一口径，见 #1060 段）：
   卡片顶部的主标题已经说清「这是哪个系统」，再顶一句问候语属重复信息；
   极光式讲的就是「元素不要太多太杂」，这两行是首选裁掉项。
   收起后卡片高度自动收紧 —— .main 是纵向 flex 居中容器、.box 无需补回呼吸
   （居中式当初要补，是因为它的 .box 参与等高分配；极光式不参与）。
   ⚠️ 只作用于极光式：左右式的卡片里**没有**主标题，那两行是必要的，照旧保留。
   ⚠️ 用 CSS 收起而非模板条件渲染 ⇒ 四套版式仍是同一份 DOM，将来还原只需删这两行规则。 */
html.layout-aurora .box h3,
html.layout-aurora .box .cap { display: none; }

/* 卡片高度随内容（不做等高）⇒ 两个黄金分割间隔块恒为 0 高，不会撑出空档。
   ⚠️ 它们的 flex-grow 只在 html.layout-split* 下被打开（见 #1064 段），此处无需处理。 */

/* 底部信息行：与卡片下沿收口，加一条极淡的分隔线 */
html.layout-aurora .box-note {
  margin-top: 22px; padding-top: 14px;
  border-top: 1px solid rgba(255, 255, 255, .07);
}

/* ≤960（含移动端）：卡片占满可用宽，纵向仍居中；极光与描边照旧 ——
   本版式没有「左右式」那种半屏约束，因此不存在需要还原的堆叠态。 */
@media (max-width: 960px) {
  html.layout-aurora .stage { max-width: none; }
  html.layout-aurora .box { padding: 26px 20px 20px; }
}

/* ==========================================================================
   版式 ⑤：行情式（ticker）—— #1079 重构终稿（方案 B「暗金仪表屏」）
   --------------------------------------------------------------------------
   来源：用户指定参考 goldmorning.cn 首页首屏那块「实时金价 + 门店成交滚动播报」——
   定时轮询取数、纵向循环滚动、姓名脱敏。本版式把同一套「行情感」搬进登录页左栏：
     · 顶部：品牌行 + 「实时」呼吸点 + 秒级时钟（时钟在金价标题行内，**不推右**）
     · 金价：足金 999.9 现价大数 + 涨跌（红涨绿跌）+ 今高 / 今低 / 昨收
     · 播报：实时成交**台账**（无缝纵向滚动，演示数据，姓名脱敏）
   右栏仍是一张标准登录卡，但**不再撑满高度** —— 本版式没有「左右等高」的诉求，
   卡片按内容高度在右栏垂直居中即可。

   ── 定稿来源 ─────────────────────────────────────────────────────────────
   #1079（杨大爷：「行情式登陆页丑的很，强烈建议推翻重新优化」）：先出原型、后定稿。
   两套方案（A 数据看板式 / B 暗金仪表屏）在**真实页面**上做过量化对比，最终选定 B。
   设计底稿、探针脚本与量化留痕见 outputs/登录页原型_行情式重构_20261006/
   （ticker-v2.css / _build_prototype.py / measurements.json / 对比_*.png）——
   那是**设计快照**，不再随本文件演进，不要以它为准。
   本段即 B 的**合并终稿**：原型期那层 .pv-b 覆盖规则已全部内联，不留「基线 + 覆盖」两层。

   ── 先量化再动手：三处根因（全部实测，不是观感）────────────────────────
   ★ R1 左栏文字对比度不足（**baseline 就已存在的真缺陷，也是「丑」的物理原因**）
       `.pulse { color: var(--text) }` 取的是**页面正文色**：
         · 深蓝 --text = #e0e8f0（亮）⇒ 落在深色左栏上，正常；
         · 炽红 --text = #1a1a1a（暗）⇒ 落在红色左栏上，实测
           主数字 1.62:1 · 播报姓名 1.62:1 · 克重 2.65:1 · 金价标签 1.62:1 · 注脚 1.02:1
           ⇒ **五项全部低于 WCAG AA 的 4.5:1**；同页深蓝主题则 11.30 / 11.30 / 6.90 /
           11.30 / 5.41 全数达标 ⇒ **该缺陷只在亮底主题（炽红）暴露**。
       修法（最小且根治）：在 .pulse 上把 --text / --text-dim / --text-faint
       **本地重定义**为面板自洽的亮色三档 —— CSS 变量沿继承生效，内部所有
       var(--text) 一次性转正，无需逐个选择器改。
       实测修正后：主数字 / 姓名 / 标签 5.84:1 · 克重 4.77:1 ⇒ 达标。
   ★ R2 涨跌语义色在红底上不可读：--up-rgb 原为 (255,104,104)，落红底仅 **2.72:1**。
       ⇒ 提亮一档 + **加暗底胶囊承托**（rgba(0,0,0,.26)，与主题解耦），实测 5.74:1；
       跌色同步提亮 —— 两个方向在同一底上必须对称可读。
   ★ R3 不能用 --panel 当局部底：炽红下 --panel 是 rgba(255,255,255,.7)（亮）
       ⇒ 会与 R1 的亮字对冲（白底白字）。本段一律改用与主题**无关**的
       「压暗层 + 白色低透明描边」：三主题的左栏底色都是饱和深色，压暗恒成立。
   ── 设计层（版式重做，不是修 bug）──────────────────────────────────────
     · 主数字 80.6px → clamp(44px, 4.2vw, 60px)：原尺寸与同屏 10~17px 文字相差 4.7~8 倍，
       整屏只有一个「高音」，读作行情站而非企业平台入口。
     · 播报行原为 grid `1fr auto auto`：姓名在 1fr 格内左贴边、克重右贴边，中间数百像素
       无任何视觉承载（实测中段承载仅 24px / 行宽 720）⇒ 改为「地区姓名 | 量程柱 | 克重」，
       中段交给**按真实克重归一**的量程柱（实测 550px）——台账，不是长名单。
     · 金价区不套卡片外框，改「左侧金色竖标 + 数值上方刻度尺」，读作仪表。
     · 三副指标由三处飘着的裸数字收成一条带上下细线的等分带。
     · 右卡 `.box-head .bh-line` 没有文字承接时读作「断线」而非「引导」⇒ 收起；
       竖向节奏改用明确像素收口（baseline 的间距全由 --air 放大而来，屏越高等比越松；
       实测 1440×900：控件高 59px / 字段距 31px / .cap 下距 48px / .row-between 下距 34px）。
   --------------------------------------------------------------------------
   ⚠️ 红涨绿跌是**有意**的（--up-rgb / --dn-rgb）：与国内金市 / A 股口径一致，
      不要按欧美习惯反过来。
   ⚠️ 与既有四套的关系：**只做加法**。.pulse 默认 display:none，上面所有规则
      一行不改 ⇒ split / center / split73 / aurora 逐像素不变。
   ⚠️ 本段选择器都是 (0,2,1)，会**盖过** ≤960 那批 (0,1,0) 的堆叠规则
      （.split / .side / .main / .stage / .box 全在其中）⇒ 窄屏形态必须由本段
      自带一份，不能指望上面的断点自动生效（极光式同理，见其段末）。
   ⚠️ **量程柱宽度由模板算好、用内联 `--w` 传进来**（见 login.html 的 .pl-row）。
      本段**不得**出现任何按行号硬编码的百分比：那样一改 _feed 就会与数值脱钩，
      静默画出错误的柱长（原型期正是用 nth-child 硬编码 16 档 —— 因为当时只能注入
      CSS、碰不到 DOM）。现由模板按「克重 ÷ 本批最大值」统一算出，单一来源。
   ========================================================================== */
.pulse { display: none; }   /* 兜底：非行情式一律不渲染（与 .crown / .aurora 同口径） */

html.layout-ticker .split {
  display: grid;
  /* 右栏：420px 起步（1024 视口下卡片仍装得下）、34% 随屏增长、560px 封顶
     （超宽屏上把余量全给左栏的行情屏，卡片不至于被拉成一张空旷的大白板）。 */
  grid-template-columns: minmax(0, 1fr) clamp(420px, 34%, 560px);
  height: 100vh; height: 100dvh;
  overflow: hidden;
}
html.layout-ticker body { overflow: hidden; }
/* 左栏底色与右侧分隔光带**沿用 .side 自身**（三主题下它恒为深底，正好是行情屏需要的底），
   这里只补两条与行情语义强相关的令牌。 */
html.layout-ticker .side {
  --up-rgb: 255, 104, 104;      /* 涨：红 */
  --dn-rgb: 58, 214, 132;       /* 跌：绿 */
}
/* 炽红主题：左栏是深红底 ⇒ 红绿字对比度都不足（实测涨色原 2.72:1），两色一并提亮；
   再叠一层暗底胶囊承托（见 .pl-q-chg），实测 5.74:1。 */
html.theme-red.layout-ticker .side {
  --up-rgb: 255, 192, 192;
  --dn-rgb: 110, 236, 170;
}

/* 收起左栏原有的「营销层」：品牌行 / 轨道环 / 特性网格 / 底部读数条 ——
   它们的活由 .pulse 接管（同一位置换成行情数据，信息密度更高、更贴业务）。 */
html.layout-ticker .side-inner { display: none; }
html.layout-ticker .side .coords { display: none; }
/* 装饰层筛掉抢戏的：保留「网格 + 粒子 + 数据流 + HUD 角 + 暗角」——
   行情屏上这几件刚好读作「终端」；而雷达扇面 / 斜向晶格 / 扫描线 / 刻度尺 /
   金箔扫光在大字数据底下只会互相打架（本段自己另画了一把刻度尺，那里更该是独一份）。
   ⚠️ .lay* 全部是 <section class="side"> 的子元素（右栏的装饰层用的是 .dl，不是 .lay）
      ⇒ 这几条不会误伤右栏。 */
html.layout-ticker .side .lattice,
html.layout-ticker .side .sweep,
html.layout-ticker .side .scanline,
html.layout-ticker .side .ruler-x,
html.layout-ticker .side .ruler-y,
html.layout-ticker .side .gold-sweep { display: none; }
/* 颗粒噪点另收：仪表感要干净底，噪点会让刻度尺与量程柱显脏。 */
html.layout-ticker .lay.grain { display: none; }
/* #sideBg 是左栏的星空 canvas：.55 → .46，把层次让给行情数据本身 */
html.layout-ticker #sideBg { opacity: .46; }
html.layout-ticker .lay.streams { opacity: .42; }
/* 主题切换是**功能**不是装饰：从「左上角与读数并排」挪到右上角独占
   （左上角已被行情屏的品牌行占用）。与极光式同一手法。 */
html.layout-ticker .side-top { left: auto; right: var(--spx); }

/* ---- 行情屏：铺满左栏，纵向四段（品牌行 / 金价 / 台账 / 注脚） ---- */
html.layout-ticker .pulse {
  display: flex; flex-direction: column;
  position: absolute; inset: 0; z-index: 2;
  padding: clamp(30px, 5.2vh, 62px) clamp(30px, 4.4vw, 72px);
  /* 纯展示：不拦指针（否则右上角的主题切换会被这层盖住点不动） */
  pointer-events: none;
  /* ★R1：面板内自洽的亮色三档 —— **不跟随**主题正文色。
     炽红主题的 --text 是 #1a1a1a（给亮底用），落在这块恒深的左栏上实测仅 1.62:1。
     在这里本地重定义，内部所有 var() 沿继承一次性转正（无需逐个选择器改）。 */
  --text: rgba(255, 255, 255, .96);
  --text-dim: rgba(255, 255, 255, .86);
  /* faint 档 ≈3.4:1，**刻意保留**给「参考上海黄金交易所实时行情 / 演示数据」这类
     非关键注脚（是标注、不承载信息）。要卡到 4.5:1 就把它提到 .86 —— 这是显式取舍，
     不是漏改。 */
  --text-faint: rgba(255, 255, 255, .70);
  /* 面板内专用「亮金」：--gold-1 在炽红底上仅 3.9:1 ⇒ 提亮到 #ffe6ac，
     实测克重列 4.77:1（对 --side-1 / --side-2 两端取最差值）。 */
  --pv-gold: #ffe6ac;
  color: var(--text);
  font-variant-numeric: tabular-nums;   /* 价格跳动时数字不左右抖 */
}
/* 内容宽封顶：超宽屏上左栏可达 1300px+，不限制会让金价与台账拉得支离破碎 */
html.layout-ticker .pulse > * { width: 100%; max-width: 720px; }

/* ---- 品牌行：下方一条贯通内容宽的引线，与「仪表」呼应 ---- */
html.layout-ticker .pl-top {
  display: flex; align-items: center; gap: 12px;
  padding-bottom: 14px; position: relative;
}
html.layout-ticker .pl-top::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, rgba(255, 255, 255, .30), rgba(255, 255, 255, .04) 72%, transparent);
}
html.layout-ticker .pl-top .mk {
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  display: grid; place-items: center;
  background: rgba(var(--accent-rgb), .16);
  border: 1px solid rgba(var(--accent-rgb), .4);
  color: var(--accent-2);
}
html.layout-ticker .pl-top .mk svg { width: 15px; height: 15px; }
/* #1073：与左右式品牌名共用同一字号配置（同一视觉角色：左栏顶部的系统名称）。
   基准 13.5px 保持不动，仅随 --st-ts 等比缩放 ⇒ 默认（系数 1）时与改动前逐值等价。 */
html.layout-ticker .pl-top .nm { font-size: calc(13.5px * var(--st-ts, 1)); font-weight: 600; letter-spacing: .07em; }
html.layout-ticker .pl-top .en {
  font-size: 9px; letter-spacing: .24em; text-transform: uppercase; color: var(--text-faint);
}

/* ---- 金价区：左侧金色竖标 + 数值上方刻度尺 + 涨跌胶囊 ---- */
html.layout-ticker .pl-quote {
  position: relative;
  margin-top: clamp(28px, 5.2vh, 50px);
  padding-left: 22px;
  border-left: 2px solid rgba(255, 255, 255, .26);
}
html.layout-ticker .pl-quote::before {          /* 竖标顶端的高亮段 */
  content: ""; position: absolute; left: -2px; top: 0; width: 2px; height: 32px;
  background: var(--gold-1, #f6e6a8); box-shadow: 0 0 14px rgba(var(--gold-rgb, 246, 230, 168), .8);
}
html.layout-ticker .pl-q-hd { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
html.layout-ticker .pl-q-hd b { font-size: 12.5px; font-weight: 600; letter-spacing: .16em; color: var(--text-dim); }
/* ⚠️ 只命中 .src（「参考上海黄金交易所实时行情」）——
   写成 `.pl-q-hd span` 会连「实时 + 时钟」一起改变字号与颜色（它也是 span）。 */
html.layout-ticker .pl-q-hd .src { font-size: 10px; letter-spacing: .12em; color: var(--text-faint); }
/* ⚠️「实时 + 时钟」**跟着金价行一起排，绝不推到最右**：
   右上角是主题切换胶囊的固定席位（.side-top，right: var(--spx)，宽 ≈ 92px ⇒
   占掉距左栏右边 58~150px 这一段）。而内容区右缘在 1440 下距左栏右只有 127px
   ⇒ 任何 `margin-left: auto` 贴右的元素都会与胶囊**重叠**（实测首版正是这样）。
   放在「参考上金所」之后、用一条细竖线分隔，语义上也更顺。 */
html.layout-ticker .pl-live {
  display: inline-flex; align-items: center; align-self: center; gap: 7px;
  padding-left: 12px; border-left: 1px solid rgba(255, 255, 255, .18);
  font-size: 10px; letter-spacing: .14em; color: var(--text-dim);
}
/* ⚠️ 必须写全 `.pl-q-hd .pl-live b`：时钟搬进金价行之后，`.pl-q-hd b`（足金 999.9）
   与它同为 (0,2,2)，而那条规则在文件里更靠后 ⇒ 只写 `.pl-live b` 会被压掉，
   时钟会以「金价标题」的字号渲染出来。
   字号与上面的 .pl-live 取同一档 10px（两者在同一行、同一视觉角色，不留半像素差）。 */
html.layout-ticker .pl-q-hd .pl-live b {
  font-size: 10px; font-weight: 500; letter-spacing: .06em; color: var(--text);
}
/* 呼吸点：原 #ff5a5a 在炽红主题的红底上几乎消失 ⇒ 提亮并加一圈白边，
   让它在三个主题下都读作「活着」。 */
html.layout-ticker .pl-live i {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: #ff9d9d; box-shadow: 0 0 0 1px rgba(255, 255, 255, .4);
  animation: plPulse 1.9s ease-out infinite;
}
@keyframes plPulse {
  0%   { box-shadow: 0 0 0 1px rgba(255, 255, 255, .4), 0 0 0 0 rgba(255, 157, 157, .6); }
  70%  { box-shadow: 0 0 0 1px rgba(255, 255, 255, .4), 0 0 0 9px rgba(255, 157, 157, 0); }
  100% { box-shadow: 0 0 0 1px rgba(255, 255, 255, .4), 0 0 0 0 rgba(255, 157, 157, 0); }
}

html.layout-ticker .pl-q-val {
  position: relative; display: flex; align-items: flex-end; gap: 9px;
  margin-top: 4px; padding-top: 20px;
}
html.layout-ticker .pl-q-val::before {          /* 数值上方的刻度尺（自左向右淡出） */
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 8px;
  background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .34) 0 1px, transparent 1px 22px);
  -webkit-mask-image: linear-gradient(90deg, #000, transparent 92%);
          mask-image: linear-gradient(90deg, #000, transparent 92%);
}
/* ⚠️ 字号用 vw 而非 cqw：本版式内容**不是**在容器查询上下文里（.pulse 没开
   container-type），用 cqw 会解析失败、直接掉回 16px。
   上限 60px 是设计决定：1440 起就被封顶 ⇒ 超大屏上它不再继续放大。 */
html.layout-ticker .pl-q-val b {
  font-size: clamp(44px, 4.2vw, 60px); line-height: 1;
  font-weight: 700; letter-spacing: -.012em;
}
html.layout-ticker .pl-q-val i {
  font-style: normal; font-size: 12.5px; color: var(--text-dim); padding-bottom: 11px;
}
/* 价格每次更新闪一下。⚠️ JS 侧必须「摘类 → 强制读一次布局 → 挂类」，
   否则同名动画不会重播（次次都只剩第一帧，看起来像「只闪了一次」）。 */
html.layout-ticker .pl-q-val.flash-up b { animation: plFlashUp .92s ease-out; }
html.layout-ticker .pl-q-val.flash-dn b { animation: plFlashDn .92s ease-out; }
@keyframes plFlashUp {
  0%   { color: rgb(var(--up-rgb)); text-shadow: 0 0 26px rgba(var(--up-rgb), .45); }
  100% { color: var(--text); text-shadow: none; }
}
@keyframes plFlashDn {
  0%   { color: rgb(var(--dn-rgb)); text-shadow: 0 0 26px rgba(var(--dn-rgb), .45); }
  100% { color: var(--text); text-shadow: none; }
}
/* 涨跌：胶囊承托（★R2）。暗底与主题解耦 —— 它只在"这一小块局部底"上生效，
   不参与面板整体配色，因此不会与三套主题的左栏底色打架。 */
html.layout-ticker .pl-q-chg {
  display: inline-flex; align-items: center; gap: 12px;
  margin-top: 16px; padding: 4px 12px;
  border-radius: 999px; font-size: 12.5px; letter-spacing: .04em;
  border: 1px solid rgba(255, 255, 255, .22);
  background: rgba(0, 0, 0, .26);
}
html.layout-ticker .up { color: rgb(var(--up-rgb)); }
html.layout-ticker .dn { color: rgb(var(--dn-rgb)); }
/* 涨跌箭头由 CSS 供（JS 只改数值与 up/dn 类 ⇒ 不用为符号再写一处分支） */
html.layout-ticker .pl-q-chg b::before {
  content: "▲"; font-size: 8.5px; vertical-align: 1.5px; margin-right: 5px;
}
html.layout-ticker .pl-q-chg.dn b::before { content: "▼"; }
/* 三副指标：等分带 + 上下细线（原先是三个飘着的裸数字，各自为政） */
html.layout-ticker .pl-q-range {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0; margin-top: clamp(22px, 4vh, 34px);
  border-top: 1px solid rgba(255, 255, 255, .16);
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}
html.layout-ticker .pl-q-range span {
  display: flex; flex-direction: row; align-items: baseline; justify-content: space-between;
  gap: 8px; padding: 13px 14px 13px 0;
}
html.layout-ticker .pl-q-range span + span {
  padding-left: 14px; border-left: 1px solid rgba(255, 255, 255, .16);
}
html.layout-ticker .pl-q-range i { font-style: normal; font-size: 9.5px; letter-spacing: .16em; color: var(--text-faint); }
html.layout-ticker .pl-q-range b { font-size: 17px; font-weight: 600; }

/* ---- 成交播报：无缝纵向滚动的台账 ---- */
/* ⚠️ 别误读这一块的「吃掉剩余高度」是 .pl-feed 干的 —— 它**没有** flex-grow。
   实际机制是两条：① .pl-track 自身 max-height 封顶；② .pl-foot 的 margin-top:auto
   把注脚顶到底。改这里前先回看注脚那条，否则页面底部会露出一块空洞。 */
html.layout-ticker .pl-feed {
  margin-top: clamp(22px, 4.6vh, 48px);
  display: flex; flex-direction: column; min-height: 0;
}
html.layout-ticker .pl-feed-hd { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
html.layout-ticker .pl-feed-hd b {
  font-size: 11px; font-weight: 600; letter-spacing: .22em;
  color: var(--text-dim); white-space: nowrap;
}
html.layout-ticker .pl-feed-hd::after {          /* 标题向右渐隐的引线 */
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, rgba(255, 255, 255, .30), transparent);
}
html.layout-ticker .pl-track {
  position: relative; overflow: hidden;
  /* 自适应剩余高度，240px 下限保证矮屏也至少露出 7 行。
     ⚠️ max-height 必须**小于「一份」的高度**，否则循环接缝处会露出空白：
        16 条 × 32px = 512px 一份 ⇒ 上限取 420px。改行数时同步回看这里。 */
  flex: 1 1 auto; min-height: 240px; max-height: 420px;
  /* 上下缘渐隐：滚动列表不会「硬切」在一条直线上，读起来是数据流 */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 18px, #000 calc(100% - 22px), transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 18px, #000 calc(100% - 22px), transparent 100%);
}
html.layout-ticker .pl-roll { display: block; will-change: transform; animation: plRoll 40s linear infinite; }
html.layout-ticker .pl-rows { display: block; }
html.layout-ticker .pl-row {
  display: grid; grid-template-columns: 104px 1fr auto; align-items: center;
  gap: 18px; height: 32px; font-size: 12.5px; color: var(--text-dim);
  border-bottom: 1px solid rgba(255, 255, 255, .075);   /* 虚线 → 实心细线：台账不是"草稿" */
}
html.layout-ticker .pl-row .who {
  color: var(--text); letter-spacing: .06em;
  /* 兜底：地区/姓名万一超长，宁可截断也不换行 —— 换行会撑破 32px 行高、
     把整块台账的行距节奏带塌。 */
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
html.layout-ticker .pl-row .amt {
  min-width: 66px; text-align: right; font-size: 13px;
  color: var(--pv-gold); font-variant-numeric: tabular-nums;
}
/* 中段量程柱：::before 满宽轨道 + ::after 按真实克重归一后的填充。
   ⚠️ 宽度 `--w` 由**模板**按「克重 ÷ 本批最大值」算出后内联传入。
      这里**不留**按 nth-child 的硬编码百分比：一改 _feed 就会与数值脱钩。 */
html.layout-ticker .pl-row .bar {
  position: relative; display: block; height: 32px;
}
html.layout-ticker .pl-row .bar::before,
html.layout-ticker .pl-row .bar::after {
  content: ""; position: absolute; left: 0; top: 50%;
  height: 4px; border-radius: 2px; transform: translateY(-50%);
}
html.layout-ticker .pl-row .bar::before { right: 0; background: rgba(255, 255, 255, .10); }
html.layout-ticker .pl-row .bar::after {
  width: var(--w, 0%);
  background: linear-gradient(90deg, var(--gold-1, #f6e6a8), var(--gold-2, #d4a84b));
  box-shadow: 0 0 10px rgba(var(--gold-rgb, 246, 230, 168), .35);
}
/* 无缝循环：两份**等高**内容整体上移一份的高度（-50%）。
   ⚠️ 只有「一份」的高度 ≥ 可视高度时才是真无缝 —— 行数因此不能太少
      （当前 16 条 × 32px = 512px > 420px 上限）。减行数前先回看 .pl-track。 */
@keyframes plRoll { from { transform: translateY(0); } to { transform: translateY(-50%); } }

html.layout-ticker .pl-foot {
  margin-top: auto; padding-top: 16px;
  display: flex; align-items: center; gap: 10px;
  font-size: 10.5px; letter-spacing: .1em; color: var(--text-faint);
}
html.layout-ticker .pl-foot .sep { opacity: .5; }

/* ---- 右栏：卡片按内容高度居中，不再撑满；竖向节奏用明确像素收口 ---- */
html.layout-ticker .stage { height: auto; max-height: none; }
html.layout-ticker .console { height: auto; }
html.layout-ticker .box { min-height: 0; --fh: 54px; }
html.layout-ticker .box-head {
  margin-top: 0;                 /* baseline 的 margin-top:auto 只为「填满等高」，本版式不需要 */
  margin-bottom: 16px;
}
/* 那条引导线没有文字承接时不读作「引导」，只读作「断线」⇒ 收起 */
html.layout-ticker .box-head .bh-line { display: none; }
html.layout-ticker .box-head .bh-mk { width: 46px; height: 46px; border-radius: 14px; }
html.layout-ticker .box-head .bh-mk svg { width: 22px; height: 22px; }
html.layout-ticker .box .cap { margin: 8px 0 26px; }
html.layout-ticker .field { margin-bottom: 16px; }
html.layout-ticker .row-between { margin: 16px 0 22px; }
html.layout-ticker .box .error-tip,
html.layout-ticker .box .ok-tip { margin: 0 0 14px; }
/* 主按钮从「主题强调色」改为与左栏同族的金 —— 首屏只留两套色彩语言
   （金 = 品牌/操作，红绿 = 涨跌语义）。原先金价 + 红绿 + 主题强调色三套并存。
   ⚠️ **这是一处可推翻的强主张**：若坚持按钮跟随主题强调色，删掉本段即可，其余不受影响。 */
html.layout-ticker .submit {
  color: #1a1206;
  background: linear-gradient(120deg, var(--gold-2, #d4a84b), var(--gold-1, #f6e6a8));
  box-shadow: 0 8px 26px rgba(var(--gold-rgb, 246, 230, 168), .32);
}
html.layout-ticker .submit:hover {
  box-shadow: 0 12px 34px rgba(var(--gold-rgb, 246, 230, 168), .45); filter: brightness(1.05);
}
html.layout-ticker .submit .sc { border-color: rgba(26, 18, 6, .55); }

/* 矮屏（1366×768 / 1440×800 这类）：把竖向节奏整体收一档，保证台账区仍有落脚处 */
@media (max-height: 780px) {
  html.layout-ticker .pl-quote { margin-top: 20px; }
  html.layout-ticker .pl-q-range { margin-top: 14px; }
  html.layout-ticker .pl-feed { margin-top: 20px; }
}

/* ≤960：与其余版式一样切成上下堆叠。左栏退回一条「顶部行情条」——
   只留品牌行 + 金价 + 涨跌；台账与注脚收起（首屏要保证登录卡先可见）。
   ⚠️ 这几条必须写在**本段内部**：(0,2,1) 才盖得住上面那批 (0,1,0) 的堆叠规则。 */
@media (max-width: 960px) {
  html.layout-ticker .split {
    display: flex; flex-direction: column;
    height: auto; min-height: 100vh; min-height: 100dvh;
    overflow: visible;
  }
  html.layout-ticker body { overflow: auto; }
  /* padding-top 58 ≈ 「.side-top 的 top:12px + 胶囊 38px + 8px 余量」——
     给右上角的主题切换让位（行情屏的品牌行与它同一水平带）。 */
  html.layout-ticker .pulse { position: static; padding: 58px 22px 26px; }
  html.layout-ticker .pl-feed,
  html.layout-ticker .pl-foot { display: none; }
  html.layout-ticker .pl-quote { margin-top: 16px; }
  html.layout-ticker .pl-q-val b { font-size: 42px; }
  html.layout-ticker .pl-q-val i { padding-bottom: 6px; }
  html.layout-ticker .pl-q-range { margin-top: 14px; }
  html.layout-ticker .pl-q-range b { font-size: 15px; }
  /* 让右栏吃掉剩余高度：.main 本身已是「flex + 居中」，撑开后卡片自然在栏内垂直居中。
     不写这条时 .main 只有内容高 ⇒ 卡片紧贴行情条、底下留一大片深色空白（实测首版）。 */
  html.layout-ticker .main { flex: 1 1 auto; }
}
/* 小屏再降一档：英文副名与「参考上金所」那半句只会把金价行挤到换行。
   ⚠️ 只收 .src（那句话），**不能**写成 `.pl-q-hd span` —— 「实时 + 时钟」也是 span，
      一起收掉的话时钟就没了（而它正是本版式的题眼）。 */
@media (max-width: 520px) {
  html.layout-ticker .pl-top .en { display: none; }
  html.layout-ticker .pl-q-hd .src { display: none; }
  html.layout-ticker .pl-q-val b { font-size: 36px; }
  /* 三副指标：≤520 时每列只剩 ~93px，而「标签 + 等宽数值」实测正好压线
     （28 + 8 + 54 ≈ 90px）⇒ 会出现**第一列单行、另两列折成两行**的高低不齐
     （实测 390px：「今 低」「昨 收」被折成两行）。改为标签在上、数值在下的竖排，
     彻底不依赖那点余量。 */
  html.layout-ticker .pl-q-range span {
    flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 3px;
  }
}

/* ==========================================================================
   版式 ⑥：星穹式（cosmos）—— 全域沉浸 + 悬浮登乘舱（#1080 落地）
   --------------------------------------------------------------------------
   本节由原型 outputs/登录页原型_星穹式_20261006/cosmos.css 原样迁入生产，
   与上面五套版式**同一规格**：只做加法、每条选择器都带 html.layout-cosmos 前缀
   （其余五套一条都不命中）、@keyframes 一律 cos* 起头（与既有动画无重名）。
   配套 DOM = templates/login.html 中 <section class="side"> 内新增的 .cosmos 层。

   参考图：深蓝等距太空场景（悬浮轨道盘 + 发光核心 + 环绕立方体 + 星空地平线）
           + 半透明玻璃登录卡 + 亮色主按钮。

   本版式的元素构成（用户指定五项全部命中）：
     · 太空 —— 深空渐变底 + 星云漂移 + 银河带 + 等距地面网格
     · 星星 —— 四层星点，独立相位闪烁（.cos-stars i.l1~l4）
     · 黄金 —— 悬浮金色核心 + 倾斜金环 + 金色巡游光点（.cos-core / .cos-ring / .cos-spark）
     · 卫星 —— 三枚伴星，**复用现有 .sat 的类名与子结构**（.s-orbit / .s-core）
     · 流光 —— 斜向扫光带 ×2 + 流星 ×3 + 复用的 .lay.gold-sweep / .streams

   ★★ 版面语言（本版式与其余五套的**结构性**分野，不是比例微调）：
       其余五套（center / split / split73 / aurora / ticker）统统是「左右分栏」：
         `.split` 两列 grid，左 `.side` 视觉、右 `.main` 表单，中间一条硬缝。
       本版式把 `.split` 压成**单列**，并令 `.side` 与 `.main` 落进**同一个 grid 格**
       ⇒ 二者由「并排」变「叠放」：星野升为整屏底色，登录卡成为**浮在星野之上的舱体**。
       版面因此没有分栏缝、没有"两块拼接"感，视线从左侧场景自然滑向右侧舱体。

   ★ 比例（"最优方案"的落点，全部集中在 ⑥-0 的四个令牌里）：
       · 舱体让右侧留白 --cos-pr，自身宽 --cardw ⇒ 1440 宽下卡片左缘落在 **63.4%**，
         贴着黄金分割 61.8%；1280 下 62.5%、1920 下 70.2%（clamp 上限所致）。
       · 场景中心由 50% 改到 `--cos-cx`(≈34%) —— **偏心构图**，把右侧空域让给舱体。
         若沿用 50%，轨道盘与金环会正好压在舱体底下（叠放后必然发生，不是"可能"）。

   ★ 零污染保证：本文件**每一条**选择器都带 `html.layout-cosmos` 前缀，
     其余五套版式的规则一条都不会命中。
     `@keyframes` 全部以 `cos` 起头，不与既有动画重名。

   ★ 「结合现有元素」不是比喻，是真实复用（见各处注释）：
       品牌行 .side-brand / 主题切换 .side-top / 遥测行 .side-foot / 整张表单卡 .box
       卫星 .sat>.s-orbit+.s-core / 星点手法同 .d-dots / 金弧手法同 .gold-arc
       流光手法同 .streams 与 .beamGo / 倾斜椭圆轨道手法同 .orbit .ell
     ⇒ 落地实现正是如此：**只新增了一个 .cosmos 场景层**，未改动现有任何 DOM 元素。

   ★ 唯一不统一的令牌：卡片基调。炽红主题是浅底白卡，而本版式的场景必然是深空，
     玻璃卡必须是深色才成立 ⇒ ⑥-13 为三主题各给一套**深色玻璃**卡片令牌
     （含 --text 系列反转），否则炽红会出现「深空场景 + 白卡 + 深字」的割裂。
   ========================================================================== */

/* ---------------------------------------------------------------------------
   ⑥-0 版面骨架：**全域沉浸 + 悬浮登乘舱**
   ---------------------------------------------------------------------------
   ⚠️ 比例与偏心是两件事，都必须在这一层定死：
      · 比例（横向分配）—— 舱体宽度与右侧留白，见下方 .main 段；
      · 偏心（场景落点）—— 场景中心不在 50%，见 --cos-cx。
      漏掉偏心会让场景与舱体重叠：1440 下轨道盘右缘落在 620px
      （#1082 把「盘 + 辉光 + 金环 + 金核」整组左移 0.18×--cs 后为 573px），而舱体左缘在 913px
      —— 看着还留有余量，但**金环（半径 0.58×--cs）与卫星（±0.9×--cs）会够到 740px**，
      再把 --cs 放大一档（1080 高屏）就会压上舱体。所以偏心不是"美观微调"而是结构约束。
   --------------------------------------------------------------------------- */
html.layout-cosmos {
  /* ---- 版面令牌：本版式全部定位的唯一来源，改这里即可整体调比例 ---- */
  --cos-px: clamp(30px, 5.2vw, 92px);      /* 左侧安全边（品牌 / 主张 / 遥测共用） */
  --cos-py: clamp(30px, 4.6vh, 54px);      /* 上下安全边 */
  --cos-pr: clamp(44px, 6.6vw, 120px);     /* 舱体右缘到视口右缘的留白 */
  --cos-cx: 34%;                           /* 场景水平锚点（偏心，非 50%） */
  --cos-cy: 47%;                           /* 场景垂直锚点（略高于中线：把下半屏让给文案） */
}

html.layout-cosmos .split {
  grid-template-columns: 1fr;              /* 单列 ⇒ 两栏并作一层 */
  position: relative;
}
html.layout-cosmos .side,
html.layout-cosmos .main {
  grid-column: 1; grid-row: 1;             /* 同格 ⇒ 叠放（非并排） */
}

/* ⚠️ 场景层必须让出指针：它现在铺满整屏，会盖住舱体 ⇒ 整页点不动。
   但 .side-top（NODE 读数 + 三主题切换）长在 .side 里，基础样式已给它
   `pointer-events: none` + `.side-top > * { pointer-events: auto }` ⇒ 开关仍然可点。
   （若只写 `.side{pointer-events:none}` 而没确认那条，主题切换会**静默失效**：
     按钮看得见、点不动，且控制台不报任何错。） */
html.layout-cosmos .side { z-index: 1; pointer-events: none; }
html.layout-cosmos .main { z-index: 2; pointer-events: none; }
html.layout-cosmos .main .stage { pointer-events: auto; }   /* 只有舱体吃指针 */

/* 右栏装饰层（.dl：流动光带 / HUD 同心圆 / 瞄准框）属"分栏"语言 ——
   它们是按「一栏 657px 宽的容器」设计的，铺到整屏只会散成背景噪点 ⇒ 收起。 */
html.layout-cosmos .main > .dl { display: none; }

/* 左右分栏的接缝线：叠放后它落在屏幕最右侧，已无分隔语义 */
html.layout-cosmos .side::after { display: none; }

/* ---------------------------------------------------------------------------
   ⑥-1 场景容器：铺满整屏
   z-index 取 4：让场景落在 .lay.mesh(0)/.lattice(1)/#sideBg(2)/.streams(3) 之上，
   但仍在 .scanline(6)/.hud(7)/.vignette(8)/.grain(9) 之下
   ⇒ 扫描线、暗角、噪点依旧盖在场景上（这正是沉浸感的来源）。
   底色**从主题令牌派生**（--side-1 / --side-2），不写死颜色 ⇒
   深蓝=深空蓝、黑金=深棕太空、炽红=深红星野，三主题各自成立。
   --------------------------------------------------------------------------- */
html.layout-cosmos .side {
  position: relative;                     /* 兜底：场景层与 ::after 的定位上下文 */
  --spx: var(--cos-px);                   /* 顶部控制条与信息层共用同一条左基线 */
  --spy: var(--cos-py);
  background:
    radial-gradient(96% 66% at 30% 34%, rgba(var(--accent-rgb), .20), transparent 60%),
    radial-gradient(58% 42% at 12% 82%, rgba(var(--gold-rgb), .12), transparent 68%),
    linear-gradient(166deg, var(--side-1) 0%, var(--side-1) 30%, var(--side-2) 78%, var(--side-1) 100%);
}

html.layout-cosmos .cosmos {
  display: block;                 /* 反向那一半：默认 .cosmos{display:none}，见版式层开头 */
  position: absolute; inset: 0; z-index: 4;
  overflow: hidden; pointer-events: none;
}

/* 星云 + 深空压暗：星座感来自「亮元素 × 极暗底」，不压暗则星点读不出来 */
html.layout-cosmos .cos-sky {
  position: absolute; inset: -8%;
  background:
    radial-gradient(40% 30% at 26% 30%, rgba(var(--accent-rgb), .20), transparent 70%),
    radial-gradient(32% 24% at 66% 22%, rgba(120, 170, 255, .13), transparent 72%),
    radial-gradient(28% 22% at 46% 62%, rgba(var(--gold-rgb), .15), transparent 70%),
    radial-gradient(126% 92% at 42% 44%, transparent 24%, rgba(0, 0, 0, .58) 100%);
  filter: blur(14px);
  animation: cosDrift 48s ease-in-out infinite alternate;
}
@keyframes cosDrift {
  from { transform: translate3d(-1.4%, -1%, 0) scale(1.02); }
  to   { transform: translate3d(1.4%, 1.4%, 0) scale(1.06); }
}

/* ---------------------------------------------------------------------------
   ⑥-2 星星：四层星点，各层独立相位/时长 ⇒ 闪烁有层次，而非整片一起呼吸
   手法与现有 .d-dots 同源（radial-gradient 点 + 辉光），
   但改用 background-image 批量出星 —— 一次 CSS 出 44 颗，不膨胀 DOM。
   l1 大亮星 / l2 中星 / l3 小星 / l4 金色星（呼应"黄金"）
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-stars,
html.layout-cosmos .cos-stars i { position: absolute; inset: 0; }

html.layout-cosmos .cos-stars i.l1 {
  background-image:
    radial-gradient(circle at 9% 14%,  #fff 0 1.5px, transparent 2.1px),
    radial-gradient(circle at 22% 41%, #fff 0 1.3px, transparent 1.9px),
    radial-gradient(circle at 35% 9%,  #fff 0 1.6px, transparent 2.2px),
    radial-gradient(circle at 47% 27%, #fff 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 58% 13%, #fff 0 1.5px, transparent 2.1px),
    radial-gradient(circle at 69% 38%, #fff 0 1.3px, transparent 1.9px),
    radial-gradient(circle at 78% 7%,  #fff 0 1.6px, transparent 2.2px),
    radial-gradient(circle at 88% 24%, #fff 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 16% 63%, #fff 0 1.4px, transparent 2px),
    radial-gradient(circle at 62% 55%, #fff 0 1.3px, transparent 1.9px),
    radial-gradient(circle at 91% 66%, #fff 0 1.5px, transparent 2.1px);
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, .85));
  animation: cosTwinkle 5.2s ease-in-out infinite;
}
html.layout-cosmos .cos-stars i.l2 {
  background-image:
    radial-gradient(circle at 5% 33%,  rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 14% 8%,  rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 26% 22%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 31% 52%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 43% 44%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 52% 36%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 64% 21%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 73% 49%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 82% 31%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 94% 44%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 38% 71%, rgba(255,255,255,.85) 0 1px, transparent 1.5px),
    radial-gradient(circle at 71% 74%, rgba(255,255,255,.85) 0 1px, transparent 1.5px);
  animation: cosTwinkle 7.4s ease-in-out infinite -2.1s;
}
html.layout-cosmos .cos-stars i.l3 {
  background-image:
    radial-gradient(circle at 3% 47%,  rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 11% 26%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 19% 55%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 28% 12%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 37% 34%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 45% 62%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 55% 18%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 63% 43%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 75% 26%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 84% 57%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 93% 12%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 6% 78%,  rgba(255,255,255,.6) 0 .8px, transparent 1.2px),
    radial-gradient(circle at 49% 83%, rgba(255,255,255,.6) 0 .8px, transparent 1.2px);
  animation: cosTwinkle 9.6s ease-in-out infinite -4.6s;
}
html.layout-cosmos .cos-stars i.l4 {
  background-image:
    radial-gradient(circle at 13% 18%, rgba(var(--gold-rgb), .95) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 41% 15%, rgba(var(--gold-rgb), .95) 0 1px,   transparent 1.5px),
    radial-gradient(circle at 66% 30%, rgba(var(--gold-rgb), .95) 0 1.3px, transparent 1.9px),
    radial-gradient(circle at 87% 12%, rgba(var(--gold-rgb), .95) 0 1px,   transparent 1.5px),
    radial-gradient(circle at 24% 68%, rgba(var(--gold-rgb), .95) 0 1.1px, transparent 1.7px),
    radial-gradient(circle at 57% 78%, rgba(var(--gold-rgb), .95) 0 1px,   transparent 1.5px),
    radial-gradient(circle at 79% 62%, rgba(var(--gold-rgb), .95) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 95% 74%, rgba(var(--gold-rgb), .95) 0 1px,   transparent 1.5px);
  filter: drop-shadow(0 0 4px rgba(var(--gold-rgb), .9));
  animation: cosTwinkle 6.4s ease-in-out infinite -1.3s;
}
@keyframes cosTwinkle {
  0%, 100% { opacity: .42; }
  38%      { opacity: 1; }
  62%      { opacity: .72; }
}

/* ---------------------------------------------------------------------------
   ⑥-3 等距地面网格（"太空"的地平线）
   纯 2D 等距：rotateX(64deg) + rotateZ(45deg) ⇒ 菱形格，无 3D 上下文依赖。
   ⚠️ 变换原点必须钉在场景锚点 `var(--cos-cx) 0%`：
      网格元素本身横跨整屏，若用默认的 50%，旋转/缩放会围绕**屏幕中心**展开，
      网格的收束点就跑到画面正中 —— 与"地面在轨道盘下方延伸"的语义相反，
      而且这一条**只会表现为"网格位置怪怪的"**，不会报任何错。
   ⚠️ 掩膜中心同样跟锚点走，让网格向四周化开，读作「地面延伸进虚空」而不是一块贴上去的布。
   ⚠️ 用 --gold-rgb（三主题恒为金色）而不是 --hud-rgb：炽红主题下 --hud-rgb 是
      近白 255,226,226，网格会亮成一张白布。
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-grid {
  position: absolute; left: -34%; right: -34%; top: 30%; bottom: -52%;
  transform-origin: var(--cos-cx) 0%;
  transform: perspective(1000px) rotateX(64deg) rotateZ(45deg) scale(1.5);
  background-image:
    linear-gradient(rgba(var(--gold-rgb), .30) 0 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--gold-rgb), .30) 0 1px, transparent 1px);
  background-size: 64px 64px, 64px 64px;
  /* ⚠️ 掩膜中心必须够低 + 半径够小：网格的上边缘是一条**硬边**（它不渐变，
     是被掩膜截断的）⇒ 掩膜不给足衰减时，这条边会读成一条横贯画面的亮带
     （初版 top:30% + 中心 30% 就出了这个 artifact，实测截图可见）。
     掩膜中心压到网格高的 34% ⇒ 上边缘落在约 74% 半径处，
     再乘本行 opacity .55 后基本不可见。 */
  -webkit-mask-image: radial-gradient(46% 38% at var(--cos-cx) 34%, #000 0%, rgba(0,0,0,.45) 48%, transparent 84%);
          mask-image: radial-gradient(46% 38% at var(--cos-cx) 34%, #000 0%, rgba(0,0,0,.45) 48%, transparent 84%);
  opacity: .52;
}

/* ---------------------------------------------------------------------------
   ⑥-4 悬浮轨道盘（等距椭圆盘：与卫星同一套「虚线轨道 + 细环」语言）
   ★ #1082 由「等距实体台面」改写而来 —— 理由全部来自实测，不是审美偏好：
     · 台面侧壁 linear-gradient(150deg, rgba(15,15,17,.98), rgba(3,3,4,1))
       是全场景**唯一的近黑实体**（实测该区域亮度 5% 分位 27、最低 9）⇒ 在高亮金环
       与发光金核之间读作「一块黑方块」，与「太空 + 卫星」的语义正相反；
     · 形状靠 rotate(45deg) scaleY(.56) 旋转 + 非等比压缩得到：1px 描边的竖边被
       压到 0.56px、横边仍是 1px ⇒ 描边粗细不匀（肉眼可见「上粗下细」）；
     · 菱形（等距正方形）是"实体平台"的语言，而本版式其余元素（金环 / 卫星轨道 /
       地面网格）**全是圆或椭圆** ⇒ 菱形在这里是异类。
   现实现 = 同一箱体上的**三层同心椭圆轨道**：
     外虚线（hud 白 .40）｜ 主环（金线 + 极淡 accent 薄雾）｜ 内虚线（hud 白 .46）
   外加一条沿主环滑行的金色扫描弧 —— 与 `.sat > .s-orbit`（虚线轨道圈）+
   `.s-core`（细环 + 金色核心）逐点同源，只是放大到"母舰级"尺度。
   ⚠️ 不再用 `scaleY()` 做透视：椭圆半轴直接写进 width/height（--cs × .56×--cs），
      描边因此是**真正的 1px 等宽**（旧实现 0.56px 的竖边在这里被修掉）。
   ⚠️ 盘子必须是**透**的：只留一层 accent 薄雾（.12）——实心填充会把金核辉光挡在
      下面、重新变回一块暗色块（那正是要消灭的东西）。
   ⚠️ 三层共用同一箱体（`.cos-pad i { inset: 0 }`）⇒ 同心由构建方式保证，不靠手调。
      扫描弧是**正方形**子元素 + `transform: scaleY(.56) rotate(θ)`（手法同
      .cos-ring::after）：先转渐变、再压扁 ⇒ 弧沿**椭圆**走，而元素不会翻滚。
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-scene {
  position: absolute; inset: 0;
  --cs: clamp(240px, 29vh, 356px);          /* 场景唯一缩放锚点：轨道盘长轴 */
  /* ★ #1082 场景主体水平偏移：把「盘 + 辉光 + 金环 + 金核」**整组**左移。
     只移盘会把金核与金环留在原地 ⇒ 环心不再落在盘心上（读作错位），
     故整组一起移；卫星与地面网格不动（它们是"散布 / 环境"，不是主体）。
     ⚠️ 用 var 而不是直接改 --cos-cx：--cos-cx 同时供**地面网格掩膜方向**与
        **卫星落点**使用，改它会连带挪动这两者（那才是真的错位）。 */
  --cos-dx: calc(var(--cs) * -.18);
}
html.layout-cosmos .cos-pad {
  position: absolute;
  left: calc(var(--cos-cx) + var(--cos-dx));
  top: var(--cos-cy);
  width: var(--cs); height: calc(var(--cs) * .56);
  margin: calc(var(--cs) * -.28) 0 0 calc(var(--cs) * -.5);
}
html.layout-cosmos .cos-pad i {              /* 三层轨道共用同一箱体 ⇒ 天然同心 */
  position: absolute; inset: 0; border-radius: 50%;
}
/* 外层虚线轨道（= .s-orbit 的语言按比例放大） */
html.layout-cosmos .cos-pad .pb {
  inset: -8%;
  border: 1px dashed rgba(var(--hud-rgb), .40);
}
/* 主环：细金线 + 极淡 accent 薄雾（"透"是硬要求，见段头） */
html.layout-cosmos .cos-pad .pt {
  border: 1px solid rgba(var(--gold-rgb), .50);
  background: radial-gradient(58% 58% at 50% 50%, rgba(var(--accent-rgb), .12), transparent 74%);
  box-shadow:
    0 0 34px rgba(var(--gold-rgb), .14),
    inset 0 0 30px rgba(var(--accent-rgb), .16);
}
/* 内层虚线轨道 */
html.layout-cosmos .cos-pad .pt::after {
  content: ""; position: absolute; inset: 25%;
  border-radius: 50%; border: 1px dashed rgba(var(--hud-rgb), .46);
}
/* 沿主环外缘滑行的金色扫描弧（手法同 .cos-ring::after：conic-gradient + 只留最外一层 mask） */
html.layout-cosmos .cos-pad .pt::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: var(--cs); height: var(--cs);
  margin: calc(var(--cs) * -.5) 0 0 calc(var(--cs) * -.5);
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(var(--gold-rgb), 0) 0deg,
    rgba(var(--gold-rgb), .85) 28deg,
    var(--gold-1) 54deg,
    rgba(var(--gold-rgb), 0) 96deg,
    rgba(var(--gold-rgb), 0) 360deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 96.6%, #000 98% 100%);
          mask-image: radial-gradient(closest-side, transparent 0 96.6%, #000 98% 100%);
  /* ⚠️ 这行 `transform` 不是冗余（关键帧里也有）：
     本文件末尾的 `@media (prefers-reduced-motion: reduce)` 会把 animation 全关
     ⇒ 只剩关键帧时弧会退回**正圆**（一个横跨盘子的圆环 = 明显穿帮）。
     本行的角色是「关掉动画时的静态形态」——删它等于删掉无障碍档的正确外观。
     （实测口径：本轮所有验收截图都注入了 `animation:none`，弧仍是椭圆。） */
  transform: scaleY(.56);
  animation: cosPadScan 26s linear infinite;
}
@keyframes cosPadScan {
  from { transform: scaleY(.56) rotate(0deg); }
  to   { transform: scaleY(.56) rotate(360deg); }
}

/* 轨道盘底部辉光（把盘子"托"在虚空上，而不是让它平贴背景） */
html.layout-cosmos .cos-halo {
  position: absolute; left: calc(var(--cos-cx) + var(--cos-dx)); top: var(--cos-cy);
  width: calc(var(--cs) * 1.5); height: calc(var(--cs) * .68);
  margin: calc(var(--cs) * -.34) 0 0 calc(var(--cs) * -.75);
  background: radial-gradient(50% 50% at 50% 50%, rgba(var(--gold-rgb), .30), transparent 70%);
  filter: blur(22px);
  animation: cosHaloPulse 6.8s ease-in-out infinite;
}
@keyframes cosHaloPulse {
  0%, 100% { opacity: .62; transform: scale(.96); }
  50%      { opacity: 1;   transform: scale(1.05); }
}

/* ---------------------------------------------------------------------------
   ⑥-5 黄金核心（本版式的视觉主角）
   手法与现有 .orbit .core 同源（径向高光 + 环 + 呼吸），但**材质改为黄金**：
   外发光三层递进 + 内阴影造球体的暗面，避免读成"一个黄色圆点"。
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-core {
  position: absolute; left: calc(var(--cos-cx) + var(--cos-dx)); top: calc(var(--cos-cy) - var(--cs) * .48);
  width: calc(var(--cs) * .28); height: calc(var(--cs) * .28);
  margin: calc(var(--cs) * -.14) 0 0 calc(var(--cs) * -.14);
  border-radius: 50%;
  background: radial-gradient(circle at 34% 28%,
    #fffdf3 0%, var(--gold-1) 24%, var(--gold-2) 56%, #7d5410 86%, #2e1d04 100%);
  box-shadow:
    0 0 30px rgba(var(--gold-rgb), .90),
    0 0 88px rgba(var(--gold-rgb), .46),
    0 0 176px rgba(var(--gold-rgb), .24),
    inset -10px -14px 28px rgba(74, 44, 0, .62),
    inset 8px 10px 22px rgba(255, 246, 214, .58);
  animation: cosCoreFloat 7.2s ease-in-out infinite;
}
html.layout-cosmos .cos-core b {             /* 内核高光点（与 .orbit .core b 同手法） */
  position: absolute; left: 50%; top: 50%;
  width: 18%; height: 18%; margin: -9% 0 0 -9%; border-radius: 50%;
  background: #fffdf2;
  box-shadow: 0 0 22px 7px rgba(255, 240, 190, .95);
  animation: breathe 3.4s ease-in-out infinite;
}
@keyframes cosCoreFloat {
  0%, 100% { transform: translateY(0) scale(1); }
  50%      { transform: translateY(calc(var(--cs) * -.055)) scale(1.015); }
}

/* ---------------------------------------------------------------------------
   ⑥-6 金环 + 沿环巡游的金色光点
   手法与现有 .orbit .ell 完全一致：rotate(-17deg) scaleY(.3) rotate(θ)
   ⇒ 倾斜椭圆轨道；把 θ 做成动画即为「环在自转」。
   （不用 rotateX：那需要 3D 上下文，且与既有倾斜椭圆语言不统一。）
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-ring,
html.layout-cosmos .cos-spark {
  position: absolute; left: calc(var(--cos-cx) + var(--cos-dx)); top: calc(var(--cos-cy) - var(--cs) * .48);
  width: calc(var(--cs) * 1.16); height: calc(var(--cs) * 1.16);
  margin: calc(var(--cs) * -.58) 0 0 calc(var(--cs) * -.58);
  border-radius: 50%;
  animation: cosRing 22s linear infinite;
}
html.layout-cosmos .cos-ring::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(var(--gold-rgb), .46);
  border-top-color: var(--gold-1);
  border-left-color: rgba(var(--accent-rgb), .85);
  box-shadow: 0 0 26px rgba(var(--gold-rgb), .55), inset 0 0 26px rgba(var(--gold-rgb), .22);
}
/* 沿环滑行的高亮弧（手法同 .gold-arc：conic-gradient + 只留最外 1~2px 的 mask） */
html.layout-cosmos .cos-ring::after {
  content: ""; position: absolute; inset: -2px; border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(var(--gold-rgb), 0) 0deg,
    var(--gold-1) 34deg,
    rgba(var(--gold-rgb), .85) 62deg,
    rgba(var(--gold-rgb), 0) 104deg,
    rgba(var(--gold-rgb), 0) 360deg);
  -webkit-mask-image: radial-gradient(closest-side, transparent 0 96.4%, #000 97.2% 100%);
          mask-image: radial-gradient(closest-side, transparent 0 96.4%, #000 97.2% 100%);
  filter: drop-shadow(0 0 8px rgba(var(--gold-rgb), .8));
}
html.layout-cosmos .cos-spark::before {
  content: ""; position: absolute; left: 50%; top: -4px;
  width: 8px; height: 8px; margin-left: -4px; border-radius: 50%;
  background: #fffdf0;
  box-shadow: 0 0 14px 5px rgba(var(--gold-rgb), .95), 0 0 34px 10px rgba(var(--gold-rgb), .5);
}
@keyframes cosRing {
  from { transform: rotate(-17deg) scaleY(.34) rotate(0deg); }
  to   { transform: rotate(-17deg) scaleY(.34) rotate(360deg); }
}

/* ---------------------------------------------------------------------------
   ⑥-7 卫星 ×3
   ★ 复用现有 .sat 的**类名与子结构**（.s-orbit 虚线轨道圈 / .s-core 细环+金色核心）。
     落地时这段应与 `html.layout-split .orbit > .sat` 那段**合并为一条不带版式前缀的
     公共规则**（尺寸/描边/动画只有一份定义 = 唯一实现）；原型阶段两份同参并存，
     这是注入式原型的无奈，不是设计意图。
   ⚠️ 现有 .sat 基础段是 `.orbit .axis, .orbit .sat { display: none }`，本版式下
     .sat 不在 .orbit 内 ⇒ 那条不命中，这里必须显式 display:block。
   --------------------------------------------------------------------------- */
html.layout-cosmos .sat {
  --so: calc(var(--cs) * .115);
  position: absolute; display: block;
  width: var(--so); height: var(--so);
  transform: translate(-50%, -50%);
  animation: floaty 6.5s ease-in-out infinite;
}
html.layout-cosmos .sat > .s-orbit {
  position: absolute; inset: calc(var(--so) * -.42); border-radius: 50%;
  border: 1px dashed rgba(var(--hud-rgb), .58);
  animation: spin 26s linear infinite;
}
html.layout-cosmos .sat > .s-core {
  position: absolute; inset: 0; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .55);
  background: radial-gradient(circle at 36% 32%, rgba(255,255,255,.26), rgba(255,255,255,.05) 58%, transparent);
  box-shadow: 0 0 20px rgba(var(--hud-rgb), .45), inset 0 0 12px rgba(255, 255, 255, .14);
}
html.layout-cosmos .sat > .s-core::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 26%; height: 26%; margin: -13% 0 0 -13%; border-radius: 50%;
  background: var(--gold-1);
  box-shadow: 0 0 10px 2px rgba(var(--gold-rgb), .9);
  animation: breathe 3.4s ease-in-out infinite;
}
/* 三枚卫星的落点：以场景锚点为原点、按 --cs 的倍数散开 ⇒ 改尺寸自动跟随 */
html.layout-cosmos .sat.s1 { left: calc(var(--cos-cx) - var(--cs) * .82); top: calc(var(--cos-cy) - var(--cs) * .62); --so: calc(var(--cs) * .13);  animation-delay: -.8s; }
html.layout-cosmos .sat.s2 { left: calc(var(--cos-cx) + var(--cs) * .90); top: calc(var(--cos-cy) - var(--cs) * .34); --so: calc(var(--cs) * .10);  animation-delay: -3.4s; }
html.layout-cosmos .sat.s3 { left: calc(var(--cos-cx) + var(--cs) * .62); top: calc(var(--cos-cy) + var(--cs) * .50); --so: calc(var(--cs) * .105); animation-delay: -5.1s; }

/* ---------------------------------------------------------------------------
   ⑥-8 流光：斜向扫光带 ×2 + 流星 ×3
   扫光带手法同 .d-beam b（rotate(19deg) + 横向位移循环），
   流星是本版式新增（参考图无，但用户明确要求"流光"）。
   --------------------------------------------------------------------------- */
html.layout-cosmos .cos-flux { position: absolute; inset: 0; overflow: hidden; }
html.layout-cosmos .cos-flux i {
  position: absolute; top: -58%; bottom: -58%; width: 132px; left: -30%;
  transform: rotate(19deg);
  filter: blur(9px);
  animation: cosFlux var(--fd, 15s) linear infinite var(--fdl, 0s);
}
/* ⚠️ 带子必须"有芯"才读作流光、而不是一团雾。
   初版只有一条 190px 宽的高斯模糊带（blur 7px）⇒ 实测在 1440×900 上读成
   一条横贯画面的白色雾带（诊断脚本 _diag_band.py 逐层排除后确认就是它本身，
   不是任何背景层的 artifact）。
   改法：宽度收到 132px、模糊加到 9px 做外晕，再用 ::after 加一根 2px 的**亮芯**
   ⇒ 一眼读作「一道掠过的光」。 */
html.layout-cosmos .cos-flux i.a {
  background: linear-gradient(90deg, transparent, rgba(var(--gold-rgb), .16) 38%,
              rgba(255, 249, 226, .22) 50%, rgba(var(--gold-rgb), .16) 62%, transparent);
}
html.layout-cosmos .cos-flux i.b {
  background: linear-gradient(90deg, transparent, rgba(var(--hud-rgb), .12) 40%,
              rgba(255, 255, 255, .16) 50%, rgba(var(--hud-rgb), .12) 60%, transparent);
}
html.layout-cosmos .cos-flux i::after {      /* 光芯 */
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 2px;
  margin-left: -1px;
  background: linear-gradient(180deg, transparent, rgba(255, 252, 238, .62) 22%,
              rgba(255, 255, 255, .78) 50%, rgba(255, 252, 238, .62) 78%, transparent);
  filter: blur(1.2px);
}
@keyframes cosFlux {
  from { left: -34%; }
  to   { left: 124%; }
}

html.layout-cosmos .cos-meteor {
  position: absolute; width: 2px; height: 2px; border-radius: 50%;
  background: #fff; opacity: 0;
  box-shadow: 0 0 10px 2px rgba(255, 255, 255, .9);
  animation: cosMeteor var(--md, 8s) linear infinite var(--mdl, 0s);
}
html.layout-cosmos .cos-meteor::after {          /* 拖尾：朝右上方渐隐 */
  content: ""; position: absolute; right: 2px; top: 50%;
  width: 150px; height: 1px; transform: translateY(-50%);
  background: linear-gradient(270deg, rgba(255,255,255,.9), rgba(var(--gold-rgb), .4) 46%, transparent);
}
@keyframes cosMeteor {
  0%   { opacity: 0; transform: translate3d(0, 0, 0); }
  5%   { opacity: 1; }
  26%  { opacity: 1; }
  38%  { opacity: 0; transform: translate3d(-52vw, 40vh, 0); }
  100% { opacity: 0; transform: translate3d(-52vw, 40vh, 0); }
}
html.layout-cosmos .cos-meteor.m1 { left: 74%; top: 8%;  --md: 9s;   --mdl: -1.2s; }
html.layout-cosmos .cos-meteor.m2 { left: 92%; top: 22%; --md: 12.5s; --mdl: -6.4s; }
html.layout-cosmos .cos-meteor.m3 { left: 58%; top: 4%;  --md: 15s;   --mdl: -9.8s; }

/* ---------------------------------------------------------------------------
   ⑥-9 信息层重排：品牌（左上）/ 主张（左下）/ 遥测（最底）
   场景是绝对定位铺满整屏，文字必须与它的**实体区**错开。三个信息块沿**同一条左基线**
   （--cos-px）排成一条竖向的"信息脊柱"，在偏心构图的左半边独立成组。
   ⚠️ .hero 用绝对定位钉在下方，而不是留在 space-between 流里：
      留在流里它会落在垂直中段 —— 正好是场景主体所在（叠放后必然重叠，实测如此）。
      钉住后 .side-inner 的 space-between 只剩品牌与遥测两端，正好。
   ⚠️ .hero 必须显式**定宽**，不能靠 left/right 两端定位：
      基础样式里 `.hero h1` 的字号是 `clamp(24px, 5.5cqw, 52px)` —— **container query**，
      5.5cqw = 该元素自身内容宽的 5.5%。若让它撑满（旧布局里它天然占满半屏 667px），
      这里就会占满整屏 ⇒ 5.5cqw 变成 79px（被 clamp 到 52px），标题大得离谱。
      定宽同时把标题字号钉在可预期的档位（43vw ⇒ 1440 下约 34px）。
   --------------------------------------------------------------------------- */
html.layout-cosmos .side-inner { padding: var(--cos-py) var(--cos-px); }
html.layout-cosmos .hero {
  position: absolute; left: var(--cos-px); bottom: calc(var(--cos-py) + 46px);
  top: auto; right: auto; margin: 0;
  /* ⚠️ 宽度不是"随手的排版尺寸"，它是**标题字号的基准**（见上条 container query）。
     标定口径：复现原布局的字号 ⇒ 让 boder-box 宽 ≈ 原左栏内容宽。
       原左栏内容宽 = 视口 × (1.19/2.19) − 2×58px  ⇒ 1440 下 666px、1920 下 927px。
     本行 48vw 在 1440 下得 691px（内容宽 668px）⇒ h1 与改版前**等值**（15.89px vs 15.83px）。
     实测反推：若沿用首版的 43vw（内容宽 596px），h1 会掉到 14.18px —— 同样"看着正常"，
     但比改版前小 1.6px，属于**静默的字号回归**。 */
  width: clamp(440px, 48vw, 950px);
  padding: 0 0 0 22px;
  /* 左侧竖标：把三行文案收成一个"读数块"，与左栏 HUD 语言一致 */
  border-left: 1px solid rgba(var(--gold-rgb), .30);
}
/* 本版式收起轨道环与特性网格：环由 .cos-ring 承担，
   特性列表会把文案区撑高、顶进场景主体（实测重叠） */
html.layout-cosmos .orbit,
html.layout-cosmos .feats { display: none; }
html.layout-cosmos .hero h1 { margin-bottom: 12px; }
html.layout-cosmos .hero .sub { max-width: 46ch; }
html.layout-cosmos .kicker { margin-bottom: 12px; }

/* ⚠️ .side-brand 是块级 flex 容器 ⇒ 宽度**撑满整行**（实测 1290px）。
   视觉上无碍（内容本就靠左），但任何"量品牌行边界"的判定都会失真：
   探针按矩形相交判「场景是否压住品牌」时，1290px 宽的盒子几乎与一切相交
   ⇒ **稳定产生一条假红**（实测踩到，报 overlapBrand=True 而画面明明分居左右）。
   改 fit-content：盒子边界 = 真实内容边界，判据才有意义。 */
html.layout-cosmos .side-brand { width: fit-content; max-width: 100%; }

/* 品牌行：徽记加一圈黄金辉光（本版式「黄金」在文字层的唯一落点） */
html.layout-cosmos .side-brand .mk {
  box-shadow: 0 0 26px rgba(var(--gold-rgb), .40), inset 0 0 18px rgba(var(--gold-rgb), .18);
}
html.layout-cosmos .side-brand h2 { letter-spacing: .06em; }

/* 遥测行：压到最底，做成"空间站仪表"的收口 */
html.layout-cosmos .side-foot { opacity: .82; }

/* 现有装饰层降载：等距网格已经承担了"地面"，再叠一层正视网格会读成脏 */
html.layout-cosmos .lay.mesh,
html.layout-cosmos .lay.lattice { opacity: .18; }
html.layout-cosmos .lay.streams { opacity: .42; }
html.layout-cosmos .lay.ruler-x,
html.layout-cosmos .lay.ruler-y { display: none; }

/* ---------------------------------------------------------------------------
   ⑥-10 悬浮登乘舱（右栏 → 右侧悬浮卡）
   ⚠️ 卡片不再"与左栏等高"：
      基础样式里 .stage 是 height:100%、.box 是 min-height:100%，那是**分栏语言**
      （左栏满高 ⇒ 右栏卡片跟着撑满，两端对齐）。全域叠放下，左栏就是整屏，
      卡片再撑满就变成"一堵墙"（900 视口下高 900px，与舱体的意象完全相反）。
      ⇒ .stage 改高自适应、.box 去掉 min-height，卡片高度由内容决定。
   ⚠️ 舱体必须"浮"起来，靠三层证据叠加（缺一层就贴回背景）：
      ① 大范围柔影（自身 box-shadow，⑥-10 下方）
      ② 背后的承托光（.main::before 的径向光）
      ③ 下方地面光斑（.stage::after，像轨道盘下面的 .cos-halo）
   --------------------------------------------------------------------------- */
html.layout-cosmos .main {
  /* 舱体宽度：唯一来源。基础值 460px 是"半栏宽"口径（右栏 657px 时占 70%），
     全域叠放后必须按**视口**口径重算 ⇒ 30vw + 上限 452（1440 下 432px）。 */
  --cardw: clamp(440px, 34vw, 500px);
  align-items: center;
  justify-content: flex-end;
  padding: 0 var(--cos-pr) 0 0;
}
html.layout-cosmos .main::before {
  /* 只留"舱体承托光 + 全局轻暗角"；原来的横向渐隐是分栏语言，叠放后没有承托对象。
     柔光锚点写 `right calc(--cos-pr + 190px)`：190px ≈ 舱体半宽（396~452 的一半），
     让光心落在舱体中心而不是边缘。 */
  background:
    radial-gradient(38% 44% at right calc(var(--cos-pr) + 235px) 50%, rgba(var(--rhud-rgb), .20), transparent 72%),
    radial-gradient(24% 28% at right calc(var(--cos-pr) + 190px) 80%, rgba(var(--gold-rgb), .13), transparent 74%),
    radial-gradient(130% 116% at 42% 46%, transparent 44%, var(--rvig) 100%);
}
html.layout-cosmos .main::after { display: none; }   /* 点阵收起：与 .lay.mesh 重复 */

html.layout-cosmos .stage {
  height: auto;
  max-height: calc(100vh - var(--cos-py) * 2);
}
html.layout-cosmos .stage::after {                    /* 地面光斑：悬浮的第三层证据 */
  content: ""; position: absolute; left: 50%; bottom: -30px;
  width: 78%; height: 42px; transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(var(--gold-rgb), .20), transparent 72%);
  filter: blur(14px);
  pointer-events: none;
}
html.layout-cosmos .box {
  min-height: 0;
  /* 控件高度：基础值 `48px + --air*11` 是"卡片被撑高后补偿空档"的产物
     （--air 随视口高度线性增长，1080 屏到 74px）。本版式卡片高度自适应、
     没有需要填的空档 ⇒ 回到以视口高为锚的适度档位。 */
  --fh: clamp(48px, 5.6vh, 54px);
  border-radius: 22px;
  background:
    linear-gradient(168deg, rgba(255, 255, 255, .08), rgba(255, 255, 255, 0) 42%),
    var(--card);
  border: 1px solid rgba(var(--hud-rgb), .34);
  box-shadow:
    /* 外圈 1px 金线 + 两层外发光：把玻璃卡从深空背景里"抬"出来。
       光靠 border 不够 —— 半透明卡片与背景亮度接近时，1px 边读不出来。 */
    0 0 0 1px rgba(var(--gold-rgb), .22),
    0 30px 84px rgba(0, 0, 0, .66),
    0 0 74px rgba(var(--accent-rgb), .20),
    inset 0 1px 0 rgba(255, 255, 255, .12);
  -webkit-backdrop-filter: blur(26px) saturate(1.3);
          backdrop-filter: blur(26px) saturate(1.3);
}
/* 玻璃卡的顶部高光：一条内嵌的细亮线，是"玻璃"与"深色板"的分界 */
html.layout-cosmos .box::before {
  left: 8%; right: 8%; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .55) 50%, transparent);
  box-shadow: none;
}
/* 四角金标收敛为两角（与左栏 HUD 的"两角金"同一口径） */
html.layout-cosmos .console .cf.b,
html.layout-cosmos .console .cf.c { border-color: rgba(var(--gold-rgb), .38); }
html.layout-cosmos .console::before { border-color: rgba(var(--hud-rgb), .16); }

/* 标题居中 + 副标题下的金色细线（参考图"欢迎登录 / welcome to login"的分隔笔触） */
html.layout-cosmos .box h3,
html.layout-cosmos .box .cap { text-align: center; }
html.layout-cosmos .box .cap {
  position: relative; margin: 10px 0 30px; padding-bottom: 17px;
}
html.layout-cosmos .box .cap::after {
  content: ""; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 116px; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--gold-rgb), .78), transparent);
}

/* ---------------------------------------------------------------------------
   ⑥-11 表单控件：仪器插槽 → 玻璃凹槽
   --------------------------------------------------------------------------- */
html.layout-cosmos .field input {
  border-radius: 13px;
  background: rgba(255, 255, 255, .055);
  border: 1px solid rgba(var(--hud-rgb), .24);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .08), inset 0 0 22px rgba(0, 0, 0, .22);
}
html.layout-cosmos .field input:focus {
  border-color: rgba(var(--gold-rgb), .78);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .08),
    inset 0 0 22px rgba(0, 0, 0, .22),
    0 0 0 3px rgba(var(--gold-rgb), .16),
    0 0 24px rgba(var(--gold-rgb), .22);
}
html.layout-cosmos .field input:focus ~ .ico { color: var(--gold-1); filter: drop-shadow(0 0 6px rgba(var(--gold-rgb), .9)); }

/* 主按钮：黄金渐变（参考图的亮色主按钮，在本版式里改用本项目的黄金语言）
   ⚠️ 收尾色必须提成令牌：按钮上的深色文字要在**渐变两端取最差**算对比度，
      端点写死在 background 里，探针就只能靠硬编码镜像一份 —— 那是会漂的重复源。 */
html.layout-cosmos { --cos-btn-txt: #1a1206; --cos-btn-3: #b8862f; }
html.layout-cosmos .submit {
  color: var(--cos-btn-txt);
  background: linear-gradient(118deg, var(--gold-1), var(--gold-2) 54%, var(--cos-btn-3));
  box-shadow: 0 12px 34px rgba(var(--gold-rgb), .34), inset 0 1px 0 rgba(255, 255, 255, .55);
}
html.layout-cosmos .submit:hover {
  box-shadow: 0 16px 42px rgba(var(--gold-rgb), .46), inset 0 1px 0 rgba(255, 255, 255, .55);
  filter: brightness(1.05);
}
/* ⚠️ 四角 HUD 白角标压在浅金底上会消失 ⇒ 改深色描边 */
html.layout-cosmos .submit .sc { border-color: rgba(26, 18, 6, .55); }
html.layout-cosmos .submit .bar { background: rgba(26, 18, 6, .7); box-shadow: 0 0 10px rgba(26, 18, 6, .6); }
html.layout-cosmos .submit::after { background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .6), transparent); }

html.layout-cosmos .chk input { border-color: rgba(var(--hud-rgb), .3); }
html.layout-cosmos .link { color: var(--gold-1); }
html.layout-cosmos .link:hover { color: var(--gold-2); }

/* ---------------------------------------------------------------------------
   ⑥-12 卡片内留白：玻璃卡不需要"填满等高"的仪式感，收起两支黄金分割间隔块
   （它们只在左右式 ≥961px 参与分配，本版式同样是叠放 ⇒ 必须显式收起）
   --------------------------------------------------------------------------- */
html.layout-cosmos .box-sp { display: none; }
html.layout-cosmos .box-head { margin-top: 0; margin-bottom: 20px; }
html.layout-cosmos .box-head .bh-line { display: none; }

/* ---------------------------------------------------------------------------
   ⑥-13 主题适配：玻璃卡在炽红主题下必须转深
   三主题的 --side-* / --gold-* / --accent-rgb 自动生效，只有卡片基色需要分主题覆盖。
   ⚠️ 炽红主题原为**浅底白卡 + 深字**，本版式的深空场景要求深色玻璃
     ⇒ 必须连带反转 --text 系列，否则会渲染出"深底 + 深字"的不可读组合。
   --------------------------------------------------------------------------- */
html.layout-cosmos {
  --card: rgba(8, 20, 40, .70);
}
html.layout-cosmos.theme-gold {
  --card: rgba(20, 16, 8, .74);
}
html.layout-cosmos.theme-red {
  --card: rgba(28, 8, 11, .76);
  --text: rgba(255, 255, 255, .96);
  --text-dim: rgba(255, 255, 255, .84);
  --text-faint: rgba(255, 255, 255, .66);
  --input: rgba(255, 255, 255, .07);
  --border: rgba(255, 180, 180, .26);
  --ok: #6ee7a8;
}

/* ★ 真缺陷修复（实测抓到，不是预防性加固）：
   static/css/theme-red.css:644-653 有一组
     `.theme-red input[type="text"], .theme-red input[type="password"], … {
        background:#fff; border:1px solid #d9d9d9; color:#333333; }`   ← 特异性 (0,2,1)
   它压过登录页 .field input 的 (0,1,1)。在**浅底白卡**下这组规则是对的，
   但本版式把卡片改成深色玻璃：
     · background / border 被我这边 (0,2,2) 的 .field input 挡住了 ⇒ 没出问题；
     · **color 我没设** ⇒ #333333 生效 ⇒ 深灰字压在深红玻璃上，
       实测对比度 **1.01**（几乎不可读）。
   ⇒ 必须用更高特异性 (0,3,1) 显式接管色值；顺带把 ::placeholder 也接过来
     （`.theme-red input::placeholder` 与登录页 `.field input::placeholder` 同为 (0,1,2)，
       靠加载顺序决定胜负，那是不该依赖的偶然）。 */
html.layout-cosmos.theme-red .field input {
  color: var(--text);
  background: rgba(255, 255, 255, .055);
  border-color: rgba(var(--hud-rgb), .24);
}
html.layout-cosmos.theme-red .field input::placeholder { color: var(--text-faint); }

/* ---------------------------------------------------------------------------
   ⑥-14 响应式
   ≤1200：安全边收一档（场景 --cs 由 clamp 的 vh 项自动跟随，这里只调文字与留白）
   ≤960 ：由「叠放」退回「上下堆叠」—— 窄屏没有横向余量做偏心构图，
          继续叠放只会让舱体压在场景上。此档：
            · 场景实体只留 金核 + 金环 + 1 枚卫星（轨道盘/辉光/网格收起）
            · 场景锚点移到右上（--cos-cx: 88%），文字块限宽 56% 与之分居左右
            · .side 给 min-height，让"顶部场景带"有稳定的高度
   ≤520 ：再压一档（场景再缩、流星放慢）
   --------------------------------------------------------------------------- */
@media (max-width: 1200px) {
  html.layout-cosmos { --cos-px: 44px; --cos-py: 40px; }
  html.layout-cosmos .hero { bottom: calc(var(--cos-py) + 40px); }
}
@media (max-width: 960px) {
  /* ★ 真缺陷修复（窄屏实测抓到）：
     基础样式表里「≤960 改单列」那条是 `.split { grid-template-columns: 1fr; … }`，特异性 (0,1,0)；
     而本文件 ⑥-0 为做叠放写了 `html.layout-cosmos .split`，特异性 (0,2,1)
     ⇒ **那条断点被静默盖住**，390px 下仍按两列排（每列约 220px），
     结果：卡片窄到「记住账号 / 注册用户 / 忘记密码」逐字竖排、卡内容被右缘裁切。
     这是「用了更高特异性 ⇒ 必须把相关断点一并复制过来」的典型陷阱。
     （漏掉它的原因很直白：原型首轮只验证了 ≥1200 的桌面档。） */
  html.layout-cosmos {
    /* 窄屏改「场景居右上 / 文字居左」的左右分居，锚点跟着走 */
    --cos-cx: 88%; --cos-cy: 44%;
    --cos-px: 22px; --cos-py: 24px;
  }
  html.layout-cosmos .split {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    height: auto;
    min-height: 100vh;
  }
  /* 退回上下堆叠：两栏各占一行，不再叠放 */
  html.layout-cosmos .side { grid-column: 1; grid-row: 1; min-height: 396px; }
  html.layout-cosmos .main {
    grid-column: 1; grid-row: 2; justify-content: center;
    padding: 0 var(--cos-px) 44px; --cardw: 100%;
  }
  /* ⚠️ .side 给了 min-height，但 .side-inner 的基础值是 height:100% ——
     在**高度由 min-height 撑起**的父元素上，百分比高度解析不到参照 ⇒ 塌成内容高，
     space-between 随之失效：品牌 / 主张 / 遥测紧堆成一段，顶部那 146px 空档反而空着，
     而场景锚点仍按 44% 落在"有高度"的位置 ⇒ 场景与文字错位。
     把 min-height 与父同步，让 space-between 有真实空间可分。 */
  html.layout-cosmos .side-inner { height: auto; min-height: 396px; padding: var(--cos-py) var(--cos-px); }
  html.layout-cosmos .hero {
    position: static; left: auto; right: auto; bottom: auto; top: auto; margin: 0;
    width: auto; max-width: 56%; padding-left: 16px;
  }
  html.layout-cosmos .side-brand { max-width: 56%; }
  /* 窄屏场景：--cs 收到 16vh，并收掉会与文字抢位的实体 */
  /* ★ #1082：窄屏**不做**偏心微移（--cos-dx 归零）—— 此档场景已收到 88%
     且 --cs 仅 112~140px，再左移会让金核贴住左侧文字块；而平台与辉光
     在本档本就 display:none（见下方三行），所以归零不影响盘子的呈现。 */
  html.layout-cosmos .cos-scene { --cs: clamp(112px, 16vh, 140px); --cos-dx: 0px; }
  html.layout-cosmos .cos-pad,
  html.layout-cosmos .cos-halo,
  html.layout-cosmos .cos-grid { display: none; }
  html.layout-cosmos .sat.s2,
  html.layout-cosmos .sat.s3 { display: none; }
  html.layout-cosmos .sat.s1 {
    left: calc(var(--cos-cx) - var(--cs) * .72);
    top: calc(var(--cos-cy) - var(--cs) * .52);
  }
  html.layout-cosmos .main::before { background: none; }
  html.layout-cosmos .stage { max-height: none; }
  html.layout-cosmos .box { border-radius: 18px; }
  html.layout-cosmos .console::before { display: none; }
  html.layout-cosmos .side::after { display: none; }
}
@media (max-width: 520px) {
  html.layout-cosmos .cos-meteor { --md: 11s; }
  html.layout-cosmos .box .cap { margin-bottom: 24px; }
}

/* ==========================================================================
   版式改造（#1081）：左右分栏（split / split73）→「全域铺满 + 悬浮登录窗」
   ==========================================================================
   【诉求（用户原话）】「左右分栏式的也参考苍穹式进行调整，意思是不要左右卡片。
     左边卡片铺满，右边登陆窗口悬浮的这种」

   【做法】沿用版式⑥星穹式已验证的「叠放」手法，但**不动卡片内部几何**：
     · .split 由两列压成一格 ⇒ .side 与 .main 同格叠放（不再并排）；
     · .side 原样铺满整屏（它本来就是 100vh、自成背景的视觉层，无需改造）；
     · .main 改为**靠右、宽度 = 原右栏宽**的一列。
       ★ 这一条是本改造的关键：只要 .main 仍是「与原右栏同宽、同 x 区间的一列」，
         .stage / .console / .box / --cardw / --mpx / --cpy / --cpx 全部原封不动，
         卡片的**位置与宽度逐像素不变**，变的只是「它下面压着什么」。
         反面做法是"整屏容器 + 卡片靠右对齐" —— 那要重算 .d-reticle / .d-halo /
         .main::before 一整串按"栏"设计的定位，纯属自找的回归面（且很难证明没伤到）。

   【唯一有意的形态变化】卡片高度由「撑满一栏」改为「随内容」：
     .stage{height:auto} + .box{min-height:0}。
     理由：撑满一栏时卡片高 ≈ 视口高（1440×900 实测 812px），读作"一堵墙"或"一块板"，
     与"悬浮登陆窗口"正相反。⇒ **水平位置/宽度不变、高度改为内容自适应**，
     是本次的预期结果，不是回归（探针把两者分开断言，见 _verify_float.py）。

   【★ 断点闸门】本段全部规则关在 @media (min-width: 961px) 内，两条都必须：
     ① ≤960 已切成上下堆叠、高度自适应（`.split{grid-template-columns:1fr;
        grid-template-rows:auto 1fr;height:auto}`），"铺满 + 悬浮"在那里没有意义；
     ② ≤960 那批降载规则特异性只有 (0,1,0)，本段是 (0,2,1) ⇒ 不设闸门会**静默压掉**它们
        （最直接的就是 `.stage{height:auto;max-height:none;transform:none!important}`
         与 `.box{min-height:0;padding-bottom:20px}` 会被反向顶掉）。
     闸门口径与 #1064 段完全一致，不是新发明的写法。
   ========================================================================== */
@media (min-width: 961px) {

  /* ---- ⑦-0 版面令牌 ----------------------------------------------------
     ★ --sf-col 必须与基础样式里 .split 的列比例**逐值对应**，漏掉任一档就是该视口下
       卡片宽度与改版前不一致（静默的宽度回归）：
         split    基础 1.19fr 1fr ⇒ 1/2.19 = 45.662%    ≤1280 → 1.10fr 1fr ⇒ 1/2.10 = 47.619%
         split73  基础  7fr 3fr   ⇒ 3/10   = 30%         ≤1440 → 2.05fr 1fr ⇒ 1/3.05 = 32.787%
                                                         ≤1200 → 1.62fr 1fr ⇒ 1/2.62 = 38.168%
     ★ 用 vw 而不是 % 的理由：--sf-safe 要同时给 .hero（绝对定位；% 基准 = .side 宽）
       与 .side-foot（静态块；% 基准 = .side-inner 内容宽）用，两者基准差 2×--spx
       ⇒ 写成 % 会让"文案右缘"与"遥测行右缘"**差 116px**（实测口径）。vw 是绝对长度，
       同一令牌到处同值。页面 ≥961 时 body 是 overflow:hidden（无滚动条）⇒ vw 无偏差。
     ---------------------------------------------------------------------- */
  html.layout-split,
  html.layout-split73   { --sf-gap: clamp(36px, 5vw, 120px); }
  html.layout-split     { --sf-col: 45.662vw; }
  html.layout-split73   { --sf-col: 30vw; }

  /* ★ --sf-safe 定义在 .side 上而不在 html 上：它要用 --spx，而 --spx 是**声明在 .side**
     上的令牌（基础 58 / ≤1080 38 / ≥1600 76 / split73 50）。写在 html 上会取不到
     --spx ⇒ calc 整体失效 ⇒ width 声明被丢弃 ⇒ .hero 退回 #1064 的 left+right 两端
     定位、宽度撑满整屏 ⇒ **h1 字号被 5.5cqw 静默放大到 52px**。这类"令牌取不到"
     不会报任何错，是本项目最容易踩的一类静默失效。 */
  html.layout-split .side,
  html.layout-split73 .side {
    --sf-safe: min(calc(100vw - var(--spx) - var(--sf-col) - var(--sf-gap)), 900px);
  }

  /* ---- ⑦-1 骨架：两列 → 同格叠放 ---- */
  html.layout-split .split,
  html.layout-split73 .split {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr;
  }
  html.layout-split .side,
  html.layout-split73 .side,
  html.layout-split .main,
  html.layout-split73 .main {
    grid-column: 1; grid-row: 1;          /* 同格 ⇒ 叠放（非并排） */
  }
  /* .main 铺满一格但**只占右侧一段**：justify-self 把它钉到右缘，宽度用原右栏宽 */
  html.layout-split .main,
  html.layout-split73 .main {
    justify-self: end;
    width: var(--sf-col);
  }
  /* 显式分层：两个 grid item 都只是 position:relative（不建栈上下文），
     而 .side-inner 自身 z-index:12、.main > * 是 z-index:3 ⇒ 同为 auto 时
     左栏文字会**跨层盖到卡片上**（本版式两块真正重叠后才暴露）。 */
  html.layout-split .side,
  html.layout-split73 .side { z-index: 1; }
  html.layout-split .main,
  html.layout-split73 .main { z-index: 2; }

  /* ---- ⑦-2 撤掉四件「分栏语言」---- */
  /* ① 两栏之间的接缝光带：叠放后它落在屏幕最右缘，已无分隔语义 */
  html.layout-split .side::after,
  html.layout-split73 .side::after { display: none; }
  /* ② 右栏点阵：与铺满后的 .lay.mesh / .lay.lattice 重复，且它会把右半屏重新读成"一块板" */
  html.layout-split .main::after,
  html.layout-split73 .main::after { display: none; }
  /* ③ .dl 整组（流动光带 / HUD 同心圆 / 瞄准框 / 刻度盘 / 光柱）是按「一栏 657px 的容器」
     设计的；铺满后它们的作用域仍是 .main（= 原右栏宽），会与左栏 .streams / .hud
     叠成第二种语言 ⇒ 与版式⑥同一取舍，整组收起 */
  html.layout-split .main > .dl,
  html.layout-split73 .main > .dl { display: none; }
  /* ④ .main::before 只留「卡片承托光」。
     原式三件事里前两件是分栏语言（横向渐隐 `620px 430px at 78% 6%` 的承托对象是"一栏"；
     四周暗角 `142% 124% at 50% 46%` 在 657px 宽的一栏里正好把整栏压暗成一"块"）——
     叠放后 .main 的宽度没变，暗角仍会把它读成板，故只保留一条柔光。
     ★ 这条柔光有一个必须满足的硬条件：**在 .main 的边界上衰减到 0**。
       渐变按 .main 自身盒子取坐标 ⇒ 只要它的 100% 停靠点落在盒子内部，边界处就会出现
       一条「亮度阶跃」。实测（首版把暗角椭圆改成 `76% 54% ... rgba(0,0,0,.28)`）：
       .main 左边界 x=782 处左右相差 8~11 个亮度单位，在深色星野上肉眼可见一条竖缝。
       下面这条 58%×40% + 72% 停靠点的组合，实测在边界处的归一化距离 ≥1.12 > 0.72
       ⇒ 完全透明，不会产生阶跃。
     ⚠️ 「悬浮感」的承重件**不是**这里，而是卡片自身的 box-shadow（见 ⑦-3）。 */
  html.layout-split .main::before,
  html.layout-split73 .main::before {
    background: radial-gradient(58% 40% at 50% 50%, rgba(var(--rhud-rgb), .13), transparent 72%);
  }

  /* ---- ⑦-3 卡片脱离"撑满"（唯一有意的形态变化，见段头）---- */
  html.layout-split .stage,
  html.layout-split73 .stage { height: auto; max-height: calc(100vh - var(--mpy) * 2); }
  /* 悬浮的第一层证据：卡片自身的大范围柔影。
     ★ 必须用 box-shadow 而不是 .main::before 上的暗角椭圆 —— 阴影跟着**卡片形状**走，
       而 .main 上的渐变只能在 .main 自己的盒子里取值，边界处必然出现亮度阶跃（见 ⑦-2 ④）。
     ⚠️ 横向不能被 .main 的 overflow-x:hidden 裁掉：卡片宽 412 / 栏宽 658 ⇒ 两侧各余 123px，
       本式横向外扩 ≈ blur/2 = 45px ⇒ 安全。 */
  html.layout-split .box,
  html.layout-split73 .box {
    min-height: 0;
    box-shadow: 0 34px 90px rgba(0, 0, 0, .52), 0 10px 28px rgba(0, 0, 0, .34);
  }
  /* 悬浮的第三层证据（与版式⑥ .stage::after 同手法）：卡片下方的地面光斑。
     少它一层，卡片就会"贴"回背景。 */
  html.layout-split .stage::after,
  html.layout-split73 .stage::after {
    content: ""; position: absolute; left: 50%; bottom: -26px;
    width: 74%; height: 38px; transform: translateX(-50%);
    background: radial-gradient(50% 50% at 50% 50%, rgba(var(--gold-rgb), .18), transparent 72%);
    filter: blur(14px); pointer-events: none;
  }

  /* ---- ⑦-4 左栏文案收进「安全区」：任何一块文字都不许压到悬浮窗上 ----
     安全区宽度 = 100vw − 左内边(--spx) − 右栏宽 − 间隙。
     ★ 它不是"随手的排版尺寸"，它是**标题字号的基准**：.hero h1 的字号是容器查询
       `clamp(24px, 5.5cqw, 52px)`，5.5cqw = .hero 自身内容宽的 5.5%。
       标定口径 = 复现改版前的字号 ⇒ 让 .hero 宽 ≈ 改版前的左栏内容宽
         （1440 下 = 1440×(1.19/2.19) − 2×58 = 666px）；
       本式 1440 下得 100vw−58−657.5−72 = 652.5px ⇒ h1 35.9px vs 改版前 36.6px（差 0.7px）。
       若改用"看着合适"的 52vw，1440 下 .hero 会变 748px、h1 变 41px ——
       同样"看着正常"，但比改版前大 4.4px，属**静默的字号回归**。
     ⚠️ .hero 的 `right: auto` 必须显式写：绝对定位元素同时给 left/right/width 时
        （ltr）right 会被忽略，但显式写出来才不会让后读的人以为它还受 #1064 的 right 约束。 */
  html.layout-split .hero,
  html.layout-split73 .hero { right: auto; width: var(--sf-safe); }
  /* 顶部控制条（NODE 读数 + 三主题切换）同样收进安全区：
     它原本横贯整栏，叠放后会横贯整屏 —— 主题切换胶囊会压到卡片右上角
     （`top:16px` + 胶囊高 30px = 46px，而卡片上沿在 44px ⇒ 实测 2px 重叠，
       且 .main 在上层 ⇒ 按钮看得见、点不动）。收进安全区后：
       胶囊右缘 = --spx + --sf-safe，与卡片左缘至少留出 --sf-gap。 */
  html.layout-split .side-top,
  html.layout-split73 .side-top { right: auto; width: var(--sf-safe); }
  /* 遥测行右端收口（#1066 的 margin-left:auto 落在这条右缘上）⇒ 三者的右缘严格对齐 */
  html.layout-split .side-foot,
  html.layout-split73 .side-foot { max-width: var(--sf-safe); }
}

/* ---- ⑦-0b 令牌的断点阶梯（与 ⑦-0 的列比例逐值对应）------------------------
   ⚠️ 这三条必须**写在上方那个 @media 块之后**：选择器特异性相同（html.layout-split /
      html.layout-split73），靠顺序决胜；写在前面会被基础值 45.662vw / 30vw 顶掉，
      表现为"1280 以下卡片比改版前宽一点"——差 28px，肉眼根本看不出来（静默回归）。
   ⚠️ split73 两条的先后不可交换：≤1200 必须压在 ≤1440 之后。 */
@media (min-width: 961px) and (max-width: 1280px) { html.layout-split   { --sf-col: 47.619vw; } }
@media (min-width: 961px) and (max-width: 1440px) { html.layout-split73 { --sf-col: 32.787vw; } }
@media (min-width: 961px) and (max-width: 1200px) { html.layout-split73 { --sf-col: 38.168vw; } }

/* ==========================================================================
   ⑧ 登录卡头部两处收敛（#1083，2026-10-06）
   --------------------------------------------------------------------------
   诉求（用户原话，三轮逐步锁定）：
     ①「红色区域往下移动点，最稳妥就是去除，同时让"欢迎回来、请使用企业帐号登陆"居中显示」
     ②「删除所有登陆页面样式里面的"欢迎回来"」
     ③「徽标处理的没问题，欢迎回来也要删除」
   ⇒ 最终口径 = ① 收起「欢迎回来」正上方那条**徽标带** `.box-head`（用户已确认"没问题"）；
                ② 删除卡片**标题行**「欢迎回来」（`.box > h3`）；
                ③ 副标题「请使用企业账号登录」**居中**。

   ① `.box-head`（六边形徽标 + 向右淡出的引导线）收起 —— split / split73 / ticker 三套。
      它**不是信息**：#1064 加它就是为了填满「卡片被撑到与左栏等高」留下的空带，
      base 段自己的注释就写着：「存在的理由 —— 卡片为与左栏「等高」而撑满，
      标题栏到「欢迎回来」之间会空出一大片；这条 44px 的头带把它收成一段有内容的高度」。
      ⚠️ cosmos（星穹式）**刻意保留**：用户在本轮只对「左右式」那一屏提了这事，
         cosmos 的徽标带刚随 #1082 一起验收过，不在无诉求时扩大改动面。
         🔁 后续：#1084 按新诉求把 cosmos 也收起了，至此**六套全停用** —— 见文件末尾 ⑨。
      ⚠️ center / aurora 本来就 display:none 了它，无需重复写。
   ② `.box > h3` 收起 —— **六套全收**（诉求明说「所有登陆页面样式」）。
      center / aurora 本就 none，实际生效的是 split / split73 / ticker / cosmos 四套。
      ⚠️ 用 CSS 收起、**不删模板里的 `<h3>欢迎回来</h3>`**，两个理由：
         ① 文案可一字恢复（本诉求在一天内已改过一轮口径，可逆是硬要求）；
         ② center / aurora 的 CSS 按「.box-bar → .crown → .box-sp-a → …」这套兄弟
            顺序写，抽掉节点会让那两套**静默走形** —— 这正是本项目「常驻 DOM 靠 CSS
            分层控制」的既定口径（见 templates/login.html #1060 段注释）。
   ③ `.box .cap` 居中。此前只有 split73（#1071 诉求③）与 cosmos 居中，split / ticker
      仍左对齐 —— 同一张卡片在不同版式下对齐口径不一致，属既有缺陷。

   📐 几何后果（1440×900 实测，A/B 见交付目录）：
      卡片高度 −(头带 48 + 其下 27) 再 −(标题行 29)；卡片自 #1081 起是**内容驱动高度**
      （`.box{min-height:0}`）⇒ 回收的是卡片自身，卡片靠垂直居中整体下移半个回收量，
      卡内元素**相对卡片顶边**的位移 = 「头带以上 0 / 其余整段上移一个标题行高度」，
      且下方各元素**彼此间距逐值不变**（C 组断言逐项核过）。

   ⚠️ 可逆：删掉下面这个 @media 块与最后两行即完全回到原状
      （有头带 + 有标题 + split/ticker 左对齐）。
   ⚠️ ≤960：`.box-head` 由窄屏段整体收起（本就不显示）；标题行**照收**
      （诉求是「所有登陆页面样式」，手机端不该还留一行已删除的文案）；
      副标题居中只在 ≥961px 生效（与 #1071 split73 的居中同口径），
      窄屏排版逐像素不变。
   ⚠️ 写法：头带用 `html.layout-*` 前缀（只落三套），标题行用 `.box > h3`（卡内全站），
      两者都写在文件**最末**，同特异性时靠顺序决胜（本文件既定口径：不用 !important）。 */
@media (min-width: 961px) {
  html.layout-split .box-head,
  html.layout-split73 .box-head,
  html.layout-ticker .box-head { display: none; }

  html.layout-split .box .cap,
  html.layout-split73 .box .cap,
  html.layout-ticker .box .cap { text-align: center; }
}
.box > h3 { display: none; }

/* ==========================================================================
   ⑨ 苍穹式两处微调（#1084，2026-10-06）
   --------------------------------------------------------------------------
   诉求（用户原话）：「苍穹式，去除右边红色区域的徽标，左边区域徽标往下调整」
     · 「右边红色区域的徽标」= 卡片头部徽标带 `.box-head` 里那枚徽记（`.bh-mk`，
        48×48 圆角方块 + 六边形）。炽红主题下整块是红的 —— 就是它。本版式里它的
        引导线 `.bh-line` 本就不显示（实测 display:none）⇒ 收起头带 = 恰好只去掉这一块。
     · 「左边区域徽标」= 左栏品牌行 `.side-brand`（同源的六边形徽记 + 系统名）。

   ① `.box-head` 在苍穹式收起。它是 #1083 唯一被**刻意保留**的一套；至此
      split / split73 / ticker / cosmos 四套显式收起、center / aurora 本就是 none
      ⇒ **六套全部不再渲染**。
      ⚠️ 仍**不删模板 DOM**（与 #1083 同口径，那两个理由现在依然成立）：
         ① 可逆 —— 本诉求的口径一天内已改过两轮；
         ② center / aurora 的 CSS 按「.box-bar → .box-head → .crown → .box-sp-a → …」
            这套兄弟顺序写，抽掉节点会让那两套**静默走形**。
      ⚠️ 卡片是**内容驱动高度**（本版式的 `.box{min-height:0}`，见 ⑥-10）⇒ 收起后卡片变矮，
         卡内元素**相对卡片顶边**整体上移一个头带高度，下方各元素彼此间距逐值不变。
         （绝对 y 会同时混进卡片自身的居中位移 —— 判据必须取"相对卡片顶边"，见 #1083。）

   ② 品牌行**整行**下移，而不是只挪徽记。原因：
      `.side-top`（NODE 读数 + 主题切换）是**绝对定位** `top:16px`、高 32px（占 16–48），
      而 `.side-inner` 的上内边距 `--cos-py` 在 1440×900 下 = 41.4px ⇒ **品牌行顶边落在
      NODE 行带之内，两者重叠 6.6px**；徽记的呼吸光环（`::after{inset:-5px}`，还在做
      `mkPulse` 缩放）视觉上顶到 y≈36，与 NODE 文字只隔几像素 —— 这就是"挤"的来源。
      ⚠️ 只挪徽记会破坏它与右侧两行文字的对齐：`.side-brand{align-items:center}` 是刻意的，
         实测两者居中偏差 < 1px（1440 下徽记 45.2–89.2 / 文字块 41.7–92.7）。
         ⇒ 整行下移，**内部对齐关系逐值不变**。
      ⚠️ 18px 不是随手取的：它让品牌行顶边落到 `NODE 行底(48px) + 12px`，
         而 12px 正是本版式既有的节奏值（见 ⑥-9：`h1` / `.kicker` 的 margin-bottom）。
      ⚠️ 只在 ≥961px：窄屏的 `.side-inner` 是另一套（min-height:396px + 居中堆叠，
         品牌行不与场景主体争位）⇒ 不跟着动，与 #1083「窄屏排版逐像素不变」同口径。
   ⚠️ 可逆：删掉下面这个 @media 块即完全回到原状（两处一起恢复）。
   -------------------------------------------------------------------------- */
@media (min-width: 961px) {
  html.layout-cosmos { --cos-brand-dy: 18px; }   /* ← 想再上/下移，只改这一个数 */
  html.layout-cosmos .box-head { display: none; }
  html.layout-cosmos .side-brand { margin-top: var(--cos-brand-dy); }
}

/* ==========================================================================
   ⑩ 左右式「副标题」落位修正（#1098，2026-10-08）
   ==========================================================================
   【诉求（用户原话）】「红色框区域内容往下排版，登陆窗口涉及到的『请使用企业账号登录』，
     都请适当调整位置」——用户截图标出的正是登录卡副标题 `.box .cap`。

   【根因】不是这一个元素写错，而是**两次改动叠加**留下的空洞：
     · ⑦ 段（#1081 铺满 + 悬浮）为了把富余高度交给黄金分割间隔块，把 `.box-bar`
       的下外边距 22px 归零 ⇒ 「标题栏 → 副标题」只剩 `.cap` 自己的 `margin-top:10px`；
       而这个 10px 原本是 **h3「欢迎回来」→ 副标题** 的咬合间距，**不是**标题栏间距。
     · ⑧ 段（#1083）把 `h3` 与 `.box-head` 一并收起 ⇒ 这 10px 直接暴露成
       「标题栏 → 副标题」的距离。
     ⇒ 副标题像被贴在标题栏下沿，其下却仍留着 68.8px（原「主张 → 表单」沉降台）的大空档。

   【对照实测 · 六套版式】只有左右式一家有此问题（量测脚本
     `outputs/_measure_login_cap_1098_all.py`，1904×918）：

       layout      标题栏→副标题   副标题→表单
       split             10.0          68.8    ← 本段修
       split73           10.0          33.2    ← 本段修
       ticker            30.0          26.0    （`.box-bar` 的 22px 未被归零 ⇒ 天然健康）
       cosmos            32.0          30.0    （同上）
       center / aurora   ——  `.cap` 收起，本就没有副标题

   【做法】只把副标题在**原带内**下移，令「上 : 下 = 1 : φ(1.618)」——与 #1064 的
     「视觉中心」同一口径；并保证 `上 + 下` **逐值守恒**（与改动前一字不差）：
       · 账号框及以下**绝对位置逐值不变**（卡片高度也不变，全程只挪这一个元素）；
       · 任何视口总带不变（下述两式「基准之和 / --air 系数之和」都等于原式）。
     ⚠️ 不采用「上下平分」：副标题是**表单的引言**，不是卡片的居中标题；
        贴回上沿一侧（1:1.618）才读得出「标题栏 + 副标题」的层级。
     ⚠️ 只在 ≥961px：窄屏 `.box-bar` 仍是基础版的 22px 下边距 ⇒
        「标题栏 → 副标题」= 22 + 6 = 28px，本来就健康（与 ticker/cosmos 同因）。
        这一条同时保证「移动端排版逐像素不变」（与 ⑧ / ⑨ 同口径）。
   ⚠️ 可逆：删掉下面这个 @media 块即完全回到改动前（逐像素）。
   ⚠️ 想再上/下移：只改 `margin-top` 的**基准**，并把 `margin-bottom` 的基准**减去同一个数**
      （两项之和恒定，否则账号框会跟着动 —— 那就不再是「只挪副标题」了）。
   -------------------------------------------------------------------------- */
@media (min-width: 961px) {
  /* split：原 = 上 10px（常量）/ 下 (48px + --air×18)，即 58 + --air×18
     ⇒ 按 1:1.618 重分为 22 + --air×6.9（上）与 36 + --air×11.1（下）。
     1904×918 实测：10.0 / 68.8 → 30.0 / 48.8（副标题下移 20px，账号框不动）。 */
  html.layout-split .box .cap {
    margin-top: calc(22px + var(--air) * 6.9);
    margin-bottom: calc(36px + var(--air) * 11.1);
  }
  /* split73（紧凑档）：原 = 上 10px / 下 (24px + --air×8)，即 34 + --air×8
     ⇒ 重分为 13 + --air×3.1（上）与 21 + --air×4.9（下）。
     1904×918 实测：10.0 / 33.2 → 16.6 / 26.7（副标题下移 6.6px，账号框不动）。
     ⚠️ 紧凑档的带高只有 60px（split 是 96px）⇒ 按同一比例分出来的位移天然更小；
        这里**刻意不加大**，否则「副标题 → 账号框」会被压到 10px 出头，反而变挤。 */
  html.layout-split73 .box .cap {
    margin-top: calc(13px + var(--air) * 3.1);
    margin-bottom: calc(21px + var(--air) * 4.9);
  }
}

/* ==========================================================================
   #1085 / #1086 移动端只保留登录框（≤960）：品牌视觉区整体收起
   --------------------------------------------------------------------------
   【诉求（用户原话）】
     #1085：「移动端登录的时候不要显示上面红色区域的内容」→「只保留登录框」；
     #1086：「**所有的**移动端登陆页面都不需要显示品牌区」（把上面的范围扩到六套版式）。
   目标状态：手机上打开登录页，直接就是居中的登录卡（可立即输入）；
   桌面端（≥961）逐像素不变。

   --------------------------------------------------------------------------
   ★★ 关键：六套版式的「品牌区」不是同一个元素，必须分成两类处理
     A 类·上下堆叠式（split / split73 / ticker / cosmos）—— 品牌区 = `.side` **整块**
       split/split73：基础样式把 `.split` 切成单列两行，`.side` 就是顶部品牌面板；
       ticker：2903 段把 `.split` 改 flex column，`.side` 里是本版式的「顶部行情条」
               （品牌行 + 金价 + 涨跌）—— 用户要求「所有版式都不显示品牌区」，
               故整块收起（连带行情条，这是本条的必然结果，不是漏考虑）；
       cosmos：3722 段给它 min-height:396px，是「顶部星穹场景带」，同样整块收起。
       ⇒ 做法：`.side{display:none}` + 让登录卡跨满网格全部行（见下 ②）。

     B 类·整屏式（center / aurora）—— `.side` 是**整屏背景层**，品牌内容在 `.main` 里
       center：1451 段 `.side{position:absolute;inset:0}` 且 `html.layout-center .main`
       自己写 `background:none` ⇒ 底色全靠 `.side` 铺；品牌内容（主标题冠 / 特性网格 /
       底部读数条）是 `.main` 内的 `.crown` / `.c-feats` / `.c-foot`。
       aurora：2318 段同理（`.main{background:none}`），且 2326 段已经把 `.side-inner`
       收起，品牌内容只剩 `.main` 内的 `.crown`。
       ⇒ 做法：**保留 `.side`（否则页面退化成纯白裸底）**，只收 `.main` 内的品牌块。

   --------------------------------------------------------------------------
   ★ 四条规则各自的作用（缺一不可）

   ① A 类：`.side{display:none}` 收起品牌区本体。
      用 display 而非删 DOM —— 与 #1083/#1084「收头带」同一口径（**可逆**：
      删掉本段即逐像素回到改动前）。

   ② A 类：`.main{grid-row:1/-1}` 补回网格占位。
      `.side` 退出网格后，`.split{grid-template-rows:auto 1fr}` 的**第二行会空着**，
      登录卡被钉在 auto 行（贴顶、下方留一片空白）⇒ 让它跨满所有行；配合
      `.split` 的 min-height:100vh/dvh 与 `.main` 自身的 flex 居中，恢复
      「独占整屏 + 垂直居中」。
      ⚠️ 不改 `.split` 的 grid-template-rows：split73 在 1424 段用 (0,2,1) 写过
         `…grid-template-rows:auto 1fr`，两边一起改要「选择器 + 断点」双处同步；
         只动 `.main` 的 grid-row 是单点，且 (0,2,1) 同特异性 + 写在文件末尾即可胜出。
      ⚠️ ticker **不需要**这条：2921 段已经给了 `.main{flex:1 1 auto}`（它窄屏是
         flex 布局，grid-row 无效），重复声明只会制造「看似有用」的假规则 ⇒ 不写。
      ⚠️ cosmos 必须写：3723 段把 `.main` 钉在 `grid-row:2`，不覆盖它就会卡在第二行。

   ③ B 类：收 `.main` 内的品牌块。
      center = `.crown` + `.c-feats` + `.c-foot`（后两者在手机上分别是卡片下方的特性
      网格与读数条，与 A 类被一并收起的 `.feats`/`.side-foot` 同性质，故一并收）；
      aurora = 只有 `.crown`（`.c-feats`/`.c-foot` 的基础规则本就是 display:none）。
      ⚠️ 这三条全是 (0,2,1)，与本文件既有的 `html.layout-center .crown{display:block}` 等
         同特异性 ⇒ 必须写在文件**末尾**（本段即末尾），否则会被上面顶掉。

   ④ B 类：center 的**居中补偿**。center 的垂直居中原本靠
      `.crown{margin-top:auto}` + `.c-foot{margin-bottom:auto}` 这一对 auto 边距撑出来；
      三个品牌块一收，`.main`（`flex-direction:column` + `justify-content:flex-start`）
      里只剩 `.stage` ⇒ **卡片会贴顶**。给 `.stage` 补同一对 auto 边距即可恢复居中，
      且与 1458 段「刻意不用 justify-content:center（溢出时会裁掉顶部且滚不回去）」
      的理由一致：auto 边距在内容高于容器时会退化为「从顶部开始」，仍可滚动可达。
      ⚠️ aurora **不需要**补偿：它的 `.main` 是 `justify-content: safe center`（2387）。

   --------------------------------------------------------------------------
   ⚠️ 与脚本的兼容（已核，无需改模板）：canvas #sideBg 的 resize() 首行
      `if (!W || !H) return;`、frame() 外层 `if (W && H)` ⇒ display:none 下 W=H=0，
      粒子网只是**停绘**（不报错、也不再吃 CPU）；sideEl 的 mousemove 视差在不可见元素上
      根本不会触发；computeHY() 对取不到的元素本就 early-return。
      （B 类保留 `.side` ⇒ 它们的粒子网照常绘制，行为不变。）
   ⚠️ 主题切换入口的**结构性差异**（本次的已知代价，已与用户确认）：
      A 类的三个主题圆点长在 `.side-top` 里，`.side` 一收就一并没了 ⇒ 手机端无主题
      切换入口（`localStorage['hg_theme']` 按设备记，电脑上切不会带到手机）；
      B 类的 `.side` 保留 ⇒ 圆点仍在，可正常切换。若要给 A 类补入口，需把圆点从
      `.side-top` 搬进登录卡（改 DOM，需单独排期）。
   ⚠️ 想改生效范围：只改下面的 max-width 一处（例如收窄到 520px = 只在小屏手机收起）。
   ⚠️ 演进记录：#1085 首发只覆盖 A 类的前两套（split / split73，即当时启用中的版式）；
      #1086 按「所有版式」的要求扩到六套，并把 B 类的「收品牌块而非背景层」写明。
   ========================================================================== */
@media (max-width: 960px) {
  /* ① A 类：品牌区 = .side 整块 ⇒ 收起 */
  html.layout-split   .side,
  html.layout-split73 .side,
  html.layout-ticker  .side,
  html.layout-cosmos  .side { display: none; }

  /* ② A 类：登录卡跨满网格全部行（ticker 见上注：它在 flex 布局下不需要） */
  html.layout-split   .main,
  html.layout-split73 .main,
  html.layout-cosmos  .main { grid-row: 1 / -1; }

  /* ③ B 类：.side 是整屏背景层 ⇒ 保留；只收 .main 内的品牌块 */
  html.layout-center .crown,
  html.layout-center .c-feats,
  html.layout-center .c-foot,
  html.layout-aurora .crown { display: none; }

  /* ④ B 类：center 的居中补偿（原来是靠 .crown/.c-foot 的 auto 边距撑出来的） */
  html.layout-center .stage { margin-top: auto; margin-bottom: auto; }
}
