/* ============================================================================
   soft.css —— 「史锦泽的软件」站点唯一样式真源
   被 /software.html（总览）与 /software/copy.html /tool.html /android.html（详情）共用。

   ── 设计决定：亮色是默认，暗色是【用户自己选】的（2026-08-07 改）───────────────
   ⚠ 这一段原来写的是「本站点只做亮色，本文件没有任何 [data-theme] 选择器」。需求变了，
     但**变的不是那条判断，是那条判断的前提**，别把两件事混成一件：
       当初被否掉的是「一进来就被强制成看不清的暗色」（用户原话：「不要暗黑色系，看不清」），
       现在要的是「用户自己能切，默认仍是亮色」。所以下面三条只有第 2 条改了：
   1. 本站点【仍然不引】 /js/theme.js，也不引 /css/xiliu-tokens.css。theme.js 的默认值写死是
      'dark'（读 cookie xiliu_theme，读不到就 dark），引进来 = 首次访问的人直接被扔进暗色，
      正是当初被否掉的那个行为；而且它用的是全局变量名，和本站点的 --s-/--app- 两套会打架。
      ⚠ 2026-08-07 定案：本站点【没有自己的主题开关】。当天加过一颗（存 localStorage
      `sjz_soft_theme`），但本站被首页当内嵌页签打开后同屏出现两颗开关、各管各的值，
      按哪个都只对一半，于是撤掉 —— 主题完全跟随全站 cookie `xiliu_theme`（首页那颗浮钮），
      读不到按 dark；soft.js 里只剩「跟随」不再有「切换」。
      注意这一条与上面「默认亮色」的旧结论**已经不一致**：现在首访跟的是首页，而首页默认暗色。
   2. 本文件现在有 [data-theme="dark"] 选择器了，配色部分全部集中在第 17 节（页尾），**上面的
      1~15 节一个字节都没动** —— 亮色外观与改动前完全一致，这是硬回归底线。
      暗色是「加一层覆盖」而不是「重写样式」：本文件 188 处 var(--) 早就把颜色全收进了
      --s-* / --app-* 变量，所以只要换变量值 + 补十来条写死颜色的规则（见第 17 节的清单）。
   3. 所有 CSS 变量一律加 `--s-` / `--app-` 前缀，与 xiliu-tokens.css 的 --bg/--text/--accent
      等全局变量名【零交集】。theme.js 是把那 22 个变量以 inline style 打在 <html> 上的，
      命名不撞车 = 就算被打上也污染不到本站点。
   ── 炫酷靠什么 ────────────────────────────────────────────────────────────
   浅色玻璃拟态 + 大面积低饱和 aurora 光晕 + 极淡网格 + 高饱和渐变只用在按钮/徽标/标题局部
   + 滚动进场 + hover 抬升与光泽扫过。不靠「把背景变暗」。
   ── 对比度（逐个按 WCAG 算过，全部 ≥ 4.5:1 on --s-bg #f7f8fc，含最小号的微标签）───
   （下面每个数字给的是【最差情况】：按 .soft-bg 底渐变最深处 #eef2fa 算，不是按 --s-bg #f7f8fc）
   --s-text #1b2436 = 13.84   --s-text-2 #55617a = 5.54
   --s-text-3 #5f6a82 = 4.84  --s-text-4 #616a80 = 4.82
   强调文字 --app-ink：copy #4338ca = 7.04 / tool #0e7490 = 4.78 / android #047857 = 4.89
                       idea #9c4f08 = 5.29 / vscode #0b5f9e = 5.95 / obsidian #9a1aa8 = 6.06
   白字按钮渐变端点 --app-btn-a/b 现在是【六套皮肤 12 个色】（2026-08-06 加了三套插件皮肤）：
   最低 4.99（vscode #0d74b8）、最高 6.82（vscode #1257b8），全部 ≥4.5:1 on white。
   ⚠ 这两行原来写的是「六个色最低 5.35，最高 8.00」——那个 8.00 是 android 的旧末端 #045c46，
     它早就因为「太深、看不出渐变」被换成 #0b6f77 了，数字一直没跟着改。教训：改颜色必须回来改这里，
     不然后面的人会照着一个不存在的值去校准。
   警示条 .notice--warn 正文 #6b4108 on #fff6e8 = 8.22，其中的 <b> 走 --s-warn #8a4a06 = 6.40
   ── 对比度（暗色，2026-08-07 新增；口径与上面那段【完全一致】，都是现算的 WCAG 相对亮度）──
   （下面每个数字同样是【最差情况】。暗色的「最差」和亮色反过来 —— 亮色要防「底太深」，
     暗色要防「底太亮」，所以基准取的是【全站最亮的那一块底】：
     tool 皮肤详情页 HERO 处，= .soft-bg 最亮那一站 #141b2b + 该皮肤 aura 一层 + .hero__halo 两层
     叠出来的 #11394c。玻璃卡内部反而更暗（#203343 一带），数字只会更好看。）
   --s-text #e8edf7 = 10.44   --s-text-2 #b6c1d6 = 6.76
   --s-text-3 #a4b0c6 = 5.61  --s-text-4 #a2aec4 = 5.48
   强调文字 --app-ink（各按【本皮肤】最亮的底算，另给药丸底 --app-tint 上的复核值）：
     copy #adb0ff 6.39 / on tint 5.41      tool #4fd3e8 6.90 / on tint 5.23
     android #34d9a0 6.90 / on tint 5.32   idea #f3ba63 7.50 / on tint 5.52
     vscode #6ac2f7 6.30 / on tint 4.99    obsidian #ec8ef5 6.43 / on tint 5.64
   标题渐变字 --app-1 / --app-2（走大字号 3:1 那一档，口径同亮色那段；实测全部 ≥4.42，
     顺带把亮色遗留的「tool 2.43 / android 2.54 / android --app-2 1.92 不达标」在暗色下一并解决了）：
     --app-1  copy 4.42 / tool 6.78 / android 6.51 / idea 6.24 / vscode 5.38 / obsidian 4.94
     --app-2  copy 5.25 / tool 5.05 / android 8.21 / idea 5.55 / vscode 4.72 / obsidian 6.60
   白字按钮渐变端点 --app-btn-a/b：**暗色下【不改值】，12 个色原样沿用亮色**，所以还是
     最低 4.99 / 最高 6.82。理由见第 17 节，一句话：这两个色是「白字压在上面」，
     它的对比度只和自己有关、和页面底色无关；一改亮就把白字推到 4.5 以下 = 按钮反而先坏。
   警示条 .notice--warn 暗色正文 #f3d7ab on 该条自己的底 = 8.03~8.42（六套皮肤全测），
     其中 <b> 走暗色 --s-warn #ffc46b = 7.10~7.48
   语义色 --s-ok #3ddc9a = 6.95、--s-danger #ff8a80 = 5.37（均 on 最亮底）
   禁用按钮 rgba(255,255,255,.06) 底上的 --s-text-3 = 4.86~5.28（六套皮肤）
   图上角标 .shot__corner / .is-zoomable::after：底改成 rgba(11,16,27,.86)，
     按「压在纯白截图上」这一最差情况算，--s-text-2 = 7.18
   ⚠ 改暗色任何一个颜色，都必须回来把这段数字重算重写 —— 亮色那段上面已经写过一次
     「改颜色必须回来改这里」的教训（android 的 8.00 挂了很久），别再犯第二遍。
   ============================================================================ */

/* ==========================================================================
   1. 变量层
   ========================================================================== */
:root{
    /* —— 中性色阶 —— */
    --s-bg:        #f7f8fc;   /* 页面底色 */
    --s-bg-2:      #eef1f8;   /* 次级底色（大区块交替） */
    --s-white:     #ffffff;
    --s-surface:   rgba(255,255,255,.72);   /* 玻璃卡片底 */
    --s-surface-2: rgba(255,255,255,.55);   /* 更透的玻璃 */
    --s-surface-3: rgba(27,36,54,.035);     /* 内嵌浅槽（占位、代码块） */
    --s-text:      #1b2436;   /* 正文/标题 */
    --s-text-2:    #55617a;   /* 次级正文 */
    /* ⚠ 这两档一定要按【页面最深的那块底】算，不是按 --s-bg 算：
       .soft-bg 的底渐变最深处是 #eef2fa（页面下半部/页脚那一带），而页脚正文与
       .app-card__note / .shot__ph-size 都用这两个色，直接压在那块底上（不在玻璃卡里）。
       旧值 #64708a / #666f85 在 #f7f8fc 上是 4.68/4.74 达标，但在 #eef2fa 上只有 4.43/4.48，
       页脚那一截其实是不合格的。压深到下面这组，最差处也 ≥4.8。 */
    --s-text-3:    #5f6a82;   /* 元信息（on #eef2fa = 4.84，on #fff = 5.34） */
    --s-text-4:    #616a80;   /* 微标签（英文 eyebrow / 图位尺寸提示）on #eef2fa = 4.82 */
    --s-border:    rgba(27,36,54,.09);
    --s-border-2:  rgba(27,36,54,.16);
    --s-hairline:  rgba(255,255,255,.85);   /* 玻璃上沿的 1px 亮边 */

    /* —— 语义色 —— */
    /* ⚠ --s-warn 是【文字色】，不是装饰色：.notice--warn b 与 .app-card__note b 都用它，
       而那两处正是安卓「换了签名，覆盖安装会失败」那条最要紧的提示。
       原值 #b4620a 在警示条底 #fff6e8 上只有 4.18、在白卡上 4.41，双双不到 4.5:1
       （文件头注释里算的 8.22 是 .notice--warn 的【正文】色 #6b4108，不是 b 的色，别混）。
       压深到 #8a4a06：on #fff6e8 = 6.40、on 白 = 6.75、on #eef2fa = 6.11。 */
    --s-warn:      #8a4a06;
    --s-warn-bg:   #fff6e8;
    --s-warn-bd:   rgba(180,98,10,.26);
    --s-ok:        #0f7a4f;
    --s-danger:    #b42318;

    /* —— 圆角刻度 —— */
    --s-r-xs: 8px;
    --s-r-sm: 12px;
    --s-r:    18px;
    --s-r-lg: 24px;
    --s-r-xl: 30px;
    --s-r-pill: 999px;

    /* —— 阴影刻度（柔和多层，白底上不发灰）—— */
    --s-sh-1: 0 1px 2px rgba(27,44,80,.05), 0 4px 12px rgba(27,44,80,.05);
    --s-sh-2: 0 2px 6px rgba(27,44,80,.06), 0 12px 28px rgba(27,44,80,.08);
    --s-sh-3: 0 4px 10px rgba(27,44,80,.07), 0 24px 56px rgba(27,44,80,.12);
    --s-sh-in: inset 0 1px 0 var(--s-hairline);

    /* —— 间距刻度 —— */
    --s-1: 6px;  --s-2: 10px; --s-3: 14px; --s-4: 20px;
    --s-5: 28px; --s-6: 40px; --s-7: 56px; --s-8: 80px;

    /* —— 字体 —— */
    --s-sans: "Segoe UI Variable Text","Segoe UI","PingFang SC","Microsoft YaHei",
              -apple-system,BlinkMacSystemFont,"Helvetica Neue",sans-serif;
    --s-mono: "SF Mono",ui-monospace,"JetBrains Mono","Cascadia Mono",Consolas,monospace;

    /* —— 布局 —— */
    --s-maxw: 1180px;
    --s-pad:  30px;

    /* —— 默认皮肤（站点级/总览页用的中性蓝紫，等同 copy 皮肤的中性版）——
       每个 App 的详情页在 <html> 或某个容器上加 data-app="copy|tool|android" 覆盖 */
    --app-1:     #6366f1;                 /* 品牌主色（鲜艳，用于光晕/描边/图标底板） */
    --app-2:     #a855f7;                 /* 品牌副色 */
    --app-ink:   #4338ca;                 /* 亮底上的强调文字色，必须 ≥4.5:1 on white */
    --app-btn-a: #4f46e5;                 /* 主按钮渐变起（白字 ≥4.5:1） */
    --app-btn-b: #7c3aed;                 /* 主按钮渐变止（白字 ≥4.5:1） */
    --app-tint:  rgba(99,102,241,.10);    /* 极淡底 */
    --app-tint-2:rgba(99,102,241,.18);
    --app-bd:    rgba(99,102,241,.30);
    --app-glow:  rgba(99,102,241,.34);    /* 按钮外发光 */
}

/* —— 六套皮肤：同一套组件，换一行 data-app 就换一身衣服 ——
   前 3 套是 App（copy / tool / android），后 3 套是 IDE / 笔记插件（idea / vscode / obsidian）。
   用法：<html data-app="copy">   或   <section data-app="android">…</section>
   总览页故意把 data-app 加在【单张卡片】上而不是 <html> 上 —— 因为下面有
   `[data-app] .soft-bg{}` 这条规则，加在 <html> 上会把整页背景光晕也染成该皮肤的色，
   总览页六张卡各一个色，整页背景必须保持中性蓝紫，否则以最后一个生效的为准，乱套。 */
