/* ============================================================================
   HubPilot Design System
   ----------------------------------------------------------------------------
   全站唯一的样式来源。目标(见《HubPilot_UI_Redesign_ClaudeCode_v2.md》§32):
   改一次 token,影响整个 HubPilot。

   【使用方式】页面 <head> 里两行,顺序不能反:
     <script src="/assets/theme.js"></script>          先定主题,防首屏闪烁
     <link rel="stylesheet" href="/assets/hubpilot.css">

   【铁律】
   1. 组件只消费 token,不写死颜色。要加颜色先加 token。
   2. 不要在页面里覆盖组件样式 —— 那等于把碎片化又请回来了。
      页面确实特殊,就在这里加一个修饰类(如 .btn--wide),别在页面里写 .btn{...}。
   3. 主题只换 token,不换结构、不换布局、不换组件行为。

   审计基线(2026-08-12,改造前):31 个页面内联 CSS 15 万字符、
   153 种颜色、16 种圆角、35 种字号、19 种 font-family,26 个页面各写一套按钮。
   ============================================================================ */

/* ══════════════════════════════════════════════════════════════════════════
   1. 主题 Token
   ──────────────────────────────────────────────────────────────────────────
   默认主题(不写 data-theme 时)= HubPilot 工业深色,是主推主题。
   其余三套通过 <html data-theme="..."> 切换。
   ══════════════════════════════════════════════════════════════════════════ */

