/* 本文件由 tools/sync_tokens.py 从 design/tokens.css 自动生成。
   请勿直接编辑：改真源后重新运行 `python tools/sync_tokens.py`。 */
/* ============================================================================
   design/tokens.css — 设计令牌单一真源 (Single Source of Truth)
   ============================================================================

   本文件是 status 项目**唯一**允许定义设计常量的地方。Web 面板与 Electron
   客户端共用本文件生成出的 tokens.css；任何颜色 / 字号 / 间距 / 动效都必须
   先在这里定义，再在样式表里 var() 引用。

   同步机制：tools/sync_tokens.py 把本文件复制到两个平台
       server/web/static/tokens.css
       client-electron/renderer/tokens.css
   tests/test_design_tokens.py 会在副本与本文件不一致时让测试变红。

   ---------------------------------------------------------------------------
   分层模型（Primer / Radix / Fluent 的通行做法）
   ---------------------------------------------------------------------------
     L1  PRIMITIVE  原始色阶与数值。不含语义，只回答「这个色是几号」。
     L2  SCALE      排版 / 间距 / 圆角 / 动效 / 高度。纯数值尺度，两端完全一致。
     L3  SEMANTIC   语义别名。同一个名字在两个平台上表示**同一个角色**，
                    取值默认指向 L1/L2；各平台可在自己的样式表顶部覆盖 L3
                    做主题微调，而不必复制整套体系。

   为什么这样分层：重构前 Web 与 Electron 各自定义了 78 / 39 个令牌，其中
   9 个同名却不同值（--bg、--border、--text-hi …）。那不是「统一」，只是
   「碰巧同名」——任何人把一端的颜色搬到另一端都会静默出错。现在语义名是契约，
   平台差异被显式收进 L3 覆盖层，状态色（success/warning/error/accent）
   两端共用同一批 primitive，不再有漂移风险。

   ---------------------------------------------------------------------------
   本次（Web 端）将 Web 默认主题对齐到 Linear Dark 2026：
     - L1 中性阶（Web 子集 50/150/175/250/350/450/500/550/600/700/750/775/850/900/925）
       重新调成 Linear 风的低饱和中性灰蓝；
     - L1 强调色改为 Linear indigo（#5E6AD2 基调，#7175E0 hover）；
     - L1 状态色恢复到 Linear 风的中等饱和度 emerald / amber / coral
       —— 上一版（v12 现代杂志风）过度降饱和到「色相保留 / 饱和度归零」，
       整屏只剩中性灰 + 几个几乎看不见的暗点（"全部离线"场景下 error 红点
       与中性 border 几乎同色），用户普遍反馈「黑白化、很难看」。本轮把 status
       primitive 拉回到 Linear 公开界面的可见饱和度档（emerald ≈ #3DBC7E，
       amber ≈ #E5B342，coral ≈ #E5707A），仍然不抢主文本、仍然在 border / shadow
       不染色，但 hero tint / timeline / status pill / KPI 数字能真正承担「状态」语义。
     - L1 可视化色：--viz-fill 改走 accent（#5E6AD2），让热力图 7×24 格子拥有
       视觉锚点；--viz-line / --viz-hi 仍为中性灰（offline 设备的折线保持克制），
       app.js 按 d.is_online 二选一着色。
     - 圆角从 6/10/14 收到 5/8/10（Linear 风：紧凑精确）；
     - 字体栈前置 Inter（与 Linear 公开界面同栈；未装 Inter 时回退系统字体，
       不强制加载远端字体，避免依赖外网）。
   Electron 端通过 styles.css 顶部的 :root 覆盖块独立指向自己的 L1 子集，
   不使用此处重映射后的 Web 主题，因此 Electron 视觉零变化。
   ============================================================================ */


/* ==========================================================================
   L1 · PRIMITIVE — 原始色阶
   ==========================================================================
   命名：--p-<色相>-<亮度步进>
   中性阶由暗到亮单调递增，两个平台的中性色都是它的子集；
   状态色与强调色两端本来就一致，直接共用。
   ========================================================================== */