[data-app="copy"]{
    /* 剪贴板 = 蓝紫系 */
    --app-1:#6366f1; --app-2:#a855f7; --app-ink:#4338ca;
    --app-btn-a:#4f46e5; --app-btn-b:#7c3aed;
    --app-tint:rgba(99,102,241,.10); --app-tint-2:rgba(99,102,241,.18);
    --app-bd:rgba(99,102,241,.30);   --app-glow:rgba(99,102,241,.34);
}
[data-app="tool"]{
    /* 工具箱 = 靛青系 */
    --app-1:#06b6d4; --app-2:#3b82f6; --app-ink:#0e7490;
    --app-btn-a:#0e7490; --app-btn-b:#1d4ed8;
    --app-tint:rgba(6,182,212,.10);  --app-tint-2:rgba(6,182,212,.20);
    --app-bd:rgba(14,116,144,.30);   --app-glow:rgba(13,148,180,.32);
}
[data-app="android"]{
    /* 安卓 = 翠绿系（按钮渐变刻意压深两档，白字才够 4.5:1）*/
    --app-1:#10b981; --app-2:#34d399; --app-ink:#047857;
    /* 按钮渐变的两个端点要和另外两套「同一个量级」，否则三颗下载按钮并排时安卓那颗
       又暗又平，一眼就出戏。校准口径（白字对比度 / 色相行程，全部现算，别拍脑袋填）：
         copy  #4f46e5→#7c3aed  6.29:1 → 5.70:1，色相 243°→262°（19°）
         tool  #0e7490→#1d4ed8  5.36:1 → 6.70:1，色相 193°→224°（31°）
         android(旧) #067a55→#045c46  5.35:1 → 8.00:1，色相 161°→165°（4°）
                     ↑ 末端掉到 8:1 = 比谁都深，两端又几乎同色 = 看不出渐变 → 「一坨深绿」
         android(新) #067a55→#0b6f77  5.35:1 → 5.91:1，色相 161°→184°（23°）
       起点原样保留（绿色高饱和时想压到 4.5:1 就只能这么深，再亮白字就不合格了），
       只把末端从「更深的绿」换成「同亮度的深青」——亮度落差、色相行程都对齐了另外两套。
       ⚠ 改这两个值必须重算白字对比度，两端都得 ≥4.5:1（按钮文案是 14.5px 粗体，非大字号）。*/
    --app-btn-a:#067a55; --app-btn-b:#0b6f77;
    --app-tint:rgba(16,185,129,.11); --app-tint-2:rgba(16,185,129,.20);
    --app-bd:rgba(4,120,87,.30);     --app-glow:rgba(6,122,85,.32);
}

/* —— 下面三套是 IDE / 笔记插件的皮肤（2026-08-06 新增）——————————————————————
   所有数字都是用 node 现算的 WCAG 相对亮度对比度（不是拍脑袋估的），口径与上面三套完全一致：
     · --app-btn-a / --app-btn-b 两端【都】要对白字 ≥4.5:1
       （按钮文案 .dl-btn .os-txt b 是 14.5~15.5px 粗体，够不上 WCAG 的「大字号」豁免，
        所以走 4.5:1 这一档，不是 3:1）
     · --app-ink 是亮底上的强调文字色（.chip--accent / .badge / .eyebrow / .app-card__tag），
       白底 ≥4.5:1，同时按页面最深的底 #eef2fa 和本皮肤 --app-tint 各复核一遍
     · --app-1 / --app-2 【不是纯装饰色，它们要当标题正文用】——这一条 2026-08-06 改过，
       原文写的是「只出现在装饰里，所以允许鲜亮」，是错的，别再照那句去挑色：
         纯装饰的只有 .eyebrow 的 dot（--app-1）和 .app-card__logo 底板（--app-1 → --app-btn-b）；
         但 .grad-text 也吃这两个色 —— `linear-gradient(112deg,--app-btn-a 0%,--app-1 46%,--app-2 100%)`，
         而 .grad-text 用在 .h1 / .h2 里被高亮的那一段【标题正文】（总览页 + 六个详情页，
         七个 html 每页都有 <span class="grad-text">），中间那一站正好落在字身上。同理 .stat--big b 是
         --app-btn-a → --app-2 的渐变字。
       所以口径是：--app-1 / --app-2 走【大字号 3:1】这一档（.h1 是 clamp(34px,5.2vw,60px)、
       .h2 是 clamp(22px,2.6vw,30px)、.stat--big b 是 20px/750，都够得上 WCAG 的大字号豁免），
       不是 4.5:1，也不是「装饰随便亮」。
       ⚠ 六套里【只有新加的 idea / vscode 两套按这个口径校准过】。老三套 --app-1 对白底实测
         copy #6366f1 = 4.47（达标）、tool #06b6d4 = 2.43、android #10b981 = 2.54（两套不达标），
         --app-2 里 android #34d399 = 1.92（不达标）—— 这几笔是历史遗留，本次【故意不动】：
         它们是三个已上线页面的标题观感，改了就是动线上视觉，不在这次修插件页的范围内。
         记在这里是为了以后有人专门做「老三套配色返工」时不用再重新测一遍。
     · 「一家人但一眼分得清」怎么验的：把六套皮肤的 --app-1/--app-btn-a/--app-btn-b 两两算
       CIELAB ΔE，新皮肤对老三套（copy/tool/android）的【最小】ΔE 是
       idea 67.9（idea #9a6207 ↔ android #067a55）、vscode 25.5（vscode #0d74b8 ↔ tool #0e7490）、
       obsidian 27.2（obsidian #c026d3 ↔ copy #7c3aed）。
       作为参照：copy 自己两个端点之间的 ΔE 才 14.4（那是一条正常渐变），所以 25 已经是
       「明显不是同一个色」的量级了。
       （这三个数 2026-08-06 改完 idea / vscode 的 --app-1 之后重算过一遍：三组最小值都落在
        --app-btn-a/b 上、不涉及 --app-1，所以数值本身没被这次改动影响；原注释写的 68/26/27
        是四舍五入过的，这里换成实测原值，免得下次有人复算时以为对不上。） */
[data-app="idea"]{
    /* IDEA 书签插件 = 琥珀 / 焦橙系。六套里【只有这一套是暖色】，扫一眼就能从一排卡里挑出来。 */
    /* --app-1 原来是 amber-500 #f59e0b，2026-08-06 压深到 #c87608，理由见上面那条口径说明：
       它是 .grad-text 的中间站 = 标题正文，走大字号 3:1 那一档，而 #f59e0b 对白底只有 2.15、
       对页面最深的底 #eef2fa 只有 1.91 —— H1 的中段直接糊在背景里。
       实测（node 现算，白底 / #eef2fa 两个口径都过 3:1）：
         #f59e0b 2.15 / 1.91  ✗    ← 旧值
         #d97706 3.19 / 2.84  △    ← amber-600，白底刚过线，但页面下半屏那块 #eef2fa 上还是 2.84
         #c87608 3.47 / 3.09  ✓    ← 取它
         #b45309 5.02 / 4.48  ✗    ← amber-700 太深：它和 --app-btn-a #9a6207（5.09）亮度几乎重合，
                                      「鲜色装饰层」和「按钮底」变成同一个深浅，光晕/图标底板就没层次了
       为什么按 #eef2fa 而不是纯白算：.soft-bg 是 position:fixed 的整屏渐变，最深处 #eef2fa 永远
       压在视口底部，往下滚时 .h2 就落在那一块上（文件头 --s-text-3/-4 那两条也是这个口径）。
       挑深浅之外还确认了三件事：① 仍然是高饱和琥珀（hue 34°、sat 0.96），当光晕/图标底板够鲜；
       ② 和 --app-2 #ea580c 的 CIELAB ΔE = 28.2、和 --app-btn-a #9a6207 = 17.5，.grad-text 三站
       层次没塌（对照：copy 自己两个端点之间 ΔE 才 14）；③ 对老三套 --app-1 的最小 ΔE = 90.5，
       仍然是「一眼就能从一排卡里挑出来的那个暖色」。 */
    --app-1:#c87608; --app-2:#ea580c; --app-ink:#9c4f08;
    /* 校准（白字对比度 / 色相行程），口径同上面 android 那段：
         idea #9a6207→#b03a12   5.09:1 → 6.07:1，色相 37°→15°（22°）
       对照：copy 6.29→5.70（19°）/ tool 5.36→6.70（31°）/ android 5.35→5.91（23°）——同一量级。
       ⚠ 暖色想让白字达标，只能压到「焦糖 / 砖红」这个深度：amber-500 #f59e0b 对白字只有
         2.15:1、orange-600 #ea580c 也才 3.56:1，直接拿去当按钮底就是【不合格】。
         所以按钮底另配 #9a6207 / #b03a12 这两个深色，--app-1/--app-2 走的是另一档
         （大字号 3:1，因为 .grad-text 会把它们画在标题字上，不是纯装饰 —— 详见上面那条口径说明）。
         别看着「橙色不够亮」就把 --app-btn-a/b 调回鲜橙。
       --app-ink #9c4f08：on 白 5.94、on 页脚最深底 #eef2fa 5.29、on 本皮肤 --app-tint 5.50。
       ⚠ 它和全局的 --s-warn #8a4a06 长得像，但不是一回事：--s-warn 是【语义色】（警示条里的
         <b>、.app-card__note b，安卓那条「换签名会装不上」就靠它），这里是本皮肤的强调色。
         两者别互相替代，改一个不要顺手改另一个。 */
    --app-btn-a:#9a6207; --app-btn-b:#b03a12;
    /* rgba(245,158,11,…) 就是旧的 --app-1 #f59e0b，【故意】留在这里没跟着压深：
       这两个只当【底】用（.soft-bg / .hero__halo 的光晕、.shot 占位底、.eyebrow/.chip--accent
       等药丸的浅底），10%~20% 的透明度铺在白底上，正需要那支最亮的琥珀托住整页暖调；
       换成 #c87608 会让页底整体发脏。压在 --app-tint 上的文字一律走 --app-ink #9c4f08，
       那个色已经按 on --app-tint = 5.50 复核过（见下面），所以底色亮一点不影响可读性。 */
    --app-tint:rgba(245,158,11,.10); --app-tint-2:rgba(245,158,11,.20);
    --app-bd:rgba(154,98,7,.30);     --app-glow:rgba(180,83,9,.34);
}
[data-app="vscode"]{
    /* VS Code 插件 = 天蓝 azure（微软那支标志蓝的方向），全程不带一丝青绿，好和 tool 分开 */
    /* --app-1 原来直接用了 VS Code 那支标志蓝 #1f9cf0，2026-08-06 压深到 #1387d4：
       同上，它是 .grad-text 的中间站 = 标题正文，走大字号 3:1；#1f9cf0 白底 2.97 / #eef2fa 2.65，
       两个口径都差一口气。新值 #1387d4 = 白底 3.86 / #eef2fa 3.44，hue 仍是 204°、sat 0.91，
       还是那支天蓝，没往青（tool）也没往紫（copy）挪一度。
       ⚠ 代价要说清楚，别以为是没调好：#1387d4 和 --app-2 #0e8fdb 的 ΔE 只剩 3.5（旧值 #1f9cf0
         对它是 6.3），.grad-text 的层次现在主要靠 --app-btn-a #0d74b8 → --app-1 那一段（ΔE 8.5）。
         这不是能挑更好值的问题，是这条蓝通道被挤死了：任何满足 3:1 的蓝，亮度上不是贴着
         --app-btn-a（4.99）就是贴着 --app-2（3.52），中间没有空位。好处是三站变成单调的
         深→浅（4.99 → 3.86 → 3.52），比原来「深→亮→中」的折返更顺。
         真要把层次做回来，得连 --app-2 一起重排，那会动到已经校准过的按钮渐变，不在本次范围。
       另：.app-card__logo 底板是 linear-gradient(--app-1,--app-btn-b) 上盖 38px 白色 SVG，
       --app-1 压深之后白图标反而更清楚（白对 #1387d4 = 3.86，旧值只有 2.97）。 */
    --app-1:#1387d4; --app-2:#0e8fdb; --app-ink:#0b5f9e;
    /* 校准：vscode #0d74b8→#1257b8   4.99:1 → 6.82:1，色相 204°→215°（11°）
       ⚠ 这套的色相行程【故意】只有 11°，不是偷懒 —— 蓝这一段左右两边都被占了：
         再往青走就撞 tool 的起点 #0e7490（193°），再往紫走就撞 copy 的起点 #4f46e5（243°）。
         实测把末端挪到 226° 的 #2a4fcc，它和 tool 末端 #1d4ed8 的 CIELAB ΔE 只剩 8
         （≈ 肉眼同一个色），那才是真正的「分不清」。
         所以这套改用【明度行程】撑渐变：4.99→6.82 是六套里跨度最大的一组（copy 才 6.29→5.70），
         看得出是渐变，又不去踩邻居。实测最小 ΔE：对 tool = 26、对 copy = 28，都够认。
       --app-ink #0b5f9e：on 白 6.68、on #eef2fa 5.95、on 本皮肤 --app-tint 6.03。
       ⚠ VS Code 那支标志蓝 #1f9cf0 对白字只有 2.97:1，按钮底、强调文字色都轮不到它；
         2026-08-06 起连 --app-1 也不用它了（--app-1 会被 .grad-text 画在标题上，见上一段）。
         它现在只活在本皮肤的 --app-tint（rgba(31,156,240,.10)）与 --app-tint-2（同色 .19）里 ——
         那两个只当【底】用（光晕、药丸浅底、图位底），压在上面的文字一律是 --app-ink #0b5f9e
         （on --app-tint = 6.03，见下面），所以底再亮也不牵扯文字对比度，这才是它合适的位置。
         （--app-bd / --app-glow 用的是另一支 rgba(13,116,184,…)，也就是 --app-btn-a #0d74b8，别记混。） */
    --app-btn-a:#0d74b8; --app-btn-b:#1257b8;
    --app-tint:rgba(31,156,240,.10); --app-tint-2:rgba(31,156,240,.19);
    --app-bd:rgba(13,116,184,.30);   --app-glow:rgba(13,116,184,.32);
}
[data-app="obsidian"]{
    /* Obsidian 插件 = 紫红（洋红宝石）系，刻意从 copy 的蓝紫再往品红推一段 */
    --app-1:#c026d3; --app-2:#d946ef; --app-ink:#9a1aa8;
    /* 校准：obsidian #a21caf→#b52490   6.32:1 → 5.80:1，色相 295°→315°（20°）
       ⚠ Obsidian 官方那支紫（#7c3aed 一带）【正好就是】copy 的按钮末端 #7c3aed，照搬 =
         两张卡穿同一身衣服。所以整体往品红推了约 50°：copy 是 243°→262°（蓝紫），
         这套是 295°→315°（紫红），对 copy 各色的最小 ΔE = 27，并排放能分清。
       --app-ink #9a1aa8：on 白 6.80、on #eef2fa 6.06、on 本皮肤 --app-tint 5.86。
       --app-1 #c026d3 对白字 4.71:1（够 4.5），但它的用途只有装饰，别当正文色使。 */
    --app-btn-a:#a21caf; --app-btn-b:#b52490;
    --app-tint:rgba(192,38,211,.10); --app-tint-2:rgba(192,38,211,.19);
    --app-bd:rgba(162,28,175,.30);   --app-glow:rgba(162,28,175,.32);
}