:root,
:root[data-theme="hub-default"] {
  color-scheme: dark;

  --bg-main:       #07111F;
  --bg-secondary:  #0B1628;
  --bg-tertiary:   #111D31;

  --surface-1:     #111C2F;
  --surface-2:     #162238;
  --surface-3:     #1B2941;
  --surface-glass: rgba(17, 28, 47, 0.88);

  --border-subtle:  rgba(148, 163, 184, 0.12);
  --border-default: rgba(148, 163, 184, 0.20);
  --border-strong:  rgba(148, 163, 184, 0.30);

  --brand-primary: #FACC15;
  --brand-hover:   #FBBF24;
  --brand-active:  #EAB308;
  --brand-ink:     #0F172A;   /* 黄底上的文字色 */
  --brand-soft:    rgba(250, 204, 21, 0.12);
  /* 品牌色当【文字】用时的专用 token。深色主题上就是品牌黄;
     浅色主题必须压深,否则浅底上的黄字对比度不够(实测过,大标题几乎看不清)。 */
  --brand-text:    #FACC15;

  --cyan:   #72ACC6;
  --teal:   #67C1B5;
  --blue:   #7D9FCA;
  --violet: #9987CE;
  --orange: #C69974;

  --success: #35D871;
  --warning: #DBA140;
  --danger:  #DF5858;
  --info:    #578BDF;

  --text-primary:   #F8FAFC;
  --text-secondary: #CBD5E1;
  --text-muted:     #7C8CA5;
  --text-disabled:  #526078;
  /* Tailwind 桥接用的 RGB 三元组:发车 App 里有 bg-slate-800/50 这类
     带透明度修饰符的类,必须是 rgb(var(--x) / <alpha-value>) 格式才生效,
     直接塞 hex 会让所有 /50 /70 失效。改主题颜色时这两处要一起改。 */

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(250,204,21,.18);
  --glow-brand: 0 0 20px rgba(250,204,21,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --glow-danger: 0 0 14px rgba(223,88,88,.55);
  --glow-danger-soft: 0 0 6px rgba(223,88,88,.30);
  --glow-success: 0 0 14px rgba(53,216,113,.55);
  --glow-success-soft: 0 0 6px rgba(53,216,113,.30);
  --glow-warning: 0 0 14px rgba(219,161,64,.55);
  --glow-warning-soft: 0 0 6px rgba(219,161,64,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 7 17 31;
  --rgb-bg-secondary: 11 22 40;
  --rgb-surface-1: 17 28 47;
  --rgb-surface-2: 22 34 56;
  --rgb-surface-3: 27 41 65;
  --rgb-text-primary: 248 250 252;
  --rgb-text-secondary: 203 213 225;
  --rgb-text-muted: 124 140 165;
  --rgb-text-disabled: 82 96 120;
  --rgb-brand-primary: 250 204 21;
  --rgb-brand-text: 250 204 21;
  --rgb-brand-hover: 251 191 36;
  --rgb-success: 53 216 113;
  --rgb-warning: 219 161 64;
  --rgb-danger: 223 88 88;
  --rgb-info: 87 139 223;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(250,204,21,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(53, 216, 113, 0.16);
  --warning-soft: rgba(219, 161, 64, 0.16);
  --danger-soft: rgba(223, 88, 88, 0.16);
  --info-soft: rgba(87, 139, 223, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 10.00 */
  --warning-ink: #0B1220;   /* 对比 8.19 */
  --danger-ink: #0B1220;   /* 对比 5.06 */
  --info-ink: #0B1220;   /* 对比 5.50 */
}

/* Light Office —— 白天办公室长时间看。深色文字 + 浅底,黄色只做点缀。 */
:root[data-theme="light-office"] {
  --brand-text: #92400E;
  color-scheme: light;

  /* 和「暖纸」的区分点是【色温】:这套明确偏冷灰,暖纸明确偏米黄。
     两套亮度接近,靠温差区分,肉眼比色距数字更敏感。 */
  --bg-main:       #EEF1F6;
  --bg-secondary:  #E4E9F1;
  --bg-tertiary:   #DAE1EB;

  --surface-1:     #FFFFFF;
  --surface-2:     #F8FAFC;
  --surface-3:     #F1F5F9;
  --surface-glass: rgba(255, 255, 255, 0.90);

  --border-subtle:  rgba(15, 23, 42, 0.08);
  --border-default: rgba(15, 23, 42, 0.14);
  --border-strong:  rgba(15, 23, 42, 0.22);

  /* 浅底上纯黄字看不清,主色压深一档,文字用深色 */
  --brand-primary: #F59E0B;
  --brand-hover:   #D97706;
  --brand-active:  #B45309;
  --brand-ink:     #1F1300;
  --brand-soft:    rgba(245, 158, 11, 0.14);

  --cyan:   #3D7795;
  --teal:   #398B84;
  --blue:   #4463A6;
  --violet: #6C46AC;
  --orange: #A06241;

  --success: #20A651;
  --warning: #B87323;
  --danger:  #C82626;
  --info:    #275CCF;

  --text-primary:   #0F172A;
  --text-secondary: #334155;
  --text-muted:     #64748B;
  --text-disabled:  #94A3B8;
  /* Tailwind 桥接用的 RGB 三元组:发车 App 里有 bg-slate-800/50 这类
     带透明度修饰符的类,必须是 rgb(var(--x) / <alpha-value>) 格式才生效,
     直接塞 hex 会让所有 /50 /70 失效。改主题颜色时这两处要一起改。 */

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle);
  --elev-2: 2px 2px 0 0 rgba(22,36,59,.14), 0 0 0 1px var(--border-default);
  --elev-3: 4px 4px 0 0 rgba(22,36,59,.16), 0 0 0 1px var(--border-strong);
  --elev-4: 8px 8px 0 0 rgba(22,36,59,.18), 0 0 0 1px var(--border-strong);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(245,158,11,.18);
  --glow-brand: 0 0 20px rgba(245,158,11,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --glow-danger: 0 0 14px rgba(200,38,38,.55);
  --glow-danger-soft: 0 0 6px rgba(200,38,38,.30);
  --glow-success: 0 0 14px rgba(32,166,81,.55);
  --glow-success-soft: 0 0 6px rgba(32,166,81,.30);
  --glow-warning: 0 0 14px rgba(184,115,35,.55);
  --glow-warning-soft: 0 0 6px rgba(184,115,35,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 238 241 246;
  --rgb-bg-secondary: 228 233 241;
  --rgb-surface-1: 255 255 255;
  --rgb-surface-2: 248 250 252;
  --rgb-surface-3: 241 245 249;
  --rgb-text-primary: 15 23 42;
  --rgb-text-secondary: 51 65 85;
  --rgb-text-muted: 100 116 139;
  --rgb-text-disabled: 148 163 184;
  --rgb-brand-primary: 245 158 11;
  --rgb-brand-text: 146 64 14;
  --rgb-brand-hover: 217 119 6;
  --rgb-success: 32 166 81;
  --rgb-warning: 184 115 35;
  --rgb-danger: 200 38 38;
  --rgb-info: 39 92 207;
  /* 浅色主题:颗粒要更弱(浅底上颗粒更显),环境光靠纯白提亮顶部 */
  --grain-opacity: .028;
  --ambient-top: rgba(255,255,255,.85);
  --ambient-brand: rgba(245,158,11,.055);
  /* 浅色主题的 surface-1 本身就接近纯白,白色高光【完全无效】。
     所以这里归零,厚度感全部交给带色相的投影(--elev-*)。
     硬套深色那套参数只会得到一张看不出任何变化的卡片。 */
  --card-sheen: 0;
  --card-edge: 0;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(32, 166, 81, 0.12);
  --warning-soft: rgba(184, 115, 35, 0.12);
  --danger-soft: rgba(200, 38, 38, 0.12);
  --info-soft: rgba(39, 92, 207, 0.12);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 5.92 */
  --warning-ink: #0B1220;   /* 对比 4.92 */
  --danger-ink: #FFFFFF;   /* 对比 5.58 */
  --info-ink: #FFFFFF;   /* 对比 5.97 */
}

/* High Contrast Dark —— 仓库、大屏、远看、夜班。对比拉满。 */
:root[data-theme="high-contrast-dark"] {
  --brand-text: #FACC15;
  color-scheme: dark;

  /* 和默认主题的区分点:这套要在仓库大屏上远看得清,所以压到近乎纯黑、
     边框和状态色都拉强。之前 #050B14 和默认深蓝差距太小(色距 28),现在拉开。 */
  --bg-main:       #000000;
  --bg-secondary:  #060606;
  --bg-tertiary:   #0C0C0C;

  --surface-1:     #101010;
  --surface-2:     #1A1A1A;
  --surface-3:     #262626;
  --surface-glass: rgba(10, 10, 10, 0.96);

  --border-subtle:  rgba(203, 213, 225, 0.20);
  --border-default: rgba(203, 213, 225, 0.34);
  --border-strong:  rgba(203, 213, 225, 0.52);

  --brand-primary: #FACC15;
  --brand-hover:   #FDE047;
  --brand-active:  #EAB308;
  --brand-ink:     #000000;
  --brand-soft:    rgba(250, 204, 21, 0.18);

  /* 远看要认得出,状态色一律提亮 */
  --success: #53DE86;
  --warning: #DDB64F;
  --danger:  #E47171;
  --info:    #6AA0E3;

  --text-primary:   #FFFFFF;
  --text-secondary: #E2E8F0;
  --text-muted:     #A9B7CA;
  --text-disabled:  #6B7C93;
  /* Tailwind 桥接用的 RGB 三元组:发车 App 里有 bg-slate-800/50 这类
     带透明度修饰符的类,必须是 rgb(var(--x) / <alpha-value>) 格式才生效,
     直接塞 hex 会让所有 /50 /70 失效。改主题颜色时这两处要一起改。 */

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(250,204,21,.18);
  --glow-brand: 0 0 20px rgba(250,204,21,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --cyan: #6AB7C2;
  --teal: #67C1B5;
  --blue: #7D9FCA;
  --violet: #A093D3;
  --orange: #C69974;
  --glow-danger: 0 0 14px rgba(228,113,113,.55);
  --glow-danger-soft: 0 0 6px rgba(228,113,113,.30);
  --glow-success: 0 0 14px rgba(83,222,134,.55);
  --glow-success-soft: 0 0 6px rgba(83,222,134,.30);
  --glow-warning: 0 0 14px rgba(221,182,79,.55);
  --glow-warning-soft: 0 0 6px rgba(221,182,79,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 0 0 0;
  --rgb-bg-secondary: 6 6 6;
  --rgb-surface-1: 16 16 16;
  --rgb-surface-2: 26 26 26;
  --rgb-surface-3: 38 38 38;
  --rgb-text-primary: 255 255 255;
  --rgb-text-secondary: 226 232 240;
  --rgb-text-muted: 169 183 202;
  --rgb-text-disabled: 107 124 147;
  --rgb-brand-primary: 250 204 21;
  --rgb-brand-text: 250 204 21;
  --rgb-brand-hover: 253 224 71;
  --rgb-success: 83 222 134;
  --rgb-warning: 221 182 79;
  --rgb-danger: 228 113 113;
  --rgb-info: 106 160 227;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(250,204,21,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(83, 222, 134, 0.16);
  --warning-soft: rgba(221, 182, 79, 0.16);
  --danger-soft: rgba(228, 113, 113, 0.16);
  --info-soft: rgba(106, 160, 227, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 10.84 */
  --warning-ink: #0B1220;   /* 对比 9.70 */
  --danger-ink: #0B1220;   /* 对比 6.15 */
  --info-ink: #0B1220;   /* 对比 6.91 */
}

/* Paper Warm —— 暖纸浅色。白天久看比冷白舒服,像看纸质单据。 */
:root[data-theme="paper-warm"] {
  --brand-text: #7C2D12;
  color-scheme: light;
  --bg-main: #F6F1E4; --bg-secondary: #EFE8D7; --bg-tertiary: #E5DCC6;
  --surface-1: #FFFDF8; --surface-2: #F7F4EC; --surface-3: #EFEAE0;
  --surface-glass: rgba(255, 253, 248, 0.92);
  --border-subtle: rgba(66, 52, 30, 0.10);
  --border-default: rgba(66, 52, 30, 0.16);
  --border-strong: rgba(66, 52, 30, 0.26);
  --brand-primary: #B45309; --brand-hover: #92400E; --brand-active: #78350F;
  --brand-ink: #FFF9EC; --brand-soft: rgba(180, 83, 9, 0.12);
  --cyan: #38798A; --teal: #36847E; --blue: #415AA0; --violet: #6842A3; --orange: #96573D;
  --success: #1D964A; --warning: #A85B20; --danger: #B32222; --info: #254EC2;
  --text-primary: #1C1917; --text-secondary: #44403C;
  --text-muted: #78716C; --text-disabled: #A8A29E;
  /* Tailwind 桥接用的 RGB 三元组:发车 App 里有 bg-slate-800/50 这类
     带透明度修饰符的类,必须是 rgb(var(--x) / <alpha-value>) 格式才生效,
     直接塞 hex 会让所有 /50 /70 失效。改主题颜色时这两处要一起改。 */

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle);
  --elev-2: 2px 2px 0 0 rgba(59,49,22,.14), 0 0 0 1px var(--border-default);
  --elev-3: 4px 4px 0 0 rgba(59,49,22,.16), 0 0 0 1px var(--border-strong);
  --elev-4: 8px 8px 0 0 rgba(59,49,22,.18), 0 0 0 1px var(--border-strong);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(180,83,9,.18);
  --glow-brand: 0 0 20px rgba(180,83,9,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --glow-danger: 0 0 14px rgba(179,34,34,.55);
  --glow-danger-soft: 0 0 6px rgba(179,34,34,.30);
  --glow-success: 0 0 14px rgba(29,150,74,.55);
  --glow-success-soft: 0 0 6px rgba(29,150,74,.30);
  --glow-warning: 0 0 14px rgba(168,91,32,.55);
  --glow-warning-soft: 0 0 6px rgba(168,91,32,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 246 241 228;
  --rgb-bg-secondary: 239 232 215;
  --rgb-surface-1: 255 253 248;
  --rgb-surface-2: 247 244 236;
  --rgb-surface-3: 239 234 224;
  --rgb-text-primary: 28 25 23;
  --rgb-text-secondary: 68 64 60;
  --rgb-text-muted: 120 113 108;
  --rgb-text-disabled: 168 162 158;
  --rgb-brand-primary: 180 83 9;
  --rgb-brand-text: 124 45 18;
  --rgb-brand-hover: 146 64 14;
  --rgb-success: 29 150 74;
  --rgb-warning: 168 91 32;
  --rgb-danger: 179 34 34;
  --rgb-info: 37 78 194;
  /* 浅色主题:颗粒要更弱(浅底上颗粒更显),环境光靠纯白提亮顶部 */
  --grain-opacity: .028;
  --ambient-top: rgba(255,255,255,.85);
  --ambient-brand: rgba(180,83,9,.055);
  /* 浅色主题的 surface-1 本身就接近纯白,白色高光【完全无效】。
     所以这里归零,厚度感全部交给带色相的投影(--elev-*)。
     硬套深色那套参数只会得到一张看不出任何变化的卡片。 */
  --card-sheen: 0;
  --card-edge: 0;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(29, 150, 74, 0.12);
  --warning-soft: rgba(168, 91, 32, 0.12);
  --danger-soft: rgba(179, 34, 34, 0.12);
  --info-soft: rgba(37, 78, 194, 0.12);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 4.92 */
  --warning-ink: #FFFFFF;   /* 对比 5.02 */
  --danger-ink: #FFFFFF;   /* 对比 6.63 */
  --info-ink: #FFFFFF;   /* 对比 7.12 */
}

/* 工程蓝图 —— 从"浅蓝纸"改成真·深蓝图。原来那版和 light-office 只差 RGB 4.6、色相 2.5°,是全站唯一一对撞车的主题。改成中深蓝之后既消除重复,又填上了 L* 5→95 之间的空档。 */
:root[data-theme="blueprint"] {
  color-scheme: dark;
  /* 深蓝图底 + 黄色仪表强调。品牌黄在蓝图蓝上对比极好,是这套的性格所在。 */
  --bg-main: #0E3A5C;
  --bg-secondary: #0A2F4B;
  --bg-tertiary: #14476F;
  --surface-1: #164A75;
  --surface-2: #1C5886;
  --surface-3: #246798;
  --surface-glass: rgba(22, 74, 117, 0.90);
  --border-subtle: rgba(190, 225, 250, 0.14);
  --border-default: rgba(190, 225, 250, 0.22);
  --border-strong: rgba(190, 225, 250, 0.36);
  --brand-primary: #FACC15;
  --brand-hover: #FDE047;
  --brand-active: #EAB308;
  --brand-ink: #0A2238;
  --brand-text: #FDE047;
  --brand-soft: rgba(250, 204, 21, 0.13);
  --cyan: #85B6CD;
  --teal: #78C8BB;
  --blue: #8AABCF;
  --violet: #A093D3;
  --orange: #CCA883;
  --success: #76E59E;
  --warning: #E1C362;
  --danger: #E98B8B;
  --info: #95CDEB;
  --text-primary: #EAF4FD;
  --text-secondary: #BBD9F0;
  --text-muted: #84AECE;
  --text-disabled: #5F87A6;
  /* 立面:深色靠 inset 顶边高光,浅色靠带色相的投影 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --ring-focus: 0 0 0 3px rgba(250,204,21,.18);
  --glow-brand: 0 0 20px rgba(250,204,21,.38);
  --glow-danger: 0 0 14px rgba(233,139,139,.55);
  --glow-danger-soft: 0 0 6px rgba(233,139,139,.30);
  --glow-success: 0 0 14px rgba(118,229,158,.55);
  --glow-success-soft: 0 0 6px rgba(118,229,158,.30);
  --glow-warning: 0 0 14px rgba(225,195,98,.55);
  --glow-warning-soft: 0 0 6px rgba(225,195,98,.30);
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* Tailwind 桥接三元组 —— 从上面的 hex 自动生成,不要手写 */
  --rgb-bg-main: 14 58 92;
  --rgb-bg-secondary: 10 47 75;
  --rgb-surface-1: 22 74 117;
  --rgb-surface-2: 28 88 134;
  --rgb-surface-3: 36 103 152;
  --rgb-text-primary: 234 244 253;
  --rgb-text-secondary: 187 217 240;
  --rgb-text-muted: 132 174 206;
  --rgb-text-disabled: 95 135 166;
  --rgb-brand-primary: 250 204 21;
  --rgb-brand-text: 253 224 71;
  --rgb-brand-hover: 253 224 71;
  --rgb-success: 118 229 158;
  --rgb-warning: 225 195 98;
  --rgb-danger: 233 139 139;
  --rgb-info: 149 205 235;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(250,204,21,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(118, 229, 158, 0.16);
  --warning-soft: rgba(225, 195, 98, 0.16);
  --danger-soft: rgba(233, 139, 139, 0.16);
  --info-soft: rgba(149, 205, 235, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 12.01 */
  --warning-ink: #0B1220;   /* 对比 10.86 */
  --danger-ink: #0B1220;   /* 对比 7.61 */
  --info-ink: #0B1220;   /* 对比 10.89 */
}

/* 石墨 —— 中间调。全站原本非黑即白(背景 L* 不是 5 就是 95),中间整段是空的。这套 L*=30,长时间盯屏最不累:既没有深色主题的高反差,也没有浅色主题的高亮度。 */
:root[data-theme="graphite"] {
  color-scheme: dark;
  /* 中性石墨灰,几乎无色偏(S=0.02)。定位是设备面板/控制台的那种质感。 */
  --bg-main: #45474B;
  --bg-secondary: #3C3E42;
  --bg-tertiary: #4E5055;
  --surface-1: #505257;
  --surface-2: #5A5C62;
  --surface-3: #66686F;
  --surface-glass: rgba(80, 82, 87, 0.90);
  --border-subtle: rgba(226, 232, 240, 0.14);
  --border-default: rgba(226, 232, 240, 0.22);
  --border-strong: rgba(226, 232, 240, 0.36);
  --brand-primary: #FACC15;
  --brand-hover: #FDE047;
  --brand-active: #EAB308;
  --brand-ink: #26282B;
  --brand-text: #FDE047;
  --brand-soft: rgba(250, 204, 21, 0.13);
  --cyan: #85B6CD;
  --teal: #78C8BB;
  --blue: #96AED4;
  --violet: #B4A8DC;
  --orange: #CCA883;
  --success: #82E7A0;
  --warning: #E1C362;
  --danger: #EB9898;
  --info: #95CDEB;
  --text-primary: #F5F6F8;
  --text-secondary: #D4D8DE;
  --text-muted: #A6ADB7;
  --text-disabled: #7C838D;
  /* 立面:深色靠 inset 顶边高光,浅色靠带色相的投影 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --ring-focus: 0 0 0 3px rgba(250,204,21,.18);
  --glow-brand: 0 0 20px rgba(250,204,21,.38);
  --glow-danger: 0 0 14px rgba(233,139,139,.55);
  --glow-danger-soft: 0 0 6px rgba(233,139,139,.30);
  --glow-success: 0 0 14px rgba(130,231,160,.55);
  --glow-success-soft: 0 0 6px rgba(130,231,160,.30);
  --glow-warning: 0 0 14px rgba(225,195,98,.55);
  --glow-warning-soft: 0 0 6px rgba(225,195,98,.30);
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* Tailwind 桥接三元组 —— 从上面的 hex 自动生成,不要手写 */
  --rgb-bg-main: 69 71 75;
  --rgb-bg-secondary: 60 62 66;
  --rgb-surface-1: 80 82 87;
  --rgb-surface-2: 90 92 98;
  --rgb-surface-3: 102 104 111;
  --rgb-text-primary: 245 246 248;
  --rgb-text-secondary: 212 216 222;
  --rgb-text-muted: 166 173 183;
  --rgb-text-disabled: 124 131 141;
  --rgb-brand-primary: 250 204 21;
  --rgb-brand-text: 253 224 71;
  --rgb-brand-hover: 253 224 71;
  --rgb-success: 130 231 160;
  --rgb-warning: 225 195 98;
  --rgb-danger: 235 152 152;
  --rgb-info: 149 205 235;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(250,204,21,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(130, 231, 160, 0.16);
  --warning-soft: rgba(225, 195, 98, 0.16);
  --danger-soft: rgba(235, 152, 152, 0.16);
  --info-soft: rgba(149, 205, 235, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 12.38 */
  --warning-ink: #0B1220;   /* 对比 10.86 */
  --danger-ink: #0B1220;   /* 对比 8.45 */
  --info-ink: #0B1220;   /* 对比 10.89 */
}

/* 森林深绿 —— 深色里唯一的绿色调,和深蓝/纯黑/暖褐色相完全分开。 */
:root[data-theme="forest"] {
  color-scheme: dark;
  --bg-main: #04150C;
  --bg-secondary: #071E11;
  --bg-tertiary: #0C3C1F;
  --surface-1: #0A2415;
  --surface-2: #0F331E;
  --surface-3: #164529;
  --surface-glass: rgba(10, 36, 21, 0.90);
  --border-subtle: rgba(134, 222, 172, 0.14);
  --border-default: rgba(134, 222, 172, 0.22);
  --border-strong: rgba(134, 222, 172, 0.36);
  --brand-primary: #FACC15;
  --brand-hover: #FDE047;
  --brand-active: #EAB308;
  --brand-ink: #0A1410;
  --brand-soft: rgba(250, 204, 21, 0.13);
  --brand-text: #FACC15;
  --text-primary: #F0F7F2;
  --text-secondary: #C8DDD0;
  --text-muted: #6FA383;
  --text-disabled: #55705F;
  /* Tailwind 桥接三元组(发车 App 靠它,加主题别忘了这组)*/

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(250,204,21,.18);
  --glow-brand: 0 0 20px rgba(250,204,21,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --success: #A6DD4D;
  --warning: #DDB64F;
  --danger: #E47283;
  --info: #78C2E5;
  --cyan: #7EC2CA;
  --teal: #78C8BB;
  --blue: #8AABCF;
  --violet: #A093D3;
  --orange: #CCA883;
  --glow-danger: 0 0 14px rgba(228,114,131,.55);
  --glow-danger-soft: 0 0 6px rgba(228,114,131,.30);
  --glow-success: 0 0 14px rgba(166,221,77,.55);
  --glow-success-soft: 0 0 6px rgba(166,221,77,.30);
  --glow-warning: 0 0 14px rgba(221,182,79,.55);
  --glow-warning-soft: 0 0 6px rgba(221,182,79,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 4 21 12;
  --rgb-bg-secondary: 7 30 17;
  --rgb-surface-1: 10 36 21;
  --rgb-surface-2: 15 51 30;
  --rgb-surface-3: 22 69 41;
  --rgb-text-primary: 240 247 242;
  --rgb-text-secondary: 200 221 208;
  --rgb-text-muted: 111 163 131;
  --rgb-text-disabled: 85 112 95;
  --rgb-brand-primary: 250 204 21;
  --rgb-brand-text: 250 204 21;
  --rgb-brand-hover: 253 224 71;
  --rgb-success: 166 221 77;
  --rgb-warning: 221 182 79;
  --rgb-danger: 228 114 131;
  --rgb-info: 120 194 229;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(250,204,21,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(166, 221, 77, 0.16);
  --warning-soft: rgba(221, 182, 79, 0.16);
  --danger-soft: rgba(228, 114, 131, 0.16);
  --info-soft: rgba(120, 194, 229, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 11.65 */
  --warning-ink: #0B1220;   /* 对比 9.70 */
  --danger-ink: #0B1220;   /* 对比 6.27 */
  --info-ink: #0B1220;   /* 对比 9.49 */
}

/* 暖褐 —— 深色里唯一的暖色调。夜班久看比冷色不容易疲劳。 */
:root[data-theme="espresso"] {
  color-scheme: dark;
  --bg-main: #14100C;
  --bg-secondary: #1A1510;
  --bg-tertiary: #231C15;
  --surface-1: #211A13;
  --surface-2: #2E241A;
  --surface-3: #3D3123;
  --surface-glass: rgba(33, 26, 19, 0.90);
  --border-subtle: rgba(214, 190, 160, 0.12);
  --border-default: rgba(214, 190, 160, 0.20);
  --border-strong: rgba(214, 190, 160, 0.32);
  --brand-primary: #F5B841;
  --brand-hover: #FBCC6B;
  --brand-active: #D99A20;
  --brand-ink: #14100C;
  --brand-soft: rgba(245, 184, 65, 0.14);
  --brand-text: #F5B841;
  --text-primary: #FAF5EE;
  --text-secondary: #E0D4C4;
  --text-muted: #A2907B;
  --text-disabled: #6E6154;
  /* Tailwind 桥接三元组(发车 App 靠它,加主题别忘了这组)*/

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-2: 2px 2px 0 0 rgba(0,0,0,.45), 0 0 0 1px var(--border-default), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-3: 4px 4px 0 0 rgba(0,0,0,.5), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  --elev-4: 8px 8px 0 0 rgba(0,0,0,.55), 0 0 0 1px var(--border-strong), inset 0 1px 0 rgba(255,255,255,.05);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(245,184,65,.18);
  --glow-brand: 0 0 20px rgba(245,184,65,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --success: #76E59E;
  --warning: #E7D27F;
  --danger: #E98B8B;
  --info: #83B2E7;
  --cyan: #7EC2CA;
  --teal: #78C8BB;
  --blue: #8AABCF;
  --violet: #A093D3;
  --orange: #CCA883;
  --glow-danger: 0 0 14px rgba(233,139,139,.55);
  --glow-danger-soft: 0 0 6px rgba(233,139,139,.30);
  --glow-success: 0 0 14px rgba(118,229,158,.55);
  --glow-success-soft: 0 0 6px rgba(118,229,158,.30);
  --glow-warning: 0 0 14px rgba(231,210,127,.55);
  --glow-warning-soft: 0 0 6px rgba(231,210,127,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 20 16 12;
  --rgb-bg-secondary: 26 21 16;
  --rgb-surface-1: 33 26 19;
  --rgb-surface-2: 46 36 26;
  --rgb-surface-3: 61 49 35;
  --rgb-text-primary: 250 245 238;
  --rgb-text-secondary: 224 212 196;
  --rgb-text-muted: 162 144 123;
  --rgb-text-disabled: 110 97 84;
  --rgb-brand-primary: 245 184 65;
  --rgb-brand-text: 245 184 65;
  --rgb-brand-hover: 251 204 107;
  --rgb-success: 118 229 158;
  --rgb-warning: 231 210 127;
  --rgb-danger: 233 139 139;
  --rgb-info: 131 178 231;
  /* 质感层参数:深色主题上颗粒提亮、环境光从上方打下来 */
  --grain-opacity: .042;
  --ambient-top: rgba(255,255,255,.055);
  --ambient-brand: rgba(245,184,65,.05);
  /* 卡片质感:表面渐变强度 + 顶边内高光强度。
     深色主题上白色高光是有效的 —— 它模拟光打在卡片上边缘的折射。 */
  --card-sheen: .030;
  --card-edge: .11;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(118, 229, 158, 0.16);
  --warning-soft: rgba(231, 210, 127, 0.16);
  --danger-soft: rgba(233, 139, 139, 0.16);
  --info-soft: rgba(131, 178, 231, 0.16);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 12.01 */
  --warning-ink: #0B1220;   /* 对比 12.41 */
  --danger-ink: #0B1220;   /* 对比 7.61 */
  --info-ink: #0B1220;   /* 对比 8.46 */
}

/* 薄荷浅绿 —— 浅色里的绿色调,和冷灰/暖纸/蓝图分开。 */
:root[data-theme="mint"] {
  color-scheme: light;
  --bg-main: #E9F4EC;
  --bg-secondary: #DEEDE3;
  --bg-tertiary: #D2E5D9;
  --surface-1: #FBFEFC;
  --surface-2: #F0F8F3;
  --surface-3: #E3F0E8;
  --surface-glass: rgba(251, 254, 252, 0.92);
  --border-subtle: rgba(16, 68, 45, 0.10);
  --border-default: rgba(16, 68, 45, 0.17);
  --border-strong: rgba(16, 68, 45, 0.28);
  --brand-primary: #0F766E;
  --brand-hover: #115E59;
  --brand-active: #134E4A;
  --brand-ink: #FFFFFF;
  --brand-soft: rgba(15, 118, 110, 0.11);
  --brand-text: #115E59;
  --cyan: #396E8C;
  --teal: #36847E;
  --blue: #415AA0;
  --violet: #6842A3;
  --orange: #96573D;
  --success: #1D964A;
  --warning: #A85B20;
  --danger: #B12245;
  --info: #254EC2;
  --text-primary: #07231A;
  --text-secondary: #1E4032;
  --text-muted: #4F7462;
  --text-disabled: #8FAB9C;
  /* Tailwind 桥接三元组(发车 App 靠它,加主题别忘了这组)*/

  /* ── 立面 Elevation(2026-08-15)────────────────────────────────────────
     统一的高度层级。**深色和浅色用的是两套不同公式**:
       深色:底色接近纯黑,投影几乎看不见 → 主要靠 inset 顶边高光造"浮起",
             投影只负责把大面积卡片和背景推开
       浅色:投影带【底色的色相】而不是纯灰 —— 纯灰阴影在有色底上发脏,
             带色相的阴影是"高级感"里最省力的一处
     用法:elev-1 卡片静置 / elev-2 hover、下拉 / elev-3 抽屉、浮层 / elev-4 模态框 */
  --elev-1: 0 0 0 1px var(--border-subtle);
  --elev-2: 2px 2px 0 0 rgba(22,59,32,.14), 0 0 0 1px var(--border-default);
  --elev-3: 4px 4px 0 0 rgba(22,59,32,.16), 0 0 0 1px var(--border-strong);
  --elev-4: 8px 8px 0 0 rgba(22,59,32,.18), 0 0 0 1px var(--border-strong);
  /* 焦点环与品牌辉光:原本散在页面里写死,收进来 */
  --ring-focus: 0 0 0 3px rgba(15,118,110,.18);
  --glow-brand: 0 0 20px rgba(15,118,110,.38);
  /* 旧名保留为别名,已用它们的组件不用改 */
  --shadow-card:  var(--elev-1);
  --shadow-hover: var(--elev-2);
  --shadow-pop:   var(--elev-4);
  /* ── 补齐 + 状态辉光(2026-08-15 自动生成)─────────────────
     缺失的语义色会静默继承默认深色主题的值 —— 不报错,但这套主题的
     状态色就从来没为它调过。辉光从该主题的语义色推导,自动跟着主题走。 */
  --glow-danger: 0 0 14px rgba(177,34,69,.55);
  --glow-danger-soft: 0 0 6px rgba(177,34,69,.30);
  --glow-success: 0 0 14px rgba(29,150,74,.55);
  --glow-success-soft: 0 0 6px rgba(29,150,74,.30);
  --glow-warning: 0 0 14px rgba(168,91,32,.55);
  --glow-warning-soft: 0 0 6px rgba(168,91,32,.30);
  /* Tailwind 桥接三元组 —— 【从上面的 hex 自动生成,不要手写】。
     发车 App 里 bg-slate-800/50 这类带透明度的类必须走 rgb(var(--x) / <alpha>)。
     以前 hex 和三元组是两处独立维护的,改一处忘另一处就会不同步。 */
  --rgb-bg-main: 233 244 236;
  --rgb-bg-secondary: 222 237 227;
  --rgb-surface-1: 251 254 252;
  --rgb-surface-2: 240 248 243;
  --rgb-surface-3: 227 240 232;
  --rgb-text-primary: 7 35 26;
  --rgb-text-secondary: 30 64 50;
  --rgb-text-muted: 79 116 98;
  --rgb-text-disabled: 143 171 156;
  --rgb-brand-primary: 15 118 110;
  --rgb-brand-text: 17 94 89;
  --rgb-brand-hover: 17 94 89;
  --rgb-success: 29 150 74;
  --rgb-warning: 168 91 32;
  --rgb-danger: 177 34 69;
  --rgb-info: 37 78 194;
  /* 浅色主题:颗粒要更弱(浅底上颗粒更显),环境光靠纯白提亮顶部 */
  --grain-opacity: .028;
  --ambient-top: rgba(255,255,255,.85);
  --ambient-brand: rgba(15,118,110,.055);
  /* 浅色主题的 surface-1 本身就接近纯白,白色高光【完全无效】。
     所以这里归零,厚度感全部交给带色相的投影(--elev-*)。
     硬套深色那套参数只会得到一张看不出任何变化的卡片。 */
  --card-sheen: 0;
  --card-edge: 0;

  /* 语义色的柔和底 —— 徽章/提示条用。
     文字用 --success 本色,底用它的低透明度版本:同色系但明度差足够,
     深浅主题下都成立。改造前这些底色是写死的浅绿/浅粉,深色主题下格格不入。 */
  --success-soft: rgba(29, 150, 74, 0.12);
  --warning-soft: rgba(168, 91, 32, 0.12);
  --danger-soft: rgba(177, 34, 69, 0.12);
  --info-soft: rgba(37, 78, 194, 0.12);

  /* ── 放在语义色【背景】上的文字色 ─────────────────────────────────
     徽章、按钮会用 background: var(--danger) 这种写法,文字色不能随便给。
     实测:白字放在 --info 上,9 套主题里有 6 套对比度只有 1.7~3.4 —— 读不清。
     这里按对比度自动选黑或白,谁高用谁。**语义色当底时,文字一律用对应的 -ink。** */
  --success-ink: #0B1220;   /* 对比 4.92 */
  --warning-ink: #FFFFFF;   /* 对比 5.02 */
  --danger-ink: #FFFFFF;   /* 对比 6.60 */
  --info-ink: #FFFFFF;   /* 对比 7.12 */
}

/* ══════════════════════════════════════════════════════════════════════════
   1.5 字体自托管(2026-08-14)
   ──────────────────────────────────────────────────────────────────────────
   ⚠️ 在这之前,全站声明了 4 种字体(Inter / IBM Plex Mono / Bebas Neue /
   JetBrains Mono)但【一个都没加载】—— 没有 @font-face,没有本地文件。
   所以设计稿里的排版身份在任何一台机器上都没出现过:
     · Bebas Neue(LOGO、KPI 大数字、道口号)→ 回退 Arial / 系统 sans
     · Inter(全站正文)→ macOS 回退苹方、Windows 回退微软雅黑的西文
   只有 login + 3 个 admin 页从 Google Fonts CDN 加载,而那又违反了
   "不引外部字体 CDN"(仓库网络一抖就掉字体)。

   现在三种字体都放在 /assets/fonts/(共 200KB,8 个 woff2),和站点同源。
   · Inter 是【可变字体】,一个文件覆盖 100–900 全部字重
   · unicode-range 限定拉丁 —— 中文不走这些字体,继续落到苹方/微软雅黑,
     所以不需要自托管几 MB 的中文字体
   · font-display: swap —— 字体没到时先用回退字体渲染,绝不出现空白文字。
     仓库大屏宁可先看到字再换,不能白屏

   要加字重/字体:下载 woff2 放进 fonts/,在这里加 @font-face,别加 CDN 链接。
   ══════════════════════════════════════════════════════════════════════════ */

/* Inter · 可变字重 100–900 · 正文与 UI */
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('/assets/fonts/inter-100-900-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url('/assets/fonts/inter-100-900-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Mono · 400/600 · 单号、门号、时间、KPI 数字 */
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/plexmono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/plexmono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('/assets/fonts/plexmono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 600; font-display: swap;
  src: url('/assets/fonts/plexmono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Bebas Neue · 展示字体 · LOGO、KPI 大数字、道口号
   注意:这是全大写窄体,只适合短标签和数字,不要拿它排正文。 */
@font-face {
  font-family: 'Bebas Neue'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/bebas-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Bebas Neue'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/bebas-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ══════════════════════════════════════════════════════════════════════════
   2. 非主题 Token(所有主题共用:间距、圆角、字体、动效)
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  --font-sans: Inter, "PingFang SC", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
  --font-mono: "IBM Plex Mono", "SFMono-Regular", ui-monospace, Consolas, monospace;
  /* 展示字体。Bebas 没有中文字形,中文自动落到苹方/微软雅黑 —— 这是想要的行为 */
  --font-display: "Bebas Neue", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;

  /* ── 字号阶梯 ──────────────────────────────────────────────────────────
     2026-08-14 从 6 级扩到 11 级。为什么要扩:原来只有 6 级,
     但页面实际需要 10px(密集表格)、11px(大写标签)、13px(次级正文)
     这些中间档,token 里没有,页面就只能自己写死 px ——
     实测全站散着 27 种字号,失控的根源是【阶梯不够用】,不是没人守规矩。

     现在这 11 级覆盖了实测的全部真实用量,页面不该再出现裸 px 字号。 */
  --fs-4xs: 9px;    /* 只给签名条图例用 —— 它是元数据不是内容 */
  --fs-3xs: 10px;   /* 超密集:大屏道口副标签 */
  --fs-2xs: 11px;   /* 全大写标签、表头、徽章 */
  --fs-xs:  12px;   /* 辅助说明 */
  --fs-sm:  13px;   /* 次级正文 */
  --fs-md:  14px;   /* 正文(默认) */
  --fs-lg:  16px;   /* 小标题、引导文字 */
  --fs-xl:  20px;   /* 区块标题 */
  --fs-2xl: 24px;
  --fs-3xl: 32px;   /* 页面标题 */
  --fs-4xl: 40px;   /* KPI */
  --fs-5xl: 56px;   /* 大屏数字 */

  /* 旧名保留为别名,已接入的页面不用改。新代码请直接用上面的阶梯。
     只有 --fs-card 由 15px 归到 16px —— 15 不在任何阶梯上,是审计里
     "圆角/字号各写各的"的典型残留。 */
  --fs-page:    var(--fs-3xl);
  --fs-section: var(--fs-xl);
  --fs-card:    var(--fs-lg);
  --fs-body:    var(--fs-md);
  --fs-caption: var(--fs-xs);
  --fs-kpi:     var(--fs-4xl);

  /* ── 行高 ──────────────────────────────────────────────────────────────
     实测全站 13 种行高。收成 4 档:字号越大行高越紧,这是排版基本功,
     大标题用 1.55 会松散得像没排过版。 */
  --lh-tight: 1.15;  /* KPI、页面大标题 */
  --lh-snug:  1.3;   /* 区块标题、卡片标题 */
  --lh-base:  1.55;  /* 正文(默认) */
  --lh-loose: 1.7;   /* 长段落说明文字 */

  /* ── 字距 ──────────────────────────────────────────────────────────────
     实测 93 处 letter-spacing 各写各的。规律其实很固定:
     字号越大越要收紧(负值),全大写小字越要放开。 */
  --ls-tighter: -.022em;  /* 40px+ 的大数字 */
  --ls-tight:   -.012em;  /* 20-32px 标题 */
  --ls-normal:  0;
  --ls-wide:    .06em;    /* 小号强调文字 */
  --ls-caps:    .12em;    /* 全大写标签 —— 大写字母天生挤,不放开就糊成一团 */
  --ls-display: .03em;    /* Bebas 这类窄体需要一点呼吸 */

  /* ⚠️ 2026-08-16 第二轮:圆角【全部归零】。
     设计方向定为 Tactical Telemetry(战术遥测 / 工业仪表),
     brutalist-skill 的原话:"Absolute rejection of border-radius.
     All corners must be exactly 90 degrees to enforce mechanical rigidity."

     这是整套重做里【单项效果最强】的一条:直角瞬间把界面从"消费级 App"
     变成"仪表盘"。之前从 18/14/10/6 收到 16/12/8/4 只是"更紧",
     归零才是【换了一种东西】。

     阶梯名保留,是为了不用改任何组件 —— 以后想回退只改这五行。
     唯一保留弧度的是 --radius-pill,给状态圆点用(圆点必须是圆的)。 */
  --radius-xs: 0;
  --radius-sm: 0;
  --radius-md: 0;
  --radius-lg: 0;
  --radius-xl: 0;
  /* 只剩状态圆点用它 —— 圆点必须是圆的,其余一律直角 */
  --radius-pill: 999px;

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

  --page-max: 1560px;
  --page-pad: 28px;

  /* 微动效:150-250ms ease-out,全站统一 */
  --t-fast: 150ms cubic-bezier(.2, .8, .3, 1);
  --t-base: 200ms cubic-bezier(.2, .8, .3, 1);
}

/* 尊重系统的"减少动态效果"设置 —— 无障碍基本要求 */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   3. 基础层
   ══════════════════════════════════════════════════════════════════════════ */

.hp * { box-sizing: border-box; }

/* ⚠️ 这条【故意不加 .hp 作用域】,是全站唯一一条无作用域规则。
   理由:12 个页面(board / dashboard / login / loader / dock 三件套 / receive …)
   没挂 .hp 类,但它们恰恰是数字最密的页面 —— 大屏、看板、KPI。
   tabular-nums 让所有数字等宽,收益有两个,都是这套系统真正需要的:
     ① 数字刷新时不再左右抖动(看板 1 秒轮询,比例数字每次重排会跳)
     ② 表格数字列自动对齐,不用手动 text-align 凑
   只影响数字字形宽度,不影响任何布局、字号、颜色,所以无作用域是安全的。 */
body { font-variant-numeric: tabular-nums; }

body.hp {
  margin: 0;
  background: var(--bg-main);
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

.hp-page {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--sp-6) var(--page-pad) var(--sp-10);
}

/* 数字、单号、门号、线路一律等宽 —— 这是"系统终端感"最省力的来源 */
.hp-mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ── 展示字体工具类 ──────────────────────────────────────────────────────
   Bebas Neue 是全大写窄体,只用于 LOGO、KPI 大数字、道口号这类短标签。
   三件必须一起给,少一件就不像:
     · letter-spacing 放开(窄体默认挤在一起)
     · line-height 收到 1(它的字怀本来就高,1.5 会留出一大片空白)
     · text-transform: uppercase(小写字母在这个字体里是缩小的大写,
       不主动转反而会大小不一) */
.hp-display {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
}
/* 大数字专用:展示字体 + 更紧的字距。KPI、道口号、车次号用这个 */
.hp-numeric {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1;
  letter-spacing: var(--ls-normal);
  font-variant-numeric: tabular-nums;
}

/* ── 页头 ────────────────────────────────────────────────────────────── */
.hp-header {
  display: flex; align-items: flex-end; gap: var(--sp-4);
  flex-wrap: wrap; margin-bottom: var(--sp-6);
}
/* 页面主标题。redesign-skill:"Headlines lack presence. Increase size for
   display text, tighten letter-spacing, reduce line-height."
   改造前 32px / 700 字重 —— 只是"比正文大一点",没有分量。
   现在 40px、行高压到 1.0、负字距 —— 标题要像【钉在页面上】,不是漂着。 */
.hp-header__title {
  font-size: var(--fs-4xl); font-weight: 700;
  letter-spacing: -.028em;
  line-height: 1.0; margin: 0;
  text-wrap: balance;                 /* 避免最后一行只剩一个孤零零的词 */
}
/* 副标题改成【句子大小写 + 不加字距】。
   改造前是全大写 + 正字距 —— skill 明确点名:"All-caps subheaders everywhere"
   是最典型的生成痕迹。全站 36 处大写里,这一处影响面最大(每个页面都有)。
   保留大写的只剩真正的"标签"(表头、徽章、分区名),那是功能性的。 */
.hp-header__desc {
  color: var(--text-muted); font-size: var(--fs-sm);
  letter-spacing: var(--ls-normal); text-transform: none;
  margin-top: var(--sp-2);
}
.hp-header__actions { margin-left: auto; display: flex; gap: var(--sp-2); align-items: center; }

.hp-section-title {
  font-size: var(--fs-caption); font-weight: 600; letter-spacing: var(--ls-caps);
  text-transform: uppercase; color: var(--text-muted);
  margin: 0 0 var(--sp-3);
}

/* ══════════════════════════════════════════════════════════════════════════
   4. Card
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 卡片 ────────────────────────────────────────────────────────────────
   2026-08-16 重做。redesign-skill 直接点名的反面教材:
     "Generic card look (border + shadow + white background).
      Cards should exist only when elevation communicates hierarchy."
   改造前正是这三件套 —— 描边 + 阴影 + 底色,是最典型的 AI 生成痕迹。

   现在靠三样东西造"实体感",而不是靠一圈描边:
     ① 顶边 1px 内高光 —— 模拟光从上方打在卡片上边缘的折射。
        这是高端界面里最省力也最有效的一招:有了它,卡片才像一块【有厚度的材料】,
        而不是一个"填了色的矩形"。环境光层的方向(从上打下)和它是一致的。
     ② 极淡的表面渐变 —— 上亮下暗,统一光源方向。平涂没有体积。
     ③ 立面阴影 —— 已经是带主题色相的了(见 --elev-*)
   描边降到 border-subtle 只做兜底(高对比主题下需要它),不再是主要的边界手段。 */
.hp-card {
  position: relative;
  background:
    linear-gradient(180deg,
      rgb(255 255 255 / var(--card-sheen, .028)) 0%,
      rgb(255 255 255 / 0) 42%),
    var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  padding: var(--sp-5);
  transition: transform var(--t-base), box-shadow var(--t-base),
              border-color var(--t-base), background-color var(--t-base);
}
/* 顶边内高光。用 ::after + inset 边框而不是 box-shadow inset ——
   后者会被 border-radius 裁出一圈完整的环,这里只要【上半圈】。 */
.hp-card::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  border-top: 1px solid rgb(255 255 255 / var(--card-edge, .10));
  -webkit-mask-image: linear-gradient(180deg, #000 0, transparent 34%);
          mask-image: linear-gradient(180deg, #000 0, transparent 34%);
}
.hp-card--link { cursor: pointer; display: block; text-decoration: none; color: inherit; }
.hp-card--link:hover {
  transform: translateY(-2px);
  border-color: var(--border-default);
  box-shadow: var(--elev-3);
}
.hp-card__title { font-size: var(--fs-card); font-weight: 600; line-height: var(--lh-snug); margin: 0 0 var(--sp-1); }
.hp-card__desc  { color: var(--text-muted); font-size: var(--fs-caption); }

.hp-grid { display: grid; gap: var(--sp-4); }
.hp-grid--auto { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.hp-grid--kpi  { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

/* ── KPI ─────────────────────────────────────────────────────────────── */
.hp-kpi { position: relative; overflow: hidden; }
.hp-kpi__label {
  font-size: var(--fs-caption); letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--text-muted); display: flex; align-items: center; gap: var(--sp-2);
}
.hp-kpi__value {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--fs-kpi); font-weight: 600; line-height: var(--lh-tight);
  letter-spacing: var(--ls-tighter);
  margin: var(--sp-2) 0 var(--sp-1);
}
.hp-kpi__sub { font-size: var(--fs-caption); color: var(--text-muted); }
.hp-kpi__delta { font-family: var(--font-mono); font-size: var(--fs-caption); }
.hp-kpi__delta--up   { color: var(--success); }
.hp-kpi__delta--down { color: var(--danger); }
/* ── KPI 强调 ────────────────────────────────────────────────────────────
   改造前是左侧一道 3px 竖色条 —— 四张卡四种颜色,正是"10 个强调色互相抢"的
   主要现场之一。现在改成【底部一道极扁的光晕】:
     · 只有 2px 高,且带 60% 的渐隐,存在感远低于一整条竖线
     · 它不再是"分类标签",而是"这张卡的地面反光",和顶边高光一起构成上下呼应
   颜色仍然可以按 --accent 区分,但饱和度已经统一压到 42%,不会再打架。 */
.hp-kpi::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: linear-gradient(90deg,
    var(--accent, var(--brand-primary)) 0%,
    rgb(255 255 255 / 0) 62%);
  opacity: .8;
}
/* KPI 数字要有存在感 —— redesign-skill:"Headlines lack presence.
   Increase size, tighten letter-spacing, reduce line-height."
   改成展示字体 + 更大字号:Bebas 数字宽度系数只有 0.40,
   同样的横向空间可以放下大得多的字,视觉冲击直接翻倍而不挤版。 */
.hp-kpi__value {
  font-family: var(--font-display);
  font-size: var(--fs-5xl);
  font-weight: 400;
  line-height: .92;
  letter-spacing: var(--ls-normal);
  font-variant-numeric: tabular-nums;
}

/* ── 键值行(账号信息、详情面板这类"标签: 值"的排版)────────────────── */
.hp-kv {
  display: flex; align-items: baseline; gap: var(--sp-4);
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--border-subtle);
}
.hp-kv:last-child { border-bottom: 0; }
.hp-kv__k { color: var(--text-muted); font-size: var(--fs-caption); min-width: 76px; flex: 0 0 auto; }
.hp-kv__v { color: var(--text-primary); margin-left: auto; text-align: right; word-break: break-all; }

/* ══════════════════════════════════════════════════════════════════════════
   5. Button —— 4 种,统一高度/字号/间距/状态
   ══════════════════════════════════════════════════════════════════════════ */

.hp-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: 38px; padding: 0 var(--sp-4);
  font-family: inherit; font-size: var(--fs-body); font-weight: 600;
  border-radius: var(--radius-md); border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  transition: background-color var(--t-fast), border-color var(--t-fast),
              color var(--t-fast), transform var(--t-fast);
}
.hp-btn:active:not(:disabled) { transform: translateY(1px); }
.hp-btn:disabled { opacity: .45; cursor: not-allowed; }
.hp-btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--brand-soft);
  border-color: var(--brand-primary);
}
.hp-btn--sm { height: 30px; padding: 0 var(--sp-3); font-size: var(--fs-caption); }
.hp-btn--lg { height: 46px; padding: 0 var(--sp-6); }
.hp-btn--block { width: 100%; }

.hp-btn--primary {
  background: var(--brand-primary); color: var(--brand-ink); border-color: var(--brand-primary);
}
.hp-btn--primary:hover:not(:disabled) { background: var(--brand-hover); border-color: var(--brand-hover); }

.hp-btn--secondary {
  background: var(--surface-2); color: var(--text-primary); border-color: var(--border-default);
}
.hp-btn--secondary:hover:not(:disabled) { background: var(--surface-3); border-color: var(--border-strong); }

.hp-btn--ghost { background: transparent; color: var(--text-secondary); }
.hp-btn--ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--text-primary); }

.hp-btn--danger { background: var(--danger); color: var(--danger-ink, #fff); border-color: var(--danger); }
.hp-btn--danger:hover:not(:disabled) { filter: brightness(1.08); }

/* ══════════════════════════════════════════════════════════════════════════
   6. Input / Select
   ══════════════════════════════════════════════════════════════════════════ */

.hp-field { display: flex; flex-direction: column; gap: var(--sp-2); }
.hp-label {
  font-size: var(--fs-caption); font-weight: 600; color: var(--text-secondary);
  letter-spacing: var(--ls-wide);
}
.hp-input, .hp-select, .hp-textarea {
  height: 38px; padding: 0 var(--sp-3);
  background: var(--surface-2); color: var(--text-primary);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--fs-body); width: 100%;
  transition: border-color var(--t-fast), box-shadow var(--t-fast), background-color var(--t-fast);
}
.hp-textarea { height: auto; padding: var(--sp-3); resize: vertical; line-height: var(--lh-loose); }
.hp-input::placeholder, .hp-textarea::placeholder { color: var(--text-disabled); }
.hp-input:hover, .hp-select:hover, .hp-textarea:hover { border-color: var(--border-strong); }
.hp-input:focus, .hp-select:focus, .hp-textarea:focus {
  outline: none;
  border-color: rgba(250, 204, 21, .65);
  box-shadow: 0 0 0 3px var(--brand-soft);
  background: var(--surface-1);
}
.hp-input:disabled, .hp-select:disabled { opacity: .5; cursor: not-allowed; }
.hp-input--error, .hp-select--error { border-color: var(--danger); }
.hp-help { font-size: var(--fs-caption); color: var(--text-muted); }
.hp-help--error { color: var(--danger); }

.hp-select {
  appearance: none;
  /* 下拉箭头用 currentColor 画,主题切换时自动跟着变色 */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 16px) 52%, calc(100% - 11px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: var(--sp-8);
}

/* ══════════════════════════════════════════════════════════════════════════
   7. Tabs
   ══════════════════════════════════════════════════════════════════════════ */

.hp-tabs {
  display: flex; gap: var(--sp-1); position: relative;
  border-bottom: 1px solid var(--border-subtle); margin-bottom: var(--sp-5);
  overflow-x: auto;
}
.hp-tab {
  appearance: none; background: transparent; border: 0; cursor: pointer;
  font-family: inherit; font-size: var(--fs-body); font-weight: 600;
  color: var(--text-muted); padding: var(--sp-3) var(--sp-4);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  white-space: nowrap; transition: color var(--t-fast), background-color var(--t-fast);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.hp-tab:hover { color: var(--text-secondary); background: var(--surface-2); }
.hp-tab[aria-selected="true"], .hp-tab.is-active {
  color: var(--text-primary); border-bottom-color: var(--brand-primary);
  background: var(--brand-soft);
}

/* ── 分段控件(Segmented control)──────────────────────────────────────
   排班页那种"干线/支线""约车表/固定班表"的二选一切换。
   激活类名沿用 .on —— 页面 JS 用 classList.toggle('on', ...) 控制,
   改成别的名字就要动 JS,没必要。 */
.hp-seg {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--bg-secondary); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.hp-seg button {
  background: transparent; border: 0; cursor: pointer;
  font-family: inherit; font-size: var(--fs-caption); font-weight: 700;
  color: var(--text-muted); padding: 7px 16px; border-radius: var(--radius-sm);
  transition: background-color var(--t-fast), color var(--t-fast);
}
.hp-seg button:hover { color: var(--text-primary); }
.hp-seg button.on { background: var(--brand-primary); color: var(--brand-ink); }
/* 支线用青色区分,和干线不是同一条业务线 */
.hp-seg--alt button.on { background: var(--cyan); color: var(--brand-ink); }
/* 次级分段(类型切换)不抢主色 */
.hp-seg--sub button.on { background: var(--surface-3); color: var(--text-primary); }

/* 工具条:分段控件 + 说明文字的一行容器 */
.hp-toolbar {
  display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border-subtle); background: var(--bg-secondary);
}
.hp-toolbar__group { display: flex; align-items: center; gap: var(--sp-2); }
.hp-toolbar__label {
  font-size: var(--fs-2xs); color: var(--text-muted); font-family: var(--font-mono);
  letter-spacing: var(--ls-caps);
}
.hp-toolbar__divider { width: 1px; height: 24px; background: var(--border-subtle); }

/* ══════════════════════════════════════════════════════════════════════════
   8. Badge / Status
   ══════════════════════════════════════════════════════════════════════════ */

.hp-badge {
  display: inline-flex; align-items: center; gap: 6px;
  /* 直角徽章 —— 战术遥测语言里徽章是"标签贴片"不是"胶囊"。
     左侧一道 2px 的语义色竖条代替圆角,读起来像仪表上的分类标签。 */
  padding: 3px 9px; border-radius: 0;
  border-left-width: 2px; border-left-style: solid;
  font-size: var(--fs-caption); font-weight: 600; line-height: var(--lh-snug);
  border: 1px solid transparent; white-space: nowrap;
}
/* 状态不能只靠文字颜色区分 —— 一律带一个圆点,色盲和远看都认得出 */
.hp-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
.hp-badge--neutral { color: var(--text-muted);  background: var(--surface-3); border-color: var(--border-subtle); }
.hp-badge--success { color: var(--success); background: color-mix(in srgb, var(--success) 14%, transparent); border-color: color-mix(in srgb, var(--success) 30%, transparent); }
.hp-badge--warning { color: var(--warning); background: color-mix(in srgb, var(--warning) 14%, transparent); border-color: color-mix(in srgb, var(--warning) 30%, transparent); }
.hp-badge--danger  { color: var(--danger);  background: color-mix(in srgb, var(--danger) 14%, transparent);  border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.hp-badge--info    { color: var(--info);    background: color-mix(in srgb, var(--info) 14%, transparent);    border-color: color-mix(in srgb, var(--info) 30%, transparent); }
.hp-badge--brand   { color: var(--brand-primary); background: var(--brand-soft); border-color: color-mix(in srgb, var(--brand-primary) 30%, transparent); }
.hp-badge--dot-only::before { margin: 0; }

.hp-badge--pulse::before { animation: hp-pulse 2s ease-in-out infinite; }
@keyframes hp-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

/* ══════════════════════════════════════════════════════════════════════════
   9. Table
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 表格 ────────────────────────────────────────────────────────────────
   2026-08-16:去掉整行的分隔线,靠【行距 + hover】区分行。
   密集数据表里每行一条横线会织出一张"网",视觉噪声极大,也是"脏"的来源之一。
   高端数据界面(Linear / Vercel / Stripe 后台)基本都不画行线。 */
.hp-table-wrap {
  background: var(--surface-1); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); overflow: auto; box-shadow: var(--elev-1);
}
.hp-table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); }
/* 表头保留大写 —— 这是【功能性标签】,不是标题,大写在这里帮助扫读列名。
   但字号降到 --fs-2xs 并压低对比,让它退到背景里去,别和数据抢。 */
.hp-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface-1); color: var(--text-muted);
  font-size: var(--fs-2xs); font-weight: 600;
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  text-align: left; padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border-default); white-space: nowrap;
}
.hp-table tbody td {
  padding: var(--sp-3) var(--sp-4); height: 52px;
  border-bottom: 0;                   /* 不画行线 —— 靠行高和 hover 区分 */
  color: var(--text-secondary);
}
.hp-table tbody tr { transition: background-color var(--t-fast); }
.hp-table tbody tr:hover td { background: var(--surface-2); color: var(--text-primary); }
.hp-table tbody tr:last-child td { border-bottom: 0; }
/* 单号 / 门号 / 线路 / 时间用等宽,列对齐也更整齐 */
.hp-table .hp-mono, .hp-table td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.hp-table td.num, .hp-table th.num { text-align: right; }
.hp-table__actions { display: flex; gap: var(--sp-2); justify-content: flex-end; }

/* ══════════════════════════════════════════════════════════════════════════
   10. Modal / Drawer
   ══════════════════════════════════════════════════════════════════════════ */

.hp-overlay {
  position: fixed; inset: 0; z-index: 9000;
  background: rgba(2, 6, 23, .68); backdrop-filter: blur(3px);
  display: none; align-items: center; justify-content: center; padding: var(--sp-5);
}
.hp-overlay.is-open { display: flex; animation: hp-fade var(--t-base) ease-out; }
@keyframes hp-fade { from { opacity: 0; } to { opacity: 1; } }

.hp-modal {
  background: var(--surface-1); border: 1px solid var(--border-default);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-pop);
  width: min(560px, 100%); max-height: 86vh; display: flex; flex-direction: column;
  animation: hp-rise var(--t-base) ease-out;
}
@keyframes hp-rise { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
.hp-modal__head {
  padding: var(--sp-5); border-bottom: 1px solid var(--border-subtle);
  display: flex; align-items: center; gap: var(--sp-3);
}
.hp-modal__title { font-size: var(--fs-section); font-weight: 600; margin: 0; }
.hp-modal__body { padding: var(--sp-5); overflow: auto; }
.hp-modal__foot {
  padding: var(--sp-4) var(--sp-5); border-top: 1px solid var(--border-subtle);
  display: flex; gap: var(--sp-2); justify-content: flex-end;
}

/* ══════════════════════════════════════════════════════════════════════════
   11. Empty / Loading / Error
   ══════════════════════════════════════════════════════════════════════════ */

.hp-empty { text-align: center; padding: var(--sp-10) var(--sp-5); color: var(--text-muted); }
.hp-empty__title { color: var(--text-secondary); font-size: var(--fs-card); font-weight: 600; margin-bottom: var(--sp-2); }
.hp-empty__desc  { font-size: var(--fs-caption); }

.hp-skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: hp-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes hp-shimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
.hp-skeleton--text { height: 12px; margin: var(--sp-2) 0; }
.hp-skeleton--row  { height: 52px; margin-bottom: 1px; border-radius: 0; }

.hp-spinner {
  width: 15px; height: 15px; border-radius: 50%; display: inline-block;
  border: 2px solid currentColor; border-right-color: transparent;
  animation: hp-spin .7s linear infinite;
}
@keyframes hp-spin { to { transform: rotate(360deg); } }

.hp-alert {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-4); border-radius: var(--radius-md);
  border: 1px solid var(--border-default); background: var(--surface-2);
  font-size: var(--fs-body);
}
.hp-alert--error { border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: color-mix(in srgb, var(--danger) 10%, var(--surface-2)); }
.hp-alert__title { font-weight: 600; color: var(--text-primary); }
.hp-alert__desc  { color: var(--text-muted); font-size: var(--fs-caption); }

/* ══════════════════════════════════════════════════════════════════════════
   12. 主题选择器(挂在导航右侧)
   ══════════════════════════════════════════════════════════════════════════ */

.hp-theme { position: relative; }
.hp-theme__pop {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 9500;
  background: var(--surface-1); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-pop);
  padding: var(--sp-2); width: 246px; display: none;
}
.hp-theme__pop.is-open { display: block; }
.hp-theme__item {
  display: flex; gap: var(--sp-3); align-items: center; width: 100%;
  padding: var(--sp-2) var(--sp-3); border: 0; border-radius: var(--radius-md);
  background: transparent; color: var(--text-secondary); cursor: pointer;
  font-family: inherit; text-align: left; transition: background-color var(--t-fast);
}
.hp-theme__item:hover { background: var(--surface-2); color: var(--text-primary); }
.hp-theme__item[aria-checked="true"] { background: var(--brand-soft); color: var(--text-primary); }
.hp-theme__swatch {
  width: 26px; height: 26px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-default); flex: 0 0 auto;
  background: linear-gradient(135deg, var(--sw-bg) 0 55%, var(--sw-accent) 55% 100%);
}
.hp-theme__name { font-size: var(--fs-body); font-weight: 600; }
.hp-theme__desc { font-size: var(--fs-2xs); color: var(--text-muted); }

/* ══════════════════════════════════════════════════════════════════════════
   13. 兼容层 .hp-legacy
   ──────────────────────────────────────────────────────────────────────────
   给【还没整页重写】的老页面用。用法:<body class="hp hp-legacy">,
   然后把该页 <style> 里这些重复定义删掉。

   为什么要有这一层:审计发现 6 个排班/看板类页面在重复定义同一套词汇 ——
   `.btn` 6 个页面各写一遍、`input`/`table`/`.msg`/`.filterbar` 各 5 个。
   一页页改 markup 风险高(JS 会拼带旧 class 的 HTML,漏一处就是样式静默丢失),
   把这套旧词汇收进来只删 CSS 不动 markup,零风险拿到统一。

   【这是过渡产物,不是目标】页面整页重写成 hp- 组件后,
   把 body 上的 hp-legacy 去掉即可。所有规则都作用域在 .hp-legacy 下,
   不会漏到已迁移的页面上。
   ══════════════════════════════════════════════════════════════════════════ */

.hp-legacy .wrap { max-width: 1180px; margin: 0 auto; padding: var(--sp-4) var(--sp-5); }

/* 顶栏 / 返回链接 */
.hp-legacy .top {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border-subtle); background: var(--bg-secondary);
}
.hp-legacy .back, .hp-legacy .topbar a {
  font-size: var(--fs-caption); color: var(--text-muted); text-decoration: none;
}
.hp-legacy .back:hover, .hp-legacy .topbar a:hover { color: var(--text-primary); }

/* 按钮:统一成设计系统的尺寸和状态,变体沿用旧类名 */
.hp-legacy .btn, .hp-legacy button.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; padding: 0 var(--sp-3);
  background: var(--surface-2); color: var(--text-primary);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--fs-caption); font-weight: 600;
  cursor: pointer; white-space: nowrap;
  transition: background-color var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.hp-legacy .btn:hover:not(:disabled) { background: var(--surface-3); border-color: var(--border-strong); }
.hp-legacy .btn:disabled { opacity: .45; cursor: not-allowed; }
.hp-legacy .btn.primary, .hp-legacy .btn.save {
  background: var(--brand-primary); color: var(--brand-ink); border-color: var(--brand-primary);
}
.hp-legacy .btn.primary:hover, .hp-legacy .btn.save:hover { background: var(--brand-hover); }
.hp-legacy .btn.del, .hp-legacy .btn.danger {
  background: transparent; color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 45%, transparent);
}
.hp-legacy .btn.del:hover, .hp-legacy .btn.danger:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent); border-color: var(--danger);
}
.hp-legacy .btn.ghost { background: transparent; border-color: transparent; color: var(--text-secondary); }
.hp-legacy .btn.ghost:hover { background: var(--surface-2); color: var(--text-primary); }

/* 表单控件 */
.hp-legacy input:not([type=checkbox]):not([type=radio]),
.hp-legacy select, .hp-legacy textarea {
  background: var(--surface-2); color: var(--text-primary);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  font-family: inherit; font-size: var(--fs-caption); padding: 6px var(--sp-2);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.hp-legacy input:focus, .hp-legacy select:focus, .hp-legacy textarea:focus {
  outline: none; border-color: rgba(250, 204, 21, .65);
  box-shadow: 0 0 0 3px var(--brand-soft);
}
.hp-legacy input::placeholder, .hp-legacy textarea::placeholder { color: var(--text-disabled); }

/* 表格 */
.hp-legacy table { width: 100%; border-collapse: collapse; font-size: var(--fs-caption); }
.hp-legacy th {
  font-size: var(--fs-2xs); color: var(--text-muted); font-weight: 700; text-align: left;
  background: var(--surface-2); padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border-default);
  letter-spacing: var(--ls-wide); text-transform: uppercase; white-space: nowrap;
}
.hp-legacy td {
  padding: var(--sp-2) var(--sp-3); color: var(--text-secondary);
  border-bottom: 1px solid var(--border-subtle);
}
.hp-legacy tbody tr:hover td { background: var(--surface-2); color: var(--text-primary); }

/* 筛选条 */
.hp-legacy .filterbar {
  display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
  background: var(--surface-1); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-3);
}
.hp-legacy .filterbar .fl { font-size: var(--fs-2xs); color: var(--text-muted); }
.hp-legacy .filterbar .count {
  font-size: var(--fs-caption); color: var(--text-muted);
  font-family: var(--font-mono); margin-left: auto;
}

/* 提示 / 状态文字 */
.hp-legacy .msg, .hp-legacy .rowmsg { font-size: var(--fs-2xs); color: var(--text-muted); }
.hp-legacy .msg.ok, .hp-legacy .rowmsg.ok { color: var(--success); }
.hp-legacy .msg.err, .hp-legacy .rowmsg.err { color: var(--danger); }
.hp-legacy .hint { font-size: var(--fs-2xs); color: var(--text-disabled); }
.hp-legacy .loading { text-align: center; color: var(--text-muted); padding: 50px; }
.hp-legacy .perr { display: none; text-align: center; color: var(--danger); padding: 60px 20px; font-size: 15px; }
.hp-legacy .card {
  background: var(--surface-1); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: var(--sp-5); box-shadow: var(--shadow-card);
}

/* ── 兼容层第二批:收车/台账/管理类页面的共用词汇 ──────────────────────
   这批页面(receive/* · admin/* · *-records · vendors)又共用另一套名字:
   .empty 6 个页面、h1 8 个、.lead/.card/.badge 各 4 个、
   .topbar/.pagination/.pu/.route/.error-state 各 3 个。同样收进来。 */

.hp-legacy h1 {
  font-size: var(--fs-2xl); font-weight: 700; letter-spacing: var(--ls-tight);
  color: var(--text-primary); margin: 0 0 var(--sp-2);
}
.hp-legacy h2 { font-size: var(--fs-section); font-weight: 600; color: var(--text-primary); }
.hp-legacy .lead, .hp-legacy .sub {
  color: var(--text-muted); font-size: var(--fs-caption); margin-bottom: var(--sp-5);
}

.hp-legacy .topbar {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5); margin-bottom: var(--sp-4);
  border-bottom: 1px solid var(--border-subtle); background: var(--bg-secondary);
}
.hp-legacy .topbar-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 var(--sp-3);
  background: var(--surface-2); color: var(--text-secondary);
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  font-size: var(--fs-caption); font-weight: 600; text-decoration: none; cursor: pointer;
}
.hp-legacy .topbar-btn:hover { background: var(--surface-3); color: var(--text-primary); }

/* 空态 / 加载 / 错误:三种状态统一,别再各页各写 */
.hp-legacy .empty, .hp-legacy .empty-state {
  text-align: center; color: var(--text-muted);
  padding: var(--sp-10) var(--sp-5); font-size: var(--fs-caption);
}
.hp-legacy .loading-state { text-align: center; color: var(--text-muted); padding: var(--sp-10) var(--sp-5); }
.hp-legacy .error-state {
  text-align: center; color: var(--danger); padding: var(--sp-8) var(--sp-5); font-size: var(--fs-caption);
}

/* 分页 */
.hp-legacy .pagination { display: flex; gap: var(--sp-2); align-items: center; justify-content: center; padding: var(--sp-4) 0; }
.hp-legacy .pagination button {
  height: 30px; min-width: 30px; padding: 0 var(--sp-2);
  background: var(--surface-2); color: var(--text-secondary);
  border: 1px solid var(--border-default); border-radius: var(--radius-sm);
  font-size: var(--fs-caption); cursor: pointer;
}
.hp-legacy .pagination button:hover:not(:disabled) { background: var(--surface-3); color: var(--text-primary); }
.hp-legacy .pagination button:disabled { opacity: .4; cursor: not-allowed; }

/* 徽章 */
.hp-legacy .badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 0; border-left-width: 2px; border-left-style: solid;
  font-size: var(--fs-2xs); font-weight: 600; line-height: var(--lh-base);
  background: var(--surface-3); color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
}
.hp-legacy .badge.ok, .hp-legacy .badge.on, .hp-legacy .badge.active { color: var(--success); border-color: color-mix(in srgb, var(--success) 34%, transparent); }
.hp-legacy .badge.warn { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 34%, transparent); }
.hp-legacy .badge.err, .hp-legacy .badge.off, .hp-legacy .badge.disabled { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 34%, transparent); }