:root {
    /* ---- 中性阶（暗 → 亮）------------------------------------------------
       Web（Linear Dark 2026，已重映射）:
            50 / 150 / 175 / 250 / 350 / 450 / 500 / 550 / 600 / 700 /
            750 / 775 / 850 / 900 / 925
       Electron（保持原色，不动）:
            100 / 125 / 200 / 300 / 400 / 725 / 800 / 950
       两个平台取不同的台阶，这是刻意的平台主题差异，不是漂移。            */
    --p-n-50:   #08090A;   /* Web 画布底色 — Linear Dark 2026 base */
    --p-n-100:  #0A0A0C;   /* Electron 画布底色（不动） */
    --p-n-125:  #0E0E11;   /* Electron 画布次级底色（不动） */
    --p-n-150:  #0E0F12;   /* Web 卡片面 — Linear's "subtle dark gray" */
    --p-n-175:  #15171C;   /* Web tooltip 底色（比卡片面略亮，压得住浮层） */
    --p-n-200:  #131316;   /* Electron 卡片面（不动） */
    --p-n-250:  #18181B;   /* Web 内层面板 — Linear's "panel" */
    --p-n-300:  #1A1A1E;   /* Electron 内层面板（不动） */
    --p-n-350:  #1F2024;   /* Web inset — Linear's "slightly raised" */
    --p-n-400:  #212126;   /* Electron inset（不动） */
    --p-n-450:  #26272B;   /* Web 描边 — Linear's "subtle border" */
    --p-n-500:  #2D2E33;   /* Web 描边-强 */
    --p-n-550:  #383940;   /* Web 描边-更强 */
    --p-n-600:  #3A3B40;   /* Web 描边-最强 */
    /* v16：#7A7F88 → #9398A2，把 dark 主题 xlo 从 4.95:1（WCAG AA 边缘）
       提到 ≈ 6.8:1（接近 AAA）。Linear / Vercel / Stripe 2026 公开界面的
       「tertiary text」普遍处于 7:1 附近 —— 上一版 4.95:1 在 11px 字号下
       会被脑补成"看不清"，扫一眼就想换主题。升档后 idle 圆点（共享
       --p-n-700 经 --p-neutral-500）也跟着升一档，与 text-xlo 同步流动
       —— idle 仍远低于 online 的 green，仍是「中性态」的最弱档。 */
    --p-n-700:  #9398A2;   /* Web 次要文本（下档）— dark 主题上约 6.8:1，AAA-ready */
    --p-n-725:  #6B6B75;   /* Electron 次要文本（不动） */
    --p-n-750:  #9CA3AF;   /* Web 次要文本（#08090A 上约 7.85:1），由 5.6:1 AA 升至 AAA-ready */
    --p-n-775:  #6B7384;   /* 热力图坐标轴标签（画在 --surface-panel 上，单独一档） */
    --p-n-800:  #A8A8B2;   /* Electron 次要文本（不动） */
    --p-n-850:  #B4B8BF;   /* Web 正文文本 — Linear's "secondary text" */
    --p-n-900:  #F7F8F8;   /* Web 主文本 — Linear's "primary text" */
    --p-n-925:  #DDDFE4;   /* 主文本柔和档：渐变文字的下端 */
    --p-n-950:  #FAFAFA;   /* Electron 主文本（不动） */

    /* ---- 强调色 ---------------------------------------------------------- */
    --p-white:      #FFFFFF;   /* 纯白：强调色面上的前景（按钮文字 / 勾选标记） */
    --p-accent-100: #C4C9FA;   /* hover 渐变上端（Linear 风的浅蓝） */
    --p-accent-200: #DAE0FF;   /* 浅色档：渐变文字的上端 */
    --p-accent-300: #7175E0;   /* hover / 高亮 — Linear indigo */
    --p-accent-500: #5E6AD2;   /* 基准 — Linear 的招牌 indigo */
    --p-accent-700: #4652C5;   /* 按下 / 渐变收尾 */

    /* ---- 调色板原语（v19+：2 套预设，amber 在 themes.css 的
       [data-palette] 块里与 accent 对齐；indigo 直接复用 --p-accent-*，不另起一组）。
       v19 移除 rose 调色板：--p-pal-rose-500 = --p-error-500 = #E5707A，调色板与
       "错误/提醒"信号拉不开距离，已弃用。rose 三个台阶留作命名文档，不再覆盖 --accent。
       v31+ 移除 emerald 调色板：emerald-500 与 --success-500 同源（#3DBC7E），
       品牌色与"在线"状态色撞色，扫描下来两者无差别。amber 仍保留。
       命名带 "-pal" 是为了不和 L1 中性阶的 "-n"、accent 的 "-accent" 撞名，
       也避免读到 --p-emerald-500 时误以为是状态色 emerald 通道。       */
    --p-pal-amber-300:   #F0CD6E;
    --p-pal-amber-500:   #E5B342;   /* Linear amber */
    --p-pal-amber-700:   #946617;

    /* v19：rose 已废弃
       --p-pal-rose-300:  #F0A0B0;
       --p-pal-rose-500:  #E5707A;   = --p-error-500（撞色，已移除）
       --p-pal-rose-700:  #B73E3E;
       v31+：emerald 已废弃
       --p-pal-emerald-300: #6BD49A;
       --p-pal-emerald-500: #3DBC7E;   = --p-success-500（撞色，已移除）
       --p-pal-emerald-700: #1F8A56;                          */

    /* ---- 成功 ------------------------------------------------------------
       上一版（v12 现代杂志风）降到「色相偏绿的中性灰」—— #5E7A6A 在暗底上
       几乎看不出是绿色，hero tint、status pill、KPI 数字全靠它着色时整屏
       发灰。本轮回拉到 Linear 风的「emerald 但仍克制」：500 ≈ #3DBC7E
       在 #08090A 上对比度 ≈ 6.2:1（WCAG AAA），能给状态文字与 pill 背景
       真正的「绿」语义，又不会变成荧光信号灯。
       -300 用 #7FE3AC（hover / 亮档文字）；
       -700 用 #1F8A56（pressed / 渐变收尾）。                            */
    --p-success-300: #7FE3AC;   /* Linear emerald, light */
    --p-success-500: #3DBC7E;   /* Linear emerald, base */
    --p-success-700: #1F8A56;

    /* ---- 警告 ------------------------------------------------------------
       上一版暖灰 #8E7E4F 在暗底上接近深米色，partial 状态看起来像「普通文字」。
       拉到 amber #E5B342（≈ 9.8:1 AAA），状态语义回归。
       -300 用 #F0CD6E 给 hover / 文字用。                                   */
    --p-warning-300: #F0CD6E;
    --p-warning-500: #E5B342;   /* amber base */

    /* ---- 错误 ------------------------------------------------------------
       同样的逻辑：coral red 从 #8E5A5A 拉回到 #E5707A（≈ 5.5:1 AA）。
       "全部离线"场景不再靠红字喊，而是靠 hero tint + status pill + 圆点共同
       表达。 -600 / -700 仍是按下档，给 hover 渐变收尾用。                  */
    --p-error-300:   #F0A0A0;   /* coral light */
    --p-error-500:   #E5707A;   /* coral base */
    --p-error-600:   #C94747;
    --p-error-700:   #B73E3E;

    /* ---- 中性状态（无设备 / idle）--------------------------------------- */
    --p-neutral-500: var(--p-n-700);   /* idle 圆点颜色，跟 text-xlo 同步流动 */
    --p-neutral-300: var(--p-n-750);   /* neutral-hi，跟 text-lo 同步流动 */

    /* ---- 字体栈 ----------------------------------------------------------
       Linear 公开界面用 Inter（正文）+ Inter Display（标题大字号）+ Menlo
       （Mono）。这里**只前置** Inter 到 sans 栈，不强制加载远端字体：
       系统装了 Inter 自然命中，没装则回退到既有系统栈（mac/Win 各自的
       SF/Segoe、PingFang/雅黑都不会断档）。Mono 保持 JetBrains Mono 前置。 */
    --font-sans: "Inter", "Inter Display", -apple-system, BlinkMacSystemFont, "Segoe UI",
                 "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB",
                 Roboto, Helvetica, Arial, sans-serif;
    --font-mono: "JetBrains Mono", "SF Mono", "Menlo", "Fira Code", "Consolas",
                 "Roboto Mono", monospace;
}