/* ==========================================================================
   2. 基础
   ========================================================================== */
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
    font-family:var(--s-sans);
    color:var(--s-text);
    background:var(--s-bg);
    line-height:1.6;
    overflow-x:hidden;                 /* body 永不横向滚动 */
    -webkit-font-smoothing:antialiased;
    -moz-osx-font-smoothing:grayscale;
}
h1,h2,h3,h4,p,ul,ol,figure{margin:0}
ul,ol{padding:0;list-style:none}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
b,strong{font-weight:700}
code{font-family:var(--s-mono);font-size:.92em}
:focus-visible{outline:2px solid var(--app-btn-a);outline-offset:3px;border-radius:6px}

/* 宽内容（表格/代码块/长清单）必须自己横向滚动，别把 body 顶宽 */
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* ==========================================================================
   3. 背景层：aurora 光晕 + 极淡网格（亮色，绝不整屏深色）
   直接在 <body> 里放：<div class="soft-bg" aria-hidden="true"></div>
                      <div class="soft-grid" aria-hidden="true"></div>
   ========================================================================== */
.soft-bg{
    position:fixed;inset:0;z-index:0;pointer-events:none;
    background:
        radial-gradient(46% 38% at 82% -4%,  rgba(99,102,241,.20), transparent 62%),
        radial-gradient(42% 34% at 8% 6%,    rgba(168,85,247,.15), transparent 64%),
        radial-gradient(50% 36% at 50% 42%,  rgba(56,189,248,.12), transparent 66%),
        radial-gradient(56% 40% at 18% 88%,  rgba(16,185,129,.12), transparent 64%),
        radial-gradient(48% 34% at 92% 74%,  rgba(244,114,182,.10), transparent 62%),
        linear-gradient(178deg,#fbfcff 0%,#f5f7fd 46%,#eef2fa 100%);
}
/* 详情页可选：让光晕跟随当前 App 主题色（在 [data-app] 容器下自动生效） */
[data-app] .soft-bg{
    background:
        radial-gradient(46% 38% at 82% -4%, var(--app-tint-2), transparent 62%),
        radial-gradient(42% 34% at 8% 6%,   var(--app-tint),   transparent 64%),
        radial-gradient(50% 36% at 50% 42%, rgba(56,189,248,.10), transparent 66%),
        radial-gradient(56% 40% at 16% 92%, var(--app-tint),   transparent 64%),
        linear-gradient(178deg,#fbfcff 0%,#f5f7fd 46%,#eef2fa 100%);
}
.soft-grid{
    position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.7;
    background-image:
        linear-gradient(rgba(27,44,80,.045) 1px,transparent 1px),
        linear-gradient(90deg,rgba(27,44,80,.045) 1px,transparent 1px);
    background-size:52px 52px;
    -webkit-mask-image:radial-gradient(78% 62% at 50% 0%,#000 0%,transparent 78%);
    mask-image:radial-gradient(78% 62% at 50% 0%,#000 0%,transparent 78%);
}

/* ==========================================================================
   4. 布局骨架
   ========================================================================== */
.page{position:relative;z-index:1;max-width:var(--s-maxw);margin:0 auto;padding:0 var(--s-pad) var(--s-8)}
.page--narrow{max-width:980px}
.sec{margin-top:var(--s-8)}
.sec--tight{margin-top:var(--s-7)}

/* —— 顶部返回条（详情页用；总览页也可放一个回首页的）—— */
.snav{
    position:sticky;top:0;z-index:20;
    display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;
    padding:12px 0;margin-bottom:4px;
}
/* 顶栏的渐隐底【铺满视口宽度】，不能画在 .snav 自己身上。
   ⚠ 2026-08-07 修：原来 background 直接写在 .snav 上，而 .snav 在 .page 里，
     .page 有 max-width 且居中 —— 于是这块底色只有内容区那么宽，左右两侧露出页面渐变，
     看起来就是「一块白矩形浮在带光晕的背景上」，边界清清楚楚。
     再加上 .92 的不透明度把 .soft-bg 顶部那几团 radial 光斑压掉，矩形内部纯白、
     外面却是淡紫淡蓝，对比更扎眼。
   做法：背景移到 ::before，用 left:50% + width:100vw + margin-left:-50vw 顶出容器。
   body 本来就是 overflow-x:hidden（见第 1 节），100vw 不会带出横向滚动条。 */
.snav::before{
    content:"";
    position:absolute;inset:0;
    left:50%;width:100vw;margin-left:-50vw;
    background:linear-gradient(180deg,rgba(250,251,255,.92),rgba(250,251,255,.62) 70%,transparent);
    -webkit-backdrop-filter:saturate(160%) blur(10px);
    backdrop-filter:saturate(160%) blur(10px);
    pointer-events:none;z-index:-1;
}
.snav__back{
    display:inline-flex;align-items:center;gap:7px;
    padding:8px 15px;border-radius:var(--s-r-pill);
    background:var(--s-surface);border:1px solid var(--s-border);
    box-shadow:var(--s-sh-1),var(--s-sh-in);
    font-size:13px;font-weight:650;color:var(--s-text-2);
    transition:transform .16s ease,color .16s,border-color .16s,box-shadow .16s;
}
.snav__back:hover{transform:translateX(-2px);color:var(--app-ink);border-color:var(--app-bd);box-shadow:var(--s-sh-2)}
.snav__crumb{font-size:12.5px;color:var(--s-text-3);letter-spacing:.3px}
.snav__crumb b{color:var(--s-text);font-weight:700}
.snav__spacer{margin-left:auto}

/* —— 亮 / 暗主题切换按钮（七个页面都有，一律放在 .snav__spacer 之后 = 顶栏最右）——————
   长相刻意照抄 .snav__back（同样的药丸 + 玻璃底 + hover 抬升），两颗并排像一套东西。
   ⚠ 图标与文案的切换【纯靠 CSS 的 [data-theme] 选择器】，不靠 JS 改 innerHTML：
     每个页面 <head> 里那段内联脚本在首屏渲染前就把 data-theme 打好了，所以按钮第一帧
     就是对的。若改成 JS 写文案，soft.js 在 body 末尾才执行 —— 会先画一个错的再被纠正，
     和「防闪烁」是同一个坑，白做。
   ⚠ 文案给的是【点下去会变成什么】（亮色下显示「暗色」），不是当前状态。
     aria-label 写在 <button> 上、这两个 <span> 一律 aria-hidden，读屏才不会念两遍。 */
.snav__theme{
    display:inline-flex;align-items:center;gap:7px;
    padding:8px 14px;border-radius:var(--s-r-pill);
    background:var(--s-surface);border:1px solid var(--s-border);
    box-shadow:var(--s-sh-1),var(--s-sh-in);
    font-family:inherit;font-size:13px;font-weight:650;color:var(--s-text-2);
    line-height:1;cursor:pointer;
    transition:transform .16s ease,color .16s,border-color .16s,box-shadow .16s,background .16s;
}
.snav__theme:hover{transform:translateY(-2px);color:var(--app-ink);border-color:var(--app-bd);box-shadow:var(--s-sh-2)}
.snav__theme:active{transform:none}
/* display:block 的理由同 .os-tags .os-tag svg：svg 是行内替换元素，底边坐在文本基线上，
   在这种小药丸里会比文字整体高出 3~4px。 */
.snav__theme svg{width:15px;height:15px;fill:currentColor;display:block;flex:0 0 auto}
/* 亮色（含没打 data-theme 的兜底态）：显示月亮 + 「暗色」 */
.snav__theme .i-sun,
.snav__theme .t-to-light{display:none}
[data-theme="dark"] .snav__theme .i-moon,
[data-theme="dark"] .snav__theme .t-to-dark{display:none}
[data-theme="dark"] .snav__theme .i-sun{display:block}
[data-theme="dark"] .snav__theme .t-to-light{display:inline}

/* —— 页内锚点导航（详情页功能分组跳转）—— */
.tocbar{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--s-3)}
.tocbar a{
    font-size:12.5px;font-weight:600;color:var(--s-text-2);
    padding:7px 14px;border-radius:var(--s-r-pill);
    background:var(--s-surface);border:1px solid var(--s-border);
    box-shadow:var(--s-sh-1);transition:transform .16s,color .16s,border-color .16s,background .16s;
}
.tocbar a:hover{transform:translateY(-2px);color:var(--app-ink);border-color:var(--app-bd);background:var(--s-white)}

/* ==========================================================================
   5. 排版组件
   ========================================================================== */
.eyebrow{
    display:inline-flex;align-items:center;gap:9px;
    font-family:var(--s-mono);font-size:11.5px;letter-spacing:2.4px;text-transform:uppercase;
    color:var(--app-ink);font-weight:600;
    padding:7px 15px;border-radius:var(--s-r-pill);
    background:var(--app-tint);border:1px solid var(--app-bd);
}
.eyebrow .dot{
    width:7px;height:7px;border-radius:50%;background:var(--app-1);
    box-shadow:0 0 0 4px var(--app-tint);
}
.h1{margin:var(--s-4) 0 0;font-size:clamp(34px,5.2vw,60px);font-weight:850;line-height:1.06;letter-spacing:-1px}
.h2{font-size:clamp(22px,2.6vw,30px);font-weight:820;letter-spacing:-.3px}
.h3{font-size:17px;font-weight:750;letter-spacing:-.1px}
/* 渐变文字：只用在标题的一小段，不铺满 */
.grad-text{
    background:linear-gradient(112deg,var(--app-btn-a) 0%,var(--app-1) 46%,var(--app-2) 100%);
    -webkit-background-clip:text;background-clip:text;color:transparent;
    /* 极端情况下（老浏览器不支持 background-clip:text）回退成实色，不会变成透明看不见 */
    -webkit-text-fill-color:transparent;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
    .grad-text{color:var(--app-ink);-webkit-text-fill-color:currentColor;background:none}
}
.lede{margin-top:var(--s-3);font-size:clamp(14.5px,1.5vw,16.5px);line-height:1.85;color:var(--s-text-2);max-width:56ch}
.muted{color:var(--s-text-3);font-size:13px;line-height:1.8}
.mono{font-family:var(--s-mono)}

/* —— 区块标题 —— */
.section-head{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap;margin-bottom:var(--s-4)}
.section-head .en{
    font-family:var(--s-mono);font-size:11px;letter-spacing:2.4px;text-transform:uppercase;color:var(--s-text-4);
}
.section-head .rule{flex:1;height:1px;min-width:40px;background:linear-gradient(90deg,var(--s-border-2),transparent)}
.section-head p{flex-basis:100%;margin-top:2px;font-size:13.5px;color:var(--s-text-2);line-height:1.8}

/* ==========================================================================
   5.5 Hero（总览页 / 详情页顶部通用）
   总览页用 .hero.hero--center（居中大标题）；详情页可用 .hero 两栏或直接沿用居中。
   .hero__halo 是纯装饰的呼吸光斑（reduce-motion 下自动停）。
   ========================================================================== */
.hero{position:relative;padding:var(--s-7) 0 var(--s-6)}
.hero--center{display:flex;flex-direction:column;align-items:center;text-align:center}
/* 总览页的 hero 收紧一档（2026-08-07）：它只有 eyebrow + 标题 + 一行副标题三样东西，
   而 .hero 那套 56/40 的上下留白是按详情页配的 —— 那边下面还挂着 stats / chips / 下载按钮，
   撑得起来。总览页照搬就在标题和「先看视频」之间空出一大片。
   ⚠ 只写在 .hero--center 上：全站【只有 software.html 用这个类】，六个详情页仍走原值，不受影响。
   h1 的上边距一并收：默认 var(--s-4)=20px 是给「eyebrow 下面紧跟大标题」用的，居中版这里再小一档更紧凑。 */
.hero--center{padding:var(--s-5) 0 var(--s-4)}
.hero--center .h1{margin-top:var(--s-3)}
.hero--center .lede{margin-left:auto;margin-right:auto;max-width:62ch}
.hero--center .h1{max-width:18ch}
.hero__halo{
    position:absolute;left:50%;top:-40px;width:min(760px,96vw);height:420px;
    transform:translateX(-50%);pointer-events:none;z-index:-1;
    background:
        radial-gradient(42% 52% at 30% 40%,rgba(99,102,241,.22),transparent 68%),
        radial-gradient(40% 50% at 72% 34%,rgba(6,182,212,.18),transparent 68%),
        radial-gradient(38% 46% at 54% 76%,rgba(16,185,129,.16),transparent 68%);
    filter:blur(22px);
    animation:haloDrift 18s ease-in-out infinite alternate;
}
/* 详情页：光晕跟着皮肤走（2026-08-06 补）——写法完全照抄上面 `[data-app] .soft-bg` 那条。
   ── 为什么必须补 ──────────────────────────────────────────────────────────
   上面 .hero__halo 那三层是【写死的】靛蓝 .22 / 青 .18 / 翠绿 .16。老三套皮肤
   （copy 蓝紫 / tool 靛青 / android 翠绿）恰好就是蓝、青、绿，所以这三页看不出毛病；
   但 2026-08-06 加的 idea 是琥珀、obsidian 是紫红，整页底色（.soft-bg 已经跟皮肤走了）
   是暖橙 / 品红，而 H1 正后方压着一团蓝青绿光斑 —— 顶屏第一眼就撞色。
   ── 选择器为什么是 `[data-app] .hero__halo` 而不是 `.hero__halo` 直接改 ──────
   跟 .soft-bg 同一个道理，也是同一个坑：总览页 /software.html 的 <html> 上【没有】
   data-app，六张卡各自在 <article class="app-card" data-app="…"> 上带皮肤，
   而它那颗 .hero__halo 在 <header class="hero"> 里，那个 header 排在六张卡所在的
   <section class="sec"> 之前、和卡片是平级的两支，两者没有父子关系 ——
   所以这条覆盖在总览页一条都命中不了（.hero__halo 不是任何 [data-app] 的后代），总览页的光晕
   保持原来那套中性蓝青绿三色，和它的中性 .soft-bg 配套。别改成无条件覆盖 .hero__halo，
   那样总览页的光斑会变成 :root 默认皮肤的单色靛蓝，和背景糊成一块。
   （六个详情页是 <html data-app="…">，所以它们全都命中。）
   ── 三层用哪三个变量、为什么要重排尺寸 ────────────────────────────────────
   变量按「淡 → 更淡 → 浓」映射：--app-tint-2（.18~.20）/ --app-tint（.10~.11）/ --app-glow（.32~.34）。
   麻烦在 --app-glow 是给按钮外发光用的，透明度 .32~.34 正好是原来第三层 .16 的两倍到两倍多，
   原样套上去新页面的光斑就会比老页面【重一倍】。CSS 变量自带 alpha、没法在这里再乘一个系数，
   所以改用面积配平：把最浓的 --app-glow 那层从 38%×46% 缩到 24%×30%（面积只剩 41%），
   同时把前两层放大一点补回被稀释的量。按「alpha × 面积」粗算总墨量（旧 = 1120）：
     copy 987（88%，见下：copy.html 其实用不上，算出来备查）/ tool 1024（91%）/
     android 1052（94%）/ idea 1038（93%）/ vscode 998（89%）/ obsidian 998（89%）
   六套全部【比旧值轻】，没有哪一页的光斑比老页面更重 —— 这是本次的硬要求。
   三个圆心（30% 40% / 72% 34% / 54% 76%）刻意原样保留，构图不动，只动色与大小。
   ⚠ 到底哪几页会命中（grep 过 .hero__halo 的实际分布，别凭印象）：
     tool / android / idea / vscode / obsidian 五个详情页命中（它们都是 <html data-app="…">）；
     copy.html 用的是页面私有的 .dhero 两栏 Hero，整份文件里【没有 .hero__halo】，这条对它无效；
     总览页 software.html 的 data-app 挂在两个 <section class="sec"> 里的 <article class="app-card"> 上，
     和 <header class="hero"> 是平级分支、没有祖先关系，所以总览页的光斑保持原来的中性蓝青绿。
   ⚠ 副作用要知道：命中的老两套光晕从「靛蓝+青+绿」三色变成本皮肤的单色三层（tool 全青、
     android 全绿）。这是「跟皮肤走」的必然结果，不是漏改；深浅靠三层不同的
     alpha 和上面 blur(22px) 拉开，不会糊成一个纯色块。 */
[data-app] .hero__halo{
    background:
        radial-gradient(46% 56% at 30% 40%,var(--app-tint-2),transparent 68%),
        radial-gradient(48% 58% at 72% 34%,var(--app-tint),  transparent 70%),
        radial-gradient(24% 30% at 54% 76%,var(--app-glow),  transparent 72%);
}
@keyframes haloDrift{
    0%  {transform:translateX(-50%) translateY(0)     scale(1)}
    100%{transform:translateX(-50%) translateY(-16px) scale(1.06)}
}
.hero__stats{margin-top:var(--s-5);justify-content:center}
.hero .chips{margin-top:var(--s-4);justify-content:center}
/* 往下滚的小提示 */
.scroll-cue{
    margin-top:var(--s-5);display:inline-flex;flex-direction:column;align-items:center;gap:4px;
    font-family:var(--s-mono);font-size:11px;letter-spacing:2px;color:var(--s-text-4);
}
.scroll-cue i{
    width:1px;height:26px;background:linear-gradient(180deg,var(--s-border-2),transparent);
    animation:cueDrop 1.8s ease-in-out infinite;transform-origin:top;
}
@keyframes cueDrop{0%,100%{transform:scaleY(.4);opacity:.4}50%{transform:scaleY(1);opacity:1}}

/* ==========================================================================
   6. 玻璃卡片
   ========================================================================== */
.glass-card{
    position:relative;
    background:var(--s-surface);
    border:1px solid var(--s-border);
    border-radius:var(--s-r-lg);
    box-shadow:var(--s-sh-2),var(--s-sh-in);
    -webkit-backdrop-filter:saturate(170%) blur(16px);
    backdrop-filter:saturate(170%) blur(16px);
    padding:var(--s-5);
    overflow:hidden;
}
.glass-card--flat{box-shadow:var(--s-sh-1),var(--s-sh-in);background:var(--s-surface-2)}
.glass-card--pad-sm{padding:var(--s-4)}
/* 卡片右上角的品牌色柔光（纯装饰） */
.glass-card--halo::before{
    content:'';position:absolute;right:-70px;top:-90px;width:260px;height:260px;border-radius:50%;
    background:radial-gradient(circle,var(--app-tint-2),transparent 70%);pointer-events:none;
}
/* hover 抬升 + 光泽扫过（.hoverable 才开，静态卡片不动） */
.hoverable{transition:transform .22s cubic-bezier(.2,.7,.2,1),box-shadow .22s,border-color .22s}
.hoverable::after{
    content:'';position:absolute;inset:0;pointer-events:none;border-radius:inherit;
    background:linear-gradient(115deg,transparent 32%,rgba(255,255,255,.55) 50%,transparent 68%);
    transform:translateX(-130%);transition:transform .75s cubic-bezier(.3,.7,.3,1);
}
.hoverable:hover{transform:translateY(-5px);box-shadow:var(--s-sh-3),var(--s-sh-in);border-color:var(--app-bd)}
.hoverable:hover::after{transform:translateX(130%)}

/* ==========================================================================
   7. 按钮
   .btn            基础
   .btn--primary   品牌渐变实心（白字）
   .btn--ghost     次级（浅玻璃）
   .btn--lg / --sm 尺寸
   禁用一律用 aria-disabled="true"（不是 disabled 属性）—— soft.js 的 bindDownload 就是这么切的
   ========================================================================== */
.btn{
    position:relative;overflow:hidden;
    display:inline-flex;align-items:center;justify-content:center;gap:9px;
    padding:12px 22px;border-radius:var(--s-r-sm);
    font-family:inherit;font-size:14px;font-weight:700;letter-spacing:.2px;
    border:1px solid transparent;cursor:pointer;white-space:nowrap;
    transition:transform .18s cubic-bezier(.2,.7,.2,1),box-shadow .18s,background .18s,color .18s,border-color .18s;
}
.btn--lg{padding:15px 26px;font-size:15px;border-radius:var(--s-r)}
.btn--sm{padding:9px 16px;font-size:13px;border-radius:var(--s-r-xs)}
.btn--block{display:flex;width:100%}

.btn--primary{
    color:#fff;
    background:linear-gradient(122deg,var(--app-btn-a) 0%,var(--app-btn-b) 100%);
    box-shadow:0 10px 26px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.26);
}
.btn--primary::after{
    content:'';position:absolute;inset:0;
    background:linear-gradient(118deg,transparent 30%,rgba(255,255,255,.30) 50%,transparent 70%);
    transform:translateX(-130%);transition:transform .65s cubic-bezier(.3,.7,.3,1);
}
.btn--primary:hover{transform:translateY(-3px);box-shadow:0 16px 36px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.3)}
.btn--primary:hover::after{transform:translateX(130%)}
.btn--primary:active{transform:translateY(-1px)}

.btn--ghost{
    color:var(--s-text);
    background:var(--s-surface);
    border-color:var(--s-border-2);
    box-shadow:var(--s-sh-1),var(--s-sh-in);
}
.btn--ghost:hover{transform:translateY(-3px);color:var(--app-ink);border-color:var(--app-bd);background:var(--s-white);box-shadow:var(--s-sh-2)}

/* 禁用态：置灰 + 不可点。属性选择器特异性高于 .btn--primary，稳压一头 */
.btn[aria-disabled="true"],
.dl-btn[aria-disabled="true"]{
    background:rgba(27,36,54,.05)!important;
    color:var(--s-text-3)!important;
    border-color:var(--s-border)!important;
    box-shadow:none!important;
    cursor:not-allowed;pointer-events:none;transform:none!important;animation:none!important;
}
.btn[aria-disabled="true"]::after,.dl-btn[aria-disabled="true"]::after{display:none}
.dl-btn[aria-disabled="true"] svg{fill:var(--s-text-3)}

/* ==========================================================================
   8. 下载按钮（带 OS 图标 + 两行文案）
   结构见 soft.js 的 SoftUI.osIcon()；点亮/置灰走 SoftUI.bindDownload()
   ========================================================================== */
.dl-row{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.dl-btn{
    position:relative;overflow:hidden;
    flex:1 1 220px;min-width:200px;
    display:flex;align-items:center;gap:14px;
    padding:16px 22px;border-radius:var(--s-r);
    color:#fff;cursor:pointer;
    background:linear-gradient(122deg,var(--app-btn-a) 0%,var(--app-btn-b) 100%);
    border:1px solid rgba(255,255,255,.20);
    box-shadow:0 12px 30px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.26);
    transition:transform .18s cubic-bezier(.2,.7,.2,1),box-shadow .18s;
}
.dl-btn::after{
    content:'';position:absolute;inset:0;
    background:linear-gradient(118deg,transparent 30%,rgba(255,255,255,.28) 50%,transparent 70%);
    transform:translateX(-130%);transition:transform .65s cubic-bezier(.3,.7,.3,1);
}
.dl-btn:hover{transform:translateY(-4px);box-shadow:0 18px 42px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.3)}
.dl-btn:hover::after{transform:translateX(130%)}
.dl-btn .os-ic{width:30px;height:30px;flex:0 0 auto;display:flex;align-items:center;justify-content:center}
.dl-btn .os-ic svg{width:26px;height:26px;fill:#fff}
.dl-btn .os-txt{display:flex;flex-direction:column;line-height:1.3;text-align:left;min-width:0}
.dl-btn .os-txt b{font-size:15.5px;font-weight:760}
.dl-btn .os-txt span{font-size:11.5px;opacity:.9;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 当前系统的那颗按钮：柔和呼吸光圈（reduce-motion 下自动停） */
.dl-btn.is-current{animation:softPulse 2.8s ease-in-out infinite}
@keyframes softPulse{
    0%,100%{box-shadow:0 12px 30px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.26)}
    50%    {box-shadow:0 16px 42px var(--app-glow),0 0 0 4px var(--app-tint-2),inset 0 1px 0 rgba(255,255,255,.3)}
}
.dl-sub{
    margin-top:12px;display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;
    font-size:12.5px;color:var(--s-text-3);line-height:1.75;
}
.dl-sub a{color:var(--app-ink);font-weight:600}
.dl-sub a:hover{text-decoration:underline}
.dl-sub .sep{opacity:.45}
.dl-sub b{color:var(--s-text-2)}

/* ==========================================================================
   9. chip / badge / stat
   ========================================================================== */
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chip{
    display:inline-flex;align-items:center;gap:6px;
    font-size:12.5px;font-weight:600;color:var(--s-text-2);
    padding:6px 13px;border-radius:var(--s-r-pill);
    background:var(--s-surface);border:1px solid var(--s-border);
    box-shadow:var(--s-sh-1);
}
.chip b{color:var(--s-text);font-weight:700}
.chip--accent{color:var(--app-ink);background:var(--app-tint);border-color:var(--app-bd);box-shadow:none}

.badge{
    display:inline-flex;align-items:center;gap:5px;
    font-family:var(--s-mono);font-size:11px;font-weight:600;letter-spacing:.6px;
    color:var(--app-ink);padding:4px 10px;border-radius:var(--s-r-pill);
    background:var(--app-tint);border:1px solid var(--app-bd);
}
.badge--ok{color:var(--s-ok);background:rgba(15,122,79,.10);border-color:rgba(15,122,79,.26)}
.badge--muted{color:var(--s-text-3);background:rgba(27,36,54,.05);border-color:var(--s-border)}

/* 数据条：版本 / 大小 / 技术栈 这类元信息 */
.stats{display:flex;flex-wrap:wrap;gap:8px}
.stat{
    display:inline-flex;align-items:center;gap:7px;
    font-size:12px;color:var(--s-text-3);font-weight:600;
    padding:6px 12px;border-radius:var(--s-r-xs);
    background:var(--s-surface-3);border:1px solid var(--s-border);
}
.stat b{color:var(--s-text);font-weight:750;font-family:var(--s-mono);font-size:12.5px}
.stat--big{flex-direction:column;align-items:flex-start;gap:2px;padding:12px 16px;border-radius:var(--s-r-sm)}
.stat--big b{font-size:20px;letter-spacing:-.5px;background:linear-gradient(120deg,var(--app-btn-a),var(--app-2));
    -webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent}
.stat--big span{font-size:11.5px;font-weight:600;color:var(--s-text-3)}

/* 提示条 */
.notice{
    display:flex;gap:12px;align-items:flex-start;
    padding:14px 18px;border-radius:var(--s-r-sm);
    font-size:13px;line-height:1.8;color:var(--s-text-2);
    background:var(--s-surface-3);border:1px solid var(--s-border);
}
.notice__ic{flex:0 0 auto;font-size:16px;line-height:1.4}
.notice b{color:var(--s-text)}
.notice--warn{color:#6b4108;background:var(--s-warn-bg);border-color:var(--s-warn-bd)}
.notice--warn b{color:var(--s-warn)}

/* ==========================================================================
   10. .app-card —— 总览页的三张大卡
   结构（stretched-link 模式：整卡可点进详情，下载按钮不冒泡）：
   <article class="app-card glass-card glass-card--halo hoverable" data-app="copy">
     <div class="app-card__body">                                          ← 图标 / 标题 / 截图 / 简介 / chips
       <a class="app-card__link" href="/software/copy.html">…卡片标题…</a> ← ::after 铺满整卡
       …
     </div>
     <div class="app-card__foot">                                          ← 平台标 + 按钮（+ 可选的一行小字）
       <div class="app-card__actions">…下载按钮（z-index 高于 ::after，点它不进详情）…</div>
     </div>
   </article>
   ========================================================================== */
.app-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-4);align-items:stretch}
.app-card{display:flex;flex-direction:column;gap:var(--s-3);padding:var(--s-5);isolation:isolate}
/* __body = 卡片里 foot 以上的全部内容，只为下面那条 subgrid 规则而存在（本身就是原来的
   flex column，视觉上和没包一层完全一样）。 */