/* 单号 / 线路:一律等宽,这是"系统终端感"最省力的来源 */
.hp-legacy .pu, .hp-legacy .route, .hp-legacy .mono, .hp-legacy .num {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.hp-legacy .route { color: var(--brand-text); font-weight: 700; }

/* 表单字段 */
.hp-legacy .fld { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--sp-3); }
.hp-legacy .fld label { font-size: var(--fs-2xs); color: var(--text-muted); font-weight: 600; letter-spacing: var(--ls-wide); }

.hp-legacy .hidden { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   动效层配套样式(2026-08-15)
   ──────────────────────────────────────────────────────────────────────────
   行为在 /assets/motion.js,这里只放它需要的静态样式。
   分区制:门面区(登录/首页/3D)可以做足;作业区只做"一次性事件 + 携带信息"。
   ══════════════════════════════════════════════════════════════════════════ */

/* Tab 滑动指示条。绝对定位在 tab 容器底部,由 JS 平移到当前项。
   位移用 transform 不用 left —— transform 走 GPU 合成层,left 每帧重排。 */
.hp-tabbar__indicator {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
  background: var(--brand-primary); border-radius: 0;
  pointer-events: none; opacity: 0;
  will-change: transform, width;
  /* 没有 GSAP 时的兜底过渡,保证降级后依然是滑的而不是跳的 */
  transition: transform var(--t-base), width var(--t-base), opacity var(--t-fast);
}

/* 抽屉:初始态在屏幕外。visibility 而不是 display —— display:none 会让
   transform 动画没有起始帧可插值。 */
.hp-drawer {
  position: fixed; top: 0; bottom: 0; width: min(420px, 92vw);
  background: var(--surface-1); border-left: 1px solid var(--border-default);
  box-shadow: var(--elev-4); z-index: 60;
  transform: translateX(100%); visibility: hidden;
  display: flex; flex-direction: column;
}
.hp-drawer--left { left: 0; right: auto; border-left: 0; border-right: 1px solid var(--border-default); transform: translateX(-100%); }
.hp-drawer__head { padding: var(--sp-4); border-bottom: 1px solid var(--border-subtle); }
.hp-drawer__body { padding: var(--sp-4); overflow: auto; flex: 1; }

/* 数字滚动的目标元素:必须等宽,否则滚动过程中宽度变来变去,整行左右抖。
   hubpilot.css 已全局开 tabular-nums,这里显式声明是给覆盖过的页面兜底。 */
.hp-countup { font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════════════════
   质感层:颗粒 + 环境光(2026-08-16)
   ──────────────────────────────────────────────────────────────────────────
   诊断依据(redesign-skill 的 Color and Surfaces 一节):
     · "Flat design with zero texture. Pure flat vectors feel sterile."
     · "Perfectly even gradients. Break the uniformity with radial gradients."
   实测改造前:全站噪点/颗粒 0 处、径向渐变 1 处 —— 纯平涂,这就是"简陋"的来源。

   ⚠️ 为什么这样做而不是别的做法:
   1. **颗粒用 SVG feTurbulence 的 data-URI,不是图片。**
      不产生网络请求(仓库网络一抖不会掉),不占仓库体积,分辨率无关。
   2. **mix-blend-mode: overlay** —— 深色主题上它提亮、浅色主题上它压暗,
      一份代码在 9 套主题上都成立,不需要每套各写一份。
   3. **静态,不动。** 颗粒动起来就是噪声污染,违反"运动 = 告警"的原则。
      静态层只在合成时算一次,不产生每帧开销。
   4. **tv-mode 下关掉。** 大屏远看时颗粒只会让文字发毛,且仓库机器不必为它付出合成成本。
   ══════════════════════════════════════════════════════════════════════════ */
body::after {
  content: '';
  position: fixed; inset: 0; z-index: 9999;
  pointer-events: none;
  opacity: var(--grain-opacity, .038);
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}
body.tv-mode::after { display: none; }          /* 大屏远看:颗粒会让字发毛 */
@media (prefers-reduced-transparency: reduce) { body::after { display: none; } }

/* ── 环境光 ──────────────────────────────────────────────────────────────
   原来 body 是一片平涂的 --bg-main。真实空间总有光源方向,
   一层极淡的径向光就能把"平面"变成"空间"。
   用 fixed 的 ::before 而不是 background —— 页面滚动时光斑不跟着走,
   像是打在屏幕上的环境光,而不是贴在文档上的贴纸。 */
body::before {
  content: '';
  position: fixed; inset: 0; z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(120% 85% at 50% -20%, var(--ambient-top, rgba(255,255,255,.055)), transparent 62%),
    radial-gradient(90% 60% at 88% 108%, var(--ambient-brand, rgba(250,204,21,.045)), transparent 68%);
}
body.tv-mode::before { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   Bento 非对称网格(2026-08-16)
   ──────────────────────────────────────────────────────────────────────────
   redesign-skill 点名的最通用 AI 布局:"Three equal card columns as feature row."
   等宽卡片阵列的问题不是难看,是【没有主次】—— 所有入口一样大 = 用户不知道先点哪个。

   ⚠️ 只给【非密集数据页】用。道口看板/大屏那 5 个页面要一屏塞下 169 个道口,
   留白和错位会直接伤到一线效率 —— skill 自己也写了
   "Dense layouts work for data dashboards, not for marketing pages"。
   适用:首页、登录、我的、设置、供应商这类页面。

   用 grid-auto-flow: dense —— 大格子先占位后,小格子会自动回填空洞,
   不需要手工排序。改卡片顺序不会留下空洞。 */
.hp-bento {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-rows: minmax(120px, auto);
  gap: var(--sp-4);
  grid-auto-flow: dense;
}
.hp-bento > *          { grid-column: span 2; }     /* 默认 1/3 宽 */
.hp-bento .is-hero     { grid-column: span 3; grid-row: span 2; }
.hp-bento .is-wide     { grid-column: span 4; }
.hp-bento .is-half     { grid-column: span 3; }
.hp-bento .is-tall     { grid-row: span 2; }

@media (max-width: 1100px) {
  .hp-bento { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .hp-bento .is-hero { grid-column: span 4; grid-row: span 1; }
  .hp-bento .is-wide { grid-column: span 4; }
  .hp-bento .is-half { grid-column: span 2; }
}
@media (max-width: 680px) {
  .hp-bento { grid-template-columns: 1fr; }
  .hp-bento > *, .hp-bento .is-hero, .hp-bento .is-wide, .hp-bento .is-half {
    grid-column: span 1; grid-row: span 1;
  }
}

/* ── 巨型页头 ────────────────────────────────────────────────────────────
   redesign-skill:"Everything centered and symmetrical. Break symmetry."
   + "Missing whitespace. Double the spacing."
   主标题用展示字体铺满一行,状态信息【下沉到基线】而不是垂直居中 ——
   齐基线比齐中线更像"排过版"。 */
.hp-hero {
  display: flex; align-items: baseline; gap: var(--sp-6);
  flex-wrap: wrap;
  padding: var(--sp-8) 0 var(--sp-10);
  border-bottom: 1px solid var(--border-subtle);
  margin-bottom: var(--sp-10);
}
.hp-hero__title {
  font-family: var(--font-display);
  font-size: clamp(44px, 6.2vw, 92px);
  font-weight: 400; line-height: .86;
  letter-spacing: var(--ls-display);
  margin: 0; text-transform: uppercase;
  /* 展示字体的左侧边距比正文字体大,不补这一下会和下面的正文对不齐 */
  margin-left: -.02em;
}
.hp-hero__meta { margin-left: auto; display: flex; align-items: baseline; gap: var(--sp-3); }

/* 子页面用的紧凑版。整页 860px 宽的设置类页面配 92px 的巨型标题会头重脚轻 ——
   标题的分量应该和页面的分量匹配,不是越大越好。 */
.hp-hero--compact { padding: var(--sp-5) 0 var(--sp-6); margin-bottom: var(--sp-6); }
.hp-hero--compact .hp-hero__title { font-size: clamp(30px, 3.4vw, 48px); }
.hp-hero__sub  { color: var(--text-muted); font-size: var(--fs-sm); margin-top: var(--sp-3); }

/* 分区标题:轻微错位,压在内容上沿之上一点点。
   (skill: "No overlap or depth. Use negative margins to create layering.")
   ⚠️ 负边距不能给太多 —— 实测 -12px 时标题会贴在卡片边框上,读起来是"挤",不是"层次"。
   -4px 刚好:既打破了"标题一行、内容一行"的规矩感,又不碰到卡片。 */
.hp-section-title.is-float {
  position: relative; z-index: 1;
  margin-bottom: -4px;
  padding-left: var(--sp-1);
}

/* KPI 里的占用率条 —— 大格子需要一个"看得见的量",光有数字太空 */
.hp-meter { height: 4px; border-radius: 0; background: var(--surface-3); overflow: hidden; margin-top: var(--sp-4); }
.hp-meter__fill { height: 100%; border-radius: inherit; background: var(--accent, var(--brand-primary)); transition: width .6s cubic-bezier(.2,.8,.3,1); }

/* Hero KPI:数字要顶天立地 */
.hp-kpi.is-hero .hp-kpi__value { font-size: clamp(64px, 8vw, 118px); }
.hp-kpi.is-hero { display: flex; flex-direction: column; justify-content: space-between; }

/* ── KPI 专用的 bento 排布 ───────────────────────────────────────────────
   通用 bento 用 span 3 的 hero,4 张 KPI 排下来会在第二行右侧留一个洞
   (hero 占掉 3 列 × 2 行,剩下 3+3 只够放 2 张,第 4 张被挤到新一行)。
   KPI 固定就是 4 张,所以给一套【正好填满 6×2】的排布:
       ┌───────┬───────┬───────┐
       │       │   A   │   B   │
       │ HERO  ├───────┴───────┤
       │       │       C       │
       └───────┴───────────────┘ */
.hp-bento--kpi > .is-hero { grid-column: span 2; grid-row: span 2; }
.hp-bento--kpi > .is-half { grid-column: span 2; }
.hp-bento--kpi > :last-child { grid-column: span 4; }
@media (max-width: 1100px) {
  .hp-bento--kpi > .is-hero { grid-column: span 4; grid-row: span 1; }
  .hp-bento--kpi > .is-half,
  .hp-bento--kpi > :last-child { grid-column: span 2; }
}

/* ── Bento 里的入口卡:填充大格子 ─────────────────────────────────────────
   双倍留白是对的,但【空得没有重心】就变成"没做完"而不是"高级"。
   大格子需要三样东西才立得住:
     ① 内容分置两端(标题在上、动作提示在下)—— flex + space-between
     ② 一个方向指示(→),hover 时向右滑,告诉用户这是可点的
     ③ 角落一个巨大的低透明度序号 —— 填充视觉重量,同时不干扰阅读。
        这是编辑排版里的老办法:用"装饰性字符"占住空白,而不是硬塞信息。 */
.hp-bento .hp-card--link {
  display: flex; flex-direction: column;
  min-height: 118px;
  overflow: hidden;
}
.hp-bento .hp-card--link .hp-card__desc { margin-bottom: auto; }

.hp-bento .hp-card--link::before {
  content: '→';
  position: absolute; right: var(--sp-5); bottom: var(--sp-4);
  font-size: var(--fs-lg); line-height: 1;
  color: var(--text-disabled);
  transition: transform var(--t-base), color var(--t-base);
}
.hp-bento .hp-card--link:hover::before {
  transform: translateX(5px);
  color: var(--brand-text);
}

/* 大格子的标题要跟着变大,否则 3 列宽的卡上顶着一个 16px 标题会显得很空 */
.hp-bento .is-hero .hp-card__title { font-size: var(--fs-2xl); letter-spacing: var(--ls-tight); }
.hp-bento .is-wide .hp-card__title,
.hp-bento .is-half .hp-card__title { font-size: var(--fs-xl); letter-spacing: var(--ls-tight); }

/* 角落的装饰性巨大序号。pointer-events:none 且 aria-hidden ——
   它是纯装饰,不该被读屏软件念出来,也不该拦住点击。 */
.hp-bento .hp-card--link[data-card]::after {
  content: attr(data-index);
  position: absolute; right: -.06em; bottom: -.34em;
  font-family: var(--font-display);
  font-size: clamp(72px, 9vw, 168px); line-height: 1;
  color: var(--text-primary);
  opacity: .035;
  pointer-events: none;
  /* 覆盖 .hp-card::after 的顶边高光 —— 这里要的是另一种东西 */
  border-top: 0; -webkit-mask-image: none; mask-image: none;
  inset: auto -.06em -.34em auto;
}

/* ══════════════════════════════════════════════════════════════════════════
   13.5 兼容层的品质升级(2026-08-16)
   ──────────────────────────────────────────────────────────────────────────
   20 个页面挂着 .hp-legacy。**改这一段 = 一次性升级这 20 个页面**,
   不用碰任何 markup、不用碰任何 JS —— 这是全站性价比最高的一处改动。

   把 2026-08-16 视觉改造的四条结论下沉到旧词汇上:
     · 表格不画行线(每行一条横线会织出一张网,是"脏"的直接来源)
     · 卡片/面板有顶边内高光(有厚度的材料 vs 填了色的矩形)
     · 圆角收紧到阶梯上
     · 页面容器给足留白(旧 .wrap 只有 16px 上下padding,挤)
   ══════════════════════════════════════════════════════════════════════════ */

/* 页面容器:留白翻倍。旧值 sp-4/sp-5 是"能用"的密度,不是"好看"的密度。 */
.hp-legacy .wrap { max-width: 1240px; padding: var(--sp-8) var(--sp-6) var(--sp-10); }

/* 表格:去行线,靠行距 + hover 区分;表头退到背景里 */
.hp-legacy th {
  background: transparent;
  border-bottom: 1px solid var(--border-subtle);
  padding: var(--sp-3) var(--sp-3);
  letter-spacing: var(--ls-caps);
}
.hp-legacy td {
  border-bottom: 0;                       /* ← 关键:不画行线 */
  padding: var(--sp-3) var(--sp-3);
}
.hp-legacy tbody tr { transition: background-color var(--t-fast); }

/* 旧页面里所有"面板"类容器,统一拿到卡片的质感:
   表面渐变 + 顶边内高光 + 立面阴影。这几个类名是审计出来的高频容器。 */
.hp-legacy .panel, .hp-legacy .box, .hp-legacy .sec, .hp-legacy .filterbar,
.hp-legacy .tablewrap, .hp-legacy .table-wrap, .hp-legacy .card {
  position: relative;
  background:
    linear-gradient(180deg, rgb(255 255 255 / var(--card-sheen, .028)) 0%, rgb(255 255 255 / 0) 42%),
    var(--surface-1);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.hp-legacy .panel::after, .hp-legacy .box::after, .hp-legacy .sec::after,
.hp-legacy .tablewrap::after, .hp-legacy .table-wrap::after, .hp-legacy .card::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  border-top: 1px solid rgb(255 255 255 / var(--card-edge, .10));
  -webkit-mask-image: linear-gradient(180deg, #000 0, transparent 34%);
          mask-image: linear-gradient(180deg, #000 0, transparent 34%);
}

/* 按钮:圆角收紧 + 按压反馈(skill: "No active/pressed feedback") */
.hp-legacy .btn, .hp-legacy button.btn {
  border-radius: var(--radius-sm);
  transition: background-color var(--t-fast), border-color var(--t-fast), transform .08s;
}
.hp-legacy .btn:active:not(:disabled) { transform: translateY(1px); }

/* 输入框:焦点环走 token,和全站一致 */
.hp-legacy input:not([type=checkbox]):not([type=radio]),
.hp-legacy select, .hp-legacy textarea { border-radius: var(--radius-sm); }
.hp-legacy input:focus, .hp-legacy select:focus, .hp-legacy textarea:focus {
  box-shadow: var(--ring-focus);
}

/* 旧页面的 h1 —— 给它们和新版页头一样的分量。
   ⚠️ 这里【故意用最宽的 .hp-legacy h1】而不是逐个列父容器。
   实测 20 个页面的 h1 散在 6 种不同容器里(.wrap / .top / .topbar / .header /
   .back / 直接挂 body),逐个列必漏 —— 之前只列了 3 种,漏掉了 .topbar 那 6 个页面。
   h1 在任何页面都只该是"页面标题",放宽是安全的。 */
.hp-legacy h1 {
  font-family: var(--font-display);
  font-size: clamp(32px, 4vw, 56px);
  font-weight: 400; line-height: .92;
  letter-spacing: var(--ls-display);
  text-transform: uppercase;
  margin: 0 0 var(--sp-2);
}

/* ══════════════════════════════════════════════════════════════════════════
   战术遥测视觉语言(2026-08-16 第二轮)
   ──────────────────────────────────────────────────────────────────────────
   依据 brutalist-skill 的 "Tactical Telemetry & CRT Terminal" 档。
   选它的理由:HubPilot 是数据密集的调度系统,而发车页本来就是终端气质 ——
   这个方向不是外加的风格,是把全站统一到它最强的那一页上。

   三处按实际约束做了调整,不是照搬 skill:
     · 强调色用【品牌黄】不用航空红 —— GOFO 的品牌色就是黄,
       而红色在这套系统里有明确语义(--danger),不能挪作装饰
     · 保留 9 套主题(含浅色)—— 白班办公要浅色,这是功能需求。
       skill 要求"一个项目只用一种基底",这里改成【结构语言统一、基底可切换】
     · 不做 CRT 绿字、不做 1-bit 抖动 —— 会伤数据可读性
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 扫描线(只给深色主题)──────────────────────────────────────────────
   极淡(1.2%),叠在颗粒层之下。近看几乎不可察,但它让大面积深色底
   不再是"一块死漆",而像一块【正在工作的屏幕】。
   ⚠️ 4px 周期是有讲究的:2px 周期在 1080p 上会和像素网格产生摩尔纹。 */
:root[data-theme="hub-default"] body::before,
:root[data-theme="high-contrast-dark"] body::before,
:root[data-theme="forest"] body::before,
:root[data-theme="espresso"] body::before,
:root[data-theme="blueprint"] body::before,
:root[data-theme="graphite"] body::before,
:root:not([data-theme]) body::before {
  background-image:
    repeating-linear-gradient(0deg,
      rgb(255 255 255 / .012) 0px, rgb(255 255 255 / .012) 1px,
      transparent 1px, transparent 4px),
    radial-gradient(120% 85% at 50% -20%, var(--ambient-top, rgb(255 255 255 / .055)), transparent 62%),
    radial-gradient(90% 60% at 88% 108%, var(--ambient-brand, rgb(250 204 21 / .045)), transparent 68%);
}
body.tv-mode::before { background-image: none; }

/* ── 技术性框注 ──────────────────────────────────────────────────────────
   skill: "Syntax Decoration: [ DELIVERY SYSTEMS ]"。
   不是玩梗:方括号给小号全大写标签一个【明确的起止】,
   密集界面里比单纯加字距更能把标签和数据分开。 */
.hp-section-title::before { content: '[ '; opacity: .5; }
.hp-section-title::after  { content: ' ]'; opacity: .5; }

/* ── 发丝网格 ────────────────────────────────────────────────────────────
   skill 第 8.1 条工程手法:grid gap + 父子异色 = 数学上完美的细线,
   不用给每个子元素写 border(那样会出现双线和错位)。 */
.hp-hairgrid {
  display: grid; gap: 1px;
  background: var(--border-default);
  border: 1px solid var(--border-default);
}
.hp-hairgrid > * { background: var(--surface-1); margin: 0; }

/* ── 宏观 / 微观:极端尺度对比,中间不留档 ─────────────────────────────── */
.hp-hero__title { line-height: .88; letter-spacing: -.03em; }
.hp-mega {
  font-family: var(--font-display);
  font-size: clamp(64px, 11vw, 190px);
  line-height: .84; letter-spacing: -.02em;
  text-transform: uppercase; font-weight: 400;
}
.hp-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}

/* ── 整宽分隔线 ──────────────────────────────────────────────────────────
   skill: "Horizontal rules span the entire container width to segregate
   operational units." 用实线切分区块,不用留白切分。 */
.hp-rule { border: 0; border-top: 1px solid var(--border-default); margin: var(--sp-8) 0 var(--sp-6); }
.hp-rule--heavy { border-top-width: 2px; border-top-color: var(--brand-primary); opacity: .5; }

/* ══════════════════════════════════════════════════════════════════════════
   DOCK RIBBON —— 全站签名元素(2026-08-16)
   ──────────────────────────────────────────────────────────────────────────
   Anthropic 的 frontend-design skill 有两条直接改变了这轮的做法:

   1. "AI-generated design clusters around three looks … (3) broadsheet layout
      with hairline rules, zero border-radius, dense columns. These are
      **defaults rather than choices**."
      —— 上一轮做的战术遥测(直角 + 发丝线 + 框注)正好贴着这个套路。
      它有理由(对齐发车页已有气质),但光靠它撑不起"这个产品独一无二"。

   2. "The subject's own world — its materials, instruments, artifacts —
      is where distinctive choices come from."
      "Spend your boldness in one place."

   HubPilot 的 subject world 是【1160 State Street 的越库仓】:
   169 个门,上下两排隔着仓库主体相对。**这个形状全世界只有这一个仓库有。**

   所以签名元素就是它本身:一条 169 格的占用状态带。
     · 上排 83 格在上、下排 86 格在下,中间一条缝 = 仓库主体
     · 每格一个道口,颜色 = 实时状态
     · 高度只有 18px,横贯页头 —— 它是【身份】,不是控件

   为什么这是"选择"不是"默认":
     · 任何别的产品都用不了它 —— 它编码的是这栋楼的真实形状
     · 它同时是功能(每一页都能一眼看到全仓占用)
     · 它是【唯一】一处大胆的地方,其余一切保持安静(skill 的 restraint 原则)

   ⚠️ 性能:169 个 div 是静态 DOM,不参与动画,更新时只改 background。
      不要给它加过渡或动画 —— 它在每一页上常驻,常驻的东西一动就是噪声。
   ══════════════════════════════════════════════════════════════════════════ */
.hp-ribbon {
  --ribbon-h: 18px;
  display: block; width: 100%;
  user-select: none;
}
.hp-ribbon__row {
  display: grid; gap: 1px;
  height: calc(var(--ribbon-h) / 2 - 1px);
}
/* 中间那条缝就是仓库主体 —— 不是装饰性间距,是建筑本身 */
.hp-ribbon__gap {
  height: 2px;
  background: var(--brand-primary);
  opacity: .35;
}
.hp-ribbon__cell {
  background: var(--surface-3);
  min-width: 0;
}
.hp-ribbon__cell[data-s="occupied"] { background: var(--danger); }
.hp-ribbon__cell[data-s="free"]     { background: var(--success); opacity: .55; }
.hp-ribbon__cell[data-s="hold"]     { background: var(--text-disabled); }
.hp-ribbon__cell[data-s="sel"]      { background: var(--brand-primary); }

/* 页头里的紧凑变体:更矮,带一行元数据。
   ⚠️ flex-basis:100% —— 它必须【横贯整个页头】才读得出"一整栋楼"的尺度。
   挤在标题那一列里会变成一条看不懂的小色带。 */
.hp-ribbon--head {
  --ribbon-h: 14px;
  flex: 1 0 100%; order: 9;
  margin-top: var(--sp-5);
}
.hp-ribbon__legend {
  display: flex; gap: var(--sp-4); margin-top: 6px;
  font-family: var(--font-mono); font-size: var(--fs-4xs, 9px);
  letter-spacing: .09em; text-transform: uppercase; color: var(--text-disabled);
}
.hp-ribbon__legend b { color: var(--text-muted); font-weight: 600; }