/* ==========================================================================
   L2 · SCALE — 排版 / 间距 / 圆角 / 动效 / 高度
   ==========================================================================
   两端数值完全一致（这里不允许出现平台差异）。
   ========================================================================== */

:root {
    /* ---- 排版尺度 --------------------------------------------------------
       重构前：Web 15 种字号（10.5/11/11.5/12/12.5/13/14/16px + 1 个 clamp），
               Electron 11 种（11/11.5/12/12.5/13/13.5/14/15/24px）。
               半像素值说明字号是「凭眼睛调」而非按尺度排的。
       现在：16px 以下按 1px 步进（密集界面需要这个密度），16px 以上按 1.25
       模数递增。全部整数，半像素在暗底上发虚的问题一并消失。
       每个旧值都能在台阶上找到 ±0.5px 内的落点，视觉无感。               */
    --fs-2xs:  11px;   /* 微型标签、图例 */
    --fs-xs:   12px;   /* 元信息、时间戳 */
    --fs-sm:   13px;   /* 密集正文基准 */
    --fs-md:   14px;   /* 强调正文 */
    /* v19: 新增 fs-md-2（16px），承接「卡片标题」这一中间档位 —— 之前
       从 fs-md 14 直接跳到 fs-xl 24（hero），但卡片标题需要 16-18 区间。
       用 16 而不是 17/18 是因为 14→16 仅 +2px，给 hero 让出节奏 —— 大
       字号都遵循"主级往上加"（hero stat 26, display 28-56），卡片 16 已够。*/
    --fs-md-2: 16px;   /* 卡片标题 — 设备名等 */
    --fs-lg:   15px;   /* 小标题 */
    --fs-xl:   24px;   /* 主箭头 — 标题 / hero 状态 */
    --fs-2xl:  26px;   /* 大数字 — hero stats / KPI */
    --fs-3xl:  32px;   /* 倒计时 / 特大数字 */
    --fs-4xl:  40px;

    /* 流体展示字号：跨断点平滑变化，不能用固定台阶 */
    --fs-display-sm: clamp(18px, 1rem + .4vw, 22px);
    --fs-display:    clamp(18px, .9rem + .8vw, 26px);
    --fs-display-lg: clamp(28px, 1.8rem + 2vw, 56px);

    /* ---- 字重 ------------------------------------------------------------ */
    --weight-regular:   400;
    --weight-medium:    500;
    --weight-semibold:  600;
    --weight-bold:      700;
    --weight-extrabold: 800;

    /* ---- 行高 ------------------------------------------------------------ */
    --leading-none:    1;
    --leading-tight:   1.05;   /* 大标题 */
    --leading-snug:    1.25;
    --leading-normal:  1.5;
    --leading-relaxed: 1.7;    /* 多行说明 */

    /* ---- 字距 ------------------------------------------------------------
       现代简约的核心微调：大字号（≥ 20px）用 -0.01em 收紧，
       小字号保持默认 0（紧凑界面里再负向字距会糊）。
       让 hero status / 大数字在视觉上更"成块"，与下方密集正文
       形成节奏对比：上面紧、下面松。                                */
    --text-spacing: -0.01em;

    /* ---- 间距 ------------------------------------------------------------
       4px 主栅格 + 2px 半步（与 Tailwind 的 1 / 1.5 / 2.5 编号同构）。
       之所以保留 2px 半步，是因为 6px / 10px / 14px 在本项目里是**真实在用**
       的紧凑间距（胶囊、徽标、内联按钮），抹成 8/8/16 会让密度明显变松。
       命名用下划线而非小数点：CSS 自定义属性名里 `.` 不是合法标识符。   */
    --space-0:    0;
    --space-0_5:  2px;    /* 发丝间隙、图标内缩 */
    --space-1:    4px;
    --space-1_5:  6px;    /* 紧凑 gap、徽标内边距 */
    --space-2:    8px;
    --space-2_5:  10px;   /* 按钮内边距 */
    --space-3:    12px;
    --space-3_5:  14px;
    --space-4:    16px;
    --space-4_5:  18px;
    --space-5:    20px;
    --space-5_5:  22px;    /* 现代简约：加大卡片内边距 */
    --space-6:    24px;
    --space-7:    28px;
    --space-7_5:  30px;    /* 加大章节顶部留白 */
    --space-8:    32px;
    --space-9:    36px;
    --space-10:   40px;
    --space-12:   48px;
    --space-16:   64px;

    /* ---- 圆角 ------------------------------------------------------------
       v12 现代杂志风：卡片圆角从 6 → 14，主体容器 18，大型 hero/状态卡 24。
       杂志感的核心不是「更圆」而是「卡片浮在画布上」—— 圆角只是这套语言
       的视觉锚点（与更大的 padding、更柔的阴影一起读）。
       Vercel / Linear / Stripe 2024-2026 一致的当代卡片圆角都在 12-20 区间。
       Electron 端若需保留旧值 8/10，在自己的 :root 覆盖块里写回即可
       （已注释的设计契约：两端 SCALE 不强一致）。
         2xs  3       极小徽标、kbd 提示
         sm   14      卡片（app.css 的 .device-card / .timeline-today /
                       .kpi / .chart-card / .heatmap-card / .empty）
         md   18      主体容器（hero / site-meta）
         lg   24      留给未来"更钝"层级（如浮起 alert / 大型容器）
         xl   32      留给品牌头图/全屏浮层                      */
    --radius-2xs:  3px;
    --radius-xs:   4px;
    --radius-sm:   14px;
    --radius-md:   18px;
    --radius-lg:   24px;
    --radius-xl:   32px;
    --radius-full: 999px;

    /* ---- 动效：时长 ------------------------------------------------------
       收敛重构前散落的 .15s / .2s / .25s / .3s / .35s / .6s / .8s 七种时长。 */
    --dur-fast:    150ms;    /* hover / 焦点反馈 */
    --dur-base:    200ms;    /* 常规状态切换 */
    --dur-medium:  250ms;
    --dur-slow:    350ms;    /* 大面积色彩过渡（状态色渐变） */
    --dur-pulse:   600ms;    /* 呼吸 / 脉冲 */
    --dur-sweep:   800ms;    /* 顶部 stripe 扫光 */

    /* ---- 动效：缓动（按用途命名，不按数学形状命名）---------------------- */
    --ease-standard: ease;                             /* = ease(.25,.1,.25,1) */
    --ease-out:      cubic-bezier(.16, 1, .3, 1);      /* 减速进入，最常用 */
    --ease-in-out:   cubic-bezier(.4, 0, .2, 1);
    --ease-linear:   linear;
    --ease-spring:   cubic-bezier(.34, 1.56, .64, 1);  /* 轻微回弹 */

    /* ---- 高度（阴影）-----------------------------------------------------
       v12 现代杂志风：阴影从「贴在画布上」升级到「浮在画布上」。
       关键改动：偏移量加大、模糊半径放大、不透明度降低 —— 让阴影更柔、
       更有「空气感」。Vercel/Linear 的当代卡片典型阴影是 ~0 8-12px /
       32-40px blur / 30-40% alpha，与之对齐。
       新增 --shadow-card / --shadow-card-hover：前者是设备卡的常态浮起，
       后者是 hover 后更强的浮起（一上一下两层投影叠加）。

       v12+ 多主题：引入 --shadow-color 作为「阴影墨水色」，所有 --shadow-*
       通过 color-mix(... var(--shadow-color) X%, transparent) 派生。
       暗色主题用纯黑墨黑；浅色主题用冷黑墨黑（保证白底可见）；高对比度
       主题仍用黑（黑底上阴影本就不可见，靠 1px 白边支撑层级）。这样一套
       阴影 token 同时适配 3 主题，不用复制 3 套。                          */
    --shadow-color: rgba(0, 0, 0, 1);
    --shadow-xs: 0 1px 3px color-mix(in srgb, var(--shadow-color) 35%, transparent);
    --shadow-sm: 0 6px 24px color-mix(in srgb, var(--shadow-color) 40%, transparent);
    --shadow-md: 0 20px 60px color-mix(in srgb, var(--shadow-color) 45%, transparent);
    --shadow-lg: 0 32px 96px color-mix(in srgb, var(--shadow-color) 55%, transparent);
    --shadow-card:
        0 1px 2px color-mix(in srgb, var(--shadow-color) 35%, transparent),
        0 8px 24px color-mix(in srgb, var(--shadow-color) 28%, transparent),
        0 24px 60px color-mix(in srgb, var(--shadow-color) 18%, transparent);
    --shadow-card-hover:
        0 1px 2px color-mix(in srgb, var(--shadow-color) 30%, transparent),
        0 12px 32px color-mix(in srgb, var(--shadow-color) 36%, transparent),
        0 32px 80px color-mix(in srgb, var(--shadow-color) 26%, transparent);
    --shadow-toast:
        0 1px 2px color-mix(in srgb, var(--shadow-color) 30%, transparent),
        0 12px 32px color-mix(in srgb, var(--shadow-color) 40%, transparent);
}