.app-card__body{display:flex;flex-direction:column;gap:var(--s-3);min-width:0}
/* 整卡可点：链接的 ::after 铺满卡片（保留键盘可聚焦，且是真 <a> 语义） */
.app-card__link{position:static}
.app-card__link::after{content:'';position:absolute;inset:0;z-index:1;border-radius:inherit}
.app-card__link:focus-visible{outline:none}
.app-card__link:focus-visible::after{outline:2px solid var(--app-btn-a);outline-offset:-3px}

.app-card__logo{
    width:74px;height:74px;flex:0 0 auto;border-radius:20px;
    display:flex;align-items:center;justify-content:center;font-size:36px;line-height:1;
    background:linear-gradient(142deg,var(--app-1),var(--app-btn-b));
    box-shadow:0 12px 28px var(--app-glow),inset 0 1px 0 rgba(255,255,255,.35);
}
/* 图标底板里放【内联 SVG】时的尺寸与颜色（放 emoji 的老卡片走上面的 font-size:36px，不受影响）。
   为什么要有这条：三个插件（IDEA / VS Code / Obsidian）没有一个 emoji 能代表它们，只能用
   SoftUI.osIcon('idea'|'vscode'|'obsidian') 填内联 SVG；而 <svg> 不写尺寸时浏览器按
   300×150 的默认尺寸算、fill 又是黑的，直接糊在彩色底板上 = 一个黑方块。
   38px 是照着 emoji 36px 的视觉体量定的（SVG 的 path 一般不铺满画布，同尺寸下看着略小）。 */
.app-card__logo svg{width:38px;height:38px;fill:#fff;display:block}
.app-card__title{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:19px;font-weight:820;letter-spacing:-.2px}
.app-card__tag{font-size:13.5px;font-weight:700;color:var(--app-ink)}
/* ⚠ 别给 .app-card__desc 加 flex:1 —— 三张卡等高（align-items:stretch），多出来的空间会全被
   简介框吃掉，于是正文和下面的 chips 之间裂出一两百像素的空洞，卡片看着像没加载完。
   把余量交给 .app-card__foot 的 margin-top:auto：chips 紧跟正文，空隙落在按钮区上方 = 像留白。
   （下面那段 subgrid 生效后，余量改由 body 行的 stretch 承担，落点还是 chips 下方，结论不变。）*/
.app-card__desc{font-size:13.5px;line-height:1.85;color:var(--s-text-2)}
.app-card__foot{margin-top:auto;display:flex;flex-direction:column;gap:var(--s-3)}
/* 下载按钮所在层：抬到 stretched-link 的 ::after 之上，点它不会进详情页 */
.app-card__actions{position:relative;z-index:2;display:flex;flex-direction:column;gap:10px}
.app-card__actions>*{width:100%}
/* 卡内的下载按钮比详情页的窄一号，三列并排也不撑破 */
.app-card .dl-btn{flex:0 0 auto;min-width:0;padding:13px 18px;border-radius:var(--s-r-sm)}
.app-card .dl-btn .os-txt b{font-size:14.5px}
/* 卡片底部的一行小字（安卓签名提示这类），在 stretched-link 之上以便选中文字 */
.app-card__note{position:relative;z-index:2;font-size:11.5px;line-height:1.7;color:var(--s-text-3)}
.app-card__note b{color:var(--s-warn)}

/* ⚠ 同一行三张卡的【下载按钮必须落在同一条水平线上】（2026-08-06 史锦泽的要求）。
   在这条规则之前，底部区是靠上面那句 .app-card__foot{margin-top:auto} 贴到卡底的，于是按钮
   的高低取决于「它下面还有多少东西」—— 安卓卡的按钮下面多一条签名提示小字（73px），按钮就
   被顶高一截，三颗按钮参差不齐。
   做法：卡片拆成 .app-card__body + .app-card__foot 两行，整张卡 grid-template-rows:subgrid
   映射到 .app-grid 的两行 → 同排卡片的 body 行等高 → foot 一律从同一条线开始 → 按钮同排。
   三条不能动的地方：
   1. ⚠ 这段必须【写在 .app-card__foot{margin-top:auto} 之后】。两条选择器特异性一样(0,1,0)，
      靠源码顺序分胜负；放到前面去，margin-top:auto 会把归零覆盖回来 —— 现象是 subgrid 明明
      生效（display:grid、行也等高）、按钮却照旧不齐，很难看出是层叠顺序的问题（真踩过）。
   2. ⚠ margin-top 必须归零：grid item 上的 margin-top:auto 会让 foot 在被拉高的行里【贴底】，
      按钮又散开，等于白改。
   3. 为什么不干脆整卡做 6 行 subgrid（图标/标题/截图/简介/chips/foot 逐行对齐）：那样连简介
      都会被拉到等高，短简介下面裂出一大块空洞 —— 和上面「别给 .app-card__desc 加 flex:1」
      是同一个坑。只把 foot 这一条线对齐就够了。
   @supports 兜底：老浏览器整段不生效，退回原来的 flex + margin-top:auto，和改动前完全一致。
   900px 断点降单列后本段依然成立：每张卡各自占两行，互不影响，不用另写重置。 */
@supports (grid-template-rows:subgrid){
    .app-grid{grid-template-rows:auto auto}
    .app-card{display:grid;grid-template-rows:subgrid;grid-row:span 2}
    .app-card__foot{margin-top:0}
}
.app-card__more{
    display:inline-flex;align-items:center;gap:6px;
    font-size:13px;font-weight:700;color:var(--app-ink);
}
.app-card__more .arr{transition:transform .2s cubic-bezier(.2,.7,.2,1)}
.app-card:hover .app-card__more .arr{transform:translateX(4px)}
/* 平台徽标行 */
/* flex-wrap 不是可选项，是【必须】：这一行外面套的是 .glass-card（overflow:hidden），
   body 又是 overflow-x:hidden，所以不换行的话，超出去的药丸既【看不见】也【滚不出来】——
   不是难看，是内容直接消失。375 / 414px 的手机上，只要标签文案长一点（或标签多一两个）
   就会触发。row-gap 单写一遍是因为上面的 gap:7px 在换行后同时管行间距，这里显式点明，
   免得以后有人只改 gap 的横向值时把行距一起带歪。 */
.os-tags{display:flex;flex-wrap:wrap;gap:7px;row-gap:7px;align-items:center}
.os-tags .os-tag{
    display:inline-flex;align-items:center;gap:5px;
    font-size:11.5px;font-weight:650;color:var(--s-text-3);
    padding:4px 10px;border-radius:var(--s-r-pill);
    background:var(--s-surface-3);border:1px solid var(--s-border);
}
/* display:block 不是可有可无：svg 默认是行内替换元素，底边坐在文本基线上，
   在 11.5px 的小药丸里会比文字高出 3~4px（实测图标光心 6.2px / 文字光心 10.8px）。
   .app-card__logo svg 也是同样的写法。目前唯一的消费点是 vscode.html 那颗「VS Code」标签。 */
.os-tags .os-tag svg{width:13px;height:13px;fill:currentColor;display:block}

/* —— 「插件卡」为什么【没有】新样式（.plug-card / .plug-grid 一律不要新增）——————————
   结论：三个 IDE / 笔记插件的卡片【直接复用 .app-card + .app-grid】，一行 CSS 都不用加。
   逐条比过一遍，插件卡想要的东西这里全都现成：
     · 图标底板 .app-card__logo    → 皮肤色自动跟着卡上的 data-app 走，SVG 尺寸见上面那条新规则
     · 标题 + 版本徽标 .app-card__title / .badge
     · 一句话卖点 .app-card__tag、简介 .app-card__desc、要点 .chips
     · 宿主环境标签 .os-tags > .os-tag → 本来就只是「一排小药丸」，短词随便写，长文案靠
       .os-tags 的 flex-wrap 兜底（见上面那条规则），不必为此另起一套
     · 底部按钮区 .app-card__foot / .app-card__actions（z-index:2，点它不会触发整卡跳转）
     · 截图位 .shot-wrap 是【可选】的：插件没有截图就整块不写，.app-card 是 flex column，
       少一个子元素不会塌，.app-card__foot 的 margin-top:auto 照样把按钮压到底
   为什么坚决不加修饰类：踩坑记录里已有实例 —— 曾经加过 .app-card--tool{grid-template-columns:…}
   收窄卡片，桌面对了，但 900px 断点里的 .app-card{grid-template-columns:1fr}（0,1,0）
   打不过 .app-card.app-card--tool（0,2,0），窄屏不再变单列、整排挤成一条。
   凡是修饰类覆盖了已有响应式属性，就必须在 900 / 640 两个断点里把它再声明一遍 —— 能不欠这笔
   账就别欠。想要「更轻」的卡片，用现成的 .app-card__more（一行文字链 + hover 右移箭头）
   替掉 .btn--ghost 那颗实心按钮即可，它在 CSS 里早就写好了、目前一处没用。
   插件没有后端发布记录（/mysoft 下只有 clip / tool / version 三条端点，插件一条都没有），
   所以卡里那颗下载按钮要么写真实存在的静态直链，要么直接静态置灰：
   <a class="dl-btn" href="javascript:void 0" aria-disabled="true">，副文案写「暂未发布」。
   置灰靠的是属性选择器 [aria-disabled="true"]，写原生 disabled 属性【无效】（<a> 上也没这属性）。*/

/* ==========================================================================
   11. .shot —— 图位（后面往里接真图的地方，见 software/README.md）
   替换方式：把 .shot 内的 <div class="shot__ph">…</div> 整个换成
             <img class="shot__img" src="/img/software/copy/1-multi-type.png" alt="…">
   比例：默认 16:10；.shot--16x9 / .shot--4x3 / .shot--9x16（手机竖屏）
   没有图时占位也撑住布局，页面不会看起来是坏的。
   ========================================================================== */
.shot{
    position:relative;width:100%;aspect-ratio:16/10;
    border-radius:var(--s-r);overflow:hidden;
    background:linear-gradient(140deg,rgba(255,255,255,.9),var(--app-tint));
    border:1px solid var(--s-border);
    box-shadow:var(--s-sh-2),var(--s-sh-in);
    transition:transform .25s cubic-bezier(.2,.7,.2,1),box-shadow .25s;
}
.shot--16x9{aspect-ratio:16/9}
.shot--4x3 {aspect-ratio:4/3}
.shot--9x16{aspect-ratio:9/16;max-width:340px}          /* 手机竖屏截图 */
.shot--wide{aspect-ratio:21/9}
.shot:hover{transform:translateY(-4px);box-shadow:var(--s-sh-3),var(--s-sh-in)}
.shot__img{width:100%;height:100%;object-fit:cover;object-position:top center}
.shot__ph{
    position:absolute;inset:0;display:flex;flex-direction:column;
    align-items:center;justify-content:center;gap:8px;text-align:center;padding:20px;
    background-image:
        linear-gradient(rgba(27,44,80,.05) 1px,transparent 1px),
        linear-gradient(90deg,rgba(27,44,80,.05) 1px,transparent 1px);
    background-size:24px 24px;
}
.shot__ph-ic{font-size:26px;opacity:.55;line-height:1}
.shot__ph b{font-size:13.5px;font-weight:700;color:var(--s-text-2);max-width:26ch;line-height:1.6}
.shot__ph-size{font-family:var(--s-mono);font-size:11px;letter-spacing:.6px;color:var(--s-text-4)}
/* 图上角落的平台角标（mac/Windows 截图混用时标一下） */
.shot__corner{
    position:absolute;right:10px;top:10px;z-index:2;
    font-family:var(--s-mono);font-size:10.5px;letter-spacing:.6px;font-weight:600;
    color:var(--s-text-3);padding:4px 9px;border-radius:var(--s-r-pill);
    background:rgba(255,255,255,.82);border:1px solid var(--s-border);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
}

/* ==========================================================================
   12. .feature-row —— 详情页功能条（一条一条列功能）
   结构：
   <div class="feature-list">
     <article class="feature-row reveal">
       <div class="feature-row__text">
         <span class="feature-row__no">01</span>
         <h3 class="feature-row__title">标题</h3>
         <p class="feature-row__desc">说明</p>
         <div class="chips">…</div>
       </div>
       <div class="feature-row__shot"><div class="shot">…</div></div>
     </article>
     …
   </div>
   左右交替：同一个 .feature-list 内 nth-of-type(even) 自动翻面；
             跨分组要保证全局交替时，调 SoftUI.zebra() 由 JS 统一打 .feature-row--flip。
   ========================================================================== */
.feature-list{display:flex;flex-direction:column;gap:var(--s-7)}
.feature-row{display:grid;grid-template-columns:1fr 1.06fr;gap:var(--s-6);align-items:center}
.feature-row__text{min-width:0}
.feature-row__shot{min-width:0}
/* 偶数条左右对调 —— 只在这一条【没有】手写/JS 指定翻面时才生效。
   ⚠ 这两个 :not() 不能删：本规则特异性是 (0,4,0)（.feature-list + .feature-row + :nth-of-type + .feature-row__text），
   比下面 .feature-row.feature-row--flip>.feature-row__text 的 (0,3,0) 还高，写在前面也照样赢。
   于是「分组内的奇偶」会盖掉页面手写的 --noflip/--flip：某一组若以 flip 开头，parity 就和手写意图对不上，
   实测 tool.html 曾连续 6 条都排在同一侧、copy.html 出现两处 RRR。
   用 :not() 把两套机制分开（没声明就自动交替，声明了就完全听声明），比拼特异性稳妥。 */
.feature-list>.feature-row:nth-of-type(even):not(.feature-row--flip):not(.feature-row--noflip) .feature-row__text{order:2}
.feature-list>.feature-row:nth-of-type(even):not(.feature-row--flip):not(.feature-row--noflip) .feature-row__shot{order:1}
/* JS(SoftUI.zebra) 或页面手写的强制翻面：上面已用 :not() 让路，这里说了算 */
.feature-row.feature-row--flip>.feature-row__text{order:2}
.feature-row.feature-row--flip>.feature-row__shot{order:1}
.feature-row.feature-row--noflip>.feature-row__text{order:1}
.feature-row.feature-row--noflip>.feature-row__shot{order:2}

.feature-row__no{
    display:inline-flex;align-items:center;justify-content:center;
    min-width:36px;height:28px;padding:0 10px;border-radius:var(--s-r-pill);
    font-family:var(--s-mono);font-size:12px;font-weight:700;letter-spacing:1px;
    color:#fff;background:linear-gradient(122deg,var(--app-btn-a),var(--app-btn-b));
    box-shadow:0 6px 16px var(--app-glow);
}
.feature-row__title{margin-top:var(--s-2);font-size:clamp(19px,2.1vw,24px);font-weight:820;letter-spacing:-.4px;line-height:1.3}
.feature-row__desc{margin-top:var(--s-2);font-size:14.5px;line-height:1.9;color:var(--s-text-2)}
.feature-row__desc b{color:var(--s-text)}
.feature-row .chips{margin-top:var(--s-3)}
/* 通栏大图的功能条（最有画面感的那两三条用它） */
.feature-row--wide{grid-template-columns:1fr}
.feature-row--wide .feature-row__text{max-width:70ch}
.feature-row--wide .feature-row__shot{margin-top:var(--s-4)}
.feature-row--wide .feature-row__text,
.feature-row--wide .feature-row__shot{order:unset!important}

/* 功能分组小标题 */
.feature-group{margin-top:var(--s-8)}
.feature-group__head{
    display:flex;align-items:center;gap:var(--s-3);margin-bottom:var(--s-5);
    scroll-margin-top:84px;                     /* sticky 顶栏不挡锚点 */
}
.feature-group__head .n{
    display:inline-flex;align-items:center;justify-content:center;
    width:32px;height:32px;border-radius:10px;font-size:16px;
    background:var(--app-tint);border:1px solid var(--app-bd);
}
.feature-group__head h2{font-size:20px;font-weight:820;letter-spacing:-.2px}
.feature-group__head .cnt{font-family:var(--s-mono);font-size:11.5px;color:var(--s-text-4);letter-spacing:1px}
.feature-group__head .rule{flex:1;height:1px;background:linear-gradient(90deg,var(--s-border-2),transparent)}

/* 简明功能网格（不配图的辅助清单） */
.mini-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:10px}
.mini{
    display:flex;align-items:center;gap:9px;
    font-size:12.5px;font-weight:600;color:var(--s-text-2);
    padding:11px 14px;border-radius:var(--s-r-sm);
    background:var(--s-surface);border:1px solid var(--s-border);box-shadow:var(--s-sh-1);
    transition:transform .16s,border-color .16s,color .16s;
}
.mini:hover{transform:translateY(-2px);border-color:var(--app-bd);color:var(--s-text)}