/* ==========================================================================
   L3 · SEMANTIC — 语义契约
   ==========================================================================
   约定：同一个令牌名在 Web 与 Electron 上表示**同一个角色**。
   默认值取 Web 暗色主题；Electron 在自己的 styles.css 顶部用一小段
   :root 覆盖块重新指向自己的中性台阶（见该文件「平台主题」一节）。

   透明变体一律用 color-mix 从基色推导，不再手写 rgba —— 这样
   「基色改了，配套的 10%/28%/32%/35% 底色自动跟着改」，
   也省掉了原先仅为拼 rgba 而存在的 --*-rgb 通道令牌。
   ========================================================================== */

:root {
    /* ---- 画布与层级（Web 主题；Electron 覆盖 --bg / --surface-*）--------- */
    --bg:                var(--p-n-50);
    --surface-canvas:    var(--p-n-150);   /* 卡片面 */
    --surface-panel:     var(--p-n-250);   /* KPI / 图表 / 热力图 */
    --surface-inset:     var(--p-n-350);   /* tooltip / hover 强调 */
    --surface-overlay:   color-mix(in srgb, var(--p-n-150) 85%, transparent);
    --surface-tooltip:   color-mix(in srgb, var(--p-n-175) 95%, transparent);
    --surface:           var(--surface-canvas);   /* 兼容别名 */
    --surface-alt:       var(--surface-panel);    /* 兼容别名 */

    /* ---- 描边 ------------------------------------------------------------ */
    --border:    var(--p-n-450);
    --border-2:  var(--p-n-500);
    --border-3:  var(--p-n-550);
    --border-hi: var(--p-n-600);

    /* ---- 文本色 ----------------------------------------------------------
       注意命名域分离：--text-hi/md/lo/xlo 是**颜色**（强调层级），
       --fs-* 是**字号**。两套不能共用 --text-* 前缀，否则
       「--text-md = #B4B8BF」与「--text-md = 14px」会互相覆盖 ——
       这类同域异义的名字是最难查的一类设计系统缺陷。
       走 Linear Dark 2026 的对比度档位（xlo 已不再故意失败 AA ——
       上一版「fails AA by design」是状态页以外场景的判断，本场景
       —— server 状态监控——任何 UI 元数据都需要清晰可读）：
         --text-hi  #F7F8F8 在 #08090A 上 ≈ 18.5:1  → WCAG AAA
         --text-md  #B4B8BF 在 #08090A 上 ≈ 9.7:1   → WCAG AAA
         --text-lo  #9CA3AF 在 #08090A 上 ≈ 7.85:1  → WCAG AAA（≥ 7:1）
         --text-xlo #9398A2 在 #08090A 上 ≈ 6.8:1  → WCAG AA+（v16 升档）
       xlo 仍比 lo 暗一档（6.8 vs 7.85，比例 ≈ 1.15），承担「次要 / 元数据」
       语义；所有四档都已通过 AA，承载操作含义必须 AAA。
       新增文本色必须重算对比度，至少 AA 起步，承载操作含义必须 AAA。 */
    --text-hi:   var(--p-n-900);
    --text-md:   var(--p-n-850);
    --text-lo:   var(--p-n-750);
    --text-xlo:  var(--p-n-700);
    --text-dim:  var(--p-n-775);   /* 画布绘制（热力图轴标签），面板底上更暗一档 */

    /* ---- 强调色（两端共用同一批 primitive；v16 起允许调色板覆盖）--------- */
    --accent:      var(--p-accent-500);
    --accent-hi:   var(--p-accent-300);
    /* v16：--accent-grad 的收尾色提为独立令牌 --accent-grad-end，让
       [data-palette] 块（amber）能只覆盖这一档就完成
       "品牌渐变跟随调色板"；v19 移除 rose 调色板（与 --error 同色撞车），
       v31+ 移除 emerald 调色板（与 --success 同色撞车），
       这里只剩 amber 一块。改色前直接写 var(--p-accent-700)，
       palette 改色时 brand logo 仍是 indigo，与下方 accent 形成「脱钩感」。  */
    --accent-grad-end:   var(--p-accent-700);
    --accent-grad: linear-gradient(135deg, var(--accent-hi) 0%, var(--accent-grad-end) 100%);

    /* 按钮各状态的渐变整条提为令牌：只定义端点色的话，每个按钮状态都要
       在样式表里重写一遍 linear-gradient，两端很容易抄出细微差异。
       **注意：Web 端已不使用渐变按钮，但 Electron 主按钮仍在用**
       （styles.css 的 .btn-primary），所以这三个渐变令牌是跨平台契约，
       不能因为「Web 不用了」就删 —— 删了会直接弄坏 Electron。 */
    --accent-grad-hover: linear-gradient(135deg, var(--p-accent-100) 0%, var(--p-accent-500) 100%);
    --on-accent:         var(--p-white);

    /* 上一轮去装饰时，Web 侧的渐变文字被移除，--accent-2 / --accent-soft /
       --text-hi-soft / --font-display 因此失去了全部使用者，已删除。
       将来要恢复渐变文字，从 --p-accent-* 原语重新组合即可。 */
       /* 为什么不用 accent 直接画图表：
       accent（Linear 风 #5E6AD2）是**交互色** —— 它标记「这里能点/这里被选中」：
       焦点环、选中态 toggle、链接 hover。可视化不是交互，
       用同一抹紫会让人误以为图表区域可点击。

       更实际的问题是**面积**：热力图一次画 7×24 = 168 个格子，
       折线图还要铺一层渐变面积。这些加起来的着色面积是状态色的几十倍。
       用同一个高饱和紫，等于让次要信息（开机频次）拥有全屏最大的
       视觉权重，而真正重要的「设备是否在线」只剩一个小圆点。
       旧版 --viz-fill 走 #5E6AD2（accent）正是踩到这个坑 —— 3 张设备卡 × 168 格
       ≈ 500 个 indigo 色块盖在画布上，扫一眼满屏蓝紫、首屏配色失衡。

       所以可视化走**三轨**：
         · 在线设备 → accent（调色板色）：折线 + 面积走 accent 系。
           折线是 2px 实线、面积 ≤ 18% alpha —— 着色面积小、可控，
           让「在线」这件事在视觉上一眼可辨；同时调色板改 --accent
           会顺路带过 --viz-accent，chart line 跟随调色板（v22+）；
         · 离线设备 → 中性灰（--viz-line / --viz-hi）：折线回退到 text-lo 同族，
           不抢「离线」的状态色注意力；
         · 热力图（活跃时段分布）→ accent（同源）：与 chart line / 焦点环 /
           品牌渐变共享同一调色板身份，让"曾经活跃"和"现在在线"在视觉
           上同品牌 —— v26+ 起随调色板切换（修复用户反馈"换调色板热力图
           不变色不合理"）。themes.css 各 [data-palette] 块显式声明
           --viz-fill 守住这条契约。
       app.js 的 makeChart 按 d.is_online 二选一（在线/离线），
       renderHeatmap 直接走 --viz-fill（= var(--accent)）。
       之所以不让 accent 直接画所有图表：accent 同时是交互色（焦点环 / 选中
       toggle / 链接），offline 设备的图表若仍用 accent，会让人误判"这块图表
       是可点的"。offline 用灰是「这是数据，不是按钮」的信号。                  */
    /* viz-accent / viz-accent-hi / viz-line / viz-hi 的「-hi」语义：
       hi = 比基色更亮那一档，承担「hover 点 + 渐变顶端」两个角色。
       之前的写法把 hi 与基色写反了（accent=#7175E0 lighter, accent-hi=#5E6AD2 darker），
       实际渲染靠 themes.css 的 dark 块覆盖回来才不出错 —— 现在 :root 直接给正确值，
       新增主题不显式覆盖也安全。                                                            */
    --viz-accent:    var(--accent);         /* 在线折线基色：跟 --accent 走，让调色板
                                              接管 chart line 颜色。amber 调色板
                                              时 chart 变 amber。
                                              :root 默认 --accent = --p-accent-500
                                              (indigo)，行为与之前一致。              */
    --viz-accent-hi: var(--accent-hi);      /* 在线高光（hover / 渐变上端，比 line 亮一档） */
    --viz-line:  var(--p-n-750);   /* 折线（offline 默认）：与 text-lo 同步流动 */
    --viz-hi:    var(--p-n-850);   /* 折线高亮 / 悬停点（offline，比 line 亮）：与 text-md 同步流动 */
    /* v16→v26：--viz-fill 从 var(--success) 改为 var(--accent)，让热力图
       跟随调色板（与 --viz-accent 同源）。理由：
         · 用户反馈「换调色板热力图不变色不合理」—— 调色板承担品牌色身份，
           包括 chart line（已跟 accent）与 heatmap 主色，两轨合一；
         · semantic 对齐：调色板本身就是「品牌色 = 视觉身份」的载体，
           把 heatmap 也绑进去后「选 amber 调色板 = 整页带琥珀感」一致，
           而不是「amber 只在 timeline/pill 里，热力图永远是另一种绿」；
         · 实现简洁：themes.css 各 [data-palette] 块在改写 --accent 时
           顺路带过 --viz-fill，不用再为 heatmap 写一份专属级联。
       旧的「跟 --success 走 = 跟在线同族」契约已被推翻（v16+ → v26+）：
       在 [data-palette] 块里显式声明 --viz-fill，测试
       test_palette_blocks_must_override_viz_fill 守住这个新契约。
       themes.css 顶部块注释同步改写（详见 themes.css 顶部「设计契约」）。*/
    --viz-fill: var(--accent);
    --viz-grid: color-mix(in srgb, var(--viz-line) 22%, transparent);

    /* ---- 状态色：成功 ---------------------------------------------------- */
    --success:      var(--p-success-500);
    --success-hi:   var(--p-success-300);
    --success-2:    var(--p-success-700);
    --success-bg:   color-mix(in srgb, var(--success) 10%, transparent);
    --success-bd:   color-mix(in srgb, var(--success) 32%, transparent);
    --success-glow: color-mix(in srgb, var(--success) 35%, transparent);

    /* ---- 状态色：警告 ---------------------------------------------------- */
    --warning:      var(--p-warning-500);
    --warning-hi:   var(--p-warning-300);
    --warning-bg:   color-mix(in srgb, var(--warning) 10%, transparent);
    --warning-bd:   color-mix(in srgb, var(--warning) 32%, transparent);
    --warning-glow: color-mix(in srgb, var(--warning) 35%, transparent);

    /* ---- 状态色：错误 ---------------------------------------------------- */
    --error:      var(--p-error-500);
    --error-hi:   var(--p-error-300);
    --error-2:    var(--p-error-700);
    --error-bg:   color-mix(in srgb, var(--error) 10%, transparent);
    --error-bd:   color-mix(in srgb, var(--error) 32%, transparent);
    --error-glow: color-mix(in srgb, var(--error) 35%, transparent);
    --error-grad:        linear-gradient(135deg, var(--p-error-300) 0%, var(--p-error-500) 100%);
    --error-grad-hover:  linear-gradient(135deg, var(--p-error-300) 0%, var(--p-error-600) 100%);

    /* ---- 状态色：中性（无设备 / idle）----------------------------------- */
    --neutral:    var(--p-neutral-500);
    --neutral-hi: var(--p-neutral-300);
    --neutral-bg: color-mix(in srgb, var(--neutral) 10%, transparent);
    --neutral-bd: color-mix(in srgb, var(--neutral) 28%, transparent);
    --idle-bd:    var(--neutral-bd);

    /* ---- 布局外壳（流体边距，随视口平滑变化）---------------------------
       --shell-py 原本给上下留白用流体 clamp，但两侧内边距一高一低
       并不对应任何真实需求（上下不是同一个视觉角色），已改为固定的
       --space-6 / --shell-pb。死令牌会伪装成设计规范，删掉。 */
    --shell-px: clamp(16px, 3vw, 40px);
    --shell-pb: clamp(48px, 6vw, 80px);

    /* ---- 组件尺寸 --------------------------------------------------------
       顶部状态条高度原先以魔法数字 4 出现在 4 处（.stripe 的 height 加三条
       横幅的 var(--status-stripe-h, 4px) fallback），改条高度时三张横幅
       不会跟着动。现在收成一个令牌。

       这里曾有两个令牌在本轮去装饰时被删掉了对应的用法，随之移除：
         --card-status-bar  设备卡左侧状态竖条宽度
             状态色已收敛到「一个 8px 圆点」，竖条不再承载状态；
             留着它会让后来人以为"卡片左侧应该有一条状态条"。
         --timeline-h       今日时间线格子高度
             改用固定的 20px（见 app.css 的 .timeline-cell）。
             死令牌比没有令牌更糟：它看起来像"这里有设计规范"，
             实际上没有任何代码读它。 */
    --status-stripe-h:  2px;
    --chart-toggle-h:  28px;
    --timeline-cell-gap: 2px;

    /* ---- 交互反馈 --------------------------------------------------------
       焦点环：键盘焦点走这条；鼠标点击不显示（:focus-visible 控制）。
       与 --accent 同色但稍亮一档，让"我正在被键盘聚焦"在视觉上比
       "我正在被悬停"略强一档——悬停是浏览，聚焦是"准备输入"，级别不同。
       4 主题共用一份，themes.css 主题块不覆盖（保持焦点环在所有主题下
       都是品牌的代表色，方便用户在切换主题时也能立刻定位键盘焦点）。     */
    --focus-ring: var(--accent-hi);
}