/* 有说明的功能卡（不配图，但比 .mini 能多说两句） */
.fact-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(255px,1fr));gap:var(--s-3)}
.fact{
    display:flex;flex-direction:column;gap:7px;
    padding:var(--s-4);border-radius:var(--s-r-sm);
    background:var(--s-surface);border:1px solid var(--s-border);box-shadow:var(--s-sh-1);
    transition:transform .18s cubic-bezier(.2,.7,.2,1),border-color .18s,box-shadow .18s;
}
.fact:hover{transform:translateY(-3px);border-color:var(--app-bd);box-shadow:var(--s-sh-2)}
.fact__ic{
    display:inline-flex;align-items:center;justify-content:center;
    width:34px;height:34px;border-radius:10px;font-size:17px;
    background:var(--app-tint);border:1px solid var(--app-bd);
}
.fact__t{font-size:14.5px;font-weight:800;letter-spacing:-.2px;line-height:1.45}
.fact__d{font-size:12.8px;line-height:1.85;color:var(--s-text-3)}
.fact__d b{color:var(--s-text-2);font-weight:700}

/* ==========================================================================
   12.5 图的补充件：真图接上来之后才需要的几个东西
   —— .shot--fit   截图【完整显示】，不裁。默认的 object-fit:cover 会把截图上
                   那些红色批注（「左侧是历史剪切板」之类）连同边缘一起切掉，
                   凡是带批注、或者本身就不是 16:10 的窗口截图，一律用它。
   —— .shot--free  连固定比例都不要，高度随图走。窄长/近正方的设置窗口用它，
                   免得为了凑比例在上下留出两条大白边。
   —— .shot__cap   图注：说明这张图在演示什么，写在图下面，不压在图上。
   —— .shot-grid   一组图并排（SSH 那种一个功能好几张图的场景）。
   —— .shot.is-zoomable  鼠标变放大镜，点开看大图（soft.js 的灯箱）。
   ========================================================================== */
.shot--fit{background:linear-gradient(150deg,#fff,var(--app-tint))}
.shot--fit .shot__img{object-fit:contain;object-position:center;padding:10px;box-sizing:border-box}
.shot--free{aspect-ratio:auto!important}
.shot--free .shot__img{height:auto;display:block;object-fit:contain}

.shot-wrap{display:flex;flex-direction:column;gap:9px;min-width:0}
.shot__cap{
    display:flex;align-items:flex-start;gap:7px;
    font-size:12.3px;line-height:1.75;color:var(--s-text-3);padding:0 2px;
}
.shot__cap::before{
    content:'';flex:none;width:3px;align-self:stretch;border-radius:2px;
    background:linear-gradient(180deg,var(--app-btn-a),var(--app-btn-b));opacity:.55;
}
.shot__cap b{color:var(--s-text-2);font-weight:700}

.shot-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-3)}
.shot-grid--3{grid-template-columns:repeat(3,1fr)}
/* 交流群二维码（总览页「加群一起聊」）：两张码并排居中，别撑满 1180 宽——码太大反而不好扫 */
.join-grid{max-width:620px;margin-inline:auto}

/* 可点开看大图 */
.shot.is-zoomable{cursor:zoom-in}
.shot.is-zoomable::after{
    content:'⤢';position:absolute;right:10px;bottom:10px;z-index:2;
    width:26px;height:26px;display:flex;align-items:center;justify-content:center;
    font-size:13px;color:var(--s-text-3);border-radius:8px;
    background:rgba(255,255,255,.86);border:1px solid var(--s-border);
    -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
    opacity:0;transform:translateY(4px);transition:opacity .2s,transform .2s;
}
.shot.is-zoomable:hover::after{opacity:1;transform:none}

/* 灯箱：点图放大。DOM 由 soft.js 创建，页面里不用写。 */
.lb{
    position:fixed;inset:0;z-index:999;display:none;
    align-items:center;justify-content:center;padding:32px;
    background:rgba(16,24,44,.72);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
    opacity:0;transition:opacity .22s;
}
.lb.is-open{display:flex}
.lb.is-shown{opacity:1}
.lb__img{
    max-width:100%;max-height:100%;border-radius:var(--s-r);
    background:#fff;box-shadow:0 40px 90px rgba(10,18,38,.5);
}
.lb__x{
    position:absolute;right:20px;top:20px;width:38px;height:38px;border:0;cursor:pointer;
    border-radius:50%;font-size:19px;line-height:1;color:#fff;
    background:rgba(255,255,255,.16);transition:background .18s;
}
.lb__x:hover{background:rgba(255,255,255,.3)}
.lb__cap{
    position:absolute;left:0;right:0;bottom:16px;text-align:center;
    font-size:12.5px;color:rgba(255,255,255,.82);padding:0 40px;
}

/* ==========================================================================
   12.6 视频块（剪贴板详情页的介绍片）
   自带封面图、不自动播、不循环，点了才加载 —— preload=none，
   不能让一段 2MB 的视频拖慢首屏。
   ========================================================================== */
.video-card{
    position:relative;border-radius:var(--s-r);overflow:hidden;
    border:1px solid var(--s-border);box-shadow:var(--s-sh-3),var(--s-sh-in);
    background:#0d1424;aspect-ratio:16/9;
}
.video-card video{width:100%;height:100%;display:block;object-fit:cover;background:#0d1424}
.video-card__meta{
    display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;
    margin-top:var(--s-3);font-size:12.5px;color:var(--s-text-3);
}
.video-card__meta .k{
    font-family:var(--s-mono);font-size:11px;letter-spacing:.8px;
    padding:3px 9px;border-radius:var(--s-r-pill);
    background:var(--app-tint);border:1px solid var(--app-bd);color:var(--app-ink);font-weight:700;
}

/* ==========================================================================
   13. 页脚
   ========================================================================== */
.footer{
    position:relative;z-index:1;max-width:var(--s-maxw);margin:var(--s-8) auto 0;
    padding:var(--s-5) var(--s-pad) var(--s-6);
    border-top:1px solid var(--s-border);
    text-align:center;color:var(--s-text-3);font-size:12.5px;line-height:2;
}
.footer__brand{
    font-family:var(--s-mono);font-size:12px;letter-spacing:3px;font-weight:600;color:var(--s-text-2);
}
.footer__links{display:flex;justify-content:center;flex-wrap:wrap;gap:6px 16px;margin-top:6px}
.footer__links a{color:var(--s-text-3);font-weight:600}
.footer__links a:hover{color:var(--app-ink)}

/* ==========================================================================
   14. 动效：.reveal 滚动进场
   soft.js 在解析时就给 <html> 打上 .has-reveal —— 只有脚本真的跑起来才隐藏元素，
   脚本挂了/被拦了就是普通静态页面，绝不会出现「整页空白」。
   错开延迟：给元素加 style="--d:.08s"（或 .reveal--d1/d2/d3）
   ========================================================================== */
.reveal{transition:opacity .7s cubic-bezier(.2,.7,.2,1) var(--d,0s),transform .7s cubic-bezier(.2,.7,.2,1) var(--d,0s)}
.has-reveal .reveal{opacity:0;transform:translateY(18px)}
.has-reveal .reveal.is-in{opacity:1;transform:none}
.reveal--d1{--d:.08s}
.reveal--d2{--d:.16s}
.reveal--d3{--d:.24s}
.reveal--left{}
.has-reveal .reveal--left{transform:translateX(-20px)}
.has-reveal .reveal--right{transform:translateX(20px)}

/* ==========================================================================
   15. 响应式：1200 / 900 / 640 三档
   ========================================================================== */
@media (max-width:1200px){
    :root{--s-maxw:1000px;--s-pad:26px}
    .app-grid{gap:var(--s-3)}
    .app-card{padding:var(--s-4)}
}
@media (max-width:900px){
    :root{--s-pad:22px;--s-8:60px;--s-7:44px}
    /* 单列后卡片本来就占满宽度，继续保持竖排（改成横排会让 6 个子元素乱换行） */
    .app-grid{grid-template-columns:1fr}
    .app-card{gap:var(--s-3)}
    .app-card__logo{width:62px;height:62px;font-size:30px;border-radius:17px}
    .app-card .shot{max-width:640px}
    .feature-row{grid-template-columns:1fr;gap:var(--s-4)}
    /* 单列时一律「文字在上、图在下」，交替翻面在窄屏没意义。
       ⚠ 这里必须 !important：要同时压过 .feature-list>.feature-row:nth-of-type(even)…(0,4,0)
       和 .feature-row--flip…(0,3,0) 两套规则，光靠写在后面赢不了前者。
       之前就是没压住，768 宽度下所有翻面条变成了「图在上、文字在下」。 */
    .feature-row>.feature-row__text{order:1!important}
    .feature-row>.feature-row__shot{order:2!important}
    .shot--9x16{max-width:260px;margin:0 auto}
    .shot-grid--3{grid-template-columns:1fr 1fr}
}
@media (max-width:640px){
    :root{--s-pad:16px;--s-r-lg:20px;--s-r:15px}
    .h1{font-size:clamp(28px,8vw,38px)}
    .dl-row .dl-btn{flex:1 1 100%}
    .app-card .shot{max-width:none}
    .mini-grid{grid-template-columns:1fr 1fr}
    .shot-grid,.shot-grid--3{grid-template-columns:1fr}
    .join-grid{max-width:340px}
    .fact-grid{grid-template-columns:1fr}
    .lb{padding:14px}
    .hero{padding:var(--s-6) 0 var(--s-5)}
    .hero__stats .stat--big{padding:10px 13px}
    .hero__stats .stat--big b{font-size:17px}
    .snav{padding:10px 0}
    .snav__crumb{display:none}
}

/* ==========================================================================
   16. 尊重 prefers-reduced-motion：所有动效全部降级
   ========================================================================== */
@media (prefers-reduced-motion: reduce){
    html{scroll-behavior:auto}
    *,*::before,*::after{
        animation-duration:.01ms!important;animation-iteration-count:1!important;
        transition-duration:.01ms!important;transition-delay:0ms!important;
    }
    .has-reveal .reveal{opacity:1!important;transform:none!important}
    .hoverable:hover,.btn:hover,.dl-btn:hover,.shot:hover,.mini:hover{transform:none!important}
    .hoverable::after,.btn--primary::after,.dl-btn::after{display:none!important}
    .dl-btn.is-current{animation:none!important}
}

/* ==========================================================================
   17. 暗色主题（2026-08-07 新增）—— 配色相关的 [data-theme] 选择器【全部】在这一节
   （唯一的例外是第 4 节 .snav__theme 那 4 行：切换按钮自己的图标/文案显隐。那是「按钮长什么样」
     不是「页面什么配色」，跟着按钮本体放在一起更好维护，也不能包进下面的 @media screen ——
     虽然 print 里 .snav 整条已经 display:none，不隔开也不出事，但语义上它本来就不属于这一节。）
   开关：<html data-theme="dark">，由每个页面 <head> 里那段内联脚本 + soft.js 的
   SoftTheme 负责打（**不引 /js/theme.js**，理由见文件头第 1 条）。

   ── 为什么整节包在 @media screen 里 ────────────────────────────────────────
   下面这些覆盖的特异性（0,2,0 起步）比文件末尾那段 @media print 里的 `.glass-card,.shot{
   background:#fff}`（0,1,0）高，不隔开的话，用户在暗色下按 Ctrl+P 会打出一张【黑底】——
   既费墨又看不清，正是纸上最糟的结果。包一层 @media screen，print 时整节直接不存在，
   变量原样回落到第 1 节的亮色值，打印表现和改暗色之前一模一样。
   （不要改成在 print 里逐条 !important 往回掰：那等于把同一套配色写两遍，必然漏。）

   ── 定值口径（每个数字都是 node 现算的 WCAG，汇总见文件头那段）──────────────
   · 正文 ≥4.5:1 按【全站最亮的一块底】算，不是按 --s-bg 算。暗色的「最差情况」和亮色
     恰好相反：亮色怕底太深（页脚 #eef2fa），暗色怕底太亮（HERO 里 soft-bg 光晕 +
     hero__halo 两层叠出来的那一块，实测最亮的是 tool 皮肤的 #11394c）。
   · --app-1 / --app-2 仍走「大字号 3:1」那一档（它们是 .grad-text / .stat--big b 的
     渐变站点，落在 clamp(34px,5.2vw,60px) 的 .h1 和 20px/750 的数字上），口径同第 1 节。

   ── 三个【故意不动】的决定，别顺手"修" ────────────────────────────────────
   1. --app-btn-a / --app-btn-b（六套皮肤 12 个色）暗色下【原样沿用亮色值】。
      它们唯一的用途是「白字/白图标压在这个渐变上」，对比度只和自己有关、跟页面底色无关；
      为了「在暗底上更亮眼」把它们调亮，白字立刻跌破 4.5:1 —— 按钮反而先坏。
      深饱和的靛蓝/品红压在深灰蓝页面上本来就跳得出来，加上 --app-glow 外发光够了。
   2. .btn--primary::after / .dl-btn::after 那道白色光泽扫过（rgba(255,255,255,.28~.30)）
      也不动：它扫的是按钮自己的深色渐变，不是页面底，暗色下观感不变。
      **要改的是 .hoverable::after**（它扫的是玻璃卡）—— .55 的白在暗底上是一道刺眼白光，
      下面压到 .10。这两条别搞混。
   3. .video-card 的底色 #0d1424、.lb 灯箱遮罩 rgba(16,24,44,.72) 本来就是深色，不用改。

   ── 写死颜色的规则清单（变量层管不到、必须逐条覆盖的就这些）──────────────────
   .soft-bg / .soft-grid / .snav / .hero__halo / .hoverable::after / .grad-text /
   .stat--big b / .shot__cap::before / .app-card__logo / .shot / .shot--fit / .shot__ph /
   .shot__corner / .shot.is-zoomable::after / .btn|.dl-btn[aria-disabled] / .badge--ok /
   .badge--muted / .notice--warn / .lb__img / :focus-visible
   往上面 1~15 节新增写死颜色的规则时，回到这里补一条。
   ========================================================================== */
@media screen{

/* ---- 17.1 变量层：站点级中性色 + 默认皮肤 ---- */
:root[data-theme="dark"]{
    /* color-scheme 不是可有可无：它管的是滚动条、表单控件、以及 <html> 还没上背景色时
       浏览器兜底的那层白 —— 不写的话暗色页面会挂一根雪白的滚动条。 */
    color-scheme:dark;

    /* —— 中性色阶 —— */
    --s-bg:        #101725;   /* 页面底色（= .soft-bg 底渐变的中间那一站，两者要对齐） */
    --s-bg-2:      #0c1220;
    /* ⚠ --s-white 在暗色下【不是白色】，名字是历史包袱，别照着名字改回 #fff。
       它全站只有两处消费点：.btn--ghost:hover 和 .tocbar a:hover 的背景，
       语义是「比玻璃卡再实一档的表面」。暗色下自然要比 --s-bg 亮一点点，不是变白。 */
    --s-white:     #1a2231;
    /* 玻璃拟态在暗色下要反过来做：亮色是「白色半透明压在浅底上」（提亮），
       暗色是「白色低透明度压在深底上」（提一点点亮度 + 保留 backdrop-filter 的通透感）。
       别改成「黑色半透明」—— 那样压在已经很深的底上等于没有卡片边界，玻璃感全丢。
       .055 是算过的：再高就把卡内的 --s-text-3/-4 顶到 4.5:1 以下。 */
    --s-surface:   rgba(255,255,255,.055);
    --s-surface-2: rgba(255,255,255,.035);
    --s-surface-3: rgba(255,255,255,.05);
    --s-text:      #e8edf7;   /* on 最亮底 #11394c = 10.44 */
    --s-text-2:    #b6c1d6;   /* = 6.76 */
    --s-text-3:    #a4b0c6;   /* = 5.61 */
    --s-text-4:    #a2aec4;   /* = 5.48 */
    --s-border:    rgba(255,255,255,.10);
    --s-border-2:  rgba(255,255,255,.19);
    /* 玻璃上沿那道 1px 亮边：亮色是 .85 的白（模拟高光），暗色只留 .07 —— 深底上高亮边
       稍微多一点就变成一根发光的白线，卡片看着像被描了个边。 */
    --s-hairline:  rgba(255,255,255,.07);

    /* —— 语义色 —— */
    /* --s-warn 仍然是【文字色】（.notice--warn b 与 .app-card__note b，安卓那条
       「换了签名，覆盖安装会失败」就靠它）。亮色那支 #8a4a06 是深棕，在暗底上等于隐形，
       所以翻到同色相的亮端 #ffc46b：on 警示条自己的底 = 7.10~7.48（六套皮肤全测过）。 */
    --s-warn:      #ffc46b;
    --s-warn-bg:   rgba(180,98,10,.18);
    --s-warn-bd:   rgba(255,196,107,.28);
    --s-ok:        #3ddc9a;   /* on 最亮底 6.95 */
    --s-danger:    #ff8a80;   /* on 最亮底 5.37 */

    /* —— 阴影：亮色那三档是带蓝调的淡影（白底上不发灰），暗色下【看不见】，
       必须换成纯黑并加重，否则卡片和背景糊成一片、整页没有层次。 */
    --s-sh-1: 0 1px 2px rgba(0,0,0,.30), 0 4px 12px rgba(0,0,0,.28);
    --s-sh-2: 0 2px 6px rgba(0,0,0,.34), 0 12px 28px rgba(0,0,0,.38);
    --s-sh-3: 0 4px 10px rgba(0,0,0,.38), 0 24px 56px rgba(0,0,0,.50);
    /* --s-sh-in 【不用重写】：它的值是 inset 0 1px 0 var(--s-hairline)，
       自定义属性里的 var() 在使用时才解析，上面改了 --s-hairline 它就自动跟上。 */

    /* —— 默认皮肤（总览页 / 站点级中性蓝紫，等同 copy 的中性版）——
       另见下面 17.2 的六套。--app-aura / --app-aura-2 是【暗色专有】的两个新变量，
       亮色一处都不引用，具体理由见 17.2 开头。 */
    --app-1:#8b8cf8; --app-2:#c98bfb; --app-ink:#adb0ff;
    --app-tint:rgba(99,102,241,.16); --app-tint-2:rgba(99,102,241,.20);
    --app-bd:rgba(139,140,248,.34);  --app-glow:rgba(99,102,241,.30);
    --app-aura:rgba(99,102,241,.07); --app-aura-2:rgba(99,102,241,.10);
}
:root[data-theme="light"]{color-scheme:light}

/* ---- 17.2 六套皮肤的暗色版 ----
   ⚠ 每套【必须写两条选择器】，缺一条就有页面漏掉：
     `[data-theme="dark"][data-app="x"]`  → 六个详情页（<html data-app="x"> 与 data-theme
                                             打在【同一个元素】上，只能靠复合选择器命中）
     `[data-theme="dark"] [data-app="x"]` → 总览页（data-theme 在 <html>，data-app 在六张
                                             <article class="app-card"> 上，是后代关系）
   两条都是 (0,2,0)，稳压第 1 节那六条 [data-app="x"] 的 (0,1,0)。

   ── 三组变量各自的调法（不是把亮色的值取反，是分组重定）──────────────────
   A) --app-ink / --app-1 / --app-2 →【翻到亮端】。亮色下它们是"深色文字压浅底"，
      暗色下变成"亮色文字压深底"，必须整体提亮。色相原样保留（暖的还是暖的、
      青的还是青的），六套之间的辨识度不受影响。
   B) --app-tint / --app-tint-2 →【rgb 不变，只提 alpha】（.10/.19 → .16/.20）。
      它们是 .eyebrow / .chip--accent / .badge / .video-card__meta .k 的药丸底：
      同样一支色，压在白底上 10% 就看得出，压在深底上 10% 几乎没有存在感。
   C) --app-aura / --app-aura-2 →【暗色专有的新变量，alpha 只有 tint 的一半上下】。
      ⚠ 这是本节最反直觉的一处，先说清为什么不能复用 B：
        亮色下 .soft-bg / .hero__halo 的 aurora 光晕和药丸底共用 --app-tint(-2) 是安全的，
        因为往白底上加颜色 = 变暗 = 文字对比度只增不减。暗色下反过来 —— 光晕是往深底上
        【加亮】，一层 .20 的品牌色铺满 46%×38% 的屏幕，会把那一片底整体抬亮一大截，
        压在上面的 --s-text-3/-4 直接跌破 4.5:1。而药丸底又偏偏需要 .16 才看得见。
        一个变量满足不了两个相反的需求，所以拆成两套。
      六套的 alpha 【不是同一个数】，是按各自的亮度手工配平的：越亮的色给得越少
      （琥珀 .045/.06 最少，靛蓝 .07/.10 最多），目标是六页 HERO 那块底的相对亮度
      落在同一量级（实测 L = 0.0255~0.0357）。照抄一个统一值的话，idea 那页会明显发白、
      obsidian 那页会明显偏暗。
   D) --app-btn-a / --app-btn-b →【一个都不改】，理由见本节开头第 1 条。 */
:root[data-theme="dark"][data-app="copy"],
:root[data-theme="dark"] [data-app="copy"]{
    --app-1:#8b8cf8; --app-2:#c98bfb; --app-ink:#adb0ff;   /* ink 6.39 / a1 4.42 / a2 5.25 */
    --app-tint:rgba(99,102,241,.16); --app-tint-2:rgba(99,102,241,.20);
    --app-bd:rgba(139,140,248,.34);  --app-glow:rgba(99,102,241,.30);
    --app-aura:rgba(99,102,241,.07); --app-aura-2:rgba(99,102,241,.10);
}
:root[data-theme="dark"][data-app="tool"],
:root[data-theme="dark"] [data-app="tool"]{
    --app-1:#22d3ee; --app-2:#6aa8ff; --app-ink:#4fd3e8;   /* ink 6.90 / a1 6.78 / a2 5.05 */
    --app-tint:rgba(6,182,212,.16);  --app-tint-2:rgba(6,182,212,.20);
    --app-bd:rgba(34,211,238,.32);   --app-glow:rgba(6,182,212,.26);
    --app-aura:rgba(6,182,212,.055); --app-aura-2:rgba(6,182,212,.075);
}
:root[data-theme="dark"][data-app="android"],
:root[data-theme="dark"] [data-app="android"]{
    --app-1:#34d399; --app-2:#6ee7b7; --app-ink:#34d9a0;   /* ink 6.90 / a1 6.51 / a2 8.21 */
    --app-tint:rgba(16,185,129,.16); --app-tint-2:rgba(16,185,129,.20);
    --app-bd:rgba(52,211,153,.32);   --app-glow:rgba(16,185,129,.26);
    --app-aura:rgba(16,185,129,.055);--app-aura-2:rgba(16,185,129,.075);
}
:root[data-theme="dark"][data-app="idea"],
:root[data-theme="dark"] [data-app="idea"]{
    /* 六套里唯一的暖色，暗色下依然是「一排卡里一眼挑得出的那个」。
       ⚠ 亮色那套为了让白字达标，把 --app-1 从 amber-500 一路压到 #c87608（很深的琥珀）；
         暗色要反着来 —— 深琥珀压在深底上是一团泥，所以回到 amber-400 一带的 #f0a338。
         这不是把当初那次校准推翻，两边解的是两个方向相反的题。 */
    --app-1:#f0a338; --app-2:#fb8b4c; --app-ink:#f3ba63;   /* ink 7.50 / a1 6.24 / a2 5.55 */
    --app-tint:rgba(245,158,11,.16); --app-tint-2:rgba(245,158,11,.20);
    --app-bd:rgba(240,163,56,.32);   --app-glow:rgba(234,88,12,.28);
    /* aura 六套里最低：琥珀是相对亮度最高的一支，同样 alpha 抬底抬得最狠 */
    --app-aura:rgba(245,158,11,.045);--app-aura-2:rgba(245,158,11,.06);
}
:root[data-theme="dark"][data-app="vscode"],
:root[data-theme="dark"] [data-app="vscode"]{
    /* 暗色下这条蓝通道反而松快了：亮色那边 --app-1 被 --app-btn-a(4.99) 和 --app-2(3.52)
       上下夹死、只挤得出 ΔE 3.5 的层次（见第 1 节那段），暗色把三站整体挪到亮端，
       .grad-text 改走 --app-ink → --app-1 → --app-2（见 17.3），层次自然拉开。 */
    --app-1:#4fb4f0; --app-2:#38a8ee; --app-ink:#6ac2f7;   /* ink 6.30 / a1 5.38 / a2 4.72 */
    --app-tint:rgba(31,156,240,.16); --app-tint-2:rgba(31,156,240,.20);
    --app-bd:rgba(79,180,240,.32);   --app-glow:rgba(31,156,240,.28);
    --app-aura:rgba(31,156,240,.06); --app-aura-2:rgba(31,156,240,.085);
}
:root[data-theme="dark"][data-app="obsidian"],
:root[data-theme="dark"] [data-app="obsidian"]{
    --app-1:#e368f0; --app-2:#f08ffa; --app-ink:#ec8ef5;   /* ink 6.43 / a1 4.94 / a2 6.60 */
    --app-tint:rgba(192,38,211,.16); --app-tint-2:rgba(192,38,211,.20);
    --app-bd:rgba(227,104,240,.32);  --app-glow:rgba(192,38,211,.28);
    --app-aura:rgba(192,38,211,.07); --app-aura-2:rgba(192,38,211,.095);
}

/* ---- 17.3 写死颜色的规则：逐条覆盖 ----
   （变量层管不到的都在这里。清单见本节开头，新增写死颜色的规则时回来补一条。） */

/* 背景层：aurora 光晕 + 极淡网格。
   ⚠ 底渐变的三站 #141b2b → #101725 → #0b1120 是【上亮下暗】，方向和亮色那套一致
     （亮色是 #fbfcff → #eef2fa），这样往下滚时页脚那一带最沉，视觉重心不会飘。
     文件头那段暗色对比度里的「最亮底」就是从 #141b2b 这一站起算的。
   ⚠ 各层 alpha 全部比亮色低一半左右（.20/.15/.12/.12/.10 → .13/.10/.07/.07/.06）：
     往白底加色是压暗（对比度只增不减），往深底加色是提亮（会吃掉正文对比度）。 */
:root[data-theme="dark"] .soft-bg{
    background:
        radial-gradient(46% 38% at 82% -4%,  rgba(99,102,241,.13), transparent 62%),
        radial-gradient(42% 34% at 8% 6%,    rgba(168,85,247,.10), transparent 64%),
        radial-gradient(50% 36% at 50% 42%,  rgba(56,189,248,.07), transparent 66%),
        radial-gradient(56% 40% at 18% 88%,  rgba(16,185,129,.07), transparent 64%),
        radial-gradient(48% 34% at 92% 74%,  rgba(244,114,182,.06), transparent 62%),
        linear-gradient(178deg,#141b2b 0%,#101725 46%,#0b1120 100%);
}
/* 详情页：光晕跟皮肤走。选择器必须是【复合】的 [data-theme][data-app]（0,3,0），
   不能写成 `[data-theme="dark"] .soft-bg`(0,2,0) 指望它盖住第 3 节的
   `[data-app] .soft-bg`(0,2,0) —— 同特异性只能靠源码顺序分胜负，那样六个详情页的
   光晕会退回亮色那套浅色 tint，糊在深底上像一层脏印子。 */
:root[data-theme="dark"][data-app] .soft-bg{
    background:
        radial-gradient(46% 38% at 82% -4%, var(--app-aura-2),    transparent 62%),
        radial-gradient(42% 34% at 8% 6%,   var(--app-aura),      transparent 64%),
        radial-gradient(50% 36% at 50% 42%, rgba(56,189,248,.06), transparent 66%),
        radial-gradient(56% 40% at 16% 92%, var(--app-aura),      transparent 64%),
        linear-gradient(178deg,#141b2b 0%,#101725 46%,#0b1120 100%);
}
/* 网格线要从「深蓝细线」翻成「白细线」，否则在深底上等于没画。
   opacity 一并从 .7 降到 .55：深底上同样的白线看着更显眼，不压一点会喧宾夺主。 */
:root[data-theme="dark"] .soft-grid{
    opacity:.55;
    background-image:
        linear-gradient(rgba(255,255,255,.035) 1px,transparent 1px),
        linear-gradient(90deg,rgba(255,255,255,.035) 1px,transparent 1px);
}
/* sticky 顶栏的渐隐底：要化进去的是**页面顶端那一站的颜色**，不是 --s-bg。
   ⚠ 2026-08-07 修：原来写的是 rgba(16,23,37,…) = #101725 = --s-bg，
     而 --s-bg 是 .soft-bg 底渐变【46% 处】的色站；.snav 是 sticky 贴在页面最顶上，
     它底下的实际颜色是【0% 处】的 #141b2b（更亮一档）。拿中段的深色去化顶端，
     就在顶栏那一块压出一条比周围暗的横带 —— 正是这条注释原本想避免的现象。
     .soft-bg 是 position:fixed，滚动时顶端始终是这一站，取 0% 的色恒成立。
   （亮色那条一直是对的：rgba(250,251,255) ≈ 顶端的 #fbfcff，本来就按顶端配的。） */
:root[data-theme="dark"] .snav::before{
    background:linear-gradient(180deg,rgba(20,27,43,.92),rgba(20,27,43,.62) 70%,transparent);
}
/* HERO 呼吸光斑：同 .soft-bg，两条（中性 / 跟皮肤）都要，且第二条必须是复合选择器。
   总览页命中第一条（它的 .hero__halo 不是任何 [data-app] 的后代，见第 5.5 节那段长注释）。 */
:root[data-theme="dark"] .hero__halo{
    background:
        radial-gradient(42% 52% at 30% 40%,rgba(99,102,241,.16),transparent 68%),
        radial-gradient(40% 50% at 72% 34%,rgba(6,182,212,.11), transparent 68%),
        radial-gradient(38% 46% at 54% 76%,rgba(16,185,129,.09),transparent 68%);
}
:root[data-theme="dark"][data-app] .hero__halo{
    background:
        radial-gradient(46% 56% at 30% 40%,var(--app-aura-2),transparent 68%),
        radial-gradient(48% 58% at 72% 34%,var(--app-aura),  transparent 70%),
        radial-gradient(24% 30% at 54% 76%,var(--app-aura-2),transparent 72%);
}

/* 玻璃卡 hover 时扫过的那道白光：.55 的白在暗底上是一道刺眼白条，压到 .10。
   （别顺手把 .btn--primary::after / .dl-btn::after 也改了 —— 那两道扫的是按钮自己的
     深色渐变，不是页面底，见本节开头第 2 条。） */
:root[data-theme="dark"] .hoverable::after{
    background:linear-gradient(115deg,transparent 32%,rgba(255,255,255,.10) 50%,transparent 68%);
}

/* 渐变文字：首站从 --app-btn-a 换成 --app-ink。
   ⚠ 这是暗色下【必须改】的一条，不是美化：--app-btn-a 是按钮底色（深），它在暗底上
     实测只有 2.33~2.96:1，而 .grad-text 是画在 .h1 / .h2 的【标题正文】上的
     （七个 html 每页都有一个 <span class="grad-text">）—— 不改就是标题左半截糊在背景里。
     换成 --app-ink 之后三站 ≥6.30 / ≥4.42 / ≥4.72，远高于大字号那档 3:1。
   ⚠ `background` 是简写，会把 background-clip 一起重置成 border-box，所以这里必须把
     那两行 clip 再写一遍 —— 漏掉的话渐变会填满整个行盒、文字变成透明的洞（等于消失）。
     -webkit-text-fill-color 是独立属性、不受简写影响，不用重写。 */
:root[data-theme="dark"] .grad-text{
    background:linear-gradient(112deg,var(--app-ink) 0%,var(--app-1) 46%,var(--app-2) 100%);
    -webkit-background-clip:text;background-clip:text;
}
/* 同上，数字大字（.stat--big b 是 20px/750 的渐变字），亮色是 --app-btn-a → --app-2 */
:root[data-theme="dark"] .stat--big b{
    background:linear-gradient(120deg,var(--app-ink),var(--app-2));
    -webkit-background-clip:text;background-clip:text;
}
/* 图注左侧那根 3px 竖条：同样是 btn-a→btn-b 的深色渐变，暗底上看不见 */
:root[data-theme="dark"] .shot__cap::before{
    background:linear-gradient(180deg,var(--app-ink),var(--app-2));opacity:.7;
}
/* 卡片图标底板：亮色是 --app-1 → --app-btn-b，上面盖 38px 白 SVG / 36px emoji。
   暗色下 --app-1 被提到了亮端（如 #f0a338），白图标压上去只剩 1.8~2.9:1 = 糊成一片，
   所以这里改用两个按钮底色 --app-btn-a → --app-btn-b：白图标 4.99~6.82，远超图形那档 3:1。
   顺带一提这也是唯一还在用 --app-btn-* 当"底"的地方，正好和「按钮底色不改」那条自洽。 */
:root[data-theme="dark"] .app-card__logo{
    background:linear-gradient(142deg,var(--app-btn-a),var(--app-btn-b));
}

/* 图位：托住截图的那块底板。亮色是「白 → 品牌浅色」，暗色翻成「极淡的白雾 → 品牌浅色」。
   ⚠ 别改成纯白底板"让截图更清楚"：.shot--fit 是 object-fit:contain + 10px padding，
     底板会在截图四周露出一整圈，暗色页面上出现一块白框比截图糊一点难看得多。 */
:root[data-theme="dark"] .shot{background:linear-gradient(140deg,rgba(255,255,255,.06),var(--app-tint))}
:root[data-theme="dark"] .shot--fit{background:linear-gradient(150deg,rgba(255,255,255,.06),var(--app-tint))}
:root[data-theme="dark"] .shot__ph{
    background-image:
        linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px),
        linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px);
}
/* 图上的两个角标（平台标 / 放大镜）压的是【截图本身】，不是页面底。
   截图绝大多数是浅色界面，所以暗色下必须反过来做成"深底浅字"，
   按最差情况（纯白截图）算：底 rgba(11,16,27,.86) 上的 --s-text-2 = 7.18。 */
:root[data-theme="dark"] .shot__corner,
:root[data-theme="dark"] .shot.is-zoomable::after{
    background:rgba(11,16,27,.86);color:var(--s-text-2);border-color:rgba(255,255,255,.14);
}
/* 灯箱底板：亮色给白（衬浅色截图），暗色给深，免得图片加载完成前闪一块白 */
:root[data-theme="dark"] .lb__img{background:#131a29}

/* 禁用态：亮色是「深色 5% 压白底」，暗色要「白色 6% 压深底」，否则那颗按钮比周围还深，
   看着像被挖了个洞而不是被置灰。color 走 --s-text-3 变量、已自动跟着换，不用重写。
   ⚠ 必须带 !important：原规则整条都是 !important（它要压过 .btn--primary 的渐变）。 */
:root[data-theme="dark"] .btn[aria-disabled="true"],
:root[data-theme="dark"] .dl-btn[aria-disabled="true"]{
    background:rgba(255,255,255,.06)!important;
}
:root[data-theme="dark"] .badge--ok{background:rgba(61,220,154,.14);border-color:rgba(61,220,154,.30)}
:root[data-theme="dark"] .badge--muted{background:rgba(255,255,255,.06)}
/* 警示条正文：亮色写死的 #6b4108 是深棕，暗底上直接消失。其中的 <b> 走 --s-warn（变量，已换）。 */
:root[data-theme="dark"] .notice--warn{color:#f3d7ab}

/* 键盘焦点圈：亮色用 --app-btn-a（深色，衬浅底），暗色下那支深色在深底上几乎看不见，
   换成已经提亮过的 --app-ink。两处消费点都要改（全局的 :focus-visible + 整卡可点的
   .app-card__link 那条 inset 描边）。 */
:root[data-theme="dark"] :focus-visible{outline-color:var(--app-ink)}
:root[data-theme="dark"] .app-card__link:focus-visible::after{outline-color:var(--app-ink)}

}   /* ← @media screen 结束 */

/* 打印：去掉玻璃与光晕，省墨 */
@media print{
    .soft-bg,.soft-grid,.snav,.dl-row{display:none!important}
    .glass-card,.shot{box-shadow:none;background:#fff}
    body{background:#fff}
}
