/* ============================================================
   Hongrong — 站点样式表  v2
   ────────────────────────────────────────────────────────────
   四层结构，从上往下读：

     第 0 层 · 字体        @font-face，自托管 IBM Plex
     第 1 层 · 系统        设计令牌：色阶 / 字号 / 间距 / 圆角 / 阴影 / 动效
     第 2 层 · 语言        排版：字体配对、字号阶梯、行高字距
     第 3 层 · 骨架        网格、间距节奏、行宽、版块
     第 4 层 · 质感        圆角分级、投影、悬停/焦点态、纹理
     第 5 层 · 组件        具体积木

   换一家公司用：只改「第 1 层」里的 --deep-* 和 --jade-* 两组色阶
   （2026-10-13 第 12 轮之前它们叫 --navy-* / --gold-*）。
   下面所有东西都是从那几个数推出来的。
   ============================================================ */


/* ============================================================
   第 0 层 · 字体
   ------------------------------------------------------------
   自托管，不走 Google（国内打不开，而且会泄露访客 IP）。
   8 个文件共 156KB，只有一个 woff2 会被实际下载 —— 浏览器按需取。
   ============================================================ */

@font-face{font-family:"Plex Sans";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/ibm-plex-sans-latin-400-normal.woff2") format("woff2")}
@font-face{font-family:"Plex Sans";font-style:italic;font-weight:400;font-display:swap;
  src:url("fonts/ibm-plex-sans-latin-400-italic.woff2") format("woff2")}
@font-face{font-family:"Plex Sans";font-style:normal;font-weight:500;font-display:swap;
  src:url("fonts/ibm-plex-sans-latin-500-normal.woff2") format("woff2")}
@font-face{font-family:"Plex Sans";font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/ibm-plex-sans-latin-600-normal.woff2") format("woff2")}
@font-face{font-family:"Plex Sans";font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/ibm-plex-sans-latin-700-normal.woff2") format("woff2")}

@font-face{font-family:"Plex Mono";font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2")}
@font-face{font-family:"Plex Mono";font-style:normal;font-weight:500;font-display:swap;
  src:url("fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2")}
@font-face{font-family:"Plex Mono";font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2")}


/* ============================================================
   第 1 层 · 系统 —— 设计令牌
   ------------------------------------------------------------
   规矩：组件里不许出现裸数值（#C9A25E / 14px / 12px）。
   所有值必须来自这里。改一处，全站跟着变。
   ============================================================ */

:root{

  /* ---------- 1.1 色 · 色阶 ----------
     不是随便挑几个颜色，是每组一条从深到浅的梯子。
     深色用来做文字，中间色做强调，浅色做底纹和边框。

     ⚠️ 2026-10-13 第 12 轮：两条梯子整体换成青绿。它们**原来叫 --navy-* /
        --gold-*，值也真的是深蓝和品牌金** —— 改名和换值分两步做的，
        每步单独可 diff。

     为什么换：不是"想换个颜色"。洪荣**自己的展台印的就是绿色**
     （建站资料 里莫斯科、巴西那两张展会照，展板上全是绿底），页头那枚
     NG 电路标也是绿的，hrpcba.com 的主色同样是薄荷 #B6D5D0 + 深青
     #09435B。站上此前一直在用一套和公司对不上的颜色，这一轮把它对上。

     ⚠️ 换法是**按角色一一对应**，不是重新设计：--jade-400 坐的是原来
        --gold-400 那个位置 —— 两者都在做「中饱和填充 + 上面压深色字」
        （看 .tile .art 那几笔填充、.pcard .n 那枚序号）。所以整套明暗逻辑
        一个字没动，只换了色相。

     ⚠️ 2026-10-13 后来从这条梯子上**摘走了唯一一颗按钮**：用户要求
        「获取报价」结合 PCBA 行业特点，选了「阻焊绿底 + 沉金底边」。
        那三个消费点（.btn.primary / nav.main a.cta / .rail a.rq）现在走
        --green-500 底 + 白字，**不再属于这一节**。梯子本身没变，
        只是少了一个消费者。 */

  /* 深青。第 12~32 轮它主要做**文字**（底改成白/浅带之后就不当背景了）；
     第 33 轮**又当回背景** —— 首页那几段深色带和深色卡面用的就是它。
     这是站上唯一的深色底，深底上的文字色见下面 1.2 节的 --on-dark*。 */
  --deep-900:#0A0C0E;   /* 最底：页脚 / 深色带 / 首屏那道纱的落点 */
  --deep-800:#191E22;   /* 抬起一档的面：表头 / 序号块 / 芯片标签 */
  --deep-700:#22282D;
  --deep-600:#2B3238;
  --deep-500:#343B42;
  --deep-400:#525A61;   /* 插图线条：深底上仍看得见的最亮一档 */
  --deep-200:#6A737B;   /* 预留，当前 0 消费 */
  --deep-100:#8D959D;   /* 预留，当前 0 消费 */

  /* 青绿 —— 现在是**板绿**（第 25 轮换的色相，第 31 轮压暗）。
     第 31 轮用户原话：「我的 banner 部分背景图的那种墨绿」。
     色相和明度都不是挑的，是**量**出来的：
       · 色相 —— 首屏那张板子微距 assets/img/hero-pcb-macro.jpg 里
         147.9 万个"阻焊绿"像素的中位色相 ……………………………… 152.1°
         ⚠️ 第 25 轮那句 155.6° 是**另一批图、另一套过滤口径**量出来的，
            复现不出来；同一批图换个切法就在 137~154° 之间摆。
            板子照片里本来就有偏黄和偏青两个绿，别把任何单个数当铁数。
       · 明度 —— 那个墨绿的 L 是 8.8%，站上原来是 35.7%，**亮了 4 倍**。
         所以这一轮**压暗**：600 / 500 / 400 三档的 L 一律乘 0.80（保各自的 S）。
     ⚠️ **--jade-300 故意不压**：它的角色是"深底上的亮强调"（页脚那处）。
        跟着压会把 6.84:1 打到 4.76 —— 还过 AA，但余量只剩 0.26，不值当。
     ⚠️ **--jade-200 / --jade-100 也不动**：它们是淡边和极淡绿底的底色，
        压了就不是"淡"底了，chip 会变成实心中绿。
     ⚠️ 下面标的是**实测**值（无头 Chrome 探针算的），别凭感觉改。
     ⚠️ **--jade-400 不承载文字**：它压在白底上只有 4.97:1，仍然不够正文。
        它只做填充 / 边框 / 点 / 短线 / 图标；白底上的正文链接一律
        --jade-600（8.79:1）。这条约束全站有效，别在某个组件里破例。
        （第 25 轮发现全站只剩一处"文字压 --jade-400 实底"——.pcard .n，
         换色相后掉到 4.47:1 跌破 AA，已把它挪到 --jade-300（6.84:1）。）
     ⚠️ **令牌名不跟着改**：--jade-* 在这个文件里 50 来处、文档和脚本里更多，
        改名只换来一份对不上号的文档。"jade / 软玉"本来就是黄绿系、薄荷也是，
        名字现在比换之前更贴。注释里旧写的"青绿"一律按"板绿"读。 */
  --jade-600:#67DDA6;   /* 深底上的**文字**链接 · 压 --bg 10.4:1 */
  --jade-500:#3DBE87;   /* 描边 / 短线 */
  --jade-400:#23955C;   /* 主色：填充 / 边框 / 点。**不承载文字** */
  --jade-300:#7DE8B4;   /* 深底上最亮的强调（首屏眉标 / 深带链接） */
  --jade-200:rgba(125,232,180,.30);   /* 淡边（深底上必须透，不能是实色） */
  --jade-100:rgba(35,149,92,.16);     /* 极淡绿底 / chip */

  /* 极淡色带三级（第 12 轮新增，第 25 轮补彩度，2026-10-16 第 33 轮**去彩度**）。
     **这一级是上一级的 --cream-* 换来的**，名字没再改 —— 读的时候按"带底那一族"理解。
     三者按深浅排：50 最淡、200 最重。分工照旧：
       50  alt 色带里的卡片（.card / .promises li / .docs li / .tile / .badge）
       100 色带本身 + .tile .art 的内嵌插图区
       200 表头、标签带这类要再重一点的地方

     ⚠️ **第 33 轮改的是彩度，既不是色相也不是明度。**
        用户拿一份整页参考稿说「走高端感，不要清新感」。逐像素量完发现：
        参考稿的浅底 #ECF0EB，HSV 彩度 **2.1%**；我们原来是 #DFF7ED，**9.7%**。
        两者亮度几乎一样（相对亮度 .8616 vs .8832）—— 也就是说"清新"这个词，
        在像素上就是"这一族带绿"。所以整族拉到中性灰白：色相 155° → 120°
        （等量三通道），彩度落到 1.7%，明度往下走一丁点（.8832 → .8703）。
     ⚠️ 第 25 轮那次方向**是反的**（补彩度，让肉眼看得出绿）。两轮目标不同，
        别拿这条去推翻那条；要回头看当时为什么补，翻第 25 轮那条记录。
     ⚠️ **--band 那行一个字都不用改**：它是 var(--tint-100) 的转发，跟着走。
        这正是当初把 --band 做成转发（而不是另写一个色值）的原因。
     ⚠️ 这三档是**全站共用**：改完 21 个页面的色带底色一起变。只改首页会造成
        "首页灰、内页薄荷"两套底色，比统一更糟，所以是整族改，不是按页改。
     ⚠️ 这一族里**没有一处靠绿来做语义**（改前逐处 grep 过消费点，全是"面上的底色"：
        导航悬停药丸与抽屉卡、alt 带上的卡/列表/瓦片/认证牌、表格斑马行、
        FAQ 面板、.answer 的渐变、图片占位底、.tile .art）—— 去彩度不丢信息。
     ⚠️ 对比度（geo_check\contrast_r33_math.py，改前 → 改后）：
          --ink-2 正文 压带底        6.36 → 6.28
          --deep-800 标题 压带底     9.50 → 9.25
          --jade-600 链接 压带底     7.81 → 7.61
          --deep-800 标题 压带上的卡 10.21 → 9.90
        都在阈值之上，但**方向是往下走的**（新带比旧带暗一点点）。
     ⚠️ 顺手量出一条**早就存在**的破线，不属于这一轮、也没在这一轮修：
        **--mute 当文字用是过不了 AA 的** —— 白底上 3.46:1，带底上 3.08:1，
        改后 3.03:1。站上拿它当文字的有四处（.gallery figcaption、
        .stats span、.form-note、.band .gallery figcaption）。
        要修得动 --mute 这个令牌本身（21 个页面一起变），是另一个决定，别夹在这里做。
     ⚠️ 别再往下压：--band 和 --bg(#FFFFFF) 的差是**整站节奏的唯一来源**
        （见 1.2 节 --band 那段），压到和 --tint-200 一样深，白底/色带交替就没了。 */
  --tint-50:#1A1F23;    /* 带上的卡 / 斑马行（比带底亮一档） */
  --tint-100:#15191C;   /* 色带本身 = --band */
  --tint-200:#1F252A;   /* 表头 / 标签带（再亮一档） */

  /* 材料色 · 板身上的绿。⚠️ **不是品牌色**，和 --jade-* 是两套东西：
     --jade-* 是"这个站长什么样"，--green-* 是"板长什么样"。
     混进一条梯子的后果很具体：下一个人调品牌绿的时候会顺手把板也调了。

     2026-10-13 这级从七档收到只留材料色，但**收了两次**：
       · --green-700  只进叠层剖面插图（.tile .art .ag），是剖面里那层阻焊
       · --green-500/600  是「Get a quote」那颗按钮的底 —— 见下面那段注释
     别拿它们当强调色使，强调色是 --jade-400。 */
  --green-700:#0A4A2E;   /* 叠层剖面里的阻焊层 */
  --green-600:#0D6B4B;   /* 那颗按钮的 hover —— 比 500 深，白字 6.51:1 */
  --green-500:#0F7A57;   /* 那颗按钮的底 —— 白字 5.33:1 */

  /* 沉金（ENIG）· 焊盘上那层金。也是材料色。
     全站**只出现在同一个地方**：那颗按钮的底边（一道 2px 的 inset 阴影）。
     别拿它去染眉标 / 下划线 / 色带 —— 那是 --jade-* 的活。 */
  --enig:#C9A227;

  --red-600:#FF9380;    /* 深底上的警示字 */
  --red-100:rgba(142,50,37,.20);
  --red-200:rgba(255,147,128,.36);

  /* ---------- 1.2 色 · 语义 ----------
     组件只准用这一层。这样以后调色整条梯子动，组件不用动。

     2026-10-13 第 12 轮：底色从暖米换成白 + 极淡薄荷。
     （原来那级 --tint-* 梯子换成了 1.1 里的 --tint-*，不删 ——
       它下面有 11 处直接消费，删了这一整层都得跟着改。） */

  /* 2026-10-19 第 43 轮下半场：整站是深色底，原生控件就得跟着深。
     ⚠️ 不写这一条，select 一展开会弹出**一张白列表**，而 option 的 color
        是从 select 继承的 --ink（近白）—— 白底白字，整列选项等于隐形。
        第 43 轮原来只在 `.home .dark form.inquiry` 上写了这一条，覆盖面太窄：
        contact.html 的两个 select 一直在同一个坑里。 */
  color-scheme:dark;

  --bg:#0B0D0F;         /* 页面底 */
  --band:var(--tint-100);  /* 色带底（.alt / .band）。整站的"层"主要靠它读出来 ——
                             一道比白底暗一点点、又不显得是色块的浅带子。
                             第 12~32 轮它是"淡薄荷"（全站面积最大的那个绿）；
                             第 33 轮跟 --tint-100 一起**去彩度变成中性灰白**，
                             所以这句注释不能再写"绿" —— 绿现在只落在
                             --jade-*（链接/填充/眉标）和 --green-*（那颗按钮）上。
                             ⚠️ 布局是「白底 + 浅带交替」，--bg 和 --paper 都是白，
                             两者之间的差**全靠 --band 撑** —— 一旦 --band 也填白，
                             整站的 .alt 色带会全部消失。这是它最低限度的明度差。 */

  /* ---- 节带三色（2026-10-19 · 第 43 轮下半场「板块底色交替」）----------
     用户原话：「配色也不是全部一个颜色而是要穿插其他的颜色进行搭配」，
     机制是他自己选的「板块底色交替」。

     石墨底 + --band 把整页压成了同一个颜色。这里给首页那四节 .alt 开
     **三支同明度、不同色相**的深带子，按节轮换：
         --band-jade    绿（贴 --jade-* 那一支）  What we do / 常见问题
         --band-indigo  藏青                      Why teams choose us
         --band-umber   暖褐（和 --enig 沉金同调）Certifications

     ⚠️ 三支的**明度刻意贴着 --band 走**（L 约 1.15% / 1.06% / 1.15%，
        --band 是 0.90%）。差的是**色相**，不是深浅。一旦做亮就退回
        "白底灰带"那一套，深色主题立刻散架 —— 别往上调。
     ⚠️ 每支配一张同色相的卡面 --card-*：带上的卡如果还用中性 --tint-50，
        就像往彩色纸上贴了块灰纸。
     ⚠️ 对比度（数学值，全站最浅的正文色 --mute #8D959D 是卡口）：
            --mute 压 带底: jade 5.36 / indigo 5.70 / umber 5.62
            --mute 压 卡面: jade 4.89 / indigo 5.31 / umber 5.10
            --mute 压 悬停: jade 4.59 / indigo 5.07 / umber 4.83
        全部过 AA 4.5:1。**别再把卡面调亮** —— 卡面一亮，figcaption 先破线。
     ⚠️ 只在挂了时类的节上生效，内页的 section.alt 仍然吃 --band。 */
  --band-jade:#0F241D;
  --band-indigo:#101A30;
  --band-umber:#241A10;
  --card-jade:#142C23;
  --card-indigo:#16203A;
  --card-umber:#2E2216;
  --card-jade-h:#173128;
  --card-indigo-h:#19243F;
  --card-umber-h:#33261A;
  --paper:#171C20;      /* 卡面：比色带亮一档，卡片才浮得起来 */
  --paper-2:#1B2126;

  /* 首页「What we do」服务区那一带（2026-10-15 第 26 轮）。用户给的设计稿要求
     这一节是「浅灰白底 + 卡片浅灰白 + 低透明度的真实 PCB 纹理暗纹」——
     刻意**不带**薄荷色相，跟全站的白/淡薄荷交替节奏不一样，所以单开两个令牌。
     ⚠️ 别拿它们去改 --tint-* / --band：那两档是 20 个页面共用的色带，
        动一下整站节奏就变了。这一节只挂在 section.svc-sec 上。 */
  --svc-page:#0F1316;   /* 服务区底色 */
  --svc-card:#171C20;   /* 服务卡底色（比底色亮一档） */

  --ink:#F1F3F4;        /* 正文 / 标题。**不再转发 --deep-800** —— 那一族现在是面不是字 */
  --ink-2:#A6AEB4;      /* 次要正文 */
  --mute:#8D959D;       /* 标签、注脚。深底上 5.4:1，比旧值 3.46:1 过线 */

  /* 描边三级。第 25 轮跟着色相微调（162° → 154°）—— 视觉上基本看不出来，
     纯粹是让这一族和 --jade-* / --tint-* 同色相，上面那些"同一族"的说法才不假。 */
  --line:rgba(255,255,255,.10);
  --line-2:rgba(255,255,255,.18);
  --line-3:rgba(255,255,255,.28);

  --accent:var(--jade-400);
  --accent-strong:var(--jade-500);
  --accent-ink:var(--jade-600);
  --accent-soft:var(--jade-100);
  --accent-ring:rgba(35,149,92,.40);    /* = --jade-400 的 RGB，手写 —— 换色要一起改 */

  /* ⚠️ 这里原来有一组 --on-dark*（深底上的文字：白 + 三档灰 + 两条线），
     2026-10-13 第 12 轮**整组删掉**。页头 / 深色带 / 页脚 / 内页页头 / 联系条
     改浅之后 grep 了一遍 var(--on-dark，消费点是 0 —— 一个都不剩。
     按当初写它们时留的那句话办：真的没人用就删，别留死令牌。
     （唯一还会浮在照片上的文字是首页 h1，它现在压在白遮罩上，走 --ink。） */

  /* 2026-10-15 第 24 轮：页脚改回深底，这组**按实际用到的规模**回来 4 个。
     只有一个消费者（页脚）—— 上一组就是攒到"消费点 0"才被整组删的，
     别再加档。对比度是按 --deep-900(#062A38) 实测的：
       --on-dark        15.0:1   标题 / 公司名
       --on-dark-2      11.9:1   链接 —— **必须比正文亮**，层次别反过来
       --on-dark-3       8.7:1   正文 / 地址 / 版权
       --on-dark-line            页脚内的分隔线（--line 是浅绿，深底上看不见）
     ⚠️ 深底上的强调色用 --jade-300(#4FC391, 6.84:1)，**不是 --jade-400** ——
        #12804D 压在深青上只有 3.02:1（第 31 轮压暗前是 4.47:1），差得更远。
     ⚠️ 三个值都比模板站那组亮（模板站是 #B9C7D4 / #7E93A6 / 10% 线，正文
        4.7:1、链接和正文还同色）—— 用户这次要的就是"突出字体"。 */
  --on-dark:#FFFFFF;
  --on-dark-2:rgba(255,255,255,.88);
  --on-dark-3:rgba(255,255,255,.74);
  --on-dark-line:rgba(255,255,255,.16);
  /* 首屏那层纱（2026-10-16 第 28 轮新增）。
     第 28 轮首屏从「左侧白渐变 + 深色字」翻成「满幅照片 + 白字」，原来那道
     白渐变整个换掉 —— 白字要压在**深底**上，于是这里第一次出现"深色纱"。
     基色是 --deep-900（= #062A38），不是随手一个黑：压完仍是品牌那支深青，
     照片只是变暗、不掉色相。
     ⚠️ **α 是量出来的，不是估的**（geo_check\contrast_r28.py）：把文字
        visibility:hidden 藏掉再渲，拿真正的合成底色逐像素取最差，两帧各量一遍。
        上下限也是定死的：**下限**＝导航那一行 / 眉标 / 描述的最差像素要够 4.5:1，
        **上限 0.72** —— 再深照片就是一块灰板，"满幅照片"这个前提就没了。
     实测（六档 0.50/.55/.60/.64/.68/.72 × 两帧，逐档打印）：
        .50/.55/.60 导航那一行在板微距帧压不住（2.85 / 3.23 / 3.72）
        .64        只剩导航 4.15，差 0.35
        **.68       两帧全过（B 帧最差是导航 4.67、logo 6.05、h1 5.61、描述 5.20）← 取这档**
        .72        也全过，但已经贴到上限，没必要再压暗照片
     改这张照片、或改文字的落点，都要回去重量一遍。 */
  --hero-scrim:rgba(8,9,11,.72);

  /* ---------- 1.3 语言 · 字体 ---------- */

  --font-sans:"Plex Sans","Segoe UI",system-ui,-apple-system,"Helvetica Neue",
              Arial,"Microsoft YaHei","微软雅黑",sans-serif;
  --font-mono:"Plex Mono",ui-monospace,"SF Mono",Consolas,"Courier New",monospace;

  /* 字号阶梯。相邻两档必须拉开 —— 14/15/16 这种挤在一起的档位等于没有阶梯 */
  --fs-2xs:11.5px;
  --fs-xs:13px;
  --fs-sm:14.5px;     /* 界面尺寸：卡片正文 / 表格 / 按钮 / 输入框 / FAQ 摘要 */
  --fs-base:16px;
  --fs-md:18px;
  --fs-lg:22px;
  --fs-xl:30px;       /* 首屏数字带 */
  --fs-2xl:clamp(27px,2.9vw,38px);
  --fs-3xl:clamp(33px,4.0vw,48px);
  --fs-page:clamp(31px,4.2vw,50px);   /* 内页大标题。比 h2 大一档 */

  --lh-display:1.03;
  --lh-heading:1.18;
  --lh-snug:1.42;
  --lh-body:1.68;

  --ls-display:-.030em;
  --ls-heading:-.014em;
  --ls-body:0;
  --ls-label:.10em;
  --ls-wide:.16em;

  /* ---------- 1.4 骨架 · 间距（4px 基准） ---------- */

  --s-1:4px;   --s-2:8px;   --s-3:12px;  --s-4:16px;
  --s-5:20px;  --s-6:24px;  --s-7:32px;  --s-8:40px;
  --s-9:48px;  --s-10:56px; --s-11:64px; --s-12:72px;
  --s-13:88px; --s-14:104px;

  --max:1140px;        /* 版心 */
  --measure:62ch;      /* 正文行宽。超过 70ch 就难读了 */
  --header-h:84px;   /* 第 4 轮从 68 提到 84。第 12 轮之后有 4 处引用它：
                        这个定义、scroll-padding-top（锚点自动跟着让位）、
                        header.site .wrap 的高度，和 .nav-panel 的 top
                        （抽屉要挂在页头下沿）。改这一处就够了 */
  --nav-bridge:14px; /* 导航悬停桥的高度。抽屉顶边**比页头底边还高这么多**，
                        于是桥的下半截压在抽屉上，鼠标从标签移到抽屉那段
                        不会掉进缝里（没有它菜单当场就关）。
                        ⚠️ 一个数两处消费，必须同源：.nav-grp::after 的高度、
                        .nav-panel 的 top 偏移。改一头不改另一头就漏缝。 */
  --header-flip:491px; /* 首页页头"透明 → 白条"的翻转点（2026-10-16 第 33 轮重取）。
                        几何上导航压在照片上的区间是「滚动位置 ≤ 首屏高 − 页头高」，
                        所以这个数就该取那个差：量到首屏高之后填进来，不是拍的。
                        实测首屏高 575 → 575 − 84（页头内容盒高）= **491**。
                        （第 33 轮首屏加重：`.home .hero h1` 提到 clamp(34px,4.6vw,58px)、
                          `.hero-cols` 改成 1.18fr/.82fr，标题多占一行、首屏 534 → 575。
                          这一轮它又变了一次 —— 历史 337 → 401 → 450 → 491，
                          **四次里没有一次是"手改的"，全都是首屏高度真变了**。）
                        （上面 534 是**第 28 轮**那个值：修好 .hero-rule--foot 之后
                          量到的 —— 那条收口细线原先量出来是 0×0，连它上面 --s-9
                          那 48px 的间距一起没生效；补上 display:block 之后首屏
                          就从 485 长到 534。留在这里是为了让下面那条历史线看得懂。）
                        ⚠️ 别拿 486（首屏底沿）去减：页头是 sticky 且在流内，
                           它自己占掉页面最上面 85px（84 + 1px 下边线），所以
                           首屏的顶在 y=1 而不是 0，差的就是那 1px 边线。
                        ⚠️ **这个数会跟着首屏的高度变**，而首屏高由文字决定：
                           H1 换行多一行、lede 增减一句、上下的 --s-13/--s-11 一动，
                           它就得重量。填错的症状是"页头比照片先变白"（照片还压在
                           它背后就白了）或"变晚了"（白字压在页头的白底上）。
                           probe_r28.py 里有一条断言专门比这个差，别绕过它。
                        ⚠️ 第 28 轮第一版填的是 337 —— 那时量到首屏高 421，
                           而 421 本身就是错的：`.hero` 那条规则当时被上面一句
                           注释里的「星号紧跟斜杠」提前关掉了（`--deep-*` 后面
                           紧跟 `/`），整条规则被 CSS 解析器丢弃，
                           `padding-bottom` 没了，首屏自然矮 64px。
                           **量错高度不是因为量法，是因为样式压根没生效。**
                        ⚠️ 写这条注释的时候**又踩了同一个坑**：为了说清楚上一句，
                           这里原本原样引了那两个字，于是这句注释自己也被提前
                           关掉 —— 后面的中文被当成声明往下吃，一直啃到下一个
                           半角分号，把紧随其后的 `--r-xs:4px` **整条吃掉**了。
                           症状是"什么都没变"，只有全站 4 处圆角悄悄从 4px 变 0。
                           所以：**注释里永远不要写出那两个字**，要写就写
                           「星号紧跟斜杠」；`css_lint_r28.py` 就是专门查这个的，
                           它一报"代码位置出现中文"就是又发生了。
                        ⚠️ 只是"什么时候翻"，不影响任何别的数；滚动时间线的
                        animation-range 直接吃它，改这一个值就够了。
                        只在 @supports (animation-timeline: scroll()) + ≥1021px 里用。 */

  /* ---------- 1.5 质感 · 圆角（按元素大小分级，不要全站一个值） ---------- */

  --r-xs:4px;     /* 小标签、chip */
  --r-sm:7px;     /* 输入框、按钮 */
  --r-md:11px;    /* 卡片、表格 */
  --r-lg:16px;    /* 大面板 */
  --r-full:999px;

  /* ---------- 1.6 质感 · 投影（只有三级，且都很淡） ---------- */

  --sh-1:0 1px 2px rgba(9,67,91,.05);
  --sh-2:0 2px 6px -1px rgba(9,67,91,.08), 0 1px 2px rgba(9,67,91,.05);
  --sh-3:0 14px 32px -10px rgba(9,67,91,.18), 0 2px 6px -1px rgba(9,67,91,.07);

  /* ---------- 1.7 质感 · 动效 ---------- */

  --ease:cubic-bezier(.2,.65,.3,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --dur-1:110ms;
  --dur-2:190ms;
  --dur-3:340ms;
  /* 首屏两层照片之间交叉淡入的一整圈时长（含两次 1/6 的过渡）。
     12s 是照参考站 screamingcircuits.com 取的 —— 它 :root 里就是
     `--crossfade-duration:12s`，配 `sc-image-crossfade … ease-in-out infinite`。
     ⚠️ 这是**时间**轴上的时长，不是滚动距离。第 27 轮那版是滚动驱动的
        （animation-range:0 420px），第 28 轮按参考站的真实机制换成时间循环。 */
  --dur-xfade:12s;

  /* ---------- 1.8 签名 · 布线场 ----------
     浅底上的底纹。不是通用的方格 —— 是 PCB 上的走线：
     板上只允许 45° 和 90° 两种角度，所以这个图案只有这两个角度，
     配圆焊盘（过孔）。懂行的人一眼认出这是块板，而不是"某家工程公司"。
     96×96 的瓦片，四条边的出入口对齐，可无缝平铺。

     ⚠️ 2026-10-13 第 12 轮：**从四级音量收到一级，而且是深色线。**
        原来有 hero(.22) / page(.14) / dark(.07) / light(.07) 四级，几何逐字相同、
        只有浓度不同 —— 那套之所以成立，是因为背景从深蓝一路到白，浓度得跟着走。
        这一轮四块深色底（首屏 / 内页页头 / 深色带 / 页脚）全改浅，
        前三级的消费者（.hero::before、.page-head.board::before、.band::before）
        也随规则一起删了。**留着就是三个死令牌**，所以一起删掉。

        现在只剩这一条：浅底深线，.07。两个消费者，都是"白底上的一层纸纹"——
          · .page-head::before   内页页头右半边（标题是空的，正好放底纹）
          · .tile .art::before   首页瓦片的插图区
        浓淡要改就改 SVG 里的 stroke-opacity / fill-opacity。
        **不要用元素级 opacity 调** —— 那会把同一层里的方格渐变一起乘掉
        （第 1 轮踩过，浓度只能写在 SVG 自己身上）。

     ⚠️ 这是全站唯一一处「裸数值」类的例外，躲不掉：data-URI 里的 SVG 取不到
     CSS 变量，颜色只能写死。写死的两处都在下面，**换主色时手动改**：
       --routing-light  的走线色 %2309435B（= --deep-800），浅底版纹
       --ic-quote-white 的描边色 %23FFFFFF，实心绿底那颗询价图标用
       外加 --ic-mail / --ic-tel / --ic-top / --ic-wa / --ic-wechat
       五个 %2309435B（白底上的灰图标）
     第 12 轮之前这几个值写的是旧的 navy-800/navy-900，改名换值的时候
     **正则抓不到 data-URI 里的 %23 十六进制**，是一处一处手改的。 */
  --routing-light:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 96 96'%3E%3Cg fill='none' stroke='%2309435B' stroke-width='1' stroke-opacity='.07'%3E%3Cpath d='M0 32h28l16 16h24l16-16h12'/%3E%3Cpath d='M44 0v20'/%3E%3Cpath d='M44 96v-18'/%3E%3Cpath d='M28 32V14'/%3E%3Cpath d='M68 48v18'/%3E%3C/g%3E%3Cg fill='%2309435B' fill-opacity='.07'%3E%3Ccircle cx='28' cy='14' r='2.4'/%3E%3Ccircle cx='44' cy='20' r='2.4'/%3E%3Ccircle cx='44' cy='78' r='2.4'/%3E%3Ccircle cx='68' cy='66' r='2.4'/%3E%3C/g%3E%3C/svg%3E");

  /* ---------- 1.9 图标（data-URI SVG）----------
     右缘联系条用的六个图标。**和上面 --routing-* 共用同一条豁免** ——
     data-URI 里的 SVG 一样取不到 CSS 变量，所以颜色只能写死。

     为什么不内联进 HTML：6 个图标 × 21 个页面 = 126 段 SVG，
     而且它们不含任何数据（不像邮箱电话），放 CSS 里只写一遍。

     ⚠️ 2026-10-13 第 12 轮：**颜色从白色改成了深青**。
        第 4 轮它们一律是 %23FFFFFF，因为联系条那时候是深蓝底；
        现在联系条改成白底（见 .rail），白图标在白底上等于隐形。
        --ic-mail / --ic-tel / --ic-top / --ic-wa / --ic-wechat
          → %2309435B（= --deep-800），
        --ic-quote-white 那颗配的是实心阻焊绿底（--green-500），底色深，图标反过来用白。
        **这是一处不会报错的坏法**：颜色写死在 data-URI 里，
        改了底不改它，图标不会消失、也不会报错，就是看不见。

     24×24 的 viewBox，描边 1.6，只用 45° 和 90° 折角 —— 和板上的走线同一条约束。

     一共 6 个。⚠️ 原来这里写的是"各有各的底色"—— 2026-10-14 加进来的
     --ic-wa / --ic-wechat 两颗**和 --ic-mail 共用一个底色**（都是右缘条
     的白底），不再是一对一。别照旧话去推"每个图标配一种底色"。 */
  --ic-quote-white:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 3h7L18 7.5V21H6.5z'/%3E%3Cpath d='M13.5 3v4.5H18'/%3E%3Cpath d='M9.5 13h5'/%3E%3Cpath d='M9.5 16.5h5'/%3E%3C/svg%3E");
  --ic-mail:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2309435B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 6.5h18v11H3z'/%3E%3Cpath d='M3.4 7.2l7.8 5.6a1.6 1.6 0 0 0 1.6 0l7.8-5.6'/%3E%3C/svg%3E");
  --ic-tel:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2309435B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.8 3.5h2.9l1.4 3.8-2 1.4a10.6 10.6 0 0 0 6.2 6.2l1.4-2 3.8 1.4v2.9a2 2 0 0 1-2.2 2A16.8 16.8 0 0 1 4.8 5.7a2 2 0 0 1 2-2.2z'/%3E%3C/svg%3E");
  --ic-top:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2309435B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20V4.5'/%3E%3Cpath d='M5.5 11L12 4.5 18.5 11'/%3E%3C/svg%3E");
  /* WhatsApp —— 右缘条第 2 颗（2026-10-14）。方形气泡 + 三点。
     站上唯一标了 WhatsApp 的图形记号，rail 那颗靠它认。 */
  --ic-wa:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2309435B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.4 5.7h15.2v10H10l-4.2 3.6v-3.6H4.4z'/%3E%3Ccircle cx='8.9' cy='10.7' r='1.05' fill='%2309435B' stroke='none'/%3E%3Ccircle cx='12' cy='10.7' r='1.05' fill='%2309435B' stroke='none'/%3E%3Ccircle cx='15.1' cy='10.7' r='1.05' fill='%2309435B' stroke='none'/%3E%3C/svg%3E");

  /* 微信 —— 右缘条第 3 颗。两个斜对角的气泡（大泡带两个点）。
     ⚠️ 和 --ic-wa 都是"聊天气泡"，容易看混。区别靠结构不靠颜色：
        WhatsApp 是**一个**宽气泡 + 三点，微信是**两个**气泡 + 两点。
        图标是 data-URI 背景图，用不了 currentColor，所以只能靠形状区分。 */
  --ic-wechat:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2309435B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.2 11.6h9.2v5.6H7.4l-2.6 2.2v-2.2H3.2z'/%3E%3Cpath d='M11.4 5.4h9.4v5.4h-1.5v2.1l-2.2-2.1h-5.7z'/%3E%3Ccircle cx='6.4' cy='14.4' r='1.05' fill='%2309435B' stroke='none'/%3E%3Ccircle cx='9.2' cy='14.4' r='1.05' fill='%2309435B' stroke='none'/%3E%3C/svg%3E");

}


/* ============================================================
   第 2 层 · 语言 —— 排版基础
   ------------------------------------------------------------
   三条规则：
     ① 字族只有两个：Plex Sans 读文字，Plex Mono 读数据
     ② 大字号用负字距，小字号用正字距（这是专业排版的标志）
     ③ 行高跟字号成反比 —— 字越大行越紧
   ============================================================ */

*,*::before,*::after{box-sizing:border-box}

html{
  scroll-behavior:smooth;
  -webkit-text-size-adjust:100%;
  scroll-padding-top:calc(var(--header-h) + var(--s-5));
}

body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font-sans);
  font-size:var(--fs-base);
  line-height:var(--lh-body);
  font-weight:400;
  letter-spacing:var(--ls-body);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  font-kerning:normal;
  font-variant-ligatures:common-ligatures;
  text-rendering:optimizeLegibility;
}

/* 排版的隐形加分项：让标题自己找平衡点断行，不出现"孤字行" */
h1,h2,h3,h4{text-wrap:balance}
p,li,figcaption,td{text-wrap:pretty}

h1,h2,h3,h4{margin:0;font-weight:600;letter-spacing:var(--ls-heading);color:var(--ink)}
/* 正文里的小标题：上面留足空白、下面贴近正文。这条统一了长文里的阅读节奏 */
h3{margin:var(--s-7) 0 var(--s-3)}
h3:first-child,.card h3,.grid h3,.answer h3,figcaption h3{margin-top:0}
h1{font-size:var(--fs-3xl);line-height:var(--lh-display);letter-spacing:var(--ls-display)}
/* h2 的上下节奏。一页里有好几个 h2 时（FAQ 最典型），
   第二个起的 h2 要自己跟上面的内容拉开距离 —— 页面里不用再写内联 margin。 */
h2{font-size:var(--fs-2xl);line-height:var(--lh-heading);margin:var(--s-9) 0 var(--s-4)}
h2:first-child{margin-top:0}
h3{font-size:var(--fs-lg);line-height:var(--lh-snug);font-weight:600}
h4{font-size:var(--fs-sm);line-height:var(--lh-snug);font-weight:600}

p{margin:0 0 var(--s-4)}

/* 数字一律等宽 —— 表格上下对齐，是这个站"专业感"的一半来源 */
.n,table td,.stats b,.process li::before,.card .num,.tile .num{
  font-variant-numeric:tabular-nums;
}

img{max-width:100%;display:block;height:auto}

a{color:var(--accent-ink);text-decoration:none;
  transition:color var(--dur-1) var(--ease)}
a:hover{color:var(--accent-ink)}

/* ---------- 可点性：正文里的链接一律带下划线 ----------
   用户 2026-10-19：「基本上可以跳转的地方都不能明显的看到」。
   原来全站链接**只靠颜色**区分（a 是 --deep-600，正文是 --ink），
   这既是"看不见"的直接原因，也踩了 WCAG 1.4.1（不许只用颜色传达信息）。

   装饰线取 currentColor 而不是另开一个令牌：深底上的链接自己会变白
   （`.home .dark a`），下划线跟着白，不用再维护第二套颜色。
   ⚠️ 下划线不进盒模型 —— 全站高度必须 0 变化，改完跑 heights_r23.py 对账。
   ⚠️ 这里故意**不用 :where()** —— 它的优先级是 0，下面那几条 reset 就压不住了。 */
p a,li a,td a,dd a,dt a,figcaption a,blockquote a,
.answer a{
  /* 正文 400、链接 500（Plex Sans 有真 500 字重，不是合成加粗）。
     用户 2026-10-19：「字体我觉得可以变粗一点」。 */
  font-weight:500;
  text-decoration:underline;
  text-decoration-color:currentColor;
  text-decoration-thickness:1px;
  text-underline-offset:.18em;
}
p a:hover,li a:hover,td a:hover,dd a:hover,dt a:hover,figcaption a:hover,blockquote a:hover,
.answer a:hover{
  text-decoration-thickness:2px;
}

/* 外壳类组件不加下划线 —— 它们不是"句子里的链接"，划上去只会把版面弄脏。
   ⚠️ `footer.site a` 这条**必须留着**：页脚的链接包在 `<p style="margin:0">` 里
      （不是 <li>），不 reset 的话 22 页每页三十多条会一起被划上。
   ⚠️ `.more a` 已经有 border-bottom 当装饰线，再叠一条就是双线。 */
footer.site a,
.more a,
.btn{text-decoration:none}
/* 页脚不加常驻下划线（三十多条一起划会糊成一片），但**悬停补上** ——
   颜色变化在深底上本来就不明显，下划线才是那颗"能点"的信号。 */
footer.site a:hover{text-decoration:underline;text-underline-offset:.18em}

/* 卡片标题（guides 索引那 48 处）：它长得和普通 h3 一模一样，
   是全站最"点下去才知道能点"的地方。 */
.card h3 a{
  text-decoration:underline;
  text-decoration-color:currentColor;
  text-decoration-thickness:1px;
  text-underline-offset:.16em;
}
.card h3 a:hover{text-decoration-thickness:2px}

::selection{background:rgba(35,149,92,.55);color:#fff}

/* 键盘焦点 —— 鼠标点击不出现，Tab 键一定出现 */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--accent-strong);
  outline-offset:2px;
  border-radius:var(--r-xs);
}

/* 等宽小标签。全站"技术文档感"的主要来源 */
.mono,
.eyebrow,.pill,th,.gallery figcaption b,.diagram figcaption b{
  font-family:var(--font-mono);
}


/* ============================================================
   第 3 层 · 骨架 —— 网格与节奏
   ============================================================ */

.wrap{max-width:var(--max);margin:0 auto;padding:0 var(--s-6)}

/* 版块的垂直节奏。不是所有版块都 64px —— 首页那种密集排列要更紧 */
section{padding:var(--s-13) 0}
section.tight{padding:var(--s-9) 0}
section.loose{padding:var(--s-14) 0}
/* 交替色带。⚠️ 第 12 轮把底从 --paper 改成 --band（当时的淡薄荷）——
   因为 --bg 和 --paper 现在**都是白**，不改的话 .alt 会完全隐形，
   整站的"白 / 浅带"交替就没了（这是这一版唯一的节奏来源）。
   第 33 轮 --band 去了彩度（见 1.1 节 --tint-* 那段），交替**照旧成立** ——
   交替靠的是那一丁点明度差，不是绿。
   ⚠️ .alt 是相邻交替的，往 HTML 里插新 section 时要接着上一节的底色，
      别插出两条同色的相邻块。（首页第 33 轮加的 .dark 深色节**不带 .alt**，
      所以这一项在首页仍然是"去掉深色节之后相邻的 .alt 继续交替"，结论不变。） */
section.alt{background:var(--band);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}

.sub{
  color:var(--ink-2);font-size:var(--fs-md);line-height:var(--lh-snug);
  max-width:var(--measure);margin:var(--s-3) 0 var(--s-7);
}
.lead{
  font-size:var(--fs-md);line-height:1.6;color:var(--ink-2);
  max-width:var(--measure);margin:0 0 var(--s-5);
}

/* 网格：明确列数，不用 auto-fit。
   auto-fit 会让相邻两个版块的列宽对不齐，那是"廉价"最直接的来源。 */
.grid{display:grid;gap:var(--s-5)}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
@media(max-width:1000px){.g3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:720px){.g2,.g3{grid-template-columns:1fr;gap:var(--s-4)}}


/* ============================================================
   第 4 层 · 质感 —— 页头
   ============================================================

   2026-10-13 第 12 轮：页头从「板子的上沿」改回**白底 + 一道细线**。
   第 4 轮那套（2px 板边金 + 当前页一枚焊盘点）整组撤掉 ——
   板语言这一轮全站不要了，页头是最后一块。

   现在和 hrpcba 一样：半透明白 + 背景模糊 + 1px 细线。内容收在悬停展开的
   抽屉里，所以页头本身要尽量"消失"，靠留白和细线分割，不靠色块。

   ⚠️ 导航这一轮从 7 项收到 4 项 + CTA，但**抽屉里的链接从 6 条涨到 23 条**。
      折行断点已经**重量过两次**（1050 -> 950 -> 1020，见文件末尾那个 @media 里的两个数）。
      改字号、加减项都要连着重量 —— 注意"顶层项数"和"抽屉里链接数"是两回事，
      抽屉里那些条是浮层，**不参与**页头那一行的宽度，别拿它去推断点。

      （2026-10-14 第 20 轮：FAQ 从 Company 抽屉提到顶层 —— 顶层项 5 -> 6、
        抽屉里的链接 23 -> 22、断点 950 -> 1020，三个数都是当场量的。）
   ============================================================ */

header.site{
  position:sticky;top:0;z-index:50;
  background:rgba(11,13,15,.86);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
header.site .wrap{
  display:flex;align-items:center;justify-content:space-between;
  height:var(--header-h);gap:var(--s-5);
}

.brand{display:flex;align-items:center;gap:var(--s-3);color:var(--ink);
  font-weight:600;letter-spacing:-.01em;font-size:var(--fs-lg)}
.brand:hover{color:var(--jade-600)}
/* 品牌标记：2026-10-13 起是公司真 logo（绿色 wordmark 的整枚 lockup）。
   原来是 .mark —— 金底圆角方块 + 字母 H，一个 CSS 画的占位符，连同它的
   .sub 副行一起删了（两处都没有别的消费者）。
   ⚠ .mark 这个类本身还在用：.badge .mark 挂认证徽章（assets/certs/marks/），
     只是页头不再用它 —— 所以那条 .badge .mark 规则别顺手删。
   高度 46px 是渲图定的：38px 时第二行 "PCBA & ELECTRONIC" 糊，
   46px 读得清；页头高 84px，46px 的 logo 上下各留 19px，居中正好。
   白底上绿 logo 比在深蓝底上更好读 —— 第 12 轮改浅之后它反而是受益的一方。 */
.brand .logo{height:46px;width:auto;flex:0 0 auto;display:block}

nav.main{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
nav.main a{
  position:relative;   /* 给 .on::after 那条下划线当定位父 */
  color:var(--ink-2);font-size:var(--fs-base);font-weight:500;
  padding:var(--s-2) var(--s-4);border-radius:var(--r-sm);white-space:nowrap;
  transition:background-color var(--dur-2) var(--ease),
             color var(--dur-2) var(--ease);
}
nav.main a:hover{background:var(--tint-100);color:var(--ink)}
/* 当前页：深字 + 底下一道 2px 青绿下划线，**没有背景色**。
   下划线用绝对定位（左右各内缩一个 --s-4），所以当前页和别的页宽度完全一样
   —— 换页时导航不会抖。这条是第 7 轮为"药丸会改宽度"定的，理由没变，
   这一轮只把颜色从板边金换成 --jade-400。
   ⚠️ 别改成 border-bottom：那会把行高撑起来 2px，正是上面要避免的抖动。
   不做的事：光靠颜色区分"当前页"严格说不够，正解是给带 .on 的 <a> 加
   aria-current="page"，那要改 24 个 HTML。已记进改站清单.md 当待办。 */
nav.main a.on{color:var(--ink);background:transparent}
nav.main a.on::after{
  content:"";position:absolute;left:var(--s-4);right:var(--s-4);bottom:1px;
  height:2px;border-radius:2px;background:var(--jade-400);
}
/* 「Get a quote」—— 全站唯一一颗走**材料色**的按钮：阻焊绿底 + 一道沉金底边
   （--green-500 + --enig，见第 1 层那段注释）。绿是"板"，金是"焊盘"。
   其余按钮仍然是 --jade-* 品牌绿 —— 这颗之所以破例，是因为它要回答的正是
   "你们是做板的吗"，用板的颜色答比用品牌色答直接。
   inset 阴影而不是 border-bottom：后者会把行高撑起 2px，nav 换页时会抖。 */
nav.main a.cta{
  background:var(--green-500);color:#fff;font-weight:600;
  box-shadow:inset 0 -2px 0 var(--enig);
  margin-left:var(--s-2);
  transition:background-color var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
nav.main a.cta:hover{background:var(--green-600);color:#fff;transform:translateY(-1px)}


/* ---------- 分组抽屉（纯 CSS，全站没有一行 JS） ----------
   Home │ Capabilities ▾ │ Guides ▾ │ Company ▾ │ FAQ │ [Get a quote]
   顶层 5 项 + CTA，抽屉里 22 条 —— 顶层比原来还短，深的内容收起来了。

   FAQ 是 2026-10-14 第 20 轮从 Company 抽屉提上来的，**故意做成纯链接、不带
   chevron**：它是独立页、没有下拉，加箭头的唯一效果是暗示一个点不开的东西。
   形状和 Home 完全一致 —— 顶层两项"点了就走"的，读起来最干净。
   （原先它在抽屉里的副标题写着 "Twenty-one questions"，那是手写会过期的数字，
     已经 34 条了 —— 跟着搬家一起删掉，与第 19 轮删分组计数同一条纪律。）

   打开和关闭只能靠 :hover 和 :focus-within 两件事（没有 JS 可写）。
   四条必须踩对的细节，缺一条就散架：

   ① **悬停桥** `.nav-grp::after` —— 标签底边到抽屉顶边之间有 14px 是空的，
      鼠标往下移的那一瞬间会"离开" .nav-grp，没有桥菜单当场就关。
      桥本身透明，但它是 .nav-grp 的一部分，所以鼠标还在里面。
   ② `:focus-within` 一起开 —— Tab 进抽屉时也要展开，不然键盘用户根本进不去。
   ③ 父链接本身**是真链接**，所以**不写 aria-expanded** —— 没有 JS 就维护不了
      它的状态，写一个永远停在 false 的属性比不写更糟。菜单是渐进增强：
      点父链接照常跳到栏目页。
   ④ ≤ 折行断点直接 display:none —— 触屏没有 hover，这条不是可选优化。
      父链接还在，所以窄屏不会丢入口。

   ⚠️ 祖先链（header.site → .wrap → nav.main）上不许有 overflow:hidden ——
      抽屉是往下探出页头的。已核过：三处都是默认的 visible。
   ⚠️ 抽屉用 position:fixed，**不锚在自己那个 .nav-grp 上**。
      （2026-10-14 修）原来写的是 `right:0` 锚在分组上，只考虑了"不要从右边
      出血"，漏了左边：抽屉宽度是 max-content（Guides 那套三栏 961px），
      而分组右缘距视口右边只有 ~215px（Guides）/ ~320px（Capabilities）——
      视口一窄，右缘对齐之后**左缘就跑到屏幕外**，整张 feature 卡被切。
      品红底实测的起点：Capabilities 窄于 1160px 起切，Guides 窄于 1354px
      起切（1280 的笔记本首当其冲，而它正是最常见的桌面宽度之一）。
      改成 fixed 之后 right 相对页面算：右缘永远贴住 .wrap 的内容右缘
      （也就是 CTA 那颗按钮的右缘），左缘有 max-width 兜着，任何宽度都不出血。
      这个 bug 读 CSS 看不出来 —— `right:0` 本身完全合法，是"宽度 + 锚点位置"
      在窄视口下的组合才出事。只有渲图量边缘才发现得了。 */
.nav-grp{position:relative;display:flex;align-items:center}
.nav-grp>a{display:inline-flex;align-items:center;gap:6px}
.nav-grp::after{                       /* ① 悬停桥 */
  content:"";position:absolute;top:100%;left:0;right:0;height:var(--nav-bridge);
}
.chev{
  width:10px;height:10px;flex:0 0 auto;opacity:.55;
  transition:transform var(--dur-2) var(--ease),opacity var(--dur-2) var(--ease);
}
.nav-grp:hover .chev,.nav-grp:focus-within .chev{transform:rotate(180deg);opacity:.9}

.nav-panel{
  /* fixed 而不是 absolute：absolute 会把抽屉锚在分组上，分组一往左移抽屉
     就顶出屏幕（见上面那段）。fixed 的包含块与分组无关。 */
  position:fixed;
  top:calc(var(--header-h) - var(--nav-bridge));   /* 页头底边，再往上一个桥高 */
  right:calc(max(0px,(100vw - var(--max)) / 2) + var(--s-6));  /* = .wrap 的内容右缘 */
  z-index:60;
  display:grid;grid-template-columns:224px minmax(0,1fr);gap:var(--s-6);
  width:max-content;max-width:min(760px,calc(100vw - 2 * var(--s-6)));
  padding:var(--s-5);
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-3);
  /* 收起态用 visibility 而不是 display:none —— display 不能过渡，
     菜单会"啪"地出现。visibility 能等淡出走完再隐藏（见那个 transition-delay）。 */
  opacity:0;visibility:hidden;pointer-events:none;
  transform:translateY(-6px);
  transition:opacity var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out),
             visibility 0s linear var(--dur-2);
}
.nav-grp:hover .nav-panel,
.nav-grp:focus-within .nav-panel{
  opacity:1;visibility:visible;pointer-events:auto;
  transform:translateY(0);transition-delay:0s;
}

/* 左栏那张 feature 卡：照片 + 标题 + 一句。抽屉里唯一的图，所以它决定
   这个菜单给人"有内容"还是"一堆链接"的印象。 */
/* ⚠️ `white-space:normal` 这里**写了不算数**，得看下面那条带 `nav.main`
   前缀的 —— 这个坑 2026-10-13 踩过一次，记在这里免得再踩：
   `.nav-feat` 自己就是个 `<a>`，会继承 `nav.main a{white-space:nowrap}`，
   而 `nav.main a` 是 **(0,1,2)**、`.nav-feat` 只有 **(0,1,0)** —— 白名单被压着。
   后果：副标题不折行，撑出卡片右边再被上面的 overflow:hidden 切掉，
   屏幕上只剩半句 "What we can build, and what we"。
   这类 bug 没有任何报错：字在 DOM 里、AI 读得到、grep 找得到，
   **只有渲图看才发现**。 */
.nav-feat{display:block;border-radius:var(--r-md);overflow:hidden;
  border:1px solid var(--line);background:var(--tint-50);padding:0;
  /* 别被 grid 拉到和右栏一样高：图是 4/3 固定的，拉高只会让图下面空一大片。
     拉满的代价是要把 480×360 cover 成 224×330（横向裁掉一半），
     机器主体很可能被裁没 —— 不划算。 */
  align-self:start}
.nav-feat img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
.nav-feat b{display:block;padding:var(--s-3) var(--s-4) 0;
  font-size:var(--fs-sm);font-weight:600;color:var(--ink)}
.nav-feat span{display:block;padding:2px var(--s-4) var(--s-4);
  font-size:var(--fs-xs);line-height:1.5;color:var(--mute)}
.nav-feat:hover{border-color:var(--jade-200);background:var(--jade-100)}
/* ① 生效的那一条（(0,2,0) > (0,1,2)）。上面 .nav-feat 里那句不管用，原因见那里的注释。 */
nav.main .nav-feat{white-space:normal}

/* 右栏链接。每条都能带一行灰字说明 —— 抽屉里链接多，纯列表很难扫。
   ⚠️ 结构是「列」不是「行」：.nav-cols 的每个孩子是一个 <div>，里面是
      小标题 <p> + 若干 <a>。**不要**写成小标题和链接平铺在同一层 ——
      那样标题得 grid-column:1/-1 占满一行，它下面那组链接就会在 3 栏里
      排成 3+1，单出来那个孤零零挂在第二行。按列切就没这个问题。 */
.nav-cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s-2) var(--s-5);align-content:start}
.nav-cols.tri{grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-2) var(--s-6)}
/* ⚠️ 三栏必须是 `1fr` 而**不是** max-content。max-content 的栏宽是"最长那条
   链接"决定的，容器再窄也压不下去，只会从抽屉里溢出去。1fr 的栏可以缩到
   min-content（最长的单词），长链接自己折行 —— 抽屉在 1020px 视口下才不会
   撑破 max-width。 */
.nav-cols>div{display:flex;flex-direction:column;gap:1px;min-width:0}
.nav-cols p{                     /* 分栏小标题 */
  margin:0 0 var(--s-1);padding:0 var(--s-3);
  font-family:var(--font-mono);font-size:var(--fs-2xs);
  letter-spacing:var(--ls-wide);text-transform:uppercase;color:var(--mute);
}
.nav-cols a{display:block;padding:var(--s-2) var(--s-3);border-radius:var(--r-sm);
  font-size:var(--fs-sm);font-weight:500;color:var(--ink);line-height:1.35}
.nav-cols a:hover{background:var(--jade-100);color:var(--jade-600)}
.nav-cols a span{display:block;font-weight:400;font-size:var(--fs-2xs);
  color:var(--mute);margin-top:2px}
/* ②③ 同一个特异性坑：`.nav-cols a` 是 (0,1,1)，照样压不过 `nav.main a`。
   不挂上 nav.main 的话，Guides 那种三栏布局里 "What to send for a quote"
   不折行、直接撞进右边那一栏，两段字叠在一起。
   修好折行之后还得把 tri 面板放宽：760px 分三栏，每栏只剩 ~150px，
   "What to send for a quote" 实测 ~180px，折成两行还是难看。 */
nav.main .nav-cols a{white-space:normal}
.nav-panel:has(.nav-cols.tri){max-width:min(1060px,calc(100vw - 2 * var(--s-6)))}
/* ⚠️ 1060 = **实测自然宽度 1044px + 16px 余量**（`_tri_natural2.py`：
   注入 `outline:3px solid red` 量边框盒 —— outline 不进布局，不会改变被量的东西）。
   余量是给字体渲染的跨平台差异留的：Windows 的 Chrome 和客户那边的
   macOS / 安卓不是同一套字形度量，卡在 4px 上别人机器照样折。

   ⚠️ **这个数被量错过两次，两次错法不同，都记在这里：**
     ① 写 980 的时候，注释说"自然宽度 ≈ 948px 是量出来的" —— 其实是**拿算式估的**
        （224 feature + 24 间隔 + 3 栏 + 2×24 栏距 + 40 内边距），估错了。
     ② 改成 1040 的时候，用"找最长连续纯白游程"量到 1025 —— **量法本身是错的**：
        面板底是 --paper（纯白），面板外的首屏也是近白的渐变，两者分不开。
     症状一直是同一个：「Where PCBA lead time goes」折成两行。
     **这个症状不会报错、也不会让面板高度变化**（那一列本来就比 feature 卡矮），
     所以不能靠量高度发现 —— 只能渲图放大看。改栏里的字号、加减链接、
     或动 feature 卡的宽度，都要回去渲一张对着看。

   几何：右缘离屏幕右边恒为 --s-6（24px）；100vw ≤ 1108 时上限里的
   `calc(100vw - 2 * --s-6)` 更小，左缘恒 24px、跟着视口一起缩。
   所以这个数只影响 ≥1108px 的桌面。 */


/* ============================================================
   页头叠加档：导航压在首屏照片上（第 28 轮起 · 第 43 轮改成 JS）
   ------------------------------------------------------------
   第 28 轮这一步是**滚动时间线的离散关键帧**做的（纯 CSS、零 JS）。
   第 43 轮用户明确同意加一小段 JS，于是换成 IntersectionObserver。

   为什么换：
     · 老写法整块包在 @supports (animation-timeline: scroll()) 里，
       Safari 和旧 Firefox 上这一段**完全不存在** —— 那些浏览器上是
       "深色条 + 照片从条下缘开始"，连 .hero 的负外边距都没有。
       换成 JS 之后所有浏览器同一个行为，拉高也不再依赖任何特性支持。
     · 三组 keyframes（site-flip / logo-white / nav-rule）一并删掉。
       ⚠️ **别把两套留着** —— 动画来源在层叠里压得过普通声明，
          会跟 .is-top 类打架，而且不报任何错。

   状态模型只有两个：
     · 默认      → 页头**实底深色**（就是基类 header.site 那条）。
                   JS 完全不跑也读得清，不会出现"透明条浮在正文上"。
     · .is-top   → 页头透明，压在首屏照片上：白 logo、浅字、--jade-300 下划线。

   ⚠️ backdrop-filter 只能从 blur(0px) 起，**绝不许动到 none**：
      .nav-panel 是 position:fixed，它找包含块靠的就是页头这层
      backdrop-filter，中途变成 none 包含块就消失，抽屉会跳位。
      所以透明态写 blur(0px)，不是把这条删掉。 */
header.site.is-top{
  background:transparent;
  border-bottom-color:transparent;
  -webkit-backdrop-filter:saturate(180%) blur(0px);
  backdrop-filter:blur(0px);
}
header.site.is-top .brand .logo{filter:brightness(0) invert(1)}
header.site.is-top nav.main a.on::after{background:var(--jade-300)}

/* ≤1020px 页头是两行，--header-h 这个 84px 不成立，照片压不到它底下 ——
   这一档永不进透明态（nav-scroll.js 里也用 matchMedia 跳过，双保险）。 */
@media(max-width:1020px){
  header.site.is-top{
    background:rgba(11,13,15,.94);
    border-bottom-color:var(--line);
    -webkit-backdrop-filter:saturate(180%) blur(14px);
    backdrop-filter:blur(14px);
  }
  header.site.is-top .brand .logo{filter:none}
  header.site.is-top nav.main a.on::after{background:var(--jade-400)}
}




/* ============================================================
   第 4 层 · 质感 —— 首屏
   ------------------------------------------------------------
   2026-10-13 第 12 轮：从「深蓝渐变 + 走线场 + 金色柔光 + 右半张照片」
   改成「**整幅照片铺满 + 左侧白色渐变遮罩 + 深色字**」。

   2026-10-16 第 28 轮：**照参考站 screamingcircuits.com 重做**。用户拿它
   四张截图来说"我要的是这个效果"。它那版首屏的构成是：
     满幅照片 · 白字 · 上下两条细线 · 眉标起头 ·
     标题占左 45%、描述 + 按钮占右 50% · 导航压在照片上 ·
     照片之间 12 秒自动交叉淡入（`sc-image-crossfade`，时间驱动不是滚动驱动）
   我们搬的是**这套处理手法**，不是它的配色（它走的是 spruce/rainforest 那套
   深绿+奶白，跟我们的 --deep-* 和 --jade-* 是两支）。

   与第 12 轮的差别就两条：白渐变遮罩 → **满铺深色纱**；深色字 → **白字**。
   ⚠️ 这两条是一组，只做一半（换深纱但字还是深色）等于把首屏做成一片糊。

   板语言（.hero::before 走线场、.hero::after 金色柔光、.hero-frame 板框、
   .hero-silk 丝印）整组撤掉 —— 第 12 轮用户明确不要了。
   ============================================================ */

.hero{
  position:relative;overflow:hidden;
  /* 底色是白，不是照片 —— **没照片也不塌** */
  background:var(--bg);
  color:var(--ink);
  padding:var(--s-13) 0 var(--s-11);
}

/* ---------- 首屏照片：两层铺满 + 12 秒自动交叉淡入 ----------
   第 27 轮：单张照片换成**两层**（.hm-a 层叠梯田 / .hm-b 板微距）。
   第 28 轮：淡入的驱动方式从**滚动**换成**时间**（理由见下面那段关键帧）。

   两层都是 inset:0 铺满。object-position 从 right center 改回 **center**：
   原来靠右是专为那张 SMT 产线照片留的（产线在右、机器屏幕在左），
   现在两张都是满幅图案（梯田自相似、板微距均匀），居中就好。

   ⚠️ 两张都是**合成图**（AI 生成，用户 2026-10-15 指定用在这块装饰位上），
      所以这里不再有"机器屏幕会不会露出来"那种讲究；但 alt 依然只写画面里
      有什么，不写"我们的产线" —— 规矩的边界见 index.html 那六块瓦片的注释。 */
.hero-media{
  position:absolute;inset:0;z-index:0;
  overflow:hidden;pointer-events:none;
}
.hero-media .hm{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;
}
/* A 层（梯田）默认**不显示**。这一条是整块首屏的降级兜底，别动：
   没有动效的人（浏览器不支持 CSS 动画 / 开了减少动效 / 打印 / 抓取器截图）
   看到的是 B 层那块板 —— 跟业务相关的那张，而不是一片森林。
   ⚠️ 下面 ::after 那层纱必须压在这两层**上面** —— 三个都是绝对定位、
   同 z-index，靠 DOM 顺序决定绘制次序，::after 是最后一个子元素，天然在上。
   别把 ::after 挪到 .hm 前面。 */
.hero-media .hm-a{opacity:0}

/* ---------- 交叉淡入：12 秒一圈，时间驱动 ----------
   ⚠️ 关键帧是参考站那套的**取反**。参考站写的是
      `0%,33.333%{opacity:0} 50%,83.333%{opacity:1} to{opacity:0}`
      —— 它让**上层**淡入，因为它的底图（.hero__bg--initial）就是一开场要看见的那张。
      我们是反的：上层（梯田）必须第一眼可见，而下层（板）必须是掀掉动效之后
      留下的那张。于是把同一个节奏镜像过来，**节奏一分不差**：
      每张各占 1/3 时间是满的、各 1/6 时间在过渡。
   ⚠️ 用**时间**轴而不是滚动轴。参考站 `sc-image-crossfade` 的声明就是
      `animation:… var(--crossfade-duration,8s) ease-in-out infinite`，
      它本来就是时间循环 —— 第 27 轮我们做成了滚动驱动，那是自作聪明：
      滚动时间线在这里要绕"溢出祖先会顶替最近的滚动容器、进度被钉死在 1"
      那一整类**静默**失败（第 25 轮在 .tile/.pcard 上栽过一次）。
      换成时间轴之后那一类坑根本不存在，也不需要有 @supports 兜底。
   ⚠️ 必须自己包 no-preference，别指望文件末尾那条全局
      `animation-duration:.01ms!important`：那条**拦不住滚动时间线**
      （设了 animation-timeline 之后 animation-duration 被忽略），
      但对这条时间驱动的反而"有效"——有效的方式是灾难：duration 变成 .01ms
      而 iteration-count 还是 infinite，等于每秒重放上百万次，闪烁成一片。
      包在 no-preference 里，这条声明在 reduce 下**根本不存在**，才是干净的关掉。 */
@media (prefers-reduced-motion: no-preference){
  .home .hero-media .hm-a{
    animation:hero-xfade var(--dur-xfade) ease-in-out infinite;
  }
}
@keyframes hero-xfade{
  0%,33.333%{opacity:1}     /* 梯田 —— 第一眼看到的就是它 */
  50%,83.333%{opacity:0}    /* 板微距 */
  to{opacity:1}
}

/* 纱。第 12 轮那道**左侧白色渐变**整条撤掉，换成满铺的深色纱
   （--hero-scrim，值是按实测定的，见第 1 层那段注释）。
   ⚠️ 为什么要满铺而不是"左边深、右边浅"：这一版文字**横跨整个版心**
      （标题在左、描述和按钮在右），左深右浅会让右半边的描述失去保护。
      满铺之后"哪一段字压在最亮的像素上"就不再是问题。
   ⚠️ 这里不再需要 @media(min-width:901px) 包一层 —— 下面的 ≤900px 已经把
      ::after 整个 display:none 关掉了（照片那边退成一条横带，不需要纱），
      所以这一条只在铺满的那一档（≥901px）生效。 */
.hero-media::after{
  content:"";position:absolute;inset:0;
  background:var(--hero-scrim);
}
/* ⚠️ 这里原来有一段 32s 的缓慢推镜（@keyframes hero-drift），第 12 轮删掉了。
   第 27 轮加交叉淡入、第 28 轮把它改成时间循环，**都没有推翻这条禁令**
   —— 淡入只动 opacity。理由不是"动效过时"：遮罩一直是照"照片不会动"定的，
   图一推镜/位移，被纱盖住的边角就会滑出来，比不盖还糟。
   第 28 轮换成满铺的纱之后这条约束松了不少，但结论不变：
   **位移、缩放继续禁止；透明度可以。** */

.hero .wrap{position:relative;z-index:2}
.hero-head{padding-bottom:0}
/* 第 12 轮这里写着 max-width:660px，理由是"遮罩到 22% 才开始变淡，
   再宽标题就伸进照片里了" —— 那道白渐变没了，这条封顶也就没来由了。
   现在文字横跨整个版心，由 .hero-cols 的两栏自己控制宽度。 */
.hero-copy{max-width:none}

/* 眉标 + 一条通栏细线（参考站的 .hero__top-row：眉标 + 一道 flex:1 的细线）。 */
.hero-top{display:flex;align-items:center;gap:var(--s-5);margin-bottom:var(--s-7)}
.hero-top .eyebrow{margin-bottom:0}
/* ⚠️ display:block 是**必须写的**，不是啰嗦 —— 第 28 轮这里静默漏了半条线：
   细线在 .hero-top 里是 flex 项，flex:1 撑得开；可下面那条收口的
   .hero-rule--foot 挂在 .hero-copy（普通块）底下，在那儿 **flex:1 完全不生效**，
   它还是个里面没字的 inline span —— 行内盒量不出宽高、上下外边距也不生效，
   于是那条线是 0×0：**线没有，--s-9 那 48px 的间距也没有**。
   浏览器不报错、控制台干干净净，只有把 getBoundingClientRect 打出来才看得见
   （实测三个视口全 0）。写成 display:block 之后，在块里它自己占满一行；
   在 .hero-top 里也照样是 flex 项（flex 子项本来就会被块化），没有任何副作用。 */
.hero-rule{display:block;flex:1;height:1px;background:var(--line-3)}
/* 收口的第二条细线，也是参考站那条 .hero__line--bottom */
.hero-rule--foot{margin-top:var(--s-9)}

/* 两栏：标题在左、描述 + 按钮在右（参考站是 45% / 50%，我们按版心 1140
   和更长的标题调到 1.06 / 0.94）。**文字一个字没改**，只是多了两层壳。 */
.hero-cols{
  display:grid;grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:var(--s-9);align-items:center;
}
.hero-side{display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-6)}
/* lede 自带的 32px 下边距在单栏里是对的，到了 flex 列里会跟 24px 的 gap 叠加 */
.hero-side .lede{margin-bottom:0}

/* ⚠️ 这一档（≥901px 的深色覆盖）在下面 —— 在 `.hero h1` / `.hero p.lede` 的
   **基类规则之后**（搜"压在照片上的那一档"）。媒体查询不加特异性，放前面的话
   同特异性的基类规则在它后面出现，`color:var(--ink)` 会把它整个盖掉，
   h1 和 lede 就变成"深字压在暗照片上" —— 这一点是 geo_check\contrast_r28.py
   量出来的（h1 只有 1.00:1），不是看出来的。 */

/* ---------- 数字带（第 28 轮从 .hero 里搬出来） ----------
   原来是浮在照片上的白毛玻璃卡；现在落在照片下方的白底上，走 .stats 的基类
   （白卡 + 1px --line 网格）就够 —— 所以这里只剩留白和那颗状态灯。
   ⚠️ 那两条 rgba(255,255,255,.72) + backdrop-filter 是专为"浮在照片上"写的，
      跟着搬家一起删了；白底上再叠一层白毛玻璃等于什么都没做。 */
.stats-band{padding:var(--s-9) 0 var(--s-11)}
.stats-band .stats{margin-top:0}
/* 每格右上角一个绿色状态点。和说明牌的"line running"是同一句话 ——
   板上一颗绿点亮着就是一个"通了"，不用图例，看一眼就懂。
   这是绿在这一版里的主力落点：数据旁边的小状态，不是色块 */
.stats-band .stats div{position:relative}
.stats-band .stats div::after{
  content:"";position:absolute;right:var(--s-5);top:var(--s-5);
  width:7px;height:7px;border-radius:50%;background:var(--jade-400);
  box-shadow:0 0 0 3px rgba(18,128,77,.22);    /* = --jade-400 的 RGB，手写 */
}

/* ---------- ⚠️ 板框 + 丝印整块已删（2026-10-13 第 12 轮） ----------
   这里原来有 .hero-frame / .hero-silk / .page-silk / .page-head.board::after
   一大段（金板边 + 四角定位孔 + 角落丝印白字），横跨第 2 轮做到第 10 轮，
   后来还下移到了 17 个内页页头。这一轮板语言全撤，连规则带注释一起删掉了。
   要翻旧账看第 2–11 轮的记录，见 _pdfpg/_backup/style.css.before-round12。 */

/* 窄屏：照片不再铺满，退成"文字下方的一条"。
   铺满版在手机上没法用 —— 文字横跨整幅、纱又必须按"最暗处"一刀切，屏一窄
   就两头不讨好：纱按手机压，桌面那张就糊；按桌面压，手机上一整条都是灰的。
   退成一条之后照片仍是真照片，只是回到"配图"的位置，那条纱也跟着关掉。 */
@media(max-width:900px){
  .hero-media{position:relative;inset:auto;width:100%;height:260px;margin-top:var(--s-9)}
  .hero-media::after{display:none}   /* 一条横带不需要纱 */
  .hero-cols{grid-template-columns:1fr;gap:var(--s-6)}
  .hero-side{gap:var(--s-5)}
  .hero-rule--foot{margin-top:var(--s-7)}
}


/* ============================================================
   第 4 层 · 质感 —— 全幅色带
   ------------------------------------------------------------
   2026-10-13 第 12 轮：从「全幅深色版块」改成**淡薄荷色带**。
   名字 .band 保留 —— 它本来就只是"横贯全屏的一条"，深不深是这一层的取值。
   第 4 轮给它写的理由是「长页面一路刷下来是平的，要有起伏」——
   理由**这一轮仍然成立**，只是起伏的来源从"深蓝"换成"淡薄荷"。

   2026-10-16 第 33 轮：**这一层在首页之外没变，在首页多了一档深的。**
   给 `.band` 加 `dark` 类（只在 index.html 的「Inside the building」那一节）就翻成
   --deep-900 深底 —— 规则写在文件末尾那一块 `.home .dark` 里，**不在这里**：
   写在这里的话它和主规则同层，谁赢看源码顺序，下一个人挪一节就静默翻车。
   深底上「照片会跳出来」这条又反回来了，所以 `.home .dark .band-split img`
   在那里把白卡的边和投影一起去掉（浅底上要，深底上不要）。

   顺带一提：深底上照片会"跳出来"那条经验，浅底上反过来了 ——
   浅底上照片是被"供"着的，所以要给它白卡外壳和细边，不能像原来那样裸着放。
   ============================================================ */

.band{
  position:relative;
  background:var(--band);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:var(--s-12) 0;
}
/* ⚠️ 这里原来有一条 --routing-dark 走线场。色带改浅之后**删了** ——
   浅底上铺白走线等于什么都没铺（那层走线本来就是白的）。
   要走线场得换深色线，但那就成了"深色版块"的影子，正是这一轮要去掉的东西。 */
.band .wrap{position:relative;z-index:2}
.band .sub,.band p{color:var(--ink-2)}
/* 通用 .sub 下方留了 --s-8（给版块和大标题之间垫空用的），
   在两栏里那 60px 是悬空的，收掉 */
.band .sub{margin-bottom:var(--s-5)}
.band .sub:last-child,.band-split p:last-child{margin-bottom:0}
.band .eyebrow{margin-bottom:var(--s-4)}
.band .stats{margin-top:var(--s-8)}

/* 带图的全幅版块：左文右图 */
.band-split{display:grid;grid-template-columns:1fr .9fr;gap:var(--s-10);align-items:center}
.band-split img{
  width:100%;aspect-ratio:4/3;object-fit:cover;display:block;
  border-radius:var(--r-md);border:1px solid var(--line);
  box-shadow:var(--sh-3);
}

/* 色带里的图库：**要白卡**。
   原来在深底上是"去掉白卡外壳，只留图和图注"；
   换成淡薄荷底之后这条反了 —— 裸图在浅底上会糊成一片，白卡反而给图一个落点。
   所以这里不再覆盖 .gallery 的默认规则，只把图注压深一档。 */
.band .gallery figcaption{color:var(--mute)}
.band .gallery figcaption b{color:var(--jade-600)}

@media(max-width:900px){
  .band{padding:var(--s-11) 0}
  .band-split{grid-template-columns:1fr;gap:var(--s-7)}
}

/* 眉标：等宽 + 宽字距 + 一道金色短线。比圆圈药丸高级得多 */
.eyebrow{
  display:inline-flex;align-items:center;gap:var(--s-3);
  font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:var(--ls-wide);text-transform:uppercase;
  color:var(--jade-600);margin-bottom:var(--s-5);
}
/* 首屏眉标底下原来压一条青绿细线（.hero .eyebrow 的 border-bottom）——
   第 28 轮**撤掉了**：眉标现在跟一条通栏细线并排（.hero-top 里的 .hero-rule），
   两条线同时出现会一高一低对不齐（眉标那条贴着字底，通栏那条按行盒垂直居中）。
   只留通栏那条。别的页面的 .eyebrow 本来就没这条（它一直只在首屏）。 */
.hero .eyebrow{padding-bottom:0}
.eyebrow::before{
  content:"";width:26px;height:1px;background:var(--jade-500);flex:0 0 26px;
}

/* 首屏的字。基类仍是"浅底上的深字"（≤900px 那一档用它：那时照片退回文字下方
   一条横带，文字落回白底），铺满的那一档由上面的 @media(min-width:901px)
   整体翻成白字。
   ⚠️ 验收要量：白字压在**照片**上，h1 / 眉标 / 描述 / 导航那一行都要 ≥4.5:1。
      照片上量不出第 12 轮白底时代那种 7:1 —— 要够 7:1 得把纱压到很黑，
      "满幅照片"这个前提就没了。所以这一轮的门槛是 4.5:1，由 --hero-scrim 的
      α 兜住，而那个 α 是 geo_check\contrast_r28.py 两帧各量一遍取出来的。 */
.hero h1{
  color:var(--ink);max-width:30ch;
  margin:0 0 var(--s-5);
}
.hero p.lede{
  font-size:var(--fs-md);line-height:1.62;
  color:var(--ink-2);max-width:56ch;margin:0 0 var(--s-7);
}
.hero .btns{display:flex;gap:var(--s-3);flex-wrap:wrap}

/* ---------- 压在照片上的那一档（≥901px） ----------
   两栏版式 + 白字只在铺满的那一档生效；≤900px 原样回到"文字在白底上 + 照片
   是下面一条配图"，所以那些覆盖全写在这个媒体查询里。
   颜色**一个令牌都没新增** —— 白字走第 24 轮为深色页脚开的那三档 --on-dark*，
   它们本来就只有页脚一个消费者。

   ⚠️ **这一块必须待在 `.hero h1` / `.hero p.lede` 的基类规则之后**：
     媒体查询不加特异性，两条都是 (0,1,1)，谁在后面谁赢。第 28 轮第一版把它
     写在基类之前，h1 和 lede 的 color 就被后面的 `var(--ink)` / `var(--ink-2)`
     盖掉了 —— 首屏成了"深字压暗照片"，h1 实测 1.00:1。 */
@media(min-width:901px){
  /* ---------- 第 33 轮：首屏「加重」 ----------
     用户要的是参考稿那种"照片化进黑里"的收口 —— 照片底缘不能硬切在一条线上，
     最后约 40% 高度渐隐到 --deep-900，正好接上它下面那段深色数字带。
     ⚠️ z-index:1 是**必须写的**：::before 是 .hero-media 的第一个子盒，
        不抬起来它会被后面两张照片整个盖住（同层按树序绘制）。
        ::after 那层纱没写 z-index，于是这层渐变落在纱**上面** ——
        这正是想要的效果：纱把照片压暗，渐变再把底缘送进深色里。
     ⚠️ 这层渐变**只会让像素更暗**，所以首屏文字的对比度只升不降，
        --hero-scrim 的 α 不用重测（改完仍用 contrast_r33 的那支脚本量过一遍）。
     ⚠️ 整条规则写在 min-width:901px 里面 —— ≤900px 那一档照片退成一条横带，
        横带底缘接的是白底，这条渐变在那里会变成一条无来由的黑边。
        写在媒体查询里就不需要另外写一条 display:none 去关它。 */
  .hero-media::before{
    content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
    background:linear-gradient(180deg,transparent 0 58%,var(--deep-900) 100%);
  }
  /* 栏宽从 1.06 / 0.94 收到 1.18 / 0.82：标题栏更宽，H1 从三行压到两行
     （参考稿的 H1 就是两行）。文字一个字没动。 */
  .hero-cols{grid-template-columns:minmax(0,1.18fr) minmax(0,.82fr)}
  /* H1 加大。**写成 .home .hero h1 这个局部选择器**，别去动 --fs-3xl 或 --fs-page：
     --fs-3xl 全站每个 h1 都用（内页页头只是换成 --fs-page 而已），
     改令牌等于改 21 个页面的页头。这里只碰首页首屏。
     上限 58px（原来 48px），下限 34px。 */
  .home .hero h1{font-size:clamp(34px,4.6vw,58px);max-width:32ch}

  /* ⚠️ 眉标压照片上**不能用青绿**：--jade-300 自己的亮度就摆在那儿，六档 α
     （.50→.72 上限）量下来最好只有 4.61:1、最差 1.66:1，把照片压成灰板才够。
     所以文字走 --on-dark-2（和导航、描述同一档），**绿留在前面那颗装饰短线上**
     —— 短线是装饰、不承载文字，不受 4.5:1 这条约束，品牌色一点没丢。
     这是量出来的结论，不是配色偏好：见 geo_check\contrast_r28.py 那张表。 */
  .hero .eyebrow{color:var(--on-dark-2)}
  .hero .eyebrow::before{background:var(--jade-300)}
  /* h1 在单栏里那个 20px 下边距是为了跟描述拉开；两栏版里它在 grid 单元格内，
     这条下边距只会把标题在垂直居中上顶偏，所以归零。 */
  .hero h1{color:var(--on-dark);margin-bottom:0}
  .hero p.lede{color:var(--on-dark-2)}
  .hero-rule{background:var(--on-dark);opacity:.5}   /* 照参考站 .thin-line：白线 + opacity .5 */
  /* 副按钮在深底上重新要一道白描边。**这不是把第 12 轮那版搬回来** ——
     那一版是全站唯一的 .btn.ghost，写死 alpha .32 的白描边，浅底上等于隐形，
     所以才翻成浅底版并立了"全站不许再有它"的规矩。这里是**首屏作用域内的
     第二档**：alpha 用 --on-dark-3（.74，不是 .32），只在 .hero 里生效。
     规矩改成"两档并存，各自的作用域写清楚"，而不是作废。 */
  .hero .btn.ghost{border-color:var(--on-dark-3);color:var(--on-dark)}
  .hero .btn.ghost:hover{background:var(--on-dark);border-color:var(--on-dark);color:var(--deep-900)}
}


/* ============================================================
   第 5 层 · 组件
   ============================================================ */

/* ---------- 按钮 ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2);
  padding:13px 26px;border-radius:var(--r-sm);
  font-family:inherit;font-size:var(--fs-sm);font-weight:600;letter-spacing:-.005em;
  border:1px solid transparent;cursor:pointer;white-space:nowrap;
  transition:background-color var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease),
             color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
.btn:hover{transform:translateY(-1px)}
.btn:active{transform:translateY(0);transition-duration:var(--dur-1)}

/* 主按钮 = 询价那颗。阻焊绿底 + 沉金底边，和 nav 那颗 CTA 同一套（绿是板、金是焊盘）。
   ⚠️ 字色是白的。旧版是 --deep-900（深青），那配的是浅得多的 --jade-400；
      换到 --green-500 之后深青字只剩 2.82:1，反过来白字有 5.33:1。 */
.btn.primary{background:var(--green-500);color:#fff;
  box-shadow:inset 0 -2px 0 var(--enig),var(--sh-1)}
.btn.primary:hover{background:var(--green-600);color:#fff;
  box-shadow:inset 0 -2px 0 var(--enig),var(--sh-2)}

/* 副按钮 = **浅底那一档**（默认）。
   第 12 轮它从"给深色首屏用"翻成"给浅色首屏用"—— 原来写死一道 alpha 只有
   .32 的半透明白描边配白字，那是深蓝底时代的东西，浅底上等于隐形。
   ⚠️ 这里刻意**不把那串旧值逐字抄进注释** —— 抄进来之后，"那种写法全站
      已经没有了"这条断言就会拿注释里的话把自己绊倒（同一轮里栽过一次）。
   第 28 轮首屏翻回深色，于是在 .hero 作用域内**另开了一档白描边版**
   （见 @media(min-width:901px) 那一块，alpha 是 .74 不是 .32）。
   规矩从"全站只许有一档"改成"两档并存、各写清自己的作用域"——
   不是把旧写法搬回来。 */
.btn.ghost{border-color:var(--line-3);color:var(--accent-ink)}
.btn.ghost:hover{background:var(--jade-100);border-color:var(--jade-400);color:var(--jade-600)}

.btn.solid{background:var(--tint-200);color:var(--ink);box-shadow:var(--sh-1)}
.btn.solid:hover{background:var(--deep-600);color:var(--ink);box-shadow:var(--sh-2)}


/* ---------- 数字带 ----------
   注意：这里写死列数。用 auto-fit 的话，数字会摊在多出来的列里，
   右边空一格 —— 这种对齐瑕疵一眼就能看出来。
   全站只有首页用 .stats，第 44 轮 F 从五格改成四格（5 → 4）。 */
.stats{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;margin-top:var(--s-9);
}
.stats div{background:var(--paper);padding:var(--s-5) var(--s-5)}
/* 数字用 --jade-600 不用 --jade-400：这是**文字**，白底上要 6.58:1。
   --jade-400 只有 3.36:1，是整站唯一一条硬约束（见 1.1 节那条注释）。 */
.stats b{
  display:block;font-family:var(--font-mono);font-weight:600;
  font-size:var(--fs-xl);line-height:1.1;color:var(--jade-600);
  letter-spacing:-.02em;margin-bottom:var(--s-1);
}
.stats span{
  font-size:var(--fs-xs);color:var(--mute);
  letter-spacing:.02em;line-height:1.4;display:block;
}
/* 四格进 3 列会剩一个空格子 —— 容器底色从那格透出来，浮到照片上就是一块脏斑。
   四格直接走 2 列 = 2×2，不需要补洞规则（单数格才需要）。
   ⚠️ 数字再变成奇数时，这里要把 :nth-child(末)且 span 2 那条加回来。 */
@media(max-width:940px){
  .stats{grid-template-columns:repeat(2,1fr)}
}


/* ---------- 卡片 ----------
   静息只有一根发丝边线；悬停才抬手 2px + 一道金色细线从左划出。
   不要一上来就给所有卡片加投影 —— 那是"廉价"的第二种来源。 */
.card{
  position:relative;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-6);
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
.card::before{
  content:"";position:absolute;left:var(--s-6);right:var(--s-6);top:-1px;height:2px;
  background:linear-gradient(90deg,var(--jade-400),rgba(18,128,77,0));
  border-radius:2px;
  transform:scaleX(0);transform-origin:left;
  transition:transform var(--dur-3) var(--ease-out);
}
/* ⚠️ 这三个组件的底色都是 --paper，跟 section.alt 的底色是同一个令牌
   （#FFFFFF）—— 放进 alt 版块里就是白底白卡，只剩一根发丝边在撑。
   五个都中了，而且是三轮里逐个才发现的：
     .card         遍布全站
     .docs li      about 的 2 个「文件清单」
     .promises li  contact / pcba-certifications-explained / prototype-to-production
                   共 4 个「承诺清单」
     .tile         首页「What we do」6 个品类瓦片（第 3 轮加）
     .form-card    首页询盘表单卡（第 3 轮加）
   在 alt 里统一退回 --tint-50，hover 再翻回白。

   ⚠️ .tile 只退瓦片本身，**不退它的插图区** —— `.tile .art` 自己就是
   --tint-100，在 alt 里落在 --tint-50 的瓦片上，正好读成一圈内嵌的浅槽。
   .form-card 不参与 hover 翻转：表单不该在鼠标扫过时变色。

   同属纸白底、但**不做** alt 变体的：table（index / capabilities /
   pcb-surface-finishes 的 alt 版块里也有）、.gallery figure、.figure-lead ——
   这些靠行线或图片本身区分，不靠底色撑。其余（.faq details / form.inquiry /
   .field / .qr / .diagram）只出现在非 alt 版块。 */
section.alt .card,
section.alt .promises li,
section.alt .docs li,
section.alt .tile,
section.alt .form-card{background:var(--tint-50)}
section.alt .card:hover,
section.alt .promises li:hover,
section.alt .docs li:hover,
section.alt .tile:hover{background:var(--paper)}
.card:hover{border-color:var(--line-2);box-shadow:var(--sh-2);transform:translateY(-2px)}
.card:hover::before{transform:scaleX(1)}
.card h3{margin-bottom:var(--s-2)}
.card h3 a{color:var(--ink)}
.card h3 a:hover{color:var(--accent-ink)}
.card p{margin:0;color:var(--ink-2);font-size:var(--fs-sm);line-height:1.62}
.card .num{
  display:inline-grid;place-items:center;width:30px;height:30px;
  border-radius:var(--r-sm);margin-bottom:var(--s-4);
  background:var(--accent-soft);color:var(--accent-ink);
  font-family:var(--font-mono);font-weight:600;font-size:var(--fs-xs);
}


/* ---------- 可引用框 ----------
   全站最重要的组件。金左边框 + 极淡的暖色底，
   让人一眼认出"这一段是可以被抄走的标准答案"。 */
.answer{
  position:relative;
  background:linear-gradient(180deg,var(--tint-50),var(--paper));
  border:1px solid var(--line);
  border-left:3px solid var(--jade-400);
  border-radius:var(--r-xs) var(--r-md) var(--r-md) var(--r-xs);
  padding:var(--s-5) var(--s-6);
  margin:0 0 var(--s-5);
  box-shadow:var(--sh-1);
}
.answer p:last-child{margin-bottom:0}
.answer strong,.answer b{color:var(--ink);font-weight:600}


/* ---------- 承诺清单 ---------- */
/* 5 条在一行 1140px 上，每行只有一句短话，右半边整片空着 —— 像表格缺了一列。
   两列之后每格约 560px，长句自然折行，短句不再孤零零漂着；奇数条时最后一条占满整行收尾。
   窄版心（.wrap.narrow，820px）里退回单列，再切两列每格只剩 400px 就挤了。 */
.promises{list-style:none;padding:0;margin:0;display:grid;gap:var(--s-2)}
@media(min-width:900px){
  .promises{grid-template-columns:repeat(2,1fr)}
  /* 最后一条落在奇数位（即总数为奇数）时才通栏。别写死 nth-child(5) ——
     全站的清单条数不一样（实测 3 / 4 / 5 / 6 都有），写死会在 3 条的页面上
     留一个半宽的孤儿（pcba-certifications-explained、prototype-to-production）。 */
  .promises li:last-child:nth-child(odd){grid-column:span 2}
  .wrap.narrow .promises{grid-template-columns:1fr}
  .wrap.narrow .promises li:last-child:nth-child(odd){grid-column:auto}
}
/* ⚠️ li 不能是 flex 容器。一旦是，行内的每一段文字和每个 <b> 都会各自变成
   flex item，被 gap 拉开 —— 表现是 "…Class III ." 句号前凭空多一个空隙，
   而且每个加粗词两侧的间距都变宽。✓ 用绝对定位摘出文档流，行内格式才正常。
   9 处 .promises（5 个页面）都吃这一条。 */
.promises li{
  position:relative;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:var(--s-3) var(--s-4) var(--s-3) calc(var(--s-4) + var(--s-6));
  font-size:var(--fs-sm);line-height:1.6;
  transition:border-color var(--dur-2) var(--ease),background-color var(--dur-2) var(--ease);
}
.promises li:hover{border-color:var(--line-2);background:var(--tint-50)}
.promises li::before{
  content:"✓";position:absolute;left:var(--s-4);top:var(--s-3);
  color:var(--jade-600);font-weight:700;line-height:1.6;
}
.promises b{color:var(--ink);font-weight:600}


/* ---------- 表格 ---------- */
table{
  width:100%;border-collapse:separate;border-spacing:0;
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;font-size:var(--fs-sm);
}
caption{
  caption-side:top;text-align:left;padding:0 0 var(--s-3);
  font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--mute);
}
th{
  background:var(--tint-200);color:var(--ink);text-align:left;
  padding:var(--s-3) var(--s-4);font-weight:500;
  font-size:var(--fs-2xs);letter-spacing:var(--ls-label);text-transform:uppercase;
}
td{
  padding:var(--s-4);border-top:1px solid var(--line);
  color:var(--ink-2);vertical-align:top;line-height:1.55;
}
tbody tr:first-child td{border-top:none}
td:first-child{color:var(--ink);font-weight:500}
tbody tr:nth-child(even) td{background:var(--tint-50)}
td .n{font-family:var(--font-mono);font-weight:600;color:var(--ink);font-size:var(--fs-xs)}

/* ---------- 参数表 table.spec ----------
   2026-10-16 第 32 轮新增。病灶就在上面那三行里：
     · td          → color:--ink-2（淡）
     · td:first-child → color:--ink + 500（深）
     · td .n       → --fs-xs 13px，比表格正文的 --fs-sm 14.5px **还小**
   也就是**标签比值深、值比标签小** —— 一张 26 行的参数表行行同权，
   读者的眼睛没有落点。这里把重心整个翻过来。

   ⚠️ 只作用于加了 class="spec" 的表。默认 table 一字未改，其余 19 页零影响。
   ⚠️ 配色一个都没新增，全是已有的 --ink / --ink-2 / --deep-900。
      实测（sRGB 线性化）：值 --ink 压白 10.68:1、压斑马纹底 --tint-50 10.21:1；
      标签 --ink-2 压白 7.16:1、压斑马纹底 6.84:1；.n 用 --deep-900 更高。全部过 4.5。
   ⚠️ 标签列定宽 30% 是量出来的，不是拍的：capabilities.html 里最长的标签是
      「PCB fabrication — minimum line / space」= 264px 文字 + 左右各 16px 内边距，
      表宽 1092px 下 30% = 328px，正好一行放得下不折行。
      改前两张表的自然列宽一张 **64.1%**、一张 **27.8%** —— 同一页上下两张表列线错开，
      这是「不整齐」的来源之一。table-layout:fixed 让两张表不管内容长短都咬住同一条列线。 */
table.spec td:first-child{color:var(--ink-2);font-weight:400}
table.spec td{color:var(--ink)}
table.spec td .n{font-size:var(--fs-base);font-weight:600;color:var(--ink)}

/* ⚠️ 这里**故意不去动列宽**（试过 table-layout:fixed + 标签列 30%，已回退）。
   实测数据说明为什么：表A 的自然列宽本来就是 700/390（标签占 64%），
   强行压到 30% 之后，值都挤到表格左侧、右边空出 **70% 的一条白**，
   整张表显得没铺满；而表D 的自然列宽是 304/786，压到 30% 只是 304→327，
   **两个百分点**，等于什么也没做。也就是这条规则只会伤表A、帮不到表D。
   值列天然落在哪里，就让它落在哪里 —— 真正的毛病是「值比标签弱」，不是「列没对齐」。 */
/* caption 顺带修一个已有的可达性缺陷：全站 caption 用的是 --mute，白底上
   **3.46:1**，低于 4.5 的线（它是 11.5px 的小字，不能按大字号放宽）。
   ⚠️ 这条**只修了 .spec 的表**，其余 18 页 28 张表的 caption 仍是 3.46:1 ——
      那属于全站改动，等这一页 demo 定稿再说。 */
table.spec caption{color:var(--ink-2)}

/* 长表分「跑」。⚠️ 跑边界是**排版判断**（按内容自然成组），不是原文自带的分类 ——
   所以这里只用「一道更实的线 + 空白」分开，**一个字都没加**。
   将来若想让分组「有名字」，那是改文案，得先过公司那一关，别在 CSS 里编。
   ⚠️ 数值是**看着渲出来的图调过一轮的**：第一版用 --s-6(24px) + --line-2
      （在 --tint-50 上只有 1.31:1），渲出来读着像「行距不齐」而不像「分组」，
      所以加到 --s-7(32px) + --line-3（1.68:1）。这仍是根发丝线，不是横条。
   ⚠️ 注意**净增量只有一半**：本来每行就有 --s-4(16px) 上内边距，这里写 32px
      是「改成 32」不是「再加 32」—— 每个跑边界净 +16px。
   ⚠️ 选择器权重必须盖过上面那句 tbody tr:first-child td{border-top:none}
      （它是 0,1,3；这条是 0,3,5），否则每个跑的首行反而没有线。
   ⚠️ 拆多个 tbody 会让斑马纹 nth-child(even) 在每个跑里重新数 —— 结果是
      **每个跑的首行都是白底**，和「空白 + 实线」叠成起跑提示，这是有意的。 */
table.spec tbody.run + tbody.run tr:first-child td{
  padding-top:var(--s-6);border-top:1px solid var(--line-3);
}
/* ⚠️ 这两句必须成对。只写上边那句的话，多出来的空白全落在**线下面**，
   「Board cutting ／──线──／（空）／Order quantity」读起来像是上一条被划掉了。
   把下边距加给「上一跑的最后一行」，线两侧就各是 24px，线才落在缝的正中间。 */
table.spec tbody.run:not(:last-child) tr:last-child td{padding-bottom:var(--s-6)}

/* ---------- #fabrication：参数表 + 板子照片并排（第 36 轮） ----------
   用户 2026-10-19 拿来一张参考稿：「帮我把这一块做一个这样的调整，颜色要和我的
   设计风格相匹配」。参考稿的排版 = **左栏标题＋平铺的标签/值行＋右栏 2×2 照片**。
   这里只借**排版**，颜色一个都没新增：照片卡是 --paper + --line-2，行的分隔线
   是 --line，值仍然是 --ink / --deep-900。

   ⚠️ 这一节**仍然是白底**，没有改成 .alt —— 它上下各是一个 .alt 色带
      （"Production capacity" 与 #equipment），verify_site.py 查相邻交替，
      插一层底色就会破。参考稿里那层浅灰底，在这里由**照片卡自己的浅边**替代。
   ⚠️ 表仍然是 <table>：26 行参数是"参数行"（可引用句模式 2），改成 div 列表会让
      AI 读不到行列关系 —— 这一轮只改**长相**，不改语义，一个字都没重写。 */
.specsplit{
  display:grid;
  grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);
  gap:var(--s-10);
  align-items:start;
}
.specsplit .spec-main{min-width:0}

/* 右栏跟着表往下走。
   ⚠️ `align-self:start` 不写的话，grid 会把这一项拉满整行高，sticky **静默失效**
      （第 16 轮 .faq-nav 踩过同一个坑，当时也是不报错、只是不粘）。
   ⚠️ 祖先链 section → .wrap 上不许有 overflow —— 有的话滚动参照物会变成那个盒子，
      同样是静默失效（第 25 轮 view() 的坑是同一类）。这两点在本轮都实测过。 */
.specsplit .spec-side{
  position:sticky;
  top:calc(var(--header-h) + var(--s-6));
  align-self:start;
  min-width:0;
}
.boardgrid{display:grid;grid-template-columns:1fr;gap:var(--s-4)}
.boardgrid figure{
  margin:0;background:var(--paper);
  border:1px solid var(--line-2);border-radius:var(--r-md);
  overflow:hidden;
}
.boardgrid img{display:block;width:100%;height:auto}

/* 参考稿的参数行**没有外框**：只有行与行之间的发丝线，标签小一号、颜色退后，
   值仍然深。⚠️ 只作用于 .specsplit 里面这一张 —— 同页另外三张 .spec 表
   （以及全站其余 18 页的）仍是第 32 轮那张带边框的卡片，一个字都没动。 */
.specsplit table.spec{border:none;border-radius:0;background:none}
.specsplit table.spec caption{padding-bottom:var(--s-4)}
.specsplit table.spec td{
  padding:var(--s-4) var(--s-4) var(--s-4) 0;
  background:none;
}
/* 标签列 38% 是量出来的：表宽约 570px 时这一列 217px，最长的一条标签
   "Minimum line width / spacing" 在 13px 下约 170px，仍是一行。 */
.specsplit table.spec td:first-child{width:38%;padding-left:0;font-size:var(--fs-xs)}
.specsplit table.spec td:last-child{padding-right:0}
/* 外框没了之后，首行那道 border-top 会变成一条孤零零的悬空线 */
.specsplit table.spec tbody:first-of-type tr:first-child td{border-top:none}
.specsplit table.spec tbody tr:nth-child(even) td{background:none}

/* 单栏：参数表回到整幅宽（值几乎不折行了），照片排到表格和交期说明之后。
   ⚠️ 照片改成**两列**而不是留一列 560 宽 —— 实测 930px 版心下两列各 457px，
      和桌面端 466px 的图卡几乎同尺寸，整页的"板子有多大"是稳定的；
      留一列 560 的话右边会空出 390px，看着像没排完。 */
@media(max-width:1020px){
  .specsplit{grid-template-columns:1fr;gap:var(--s-9)}
  .specsplit .spec-side{position:static}
  .boardgrid{grid-template-columns:1fr 1fr}
}
@media(max-width:600px){
  .boardgrid{grid-template-columns:1fr}
}

/* 两栏对照表：绿 / 红，用来写"谁适合我们" */
table.verdict td:first-child{background:rgba(35,149,92,.13);color:var(--ink)}
table.verdict td:last-child{background:rgba(142,50,37,.15)}
table.verdict tbody tr:nth-child(even) td:first-child{background:rgba(35,149,92,.20)}
table.verdict tbody tr:nth-child(even) td:last-child{background:rgba(142,50,37,.24)}

/* ---------- 窄屏的宽表格 ----------
   实测（_tbl_probe.py，量 document.scrollWidth 与 clientWidth 之差）：
   全站只有 pcb-surface-finishes.html 会被表格顶宽 —— 420px 下文档宽 534（多出
   114px）、560px 多出 76px、720px 起正好。一张表被顶宽时**整页**跟着横向滚动，
   页头导航和正文段落一起被推出视口，不只是表格自己被裁。

   ⚠️ `display:block` 不是随手加的：`display:table` 的盒子不是块容器，浏览器会
   **忽略**它上面的 overflow —— 只写 overflow-x:auto 完全没效果。改成 block 之后
   thead / tbody 仍按表格排版（浏览器会给这串"表格内部显示类型"的子元素补一个
   匿名表盒），所以表头和数据列依旧对齐；圆角也还在，滚动容器会按 border-radius 裁。

   .plain（联系方式，标签/值两栏）和 .verdict（对照表，绿/红两栏）**不滚** ——
   它们本来就窄，强行横向滚动只会把成对的两栏拆到屏幕外。 */
@media(max-width:720px){
  table:not(.plain):not(.verdict){
    display:block;overflow-x:auto;-webkit-overflow-scrolling:touch;
  }
}


/* ---------- 折叠问答 ---------- */
.faq details{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  margin-bottom:var(--s-2);overflow:hidden;
  transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease);
}
.faq details:hover{border-color:var(--line-2)}
.faq details[open]{box-shadow:var(--sh-1)}
/* ⚠️ 这里**不能用 justify-content:space-between**。summary 是 flex 容器
   （行内文字会变成匿名 flex 项），space-between 在"两项"时才等效于两端对齐；
   FAQ 页第 18 轮给每条加了编号 ::before，变成三项，中间的问句就被推到正中
   去了。改成默认的 flex-start + 让 ::after 自己 margin-left:auto 顶到右边 ——
   两项的页面表现完全不变，三项也不会跑偏。 */
.faq summary{
  padding:var(--s-4) var(--s-5);cursor:pointer;list-style:none;
  display:flex;gap:var(--s-4);align-items:flex-start;
  font-size:var(--fs-sm);font-weight:600;color:var(--ink);line-height:1.5;
  transition:background-color var(--dur-2) var(--ease);
}
.faq summary:hover{background:var(--tint-50)}
.faq summary::-webkit-details-marker,.faq summary::marker{display:none;content:""}
.faq summary::after{
  content:"＋";color:var(--accent-strong);font-weight:400;
  font-size:var(--fs-md);line-height:1.3;flex:0 0 auto;
  margin-left:auto;                 /* 顶到右缘；替代原来 summary 上的 space-between */
  transition:transform var(--dur-2) var(--ease);
}
.faq details[open] summary::after{content:"－"}
.faq details[open] summary{border-bottom:1px solid var(--line);background:var(--tint-50)}
.faq .a{padding:var(--s-4) var(--s-5);color:var(--ink-2);font-size:var(--fs-sm);line-height:1.68}
.faq .a p{margin:0 0 var(--s-3)}
.faq .a p:last-child{margin:0}
/* ---------- FAQ 答案里嵌的小对比表（2026-10-14 第 17 轮）----------
   元素取自用户给的 AI 设计稿（好几张里都有同一样东西：一条问答的答案里直接嵌一张小表）。
   值得抄的原因是它一次满足「可引用四标准」：自包含（不依赖上下文）、具体（每格都是数）、
   直接回答（问交期就给交期表）、可验证（值在站内别处对得上）。

   ⚠️ **表里的数字全部来自站内已有页面，一个都没新编。** 改这些表时守住这条。

   全局那套 table 样式（深色表头 + 斑马纹 + 圆角）在答案框里太重，这里只压小一号、
   收紧内边距。⚠️ **不要动 display** —— 上面「窄屏的宽表格」那条 ≤720px 的
   `table:not(.plain):not(.verdict){display:block;overflow-x:auto}` 必须能继续命中
   表面处理那张三列的表，否则窄屏会被它顶宽**整页**（页头导航一起被推出视口）。 */
.faq .a table{margin:var(--s-4) 0;font-size:var(--fs-xs)}
.faq .a table caption{padding:0 0 var(--s-2)}
.faq .a th{padding:var(--s-2) var(--s-3)}
.faq .a td{padding:var(--s-3)}
/* 答案末尾的「读全文」出口。设计稿里就挂在答案最后一行 —— 和正文里的内链分开写，
   因为两者的意思不一样：正文内链是"顺手"，这一条是"这条讲完了，深入看哪一篇"。 */
.faq .a p.more{margin:var(--s-2) 0 0;padding-top:var(--s-3);border-top:1px solid var(--line)}
/* 首页那六条问答排成两栏，各三条。
   ⚠️ 两个 <div class="fcol"> 各自是一列，**不要**用 grid-auto-flow:column ——
   那样两栏共享行高，而 [open] 的那条比收起的高，同一行取高者会给另一栏
   撑出一道空档。gap 纵向留 0：details 自带 margin-bottom 就是行距。 */
.faq.cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:0 var(--s-5);align-items:start}
@media(max-width:900px){.faq.cols{grid-template-columns:1fr}}


/* ---------- FAQ 页左栏（分组导航 + 询问小卡）（2026-10-14）----------
   faq.html 有 29 条问答、6 个分组，单栏排下来就是"找不着"。这一层是
   **给人用的，不是给 AI 用的** —— AI 读整页，本来就看得到全部问答，
   所以别把这条当 GEO 成绩报（唯一对 AI 有用的只有当锚点有了真实 URL 之后
   可以内链到 faq.html#price 这种具体分组）。

   ⚠️ 四条约束：
   ① **不加 JS。** 全站零 JS，不执行 JS 的爬虫会看不到内容。所以这里
      **没有滚动高亮**（scroll-spy 得用 JS），点击后的落点反馈只有下面
      那条 h2:target 变色。要滚动高亮就等于破全站零 JS 这条线，别偷偷加。
   ② **这里不再有任何会漂移的数字。** 第 16 轮起每个分组后面挂过一个计数
      （<b class="cnt">N</b>），由 geo_check/faq_nav_r16.py 从 DOM 里数出来写进去，
      加/删一条问答就得重跑那个脚本；**第 19 轮用户要求删掉**，理由和正文侧
      两套编号同源：凡是手抄进 DOM 的计数都会过期，而 CSS 计数器不会。
      现在全站的问答条数只存在于 `<details>` 本身，加问答不再需要同步任何东西。
      （正文侧那两套编号本来就是 CSS 计数器生成的，见下面那节。
       真要再引进一个手写数字，先想清楚谁来负责重跑。）
   ③ **窄屏是横过来，不是藏起来。** 分组是这页唯一的导航，≤1000px
      一藏就等于没有；做成横向可滑的胶囊行（和 .boardrail 同一套做法）。
   ④ **第 18 轮起这一列多了张 .faq-ask 小卡**（导航只有 370px 高，底下
      是 2400px 死白）。取整列粘性的是 .faq-side，单独粘 .faq-nav 会让
      两张卡在滚动时错开。 */
.faq-layout{
  display:grid;grid-template-columns:236px minmax(0,1fr);
  gap:var(--s-9);align-items:start;
}
/* 粘的是**这一列**（导航 + 下面那张小卡），不是导航自己 —— 两个要一起走。
   ⚠️ align-self:start 不能少：不加的话 grid 项会被拉满整行高度，sticky 就没有
   可移动的余量，等于没粘。2026-10-14 第 18 轮从 .faq-nav 挪到这里。 */
.faq-side{
  position:sticky;top:calc(var(--header-h) + var(--s-6));
  align-self:start;
  display:flex;flex-direction:column;gap:var(--s-4);
}
.faq-nav{
  background:var(--tint-50);
  border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s-5) var(--s-5) var(--s-3);
}
.faq-nav-h{
  margin:0 0 var(--s-2);padding-bottom:var(--s-3);
  border-bottom:1px solid var(--line-2);
  font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:var(--ls-wide);text-transform:uppercase;
  color:var(--accent-ink);
}
.faq-nav ol{list-style:none;margin:0;padding:0}
.faq-nav li + li{border-top:1px solid var(--line)}
.faq-nav a{
  display:flex;align-items:baseline;gap:var(--s-3);
  padding:var(--s-3) 0;
  font-size:var(--fs-sm);line-height:1.45;color:var(--ink-2);
  text-decoration:none;transition:color var(--dur-2) var(--ease);
}
.faq-nav .idx{
  flex:0 0 auto;font-family:var(--font-mono);font-size:var(--fs-2xs);
  letter-spacing:var(--ls-label);color:var(--accent-ink);
  font-variant-numeric:tabular-nums;
}
.faq-nav .lbl{flex:1 1 auto}
/* .faq-nav .cnt（分组后面的问答计数）2026-10-14 第 19 轮随 DOM 里的
   <b class="cnt"> 一起删掉了，用户要求导航后面不写条数。
   ⚠️ 别顺手再加回来：那个数字是手抄进 DOM 的，加一条问答就过期一次
   （详见上面那段注释的约束②）。数值要么不写，要么交给 CSS 计数器。 */
.faq-nav a:hover,.faq-nav a:focus-visible{color:var(--accent-ink)}
/* 锚点落点：页头是 sticky 的，不补 scroll-margin 会把标题盖掉 */
.faq > h2[id]{scroll-margin-top:calc(var(--header-h) + var(--s-5))}
.faq > h2[id]:target{color:var(--accent-ink)}


/* ---------- 正文侧的设计感（2026-10-14 第 18 轮）----------
   起因：用户看着这页说"没有 UI 设计感"。指的就是三件事 —— 分组标题光秃秃一行字、
   29 条问答长得一模一样、左栏导航底下 2400px 死白。

   ⚠️ 两套编号（分组 01–06、问答逐条连号）**都是 CSS 计数器生成的，不写进 DOM**。
   好处两个：① 加/删一条问答，后面序号自动重排，永远不会漂移；
   ② 爬虫和 AI 抓到的标题文本还是干净的 "Products and services"，
   不会变成 "01 Products and services"。
   （原文这里还有一句"不会变成 …… 5"—— 第 19 轮导航里的计数删掉后就没了。） */
.faq-layout .faq{counter-reset:faqgrp faqq}

/* 分组标题：编号 + 一道顶到右缘的细线。编号和左栏导航的 .idx 同一套做法
   （等宽 + 宽字距 + --accent-ink），两边一眼对得上。 */
.faq-layout .faq > h2{display:flex;align-items:center;gap:var(--s-4)}
.faq-layout .faq > h2::before{
  counter-increment:faqgrp;
  content:counter(faqgrp,decimal-leading-zero);
  flex:0 0 auto;font-family:var(--font-mono);font-size:var(--fs-2xs);
  font-weight:500;letter-spacing:var(--ls-label);color:var(--accent-ink);
  font-variant-numeric:tabular-nums;
}
.faq-layout .faq > h2::after{content:"";flex:1 1 auto;height:1px;background:var(--line-2)}

/* 每条问答的连号。颜色用 --accent-ink，不用 --line-3 —— 那一节写着
   "--line-3 是描边色，当文字用是瞎的"，这里不破例。
   summary 是 flex 容器：::before 成为第一个 flex 项（::after 是那个加号），
   line-height 让号码和问句首行对齐。 */
.faq-layout .faq details{counter-increment:faqq}
.faq-layout .faq summary::before{
  content:counter(faqq,decimal-leading-zero);
  /* 固定 16px 而不是 auto：下面答案要靠这个宽度对齐到问句文字（实测 16px）。
     decimal-leading-zero 在 01–99 之间恒为两位，所以宽度是稳的；
     真涨到 100 条以上，要么换编号格式要么回来重测。 */
  flex:0 0 16px;font-family:var(--font-mono);font-size:var(--fs-2xs);
  font-weight:500;letter-spacing:var(--ls-label);color:var(--accent-ink);
  font-variant-numeric:tabular-nums;line-height:1.6;
}

/* 展开那条：左缘一条常亮的青竖条。
   ⚠️ 用 border-left 而不是 inset 阴影 —— inset 阴影会被 summary 自己的
   --tint-50 底色盖住，只在答案区看得见，就不成其为"一条"了。
   加粗左框会把内容右推，所以下面把 summary 和 .a 的左内边距各减 2px
   （1px→3px 只多出 2px），文字才和相邻几条严格对齐。 */
.faq-layout .faq details:hover{border-left-color:var(--jade-200)}
.faq-layout .faq details[open]{border-left:3px solid var(--accent)}
.faq-layout .faq details[open] > summary{padding-left:calc(var(--s-5) - 2px)}
/* 答案缩进到**问句文字**那一列（编号 16px + --s-4 的间距），不然同一条卡里
   问句和答案两个左缘，看着毛。 */
.faq-layout .faq details[open] > .a{padding-left:calc(var(--s-5) - 2px + 16px + var(--s-4))}

/* 左栏导航下面那张小卡，填掉桌面端那 2400px 死白（导航只有 370px 高）。
   首行的眉标直接复用全站的 .eyebrow（等宽 + 一道青绿短线），不另造一套。
   ≤1000px 藏掉：死白只在宽屏存在，窄屏那排横向胶囊已经够占地方了。 */
.faq-ask{
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:var(--s-5);
}
.faq-ask .eyebrow{margin-bottom:var(--s-3)}
.faq-ask p{margin:0;font-size:var(--fs-sm);color:var(--ink-2);line-height:1.6}
.faq-ask p + p{margin-top:var(--s-3)}
.faq-ask a{
  color:var(--accent-ink);font-weight:600;
  border-bottom:1px solid var(--jade-200);transition:border-color var(--dur-2) var(--ease);
}
.faq-ask a:hover{border-bottom-color:var(--accent-ink)}

@media(max-width:1000px){
  .faq-layout{grid-template-columns:minmax(0,1fr);gap:var(--s-7)}
  .faq-side{position:static;gap:0}
  .faq-ask{display:none}            /* 死白只在宽屏存在，窄屏别再加一块 */
  .faq-nav{
    padding:var(--s-3) var(--s-3) var(--s-1);
    border-radius:var(--r-md);
  }
  .faq-nav-h{margin:0;padding:0 0 var(--s-2) var(--s-2);border-bottom:0}
  .faq-nav ol{
    display:flex;gap:var(--s-2);overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;padding-bottom:var(--s-2);
  }
  .faq-nav ol::-webkit-scrollbar{display:none}
  .faq-nav li{flex:0 0 auto}
  .faq-nav li + li{border-top:0}
  .faq-nav a{
    gap:var(--s-2);padding:var(--s-2) var(--s-4);
    font-size:var(--fs-xs);white-space:nowrap;
    border:1px solid var(--line-2);border-radius:var(--r-full);
    background:var(--paper);
  }
}


/* ---------- 表单 ---------- */
form.inquiry{
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:var(--s-7);box-shadow:var(--sh-1);
}
/* 表单里的标题是"卡片标题"不是"版块标题"，比正文 h2 小一号 */
form.inquiry > h2{font-size:var(--fs-lg)}
.field{margin-bottom:var(--s-4)}
.field label{
  display:block;margin-bottom:var(--s-2);
  font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:var(--ls-label);text-transform:uppercase;color:var(--ink-2);
}
.field .req{color:var(--red-600)}
.field input,.field select,.field textarea{
  width:100%;padding:11px var(--s-4);
  border:1px solid var(--line-2);border-radius:var(--r-sm);
  font:inherit;font-size:var(--fs-sm);background:var(--paper);color:var(--ink);
  transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease);
}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--line-3)}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--accent-strong);box-shadow:0 0 0 3px var(--accent-ring);
}
/* 原生下拉弹层里的每一项。Chrome / Edge 会把这两个值直接画进弹层，
   **不依赖 color-scheme** —— 这是唯一硬的保证，别删。
   option 的背景本来就是透明的，弹层底色由浏览器决定；只要浏览器画浅色底，
   继承下来的近白字就全看不见（第 43 轮下半场实测踩到）。 */
.field select option{background-color:var(--paper-2);color:var(--ink)}
.field select option:disabled{color:var(--mute)}
.field textarea{min-height:112px;resize:vertical}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-4)}
form.inquiry .btn{width:100%;margin-top:var(--s-2)}


/* ---------- 提示条 ---------- */
.demo-flag{
  background:var(--accent-soft);border:1px solid var(--jade-200);
  border-radius:var(--r-sm);padding:var(--s-3) var(--s-4);
  font-size:var(--fs-xs);color:var(--accent-ink);margin:var(--s-3) 0;
}
.demo-flag b{color:var(--jade-600)}
.tbc{
  background:var(--accent-soft);border:1px dashed var(--jade-300);
  color:var(--accent-ink);padding:1px 7px;border-radius:var(--r-xs);
  font-family:var(--font-mono);font-size:.86em;font-weight:500;white-space:nowrap;
}
.warn{
  background:var(--red-100);border:1px solid var(--red-200);
  border-radius:var(--r-sm);padding:var(--s-4) var(--s-5);
  font-size:var(--fs-sm);color:var(--red-600);line-height:1.6;
}
/* 强调行比正文深一档。#7A2A1E 是第 12 轮之前写死的旧砖色，
   换成 --deep-900：它压在同一片 --red-100 上更深，而红系只剩这一个令牌组，
   不该在它内部再开一处裸值。 */
.warn b{display:block;margin-bottom:var(--s-1);color:var(--ink)}


/* ---------- 图库 ---------- */
.gallery{display:grid;gap:var(--s-4);grid-template-columns:repeat(3,1fr);margin-top:var(--s-3)}
@media(max-width:940px){.gallery,.gallery.g2{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.gallery,.gallery.g2{grid-template-columns:1fr}}
/* 2026-10-15 第 23 轮：一组只有两张照片时，别在三列里留一个空格子。
   全站只有 equipment「Where a defect is caught」是 2 张 —— 用户截图指着
   那个空格说「旁边是空的，可以补全内容，不用空着」。
   收成两列：实测每格 538px（版心 1092、减 16px 间距再折半），
   源图 668px，仍然不放大、不糊。
   ⚠️ 这两条窄屏媒体查询里必须一起写 .gallery.g2 —— 类选择器特异性比
   .gallery 高，不写的话 ≤600px 会被这条压成一列以外的两列。 */
.gallery.g2{grid-template-columns:repeat(2,minmax(0,1fr))}
.gallery figure{
  margin:0;background:var(--paper);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;
  transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
.gallery figure:hover{border-color:var(--line-2);box-shadow:var(--sh-2);transform:translateY(-2px)}
/* 高度从 200 提到 260。三列时每格宽 369px，源图最窄 451px ——
   提到 260 仍然不放大（不放大才不糊），裁切比从 1.85:1 变 1.42:1，
   照片有"实体感"了，不再是一条条色带 */
.gallery img{
  width:100%;height:260px;object-fit:cover;display:block;
  border-bottom:1px solid var(--line);background:var(--tint-200);
}
.gallery figcaption{
  padding:var(--s-3) var(--s-4);font-size:var(--fs-xs);
  color:var(--ink-2);line-height:1.55;margin:0;max-width:none;
}
.gallery figcaption b{color:var(--ink);display:block;font-size:var(--fs-2xs);
  letter-spacing:var(--ls-label);text-transform:uppercase;margin-bottom:3px;font-weight:500}


/* ---------- 文章首图 ----------
   三篇技术指南通篇是字和表，从头到尾没有一个视觉落点。
   这张图的作用是把页头和下面的数据表分开，给读者一个"出口"。

   宽度锁 560px：源图最窄只有 451px，铺满就是 1.6 倍放大，糊。
   560 以下都是原生分辨率，是锐的。**宽度第 23 轮没动。**

   **第 23 轮（2026-10-15）：改成左浮动。** 用户拿两张截图说
   「旁边是空的，可以补全内容，不用空着」。实测 1400 视口下 .wrap 宽 1140、
   图锁 560 —— 右边留一整块 556px 白，占了半栏，看着像个洞。
   左浮动以后，紧跟其后的 <h2> / .sub / 开头那几段会自动绕到图的右边，
   绕到图底再回到整栏宽 —— 用**文章自己的开头**把空的那半边填上，
   一个新字都不用编。

   ⚠️ 上面那句「窄一点在 **880 的正文栏**里读起来也像排版」是过期口径：
   文章版块现在挂的是 .wrap（1400 视口下 1140，正文实测 1082），不是 880。
   560 在 880 里只空 320，像排版；在 1082 里空 522，就是个洞 —— 这一轮就是
   按这个差值补的。

   ⚠️ 浮动能不能留在自己的版块里，靠的是后面那些正文把它围住。11 个用
   .figure-lead 的页面后面都跟着 <h2> + <p class="sub"> + 至少三段正文，
   实测最短的一节也远高于图高，所以图不会溢到下一节。**加新文章时照旧写，
   图后面别直接跟一个横向表格或图库。** */
.figure-lead{
  float:left;max-width:560px;
  margin:var(--s-7) var(--s-7) var(--s-5) 0;
  border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;
  box-shadow:var(--sh-1);background:var(--paper);
}
.figure-lead img{
  width:100%;aspect-ratio:3/2;object-fit:cover;display:block;
  background:var(--tint-200);border-bottom:1px solid var(--line);
}
.figure-lead figcaption{
  padding:var(--s-3) var(--s-4);font-size:var(--fs-xs);
  color:var(--ink-2);line-height:1.55;margin:0;max-width:none;
}
.figure-lead figcaption b{color:var(--ink);display:block;font-size:var(--fs-2xs);
  letter-spacing:var(--ls-label);text-transform:uppercase;margin-bottom:3px;font-weight:500}
/* 窄屏别浮动：1080 以下 .wrap 只剩约 1030，图的 560 + 48 间距一去，
   右边正文不到 420px，越读越窄。收回"图在上、文在下"。
   ⚠️ 这里**不能**顺手写 max-width:100% —— 那会把源图放大 2 倍，糊。 */
@media(max-width:1080px){
  .figure-lead{float:none;margin:var(--s-7) 0 0}
}

/* ⚠️ 2026-10-15 第 23 轮（补）：浮动旁边**不能带回 max-width**。
   .sub 自带 max-width:var(--measure) = 62ch = 670px。浮动只压缩**行盒**，
   不改变块的 border box —— 而图 560 + 右间距 32 = 592px 整个落在这个 670px
   的盒子里，行盒就只剩「盒右缘 − 浮动右缘」= 78px 一条缝。实测首行 76px，
   **一行一个词**（"Read the / third / column / before / …"）。
   上面那些没写 max-width 的正文段盒子是通栏 1092，右缘在浮动之外，
   所以它们的行盒正常拿到 500px —— 同一个浮动，差别只在 max-width。
   放开以后 .sub 的盒子也通栏 → 行盒 = 版心右缘 − 浮动右缘 ≈ 500px，
   约 46ch，比 62ch 还好读。
   ⚠️ 只在浮动生效的宽度区间写（>1080）。≤1080 图不浮动，这条会把 18px 的
   副标题拉成横跨整个版心的一行。 */
@media(min-width:1081px){
  .figure-lead ~ .sub{max-width:none}
}

/* ⚠️ 2026-10-15 第 23 轮（补 2）：浮动不能被版块算漏。
   版块高度**不含浮动**，图比正文长的时候它会一直垂进版块的下内边距：
   收窄 .sub 之后 pcb-surface-finishes 上量到「图底 1386 / 下一节 1393」，
   只剩 7px，图卡几乎贴住下一节的横线（改之前 84px）；turnkey 剩 40px。
   .wrap 末尾放一个 clear:both 的零高块，把浮动算进版块高度 → 下内边距
   原样恢复，图永远关在自己版块里，以后再删正文也不会压到下一节。
   ⚠️ 两个试过不能用的写法：
   · .wrap{display:flow-root} —— BFC 挡住 <h2> 的 margin-top 上溢，
     整节上移 48px（页头到正文 136 → 88，跟同页别的版块不一致）。
   · section::after —— 首页那个 section.feature 是 display:grid
     （见 2152 行），::after 变成**一个真的格子**，凭空多一行 64px。
     .wrap 永远是 block，而且 .feature 根本不套 .wrap。
   全站只有 .figure-lead 一个浮动（grep float: 只有它）。
   只在浮动生效的宽度以上写，窄屏完全不参与。 */
@media(min-width:1081px){
  .wrap::after{content:"";display:block;clear:both}
}


/* ---------- 文件清单 ---------- */
.docs{list-style:none;padding:0;margin:var(--s-4) 0 0;display:grid;gap:var(--s-2)}
/* 同 .promises：通栏单列时每行右半边空着，两列收口；奇数条时最后一条通栏。 */
@media(min-width:900px){
  .docs{grid-template-columns:repeat(2,1fr)}
  .docs li:last-child:nth-child(odd){grid-column:span 2}
  .wrap.narrow .docs{grid-template-columns:1fr}
  .wrap.narrow .docs li:last-child:nth-child(odd){grid-column:auto}
}
.docs li{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-sm);
  padding:var(--s-3) var(--s-4) var(--s-3) calc(var(--s-4) + var(--s-6));
  font-size:var(--fs-sm);line-height:1.6;position:relative;
  color:var(--ink-2);
  transition:border-color var(--dur-2) var(--ease),background-color var(--dur-2) var(--ease);
}
.docs li:hover{border-color:var(--line-2);background:var(--tint-50)}
/* 同上：▸ 用绝对定位，别让 li 变成 flex 容器 */
.docs li::before{content:"▸";position:absolute;left:var(--s-4);top:var(--s-3);
  color:var(--accent-strong);font-weight:700}
.docs b{color:var(--ink);font-weight:600}


/* ---------- 流程步骤 ---------- */
.process{list-style:none;counter-reset:step;padding:0;margin:var(--s-5) 0 0;max-width:740px}
.process li{
  counter-increment:step;position:relative;
  padding:0 0 var(--s-5) 54px;margin-left:15px;
  border-left:2px solid var(--line);color:var(--ink-2);
  font-size:var(--fs-sm);line-height:1.62;
}
.process li:last-child{border-left-color:transparent;padding-bottom:0}
.process li::before{
  content:counter(step);position:absolute;left:-16px;top:-3px;
  width:31px;height:31px;border-radius:var(--r-full);
  background:var(--tint-200);color:var(--ink);display:grid;place-items:center;
  font-family:var(--font-mono);font-size:var(--fs-xs);font-weight:600;
  box-shadow:0 0 0 4px var(--bg);
}
section.alt .process li::before{box-shadow:0 0 0 4px var(--paper)}
.process b{color:var(--ink);display:block;font-size:var(--fs-sm);font-weight:600;
  margin-bottom:2px;letter-spacing:-.008em}
.process .at{
  color:var(--mute);font-size:var(--fs-2xs);font-family:var(--font-mono);
  letter-spacing:.01em;
}


/* ---------- 示意图 ---------- */
.diagram{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-6) var(--s-6) var(--s-5);margin:var(--s-5) 0 0;
  box-shadow:var(--sh-1);
}
.diagram svg{width:100%;height:auto;display:block}
.diagram figcaption{
  font-size:var(--fs-xs);color:var(--ink-2);line-height:1.6;
  margin-top:var(--s-5);border-top:1px solid var(--line);padding-top:var(--s-4);
}
.diagram figcaption b{color:var(--ink);font-weight:500;
  letter-spacing:var(--ls-label);text-transform:uppercase;font-size:var(--fs-2xs)}

/* ---------- 尺寸面板（2026-10-19 第 34 轮） ----------
   capabilities.html 的 #assembly 末尾原来是一张白底 SVG 线图 —— 两个按同一
   比例画的矩形。用户拿了一张深绿底的参考稿：「有一个部分可以换成这个样子，
   可以借鉴这个设计风格，图片可以选用里面的图片」。这一节就是那张稿的落地方案。

   ⚠️ 事实的载体仍然是**文字**：两个尺寸、两条限制、拆分面板那句话，全部是
      DOM 里的文本。参考稿把数字烤进了图里 —— 那种图 AI 读不到、屏幕阅读器
      读不到、翻译不了，也没法被单独引用。这里一个数字都没进图。
   ⚠️ 两张照片是**示意位**（alt 为空、纯装饰），不承担任何事实主张。
      作证位（「我们有这些设备 / 产线」）必须实拍 —— 那条铁律不适用于这里。
   ⚠️ 深色底全部用现成令牌：--deep-900 + --on-dark*（第 24 轮为页脚开的四档）
      + 深底强调色 --jade-300。**没有新增任何颜色令牌。**
   ⚠️ 作用域写成 .sizepanel，**不写裸 .dark** —— 裸类和 .band 同为 (0,1,0)，
      谁赢取决于源码顺序，下一个人挪一行深底就静默没了（第 33 轮踩过这个坑）。
   ⚠️ 原来的 .diagram 规则**没删**：这一轮之后站上已经没有它的消费者，
      但删不删等下一次统一清理再定，别在这轮夹带。 */

.sizepanel{
  /* 尺寸线用的那支白。--on-dark-line 是 .16 的白，是给页脚分隔线用的，
     铺在这种整块深底上几乎看不见（第一版渲出来就是这样）——所以另开一个 .30。
     只在本组件里声明，不上第 1 层：它不是一个语义色，是这条线的粗细感。 */
  --dim-line:rgba(255,255,255,.30);
  position:relative;margin:var(--s-6) 0 0;overflow:hidden;
  background:var(--deep-900);color:var(--on-dark-3);
  border-radius:var(--r-lg);padding:var(--s-9);
}
/* 左上角那道极淡的绿光。参考稿的暗底不是纯色，是一层从亮处压下去的光。
   ⚠️ pointer-events:none 不能省 —— 它铺满整块面板，否则会吃掉文字选择。 */
.sizepanel::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 90% at 10% 0%,rgba(79,195,145,.10),rgba(79,195,145,0) 60%);
}
.sizepanel > *{position:relative}

.sizegrid{display:grid;grid-template-columns:.8fr 1fr;gap:var(--s-10);align-items:start}

/* 标题与正文。h3 / p 在排版层写死了 --ink，光靠继承改不动，必须逐条压过去。 */
.sizepanel .eyebrow{color:var(--jade-300);margin-bottom:var(--s-3)}
.sizepanel .eyebrow::before{background:var(--jade-300)}
.sizepanel h3{
  color:var(--on-dark);font-size:var(--fs-xl);line-height:var(--lh-snug);
  margin:0 0 var(--s-3);
}
.sizepanel p{color:var(--on-dark-2);font-size:var(--fs-sm);line-height:1.62;margin:0;max-width:44ch}

/* 板型三枚小牌。放在 h3 下面 —— 用户 2026-10-19 问「这里面能看出来我们能不能做
   硬板 / 软板 / 软硬结合板」，而原来它只藏在正文那句的从句里，扫读抓不到。
   事实没变（同页参数表 "Board type" 那一行写的就是这三个），只是提成了一行。 */
.sizepanel .sz-types{display:flex;flex-wrap:wrap;gap:var(--s-2);margin:0 0 var(--s-4)}
.sizepanel .sz-types b{
  font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:.04em;color:var(--on-dark);line-height:1.6;
  border:1px solid var(--dim-line);border-radius:var(--r-full);
  padding:2px var(--s-3);
}

/* 照片 + 尺寸线。图的左边留 34px 给纵向线，下边留 32px 给横向线。
   ⚠️ 标签放在线**外侧**，不压在线上 —— 这样不用背景色去垫断线，面板上那层
      绿光就不会被一块深色补丁切开。 */
.sz-shot{position:relative;margin-top:var(--s-6);padding:0 0 32px 34px}
.sz-frame{position:relative;overflow:hidden;border-radius:var(--r-xs);
  background:rgba(255,255,255,.05)}
.sz-frame img{display:block;width:100%;height:100%;object-fit:cover}
.sz-shot--tall .sz-frame{aspect-ratio:2 / 3}
.sz-shot--wide .sz-frame{aspect-ratio:6 / 5}

.sz-dim{position:absolute;color:var(--on-dark-2);
  font-family:var(--font-mono);font-size:var(--fs-2xs);
  letter-spacing:.04em;font-weight:500;white-space:nowrap}
.sz-dim-w{left:34px;right:0;bottom:0;height:32px;padding-top:12px;
  text-align:center;line-height:1}
/* 一道线 + 两端各一枚小横标。三条背景层画的，不额外加 DOM ——
   尺寸线是纯装饰，给它两个字标签反而会让读屏器多念两遍同一个数字。 */
.sz-dim-w::before{content:"";position:absolute;left:0;right:0;top:0;height:9px;
  background:
    linear-gradient(var(--dim-line),var(--dim-line)) center / 100% 1px no-repeat,
    linear-gradient(var(--dim-line),var(--dim-line)) left top / 1px 9px no-repeat,
    linear-gradient(var(--dim-line),var(--dim-line)) right top / 1px 9px no-repeat}
.sz-dim-h{left:0;top:0;bottom:32px;width:34px;
  display:flex;align-items:center;justify-content:center}
.sz-dim-h::before{content:"";position:absolute;right:0;top:0;bottom:0;width:9px;
  background:
    linear-gradient(var(--dim-line),var(--dim-line)) center / 1px 100% no-repeat,
    linear-gradient(var(--dim-line),var(--dim-line)) left top / 9px 1px no-repeat,
    linear-gradient(var(--dim-line),var(--dim-line)) left bottom / 9px 1px no-repeat}
.sz-dim-h b{writing-mode:vertical-rl;transform:rotate(180deg);font-weight:500}

/* 底注：一行警示 + 原来的图注。图注里那句「最常见的错误」是这一段最值钱的
   可引用句，换设计时**一个字都不该丢**（只把原来那句 "twice the size"
   按实数改成 3.4 倍 —— 650×1100 / 450×470 = 3.38，写 "twice" 是错的）。 */
.sizepanel .size-note{
  display:flex;align-items:flex-start;gap:var(--s-3);max-width:none;
  margin:var(--s-8) 0 0;padding-top:var(--s-5);
  border-top:1px solid var(--on-dark-line);
  color:var(--on-dark-2);font-size:var(--fs-sm);line-height:1.5;
}
.sizepanel .size-note svg{flex:0 0 18px;width:18px;height:18px;margin-top:2px;
  stroke:var(--jade-300);stroke-width:1.5;fill:none;
  stroke-linecap:round;stroke-linejoin:round}
.sizepanel figcaption{
  margin-top:var(--s-4);font-size:var(--fs-xs);line-height:1.6;
  color:var(--on-dark-3);
}
.sizepanel figcaption b{color:var(--on-dark);font-weight:500;
  letter-spacing:var(--ls-label);text-transform:uppercase;font-size:var(--fs-2xs)}

@media(max-width:900px){
  .sizepanel{padding:var(--s-7) var(--s-6)}
  .sizegrid{grid-template-columns:1fr;gap:var(--s-8)}
  /* 窄屏一列排下来，2:3 的竖图会把整节拉得很长，放宽成 4:5 */
  .sz-shot--tall .sz-frame{aspect-ratio:4 / 5}
}


/* ---------- 杂项 ---------- */
.qr{
  max-width:184px;border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-2);background:var(--paper);box-shadow:var(--sh-1);
}
.updated{font-size:var(--fs-2xs);color:var(--mute);font-family:var(--font-mono);
  letter-spacing:.02em;margin-top:var(--s-2)}

/* 「还有更多 →」—— 正文节末尾指向**另一个页面**的那一条。
   2026-10-13 第 12 轮加：设备页和 capabilities 之间要互相指路。
   2026-10-14 第 22 轮：10 篇技术指南用它回栏目页（`All technical guides →`）——
   这十页本来一条 .more 都没有，正文里链回 guides.html 的一处都没有。
   箭头用 &rarr; 排版符号，不是图标字体也不是 SVG —— 和 .docs li 的 ▸ 一路，
   读屏念得出来，也不占一次请求。
   颜色用 --accent-ink（--jade-600）：它是这一档里**唯一能在浅底上安全承载文字**的绿
   （--jade-400 压白底只有 3.36:1，不够）。 */
.more{margin:var(--s-6) 0 0;font-size:var(--fs-sm);font-weight:500}
.more a{color:var(--accent-ink);border-bottom:1px solid var(--accent-ring);padding-bottom:2px}
.more a:hover,.more a:focus-visible{color:var(--ink);border-bottom-color:var(--ink)}

/* ---------- 首页资源带（第 38 轮） ----------
   用户给的设计稿是「资源」三张卡（Blog / Guides / FAQ）。这里**不做成三张卡**：
   ① 紧跟着就是一整节「Common questions」6 个问答，再来一张 FAQ 卡是同一个出口的第二个门；
   ② 首页已有 .pcard 三卡 / .tile 六格 / 流程三卡，再来一排卡片就成卡片墙；
   ③ 末尾动线是「6 问 → 询价表单」，三个出口等于给准备填表的人开后门。
   改成一条带上下发丝线的窄带 —— 实测整节 **163px**（节内边距 48 + 带内边距 40 +
   两行说明），不是一整节 300px；首页总高 10503 → 10690（含页脚多的一行 24）。
   ⚠️ 链接**不 reset 下划线**：全站正文链接都带下划线（见上面那条「可点性」），
      资源带正是要让人一眼看出能点的地方。 */
section.ressec{padding:var(--s-6) 0}
.resband{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;
  gap:var(--s-4) var(--s-9);
  padding:var(--s-5) 0;
  border-top:1px solid var(--line-2);border-bottom:1px solid var(--line-2);
}
.resband-copy{min-width:0}
.resband h2{margin:0 0 var(--s-2);font-size:var(--fs-sm);font-weight:600;color:var(--ink)}
.resband-copy p{margin:0;font-size:var(--fs-sm);color:var(--ink-2);max-width:62ch}
.resband-links{
  margin:0;display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2) var(--s-7);
  font-size:var(--fs-sm);color:var(--ink-2);
}
@media(max-width:700px){
  .resband{flex-direction:column;align-items:flex-start}
}

/* 左右分栏。两列刻意接近等宽 —— 这样图在左或在右，图的尺寸都一样大，
   不用为了"图换到左边"再调一次比例。 */
/* 两栏等宽。原来是 1.05fr/.95fr，结果"图在右"那一块的图比"图在左"那块窄 10% ——
   两张图上下隔着一段文字，宽度差一眼能看出来。等宽之后两张图一样大，
   页面节拍变成 图｜文 → 文｜图 → 整幅大图，才是有意为之的样子 */
.split{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-9);align-items:center}

/* 分栏里的图统一裁成 3:2。
   ⚠️ 必须是 `>` 直接子元素，不能写成后代选择器 `.split img`：
   contact 页的 WhatsApp 二维码也嵌在 .split.form 里面，后代选择器会连它一起
   套上 aspect-ratio + object-fit:cover，把二维码压成 2:1 的横条 —— 现在没坏
   只是被 img{height:auto} 和 .qr 的 padding 侥幸挡住了。
   换图时如果新图不是横构图，把这行 aspect-ratio 删掉。 */
.split > img{
  border-radius:var(--r-md);border:1px solid var(--line);box-shadow:var(--sh-1);
  width:100%;aspect-ratio:3/2;object-fit:cover;
}

.pill{
  display:inline-block;background:var(--accent-soft);color:var(--accent-ink);
  font-size:var(--fs-2xs);font-weight:500;letter-spacing:var(--ls-label);
  text-transform:uppercase;padding:4px 11px;border-radius:var(--r-full);
  margin:0 var(--s-2) var(--s-2) 0;
}


/* ---------- 页脚 ----------
   2026-10-13 第 12 轮：深底（--deep-900）改成**白底 + 一道上边线**。
   2026-10-15 第 24 轮：**改回深底**。用户原话（指着页脚的截图）：
   「把网站的这个地方做一个差异化处理，最下面的颜色不用白色但要突出字体」。
   改回不只是"从命"：第 12 轮之后全站只剩白底 + 淡薄荷色带，页脚和它上面
   那个 .alt 色带**都是浅的**，读完最后一个板块等于没有落点。页脚是整站
   唯一"后面什么都没有了"的位置，理应是整页最重的一块。
   三条纪律（下面都落实了）：
     · 字色走 1.2 里的 --on-dark* —— --line 那种浅绿线在深底上直接隐形
     · **链接一定比正文亮**（--on-dark-2 / --on-dark-3）：模板站页脚把两者
       都设成 --on-dark-3，链接在正文里读不出来，这一条是照着用户的
       "突出字体"改的
     · 深底上的强调色用 --jade-300(#4FC391, 6.84:1)，
       --jade-400 在深青上只有 4.47:1，跌破线
   ⚠️ 页脚和 .band 都是"横贯全屏的一条"，仍然**不要合并成同一个类**：
      色带是淡薄荷（--band），页脚是深青（--deep-900）。
   ⚠️ 那道 1px 上边线跟着删了 —— 边线是拿来分开两块**同色**的浅底的；
      深底自己就是分界，再画一条只是多一根灰线。
   ⚠️ 打印见文件末尾的 @media print。 */
footer.site{
  background:var(--deep-900);
  color:var(--on-dark-3);
  padding:var(--s-11) 0 var(--s-7);font-size:var(--fs-sm);line-height:1.65;
}
footer.site .cols{
  display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;
  gap:var(--s-8);margin-bottom:var(--s-7);
}
footer.site h4{
  color:var(--on-dark);font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:500;
  letter-spacing:var(--ls-label);text-transform:uppercase;margin:0 0 var(--s-3);
}
/* 第一列那行公司名**不当标签排**。三个栏目名（SITE / GUIDES / COMPANY）是标签，
   公司名是名字 —— 页脚里唯一"该被读"的一行就是它。所以：正文字体、不转大写、
   不拉字距、字号大一档（14.5 → 16）、颜色最亮。
   ⚠️ margin 不在这里重写，跟上面那条共用（span 选不中 margin 就不用管）。 */
footer.site .cols > div:first-child h4{
  font-family:var(--font-sans);font-size:var(--fs-base);font-weight:600;
  letter-spacing:var(--ls-heading);text-transform:none;
}
footer.site a{color:var(--on-dark-2)}
footer.site a:hover{color:var(--jade-300)}
footer.site .bot{
  border-top:1px solid var(--on-dark-line);padding-top:var(--s-5);
  font-size:var(--fs-xs);color:var(--on-dark-3);
  display:flex;justify-content:space-between;gap:var(--s-4);flex-wrap:wrap;
}


/* ---------- 内页页头 ----------
   每个内页开头都是同一个块：小标签 → 大标题 → 一句话导语。
   做成本组件之后，页头不再各自写内联字号。 */
.page-head{padding-top:var(--s-10);padding-bottom:var(--s-9);position:relative;overflow:hidden}
/* 内页页头的右半边一向是空的（h1 封顶 24-28ch），这块地方放一层极淡的布线场。
   第 12 轮页头改浅之后它反而更成立了 —— 同一层走线场在深蓝底上是"板"，
   在白底上就是纸纹，和右边的实拍照片叠在一起正好是"图纸 + 实物"。 */
.page-head::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background-image:var(--routing-light);background-size:96px 96px;
  -webkit-mask-image:radial-gradient(70% 120% at 100% 40%,#000 0%,transparent 72%);
  mask-image:radial-gradient(70% 120% at 100% 40%,#000 0%,transparent 72%);
}
/* z-index 3：要和 `.page-media`（1）拉开，照片才压不到文字。
   首屏的 `.hero .wrap` 也是 3，两处保持一致。 */
.page-head .wrap{position:relative;z-index:3}
.page-head h1{
  font-size:var(--fs-page);line-height:var(--lh-display);
  max-width:24ch;margin:var(--s-3) 0 var(--s-4);
}
.page-head .lead{margin-bottom:0}
.page-head .lead ~ .lead{margin-top:var(--s-4)}

/* ---------- 内页页头 · 右侧实拍 + 本页真实读数 ----------
   2026-10-13 第 12 轮：页头从「板面」改回**浅色**。
   第 9–11 轮那套（深蓝渐变 + --routing-page 走线场 + 板框 + 丝印 + 深色读数牌）
   整组撤掉，`.page-head.board` 这个变体类**不再存在** —— 17 个内页的
   `board` 类和两个 `.page-silk` span 也一并从 HTML 里撤了（脚本做的）。

   留下的是它底下**真正有用的**两件东西，一件不少：
     · 右侧实拍照片 .page-media，靠一道左实右透的遮罩"融"进底色
     · 一列等宽读数牌 .reads，每条 = 本页正文里的一个真实事实

   ⚠️ **选择器从 `.page-head.board` 改成 `.page-head` 是必须做的**：
      类撤掉之后旧选择器会全部失配 —— 照片和读数牌会当场消失，
      而且 CSS 里不报任何错。这是这一轮最容易漏的一处。
   ⚠️ 读数牌是**真文字**，不是图片 —— AI 爬虫和屏幕阅读器都读得到。
      这正是它和刚被删掉的丝印（纯装饰、aria-hidden）的区别，别搞混。
   ⚠️ 照片那层才是装饰：img 的 alt 留空 + 整块 aria-hidden。
   层级：走线场 ::before = 0，照片 .page-media = 1，.wrap = 3。
   （原来是 0/1/2/3，中间那层 2 是板框，板框删了就空出来了。）
   ⚠️ 这一轮照片从"深蓝上压到 .55 透明度"改成"白底上几乎全显"——
      深色遮罩可以容忍一张烂照片，白色遮罩不能。照片只有 10 张铺 16 页，
      这是**已知短板**，不是这轮能靠 CSS 补上的（见改站清单）。 */
.page-media{
  position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 62%);
          mask-image:linear-gradient(90deg,transparent 0%,#000 62%);
}
.page-media img{
  width:100%;height:100%;object-fit:cover;
  opacity:.9;filter:saturate(.98);
}
/* 遮罩：左半边压到实白（保证标题对比度），右半边把照片留住。
   两头各留一点回压，免得右边缘直接怼到视口边。
   ⚠️ 起点是 `var(--bg)` 实白，不能改成半透明 —— 页头 h1 就压在左边，
      起点一透，标题对比度当场掉到 5:1 附近（和首页遮罩同一个道理）。 */
.page-media::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,
    var(--bg) 0%, rgba(11,13,15,.94) 26%,
    rgba(11,13,15,.62) 52%, rgba(11,13,15,.18) 78%,
    rgba(11,13,15,.38) 100%)}

/* 列表页版心宽，摆得下「左标题 / 右读数」两栏 */
.page-head .head-grid{
  display:grid;grid-template-columns:1.06fr .94fr;
  gap:var(--s-10);align-items:start;
}
.page-head .head-copy{max-width:640px}

.reads{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;gap:var(--s-3);align-items:stretch;
  max-width:330px;margin-left:auto;
}
/* 左缘那道绿 = 板上的状态标记，取自同一个 --jade-400。
   第 12 轮从"深色毛玻璃牌"改成"白牌"—— 底变浅了，牌得反过来才浮得起来。 */
.reads li{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-5);
  background:rgba(15,19,22,.88);
  border:1px solid var(--line);
  border-left:2px solid var(--jade-400);
  border-radius:var(--r-xs);padding:10px 15px;
  -webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);
  box-shadow:var(--sh-1);
}
.reads .k{
  font-family:var(--font-mono);font-size:10px;line-height:1.3;
  letter-spacing:.14em;text-transform:uppercase;color:var(--mute);
}
.reads .v{
  font-family:var(--font-mono);font-weight:600;
  font-size:var(--fs-sm);line-height:1.2;color:var(--ink);white-space:nowrap;
}

/* 技术指南用窄版心 —— 长文的行宽要比列表页更短 */
.page-head.article{}
.page-head.article .wrap{max-width:880px}
.page-head.article h1{max-width:28ch}
.page-head.article .lead{max-width:68ch}
/* 窄版心（880px）摆不下两栏。改成上下：读数折成 2×2 一条横带，
   压在导语和 .answer 之间 —— 长文页这样读起来更顺。 */
.page-head.article .head-grid{grid-template-columns:1fr;gap:var(--s-6)}
.page-head.article .head-copy{max-width:100%}
.page-head.article .reads{
  max-width:none;margin-left:0;
  display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-2);
}
.page-head.article .reads li{padding:9px 14px}

/* 窄屏：照片收掉（手机上它只会和标题抢地方），读数折成 2 列。
   用 display:none 而不是 content:none —— .page-media 是真盒子，
   不像板框那样靠 content 生成。 */
@media(max-width:900px){
  .page-media{display:none}
  .page-head{padding-top:var(--s-9);padding-bottom:var(--s-8)}
  .page-head .head-grid{grid-template-columns:1fr;gap:var(--s-6)}
  .page-head .head-copy{max-width:100%}
  .reads{
    max-width:none;margin-left:0;
    display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-2);
  }
  .reads li{padding:8px 12px}
  .reads .v{white-space:normal}
}

/* 窄版心，用在短文版块 */
.wrap.narrow{max-width:820px}

/* ---------- 卡片的"是 / 否"变体 ---------- */
.card.ok{border-left:3px solid var(--jade-600)}
.card.ok h3{color:var(--jade-600)}
/* 和 .card.ok 对称：ok 用 --jade-600，no 就用 --red-600。
   原来这里是写死的 #C08878（比 --red-600 浅两档），一深一浅不对称。 */
.card.no{border-left:3px solid var(--red-600)}
.card.no h3{color:var(--red-600)}

/* ---------- 术语小注 ----------
   跟在名词后面的灰色解释，比如 HASL 后面那句 "hot air solder levelling" */
.mute-note{
  display:block;font-size:var(--fs-2xs);color:var(--mute);
  line-height:1.5;letter-spacing:.01em;margin-top:1px;
}

/* ---------- 无边框表格 ----------
   用来排"联系方式"这种不是数据表的信息 */
table.plain{
  border:0;background:transparent;border-radius:0;font-size:var(--fs-sm);
}
table.plain td{
  border-top:1px solid var(--line);padding:var(--s-4) 0;background:none;
}
table.plain tbody tr:first-child td{border-top:none}
table.plain tbody tr:nth-child(even) td{background:none}
table.plain td:first-child{
  color:var(--ink-2);font-weight:400;width:38%;padding-right:var(--s-4);
}
table.plain td:last-child{color:var(--ink);font-weight:500}

/* ---------- 独立图片 ---------- */
/* figure 浏览器默认带 1em 40px 的外边距，40px 左右缩进会破坏栅格对齐 —— 必须清掉 */
figure{margin:var(--s-8) 0 0}

/* 散图的图注。.gallery / .diagram 里的图注另有一套，见上 */
figcaption{
  font-size:var(--fs-xs);color:var(--mute);line-height:1.55;
  margin-top:var(--s-3);max-width:var(--measure);
}

/* 厂房外景。原图是竖构图手机照（1581×2810），原比例放出来近一屏高，
   所以裁成 2:1 横构图。
   max-width:460px 那条已经删掉：那是"不裁切时防止竖图占满一屏"的上限，
   裁成 2:1 之后它只剩个副作用 —— 图只占半栏，右边留一大片空白，
   看着像坏了。现在铺满 wrap。
   object-position 47%：原图 2810 高，2:1 只取 790 高的一段，
   22% 取的是楼上半截（首层入口全没了），47% 才落在首层入口和门前绿化上。 */
.photo{
  border-radius:var(--r-md);border:1px solid var(--line);
  box-shadow:var(--sh-1);
  width:100%;aspect-ratio:2/1;object-fit:cover;object-position:center 47%;
}

/* ---------- 表单页的双栏（左表单窄、右侧信息宽） ---------- */
.split.form{grid-template-columns:1.15fr .85fr;align-items:start}

/* 对照表两栏等宽 */
table.verdict th{width:50%}

/* ---------- 小字号正文 ---------- */
.small{font-size:var(--fs-sm);line-height:1.6}


/* ---------- 右缘联系条 ----------
   桌面端贴在右缘竖排，≤900px 变成底部横条。**纯 CSS，零 JS**。

   2026-10-14 从 4 颗加到 6 颗。为什么是这 6 颗：
   · WhatsApp —— 指向 wa.me/号码（+86 133 7841 6751），**不是** contact.html
     那张二维码里的 wa.me/qr/… 令牌。号码印在正文表格里，访客点之前能先核一遍；
     令牌是一串看不出内容的乱码。⚠️ 两个入口**可能是同一个账号也可能不是**，
     改号时两处都要看。加它不只是"多一个渠道"：contact.html 正文写着
     「WhatsApp 和微信是海外买家实际最常用的两个渠道」，正文这么说、
     条子上却按不到，是自己打自己。
   · 微信 —— **不是外链**。微信没有 web 端的加好友入口，唯一入口是
     contact.html 那张二维码，所以这颗指向 contact.html#channels。
     做成 wa.me 那种链接就是条点了没反应的死链。
   · Telegram / Teams / Skype 不加：官网没这个渠道 / 只有账号名没有可点入口 /
     服务本身已关停（Skype 2025-05）。挂死渠道不如不挂。
   ⚠️ 加完之后条子是 6×44 + 5×2 间隙 ≈ 276px 高（原来约 184px）。
     top:50% + translateY(-50%) 照样居中压得住，**但再加渠道就要重量这一档**。
   ⚠️ ≤900px 的底部横条上 .rail a.ru 是 display:none（回顶部在横条上没意义），
     剩 5 颗摊进 375px = 每颗 75px，最长的那条标签「Get a quote」约 62px。
     再加渠道要回去重量"5 颗摊得下吗"。

   为什么标签向左展开：条子贴在右缘，标签只能往屏幕里长。所以用「绝对定位到
   芯片左侧 + 淡入位移」，而不是给芯片定宽再过渡 —— 变成 auto 宽是过渡不了的。

   z-index:40 —— header.site 是 sticky 且 z-index:50，必须压在它下面；
   首屏那几层是 1~4，必须盖住。 */
.rail{
  position:fixed;right:0;top:50%;transform:translateY(-50%);
  z-index:40;display:flex;flex-direction:column;gap:2px;
  border-radius:var(--r-sm) 0 0 var(--r-sm);
  box-shadow:var(--sh-3);
}
.rail a{
  position:relative;display:grid;place-items:center;
  width:44px;height:44px;
  background:var(--paper);color:var(--ink-2);
  border:1px solid var(--line);border-right:0;
  transition:background-color var(--dur-2) var(--ease),color var(--dur-2) var(--ease);
}
.rail a:first-child{border-radius:var(--r-sm) 0 0 0}
.rail a:last-child{border-radius:0 0 0 var(--r-sm)}
/* 芯片里的图：--ic-* 都是 24×24 viewBox，缩到 20px 放进 44px 的方块正中 */
.rail a::before{
  content:"";width:20px;height:20px;
  background-position:center;background-repeat:no-repeat;background-size:20px 20px;
}
.rail a.rq::before{background-image:var(--ic-quote-white)}
.rail a.rm::before{background-image:var(--ic-mail)}
.rail a.rt::before{background-image:var(--ic-tel)}
.rail a.ru::before{background-image:var(--ic-top)}
.rail a.rwa::before{background-image:var(--ic-wa)}
.rail a.rwc::before{background-image:var(--ic-wechat)}

.rail a:hover,.rail a:focus-visible{background:var(--jade-100);color:var(--jade-600)}
/* 询价那颗沿用 .btn.primary 的做法：阻焊绿底 + 白图标 + 一道沉金底边，
   和 nav 那颗 CTA 是同一颗按钮的第三种出现位置。 */
.rail a.rq{background:var(--green-500);color:#fff;border-color:var(--green-500);
  box-shadow:inset 0 -2px 0 var(--enig)}
.rail a.rq:hover,.rail a.rq:focus-visible{background:var(--green-600);color:#fff}
/* 「回顶部」和上面五颗隔开一点 —— 上面五颗是联系方式，它是导航 */
.rail a.ru{margin-top:var(--s-2)}

/* 悬停 / 键盘聚焦时向左浮出的标签。等宽 + 大写 + 宽字距。
   pointer-events:none 是必须的：它盖在芯片左边，不挡的话会把 hover 抖掉。 */
.rail a span{
  position:absolute;right:100%;top:0;height:100%;
  display:flex;align-items:center;padding:0 var(--s-3);
  background:var(--tint-200);color:var(--ink);
  font-family:var(--font-mono);font-size:var(--fs-2xs);
  letter-spacing:var(--ls-label);text-transform:uppercase;white-space:nowrap;
  opacity:0;transform:translateX(6px);pointer-events:none;
  transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease-out);
}
.rail a:hover span,.rail a:focus-visible span{opacity:1;transform:translateX(0)}


/* ---------- 品类瓦片 ----------
   首页「What we do」那六块。和 .card 的区别只有一个：上面多一块插图区 ——
   整块读起来像一份产品目录，而不是六段文字排成三列。
   **.card 本身一个字没动**（全站 40 多处在用）。

   插图区用 --tint-100 内嵌在瓦片底色上，再压一层极淡的布线场（--routing-light）
   —— 和首屏、深色版块、内页页头是同一个签名，只是音量最小。

   ⚠️ .art 里的 <svg> 将来可以一对一换成实拍照片：下面 img 的规则已经写好了，
   换的时候只改 HTML，CSS 不动。 */
.tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-5)}
.tile{
  display:flex;flex-direction:column;margin:0;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  overflow:hidden;box-shadow:var(--sh-1);
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
.tile:hover{border-color:var(--line-2);box-shadow:var(--sh-2);transform:translateY(-2px)}
.tile .art{
  position:relative;display:grid;place-items:center;
  aspect-ratio:16/10;padding:var(--s-6);
  background:var(--tint-100);border-bottom:1px solid var(--line);
  /* ⚠️ 这里**不能**加 overflow:hidden（第 25 轮试过，全站高度 diff 抓到的）。
     .art 是 .tile 的 flex 项，overflow:visible 时它的**自动最小尺寸**按内容算；
     里面那个 svg 是 viewBox 120×80（3:2）撑的，300px 宽下算出 200px + 上下 48px
     padding = 248px，比 aspect-ratio:16/10 想要的那 217.5px 高 —— 于是
     **这条 aspect-ratio 一直是被内容顶着走的**。一旦加上 overflow:hidden，
     自动最小尺寸变成 0，aspect-ratio 立刻生效，每块瓦片的插图区矮掉 31.7px，
     首页三行瓦片一共矮 95px。
     所以裁剪只写在 .art.photo 那一条上（照片进来之后才需要，见下面）。 */
}
.tile .art::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:var(--routing-light);background-size:96px 96px;
}
.tile .art svg{position:relative;width:100%;height:100%;color:var(--line-3)}
/* 实拍照片版（2026-10-15 第 25 轮加；第 29 轮起首页那 9 个 .art **全部**是照片，
   线描图版已从首页退场）。外层写 class="art photo"：padding 必须归零，否则照片
   四周留一圈 --tint-100 底，看着像"贴歪了"。框是 16:10，给 750×470 的图就一格
   不裁；750 也够 2x 屏 —— 卡片实宽约 350px，retina 要 700。
   ⚠️ 加 .photo 会**让上面那条 aspect-ratio 第一次真正生效**：.art 的自动最小
      尺寸从"内容撑的 249px"变成 0，插图区落到 218px。第 29 轮实测：全站 20 页
      只有 index.html 变矮 31px，其余 19 页零像素差。 */
.tile .art img{position:relative;width:100%;height:100%;object-fit:cover;
  transition:transform var(--dur-3) var(--ease-out)}
/* overflow:hidden 只写在这里（不能写进上面 .tile .art，那会改掉线描图的高度，
   见那条注释）：照片进来之后 img 会 scale 到 1.03，不裁的话越过这道
   border-bottom 溢进 .body。 */
.tile .art.photo{padding:0;overflow:hidden}
/* 插图里的两种点缀色。SVG 属性里不写 var() —— 挂在类上更稳，也守住"值只来自令牌"。
   ⚠️ 第 29 轮（2026-10-16）起 .art 里全是照片，所以 .art svg / .ag / .au 这三条
      **全站已无人使用**（最后用它们的就是首页那三块行业线描插图）。留着不删：
      线描版真要回来，连同 --routing-light（.art::before 那道走线暗纹，现在被照片
      整个盖住）一起复活即可；确定不要了，这三条连同令牌一并清掉。 */
.tile .art .ag{stroke:var(--jade-400)}
.tile .art .au{stroke:var(--jade-400)}
.tile .body{padding:var(--s-5) var(--s-6) var(--s-6)}
.tile .num{
  display:block;font-family:var(--font-mono);font-weight:600;
  font-size:var(--fs-2xs);letter-spacing:var(--ls-wide);
  color:var(--accent-ink);margin-bottom:var(--s-2);
}
/* ⚠️ 上边距必须写死成 0。全局 h3 是 `margin:var(--s-7) 0 var(--s-3)`（32px），
   瓦片里 .num 在 h3 前面，所以 h3 不是 :first-child，吃不到那条归零规则 ——
   不覆盖的话标题会被顶下去 32px，整块瓦片比例就垮了。 */
.tile h3{margin:0 0 var(--s-2)}
.tile p{margin:0;color:var(--ink-2);font-size:var(--fs-sm);line-height:1.62}
@media(max-width:1000px){.tiles{grid-template-columns:repeat(2,1fr)}}
@media(max-width:720px){.tiles{grid-template-columns:1fr;gap:var(--s-4)}}


/* ---------- 首页「What we do」服务卡：实拍照片 + 悬停展开（2026-10-15 第 26 轮）----------
   用户给的设计稿：6 张卡、两行三列，**每张顶部是真实工业照片**（不要线描图/草图），
   默认只出「照片 + 英文标题」；鼠标悬停时卡片向下平滑伸展、照片保留、下方露出描述，
   边框转品牌深绿、阴影略微加深。这一带的底色是浅灰白，压一层低透明度的
   真实 PCB 照片当暗纹。

   ⚠️ 全部规则挂在 section.svc-sec 下（**只有首页那一节**有这个类）。站上另外两组
      .tiles（首页下面那三块行业瓦片、别的页面的瓦片）一个像素都没动。

   ⚠️ 描述文字**始终在 DOM 里**，只是被 CSS 折起来 —— 折起来的是给人看的排版，
      不是可引用句。这条是 GEO 站点的底线（全站 FAQ 本来就是 details 折叠，
      同一个道理），所以下面那条 @media 只动 display/行高，一个字都不删。

   ⚠️ 折叠只在**真有指针**的设备上生效（hover:hover）：触屏没有"悬停"这回事，
      折了就等于手机上永远读不到描述。窄屏/触摸设备走默认值 —— 一直是展开的。 */
section.svc-sec{
  /* 底层是一张整幅板微距（hero-pcb-macro.jpg），上面压一层浅灰白纱 ——
     纱的浓度就是暗纹的存在感。

     第 26 轮用的是 products/board-04.jpg（750×580 的拼板照）+ **.965** 的纱
     （只露 3.5%）—— 那个浓度不是审美选择，是**被迫的**：board-04 在这个位置会被
     cover 放大 2.5 倍，露出更多就糊成一片灰影。第 27 轮按当时的注释办了
     （"要更明显的纹理就换一张本来就是满幅微距的照片，别靠降纱"）：换成
     hero-pcb-macro.jpg（1672×941，在这个位置是**缩小 0.82 倍**、原样清晰，
     而且和首屏第二帧同一个文件、共用一次下载），纱因此可以降到 **.90**
     （露出 10%，是原来的近三倍）。

     ⚠️ **.90 是量出来的，不是看着调的**（geo_check\contrast_r27.py：把 .sec-head
        用 visibility:hidden 藏掉再截整节，逐像素取最差对比度）。实测表：
          .965 → h2 8.99:1 / sub 6.02:1      .900 → 7.76:1 / 5.20:1  ← 取这档
          .940 → 8.50:1 / 5.70:1             .880 → 7.37:1 / 4.94:1
          .920 → 8.13:1 / 5.45:1             .860 → 7.02:1 / 4.70:1
                                             .820 → 6.36:1 / 4.26:1  ← 破 4.5
        两个达标线都是 4.5:1，卡口是**颜色最浅的 .sub**（h2 深绿离底线还远）。
        .90 是"第一档还留着 15% 余量"的；.88/.86 也过，但余量只剩 10%/4%，
        不值得为一个几乎看不出差别的浓度去贴线。**再往下别动**。

     ⚠️ 用 background 简写把颜色一起带上：section.alt 那条 background:var(--band)
        优先级比 .svc-sec 低，但只写 background-image 的话薄荷色会从照片后面透出来。 */
  background:
    linear-gradient(rgba(15,19,22,.88),rgba(15,19,22,.88)),
    url("img/hero-pcb-macro.jpg") center/cover no-repeat var(--svc-page);
}
/* 悬停只让**被指的那一张**长高。默认的 stretch 会把同排三张一起拉高，
   另外两张就在底部空出一块卡片底色，看着像坏了。 */
section.svc-sec .tiles{align-items:start}
section.svc-sec .tile{background:var(--svc-card)}
/* 边框转品牌深绿（--accent-ink = --jade-600，站上正文链接那一档深绿），
   阴影从默认 --sh-1 加深到 --sh-2 —— 用户原话"阴影略微加深"，不是 --sh-3。 */
section.svc-sec .tile:hover{
  border-color:var(--accent-ink);
  box-shadow:var(--sh-2);
}
/* 六块现在**全是照片**（第 27 轮换掉最后两块线描图之后），所以这条规则进入了
   休眠状态 —— 站上已经没有 .art:not(.photo) 落在这个选择器里了。**留着不删**：
   它记着"退回线描图时要补哪两条"—— ① 按 16:10 裁齐（不加这条，svg 的内容高度
   会把 .art 顶高，同一行里"照片格"和"插图格"差 30 像素）；② 底色调淡一档
   （--tint-100 → --tint-50）。将来真退回插图，删了还得重写一遍。 */
section.svc-sec .tile .art:not(.photo){overflow:hidden;background:var(--tint-50)}

@media (hover:hover){
  /* 标题一律预留两行的高度。六块的标题长短不一（05「Metal & plastic injection
     moulding」占两行，其余一行），probe 实测：不预留时第二排比第一排高 31px，
     一排卡片下缘参差。2em * --lh-snug 就是正好两个行盒，跟着字号走不用写死。
     ⚠️ 只写在"描述会折起来"的这一档里 —— 触摸/窄屏那档每张卡都展开、本来就不齐，
        再顶 31px 白边只是浪费。 */
  section.svc-sec .tile h3{min-height:calc(2em * var(--lh-snug))}

  /* 折叠用 grid-template-rows 的 0fr→1fr，**不用 max-height**：
     max-height 得猜一个够大的值，猜小了文字被切、猜大了前三分之一行程就走完
     （后面全在空跑）——而六块卡的描述长短差一倍（最短一行、最长七行），
     一个数字不可能同时合适。fr 那套是**按内容实际高度**插值的，多长都准。
     代价是这条比较新（Chrome 107+ / Safari 16+ / Firefox 121+）；老引擎里
     折叠/展开照样生效，只是不过渡、瞬间跳一下。 */
  section.svc-sec .tile .body{
    display:grid;grid-template-rows:auto auto 0fr;
    transition:grid-template-rows var(--dur-3) var(--ease);
  }
  /* min-height:0 + overflow:hidden 是 0fr 那一档真正折叠的条件，少一个都不塌 */
  section.svc-sec .tile .body > p{min-height:0;overflow:hidden;margin:0}
  /* 卡片里现在没有可聚焦元素，:focus-within 等于摆设 —— 留着是为了哪天标题
     变成链接时不用回来补一条。 */
  section.svc-sec .tile:hover .body,
  section.svc-sec .tile:focus-within .body{grid-template-rows:auto auto 1fr}
}

/* ---------- 瓦片下那条实拍横带 ----------
   首页「What we do」六块白描瓦片下面（2026-10-14 第 12 轮加）。

   为什么是「白描瓦片 + 实拍横带」两层，而不是干脆把瓦片换成照片：
   六块瓦片是**目录**，等宽等高才扫得动；实拍是**凭据**，回答的是
   "你们到底有没有这些机器"。混成一层两件事都做不好 —— 换成照片后六块
   参差不齐，白描那套统一线宽也白丢了。分开两层，各干各的。

   ⚠️ 这是全站第一条不套 .card / .tile 的图片带：刻意不给边框和投影。
   上面那层已经有 6 个框了，再加 3 个就成 9 个方块，密度失控。
   照片自己那圈圆角 + 1px 线已经够。

   ⚠️ 断点**必须和 .tiles 一样**（1000 → 2 栏、720 → 1 栏）。
   不一样的话，900px 那档会出现"上面两栏、下面三栏"的错位，一眼就看得出来。 */
.shotrow{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--s-5);margin-top:var(--s-7);
}
.shotrow figure{margin:0}
.shotrow img{
  display:block;width:100%;aspect-ratio:4/3;object-fit:cover;
  border:1px solid var(--line);border-radius:var(--r-md);
}
.shotrow figcaption{
  padding-top:var(--s-3);font-size:var(--fs-sm);color:var(--ink-2);line-height:1.5;
}
.shotrow b{display:block;color:var(--ink);font-weight:600;margin-bottom:2px}
@media(max-width:1000px){.shotrow{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:720px){.shotrow{grid-template-columns:1fr;gap:var(--s-4)}}


/* ---------- 首页「Boards off our lines」横向跑马灯（自动滚动） ----------
   2026-10-14 先做成可拖的 snap 滑轨；同日用户选了"A 方案"，
   改成**自动横向滚动**。全站铁律没破 —— 一行 JS 都没加，纯 CSS 动画。

   HTML 现在是两层：
     .boardrail        视窗：overflow:hidden + 两头渐隐
       .boardrail-track 跑马灯本体：width:max-content，动画 translateX
         figure × 9     真图
         figure × 9     完全相同的副本（class="rail-dup" + aria-hidden）

   ⚠️ 副本是无缝循环的**必要条件**，不是重复劳动：动画跑完一半位移那一刻，
      第二组第一张正好落在第一组第一张的位置，接缝看不出来。
      副本一律 aria-hidden="true" 且 alt=""，读屏和 AI 都不会读成两份。
      两组的图是同一个 URL，浏览器只下一次，不额外花流量。

   ⚠️ 位移量必须写 calc(-50% - var(--s-5) / 2)，**不能**只写 -50%：
      18 张之间有 17 个 gap，-50% 只算进 8.5 个，会差半个 gap 抖一下。

   ⚠️ 图宽用 clamp 定值，**不能**再用旧的 --cols 百分比：轨道是 width:max-content，
      百分比没有可解算的基数，整条会塌掉。

   ⚠️ 和 .shotrow 仍是两套东西，别合并（.shotrow 是三栏定宽网格）。
   ⚠️ overflow 只挂在 .boardrail 自己身上，**不许挂到 .wrap 或 section** ——
      挂到祖先会把悬浮导航抽屉一起裁掉。 */
.boardrail{
  --rw:clamp(232px,23vw,352px);   /* 每格宽度 */
  --speed:72s;                    /* 一轮走完的时长，调快慢就改这里 */
  margin-top:var(--s-7);
  /* 2026-10-14 用户要「自动走 + 用户自己也能滑」：overflow 从 hidden 改成 auto，
     别的什么都不用动 —— transform 的位移是相对 track 自己算的，跟容器能不能滚无关。
     轨道宽是 2 倍内容（副本那半截），所以 scrollWidth > clientWidth，滑得动。 */
  overflow-x:auto;
  overflow-y:hidden;
  overscroll-behavior-x:contain;
  /* 滚动条藏掉：两头渐隐 + 副标题那句提示已经够暗示了，再加一条灰条太吵。
     ⚠️ 藏的是「看得见」，不是「能滑」—— 别顺手把上面那行 overflow-x 也删了。 */
  scrollbar-width:none;
  -ms-overflow-style:none;
  padding-bottom:var(--s-4);
  /* 跑马灯两侧都有内容进来，所以**两端都淡**。旧滑轨只淡右缘，
     是因为一进来就是第一张，左缘淡了开场会显得是虚的。 */
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 5%,#000 95%,transparent 100%);
  mask-image:linear-gradient(to right,transparent 0,#000 5%,#000 95%,transparent 100%);
}
.boardrail-track{
  display:flex;gap:var(--s-5);
  width:max-content;
  will-change:transform;
  animation:boardrail-drift var(--speed) linear infinite;
}
.boardrail figure{
  margin:0;flex:0 0 auto;
  width:var(--rw);
}
.boardrail img{
  display:block;width:100%;aspect-ratio:4/3;object-fit:cover;
  border:1px solid var(--line);border-radius:var(--r-md);
}
.boardrail::-webkit-scrollbar{display:none}

/* 三种「停住」的时机，都是纯 CSS：
   :hover        —— 桌面鼠标，停住才看得清某一块板
   :active       —— 正按着/正拖着的时候（触摸屏没有 hover，靠这条）
   :focus-visible—— 键盘用户在轨道上落了焦点（可滚动容器 Chrome 会把它编进 Tab 序），
                    停住之后方向键才能一格一格翻。
   ⚠️ 最后这条顺带把 WCAG 2.2.2 那条「自动移动的内容要能暂停」补上了 ——
      键盘用户现在有得停，所以用户 2026-10-14 决定不加那个 checkbox 按钮。 */
.boardrail:hover .boardrail-track,
.boardrail:active .boardrail-track,
.boardrail:focus-visible .boardrail-track{animation-play-state:paused}

@keyframes boardrail-drift{
  from{transform:translateX(0)}
  to{transform:translateX(calc(-50% - var(--s-5) / 2))}
}

/* 窄屏 + 「减少动效」→ 退回原来那条可拖的 snap 滑轨。
   手机上无限横向滚动既费电又不好读；系统里开了"减少动效"的用户更不该看到它
   （这一条同时满足 WCAG 2.2.2「自动移动的内容要能暂停」）。
   副本在这里必须藏掉，否则会滑出 18 张、后 9 张全是重复的。 */
/* 副标题里那句操作提示随模式换：跑马灯说「悬停停住」，退回滑轨才说「横滑」。
   两边都写死在 HTML 里，靠 CSS 二选一 —— 这样手机上不会读到一句做不到的提示。 */
.rail-hint-swipe{display:none}

@media(max-width:720px),(prefers-reduced-motion:reduce){
  .boardrail{
    overflow-x:auto;overflow-y:hidden;
    overscroll-behavior-x:contain;
    scroll-snap-type:x mandatory;
    scrollbar-width:thin;
    -webkit-mask-image:linear-gradient(to right,#000 0,#000 92%,transparent 100%);
    mask-image:linear-gradient(to right,#000 0,#000 92%,transparent 100%);
  }
  .boardrail-track{animation:none;width:auto;will-change:auto}
  /* 用 --rw 而不是另写一个值：这样「桌面 + 开了减弱动效」的用户看到的
     格子大小和跑马灯完全一致，只是不自己动。真正的小屏在下面单独收窄。 */
  .boardrail figure{width:var(--rw);scroll-snap-align:start}
  .boardrail .rail-dup{display:none}
  .rail-hint-drift{display:none}
  .rail-hint-swipe{display:inline}
}

@media(max-width:720px){
  .boardrail figure{width:clamp(200px,62vw,300px)}
}


/* ---------- 首页询盘表单卡 ----------
   刻意**不**做成 .card 的变体：.card 悬停会抬手 2px，表单不该在鼠标扫过时
   整块抬起来。所以它是一条独立规则，只借 .card 的底色和边线。

   里面的字段全部复用 .field / .row2 / .req / .btn.primary —— 和 contact 页
   是同一套积木，改一处两处一起变。

   限宽写在 .form-block 上，.form-card 只管样子不管宽度。
   .form-block 装的是**整节**的窄栏：h2 + .lead + 卡片 + 底下那行小字，
   四样同一个宽度、同一条左缘。原先只裹了卡片，结果标题贴 .wrap 左缘、
   卡片自己居中，两块错开约 170px —— 一眼就是"没对齐"。
   本站单栏内容的既定做法是「.wrap.narrow 居中、栏内文字仍靠左」（见 .wrap.narrow），
   照这个来。 */
.form-block{max-width:760px;margin:0 auto}
.form-card{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-8);box-shadow:var(--sh-2);
}
.form-card > :last-child{margin-bottom:0}
.form-note{margin:var(--s-5) 0 0;text-align:center;color:var(--mute)}


/* ============================================================
   第 5 层 · 组件 —— 第 12 轮新增（首页丰富用的四件）
   ------------------------------------------------------------
   用户给了三张 LZJPCB.com 的截图，要求"把这些设计风格加到首页"。
   四件东西，**只给首页用**，别的页一个都不加（首页要厚，内页要薄）：

     .speedbar   页头上面一条速览条
     .sec-head   居中版块头（眉标 + 大标题 + 副题）
     .pcards     三张编号照片卡 + 勾选清单
     .feature    左右分栏：左边文字卡 + 右边出血照片

   ⚠️ 这几件是**加了内容**，不是换皮：.pcards 拆的是"PCBA 贴装"下面
      三种工艺（SMT / 通孔 / 混合与 BGA），首页原来没有这一段；
      .feature 讲的是产线怎么排。加之前先确认内容不是别处已有的。
   ============================================================ */

/* ---------- 速览条 ----------
   压在页头上面，**不是 sticky** —— 滚下去它就该走。
   颜色用淡薄荷而不是深色：这一版全站改浅，顶上再压一条深色等于开倒车。
   每条前面一颗青绿小点，和首屏数字带（.stats-band .stats）里那几颗状态点
   是同一个记号。 */
.speedbar{
  background:var(--tint-100);border-bottom:1px solid var(--line);
  font-size:var(--fs-xs);color:var(--ink-2);
}
.speedbar .wrap{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-5) var(--s-7);min-height:40px;flex-wrap:wrap;
  padding-top:var(--s-2);padding-bottom:var(--s-2);
}
.speedbar ul{display:flex;gap:var(--s-4) var(--s-7);list-style:none;margin:0;padding:0;flex-wrap:wrap}
.speedbar li{display:flex;align-items:center;gap:7px;white-space:nowrap}
.speedbar li::before{
  content:"";width:5px;height:5px;border-radius:50%;
  background:var(--jade-400);flex:0 0 5px;
}
.speedbar a{color:var(--jade-600);font-weight:600}
.speedbar a:hover{text-decoration:underline;text-underline-offset:3px}

/* ---------- 居中版块头 ----------
   .eyebrow 是 inline-flex，靠父级 text-align:center 就能居中，
   不用给它自己加 justify-content（加了反而会让那条短横线跑位）。 */
.sec-head{max-width:780px;margin:0 auto var(--s-9);text-align:center}
.sec-head h2{margin:0 0 var(--s-4)}
.sec-head p{margin:0 auto;color:var(--ink-2);max-width:64ch}
.sec-head p:last-child{margin-bottom:0}

/* ---------- 编号照片卡 + 勾选清单 ----------
   左上是编号圆牌，压在照片上。
   ⚠️ **第 25 轮：圆牌从 --jade-400 换到 --jade-300（深青数字 6.84:1）。**
      这是全站唯一"文字压在 --jade-400 实底上"的地方，色相从 168° 挪到 154°
      之后它从 4.59:1 掉到 **4.47:1，跌破 AA 4.5** —— 同梯往上换一档就回到安全区。
      换色相时要重新量它，别因为"看着还行"就跳过。
   ⚠️ 旧注释写着"和 .btn.primary / .rail a.rq 是同一套'实心绿 + 深字'"，**这句是错的**：
      那两颗按钮是实心绿 + **白字**（--green-500 / --green-600）。别照它推对比度。
   ⚠️ 编号是 .n 不是列表序号 —— 顺序换了不用改样式。 */
.pcards{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-6)}
.pcard{
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:var(--sh-1);
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease-out);
}
.pcard:hover{border-color:var(--line-2);box-shadow:var(--sh-2);transform:translateY(-2px)}
.pcard figure{position:relative;margin:0}
.pcard img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
.pcard .n{
  position:absolute;left:var(--s-4);top:var(--s-4);
  width:34px;height:34px;border-radius:var(--r-full);display:grid;place-items:center;
  background:var(--jade-300);color:var(--deep-900);   /* 6.84:1，见上面那段 */
  font-family:var(--font-mono);font-size:var(--fs-xs);font-weight:600;
  box-shadow:var(--sh-1);
}
.pcard .body{padding:var(--s-5) var(--s-6) var(--s-6);display:flex;flex-direction:column;flex:1}
.pcard h3{margin:0 0 var(--s-3)}
.pcard ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.pcard li{
  position:relative;padding-left:0;
  font-size:var(--fs-sm);color:var(--ink-2);line-height:1.55;
  /* 2026-10-19 第 37 轮：用户「让里面的英文最后的英文对齐显得专业一点」+ 三张卡截图。
     参差来自**末行** —— 每条正文的第一行本来就离栏宽只差几像素，短的是最后一两个词
     （"boards arrive." 只占 30% 栏宽），所以右边缘看起来像被啃过。
     正解是 text-wrap:balance：它重新分配换行点，让每条内部几行长度接近，
     末行不再只剩一两个词（实测末行缺口 132/209/131… → 46/86/83…，平均 120 → 59px）。
     ⚠️ **别改回 text-align:justify**（这条试过又撤回）：这一栏只有 298px，两端对齐会把
        词间距撑到肉眼可见的裂缝（"Functional    test    against    your"），
        深底小字下比参差更难读。hyphens:auto 也补不上 —— Chromium 在本机渲染时没断字。
     ⚠️ balance 不受支持的浏览器（Safari <17.5 / Firefox <121）只是退回普通折行，不塌。
     ⚠️ 它**不改行数**，所以全站高度必须 0 变化 —— 改完跑 heights_r23.py 对账。 */
  text-wrap:balance;
}
/* 勾：一个只有左+下边框的 L 转 -45°。比塞一个 SVG 勾号省，也比用字符 ✓ 稳 ——
   字符勾在不同字体里大小和基线都不一样。
   ⚠️ 勾挂进左边距（left 取负），**正文不再右缩**：用户 2026-10-19 说
      「这个地方让它们的字体对齐」—— 原来 h3 在卡内 24px 处、清单正文在 47px 处，
      差 23px；现在标题和每一条勾选文字共用同一条左边缘，勾退到 6px 宽的悬挂位。
      卡片的左右内边距因此不对称（左 24 含勾、右 24），这是悬挂缩进的正常长相。 */
.pcard li::before{
  content:"";position:absolute;left:-18px;top:.46em;
  width:11px;height:6px;
  border-left:2px solid var(--jade-500);border-bottom:2px solid var(--jade-500);
  transform:rotate(-45deg);
}

/* ---------- 左右分栏：文字卡 + 出血照片 ----------
   右边照片**撞到视口右缘**（截图里就是这样）。做法不是负 margin，
   而是让这一块自己不做 .wrap，改用 padding-left 把那半边让出来 ——
   负 margin 在窄屏上会把页面撑出横向滚动条，padding 不会。
   `max(..., calc((100% - var(--max))/2 + var(--s-6)))`：
   视口比 --max 宽时按居中算左边距，窄时退回 --s-6。
   ⚠️ 这一块是**唯一**允许直接挂在 <body> 下、不套 .wrap 的版块。 */
.feature{
  display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
  align-items:stretch;gap:var(--s-11);
  padding-left:max(var(--s-6),calc((100% - var(--max)) / 2 + var(--s-6)));
}
.feature .fcard{
  align-self:center;max-width:520px;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh-2);padding:var(--s-8);
}
.feature .fcard > :last-child{margin-bottom:0}
/* 标题最后一个词上色 —— 截图里就是这么点的。用 <em> 包那一个词，
   语义上只是"强调"，别拿它当"斜体"用（所以 font-style 要归零）。 */
.feature h2 em{color:var(--jade-600);font-style:normal}
.feature figure{margin:0;height:100%;min-height:380px}
.feature figure img{
  width:100%;height:100%;object-fit:cover;display:block;
  border-radius:var(--r-md) 0 0 var(--r-md);
}

/* ---------- .feature 右半边的 2x2 四格 ----------
   2026-10-15 第 23 轮（b）。原来这里是「一张图、占满 632x872」：
   源图 750x580 被 cover 放大 1.50x、横向裁掉 44%，截图里那块「太大」的
   图就是这么来的 —— 一块被放大的碎片，不是一块完整的板。
   改成四格：每格 308px 宽（列宽 632、减 16px 间距再折半），源图 750px
   → 0.41x 缩小，比原来清楚；格子比例写的就是源图的 750:580，cover 不裁。

   ⚠️ `align-content:center` 不能删：外层的 .feature 是 `align-items:stretch`，
      四格会被拉成 308x424 的**竖格**，cover 又得横向裁 44%，等于白改。
      居中之后每行保持 238px 高，四格整体 500px，对着左边 872px 的卡片居中。
      （版块高度由左卡决定，改完首页总高不变 —— 这是预期，不是没生效。）
   ⚠️ 具体度要压过两条旧规则：`.feature figure`(0,1,1) 的 height:100% /
      min-height:380px，和 ≤900px 那条 `.feature figure img`(0,1,2) 的
      aspect-ratio:3/2 —— 本组是 (0,2,1)，赢；img 的 aspect-ratio 显式写 auto，
      免得窄屏那条漏进来。radius 也要归零：旧规则给 img 留的是「右边贴死」的
      var(--r-md) 0 0 var(--r-md)，那是给出血单图用的。 */
.feature .fgrid{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s-4);align-content:center;
}
.feature .fgrid figure{
  margin:0;height:auto;min-height:0;aspect-ratio:750/580;
  background:var(--paper);border:1px solid var(--line);
  border-radius:var(--r-md);overflow:hidden;box-shadow:var(--sh-1);
}
.feature .fgrid img{
  width:100%;height:100%;aspect-ratio:auto;object-fit:cover;display:block;
  border-radius:0;
}
/* 窄屏只剩一栏时，两格并排每格还有 400px 左右，够看；再窄就落成一列，
   宁可多滚两屏，也别把四块板压成四个邮票。 */
@media(max-width:520px){ .feature .fgrid{grid-template-columns:1fr} }

/* ---------- .feature 左卡里的参数清单 ----------
   首页「Board types and substrates」那八行。用 <dl> 而不是 <ul>：
   左边是参数名、右边是取值，读取顺序和表格一致 —— AI 抄**单行**时
   "Copper weight — 0.5 oz to 12 oz" 天然带着上下文；抄 <ul> 里那一行
   就只剩一个孤零零的 "0.5 oz – 12 oz"，不知道说的是什么。

   分隔线逐行 border-top、最后一行补 border-bottom：比给容器画一整圈边框稳，
   行数变了、某行长到折行，都不会把它撑歪。
   `minmax(112px,auto)` 而不是写死宽度：最长的那条参数名（Smallest
   laser-drilled hole）在窄版心上会自己折行，不会把取值那一列挤出去。 */
.feature .flist{margin:var(--s-5) 0 0;display:grid;gap:0}
.feature .flist > div{
  display:grid;grid-template-columns:minmax(112px,auto) 1fr;gap:var(--s-2) var(--s-4);
  padding:10px 0;border-top:1px solid var(--line);
  font-size:var(--fs-sm);line-height:1.5;
}
.feature .flist > div:last-child{border-bottom:1px solid var(--line)}
.feature .flist dt{color:var(--ink-2)}
.feature .flist dd{margin:0;color:var(--ink);font-weight:600}

@media(max-width:1000px){
  .pcards{grid-template-columns:1fr;gap:var(--s-4)}
  .pcard{flex-direction:row}
  .pcard figure{flex:0 0 38%}
  .pcard img{height:100%;aspect-ratio:auto;min-height:180px}
}
@media(max-width:900px){
  /* 出血版在窄屏上没意义（屏幕本来就窄，让出来的那点边距还不够折腾），
     退回普通两栏再退回一栏。图**不能**再出血 —— 那正是横向滚动条的来源。 */
  .feature{
    grid-template-columns:1fr;gap:var(--s-7);
    padding-left:var(--s-6);padding-right:var(--s-6);
  }
  .feature .fcard{max-width:none;padding:var(--s-6)}
  .feature figure{min-height:260px}
  .feature figure img{border-radius:var(--r-md);aspect-ratio:3/2;height:auto;min-height:0}
}
@media(max-width:640px){
  .pcard{flex-direction:column}
  .pcard figure{flex:0 0 auto}
  .pcard img{height:auto;aspect-ratio:4/3;min-height:0}
}


/* ============================================================
   响应式
   ============================================================ */

@media(max-width:900px){
  /* --fs-2xl 已经是 clamp(27px,...)，900px 以下自然落到下限，不用再写死 */
  section{padding:var(--s-11) 0}
  section.loose{padding:var(--s-12) 0}

  /* 联系条：竖排改横排贴底。「回顶部」那颗去掉 —— 横条上滚到底用不着它。

     页脚底部补出 --s-14(104px)：横条是 fixed 的，不补就永久压住页脚最后一行。
     补在 footer 上而不是 body 上 —— body 的底是白的，补 body 会在页脚
     下面露出一条白的缝（2026-10-15 第 24 轮页脚改深底之后，这条从"看不出来"
     变成"一眼就看得见" —— 补在 body 上的白缝会明晃晃地贴在深色页脚下沿）。
     分隔用一根发丝线而不是投影：--sh-* 那三条投影全是向下的，当底栏用
     方向是反的。
     ⚠️ 第 12 轮之后页头下沿也是 1px --line 了 —— 两处一致，都是 UI 分隔，
        不再是第 4 轮那种"页头是板边、这里是发丝线"的不一致。 */
  .rail{
    top:auto;bottom:0;left:0;right:0;transform:none;
    flex-direction:row;gap:0;border-radius:0;
    background:var(--paper);border-top:1px solid var(--line);
    box-shadow:none;
  }
  .rail a{
    flex:1 1 0;width:auto;height:54px;
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
    background:transparent;border:0;
  }
  .rail a:first-child,.rail a:last-child{border-radius:0}
  /* 标签从"悬停浮出"变成"常显"，改成流内元素、放回图标下方 */
  .rail a span{
    position:static;opacity:1;transform:none;pointer-events:auto;
    background:none;padding:0;color:inherit;
  }
  .rail a.ru{display:none;margin-top:0}
  footer.site{padding-bottom:var(--s-14)}
}


/* ---------- 底栏标签在真机窄屏上的收尾（≤430px，2026-10-14 加第 5 颗时补）----------
   把底栏钉成 360px 渲一张就看得很清楚：「GET A QUOTE」和「WHATSAPP」的标签
   直接叠在一起。原因是标签 `white-space:nowrap`、`.rail a` 又是 flex 项
   （min-width:auto = 内容的 min-content），窄下去压不动，只会互相撞。

   实测标签宽度（11.5px 等宽 + .10em 字距）：Get a quote 86 / WhatsApp 63 /
   Email us 62 / Call us 53 / WeChat 47 —— 临界视口 = 86 × 5 = **430px**，
   而真机是 375（iPhone）/ 360（主流安卓）。**加第 5 颗之前是 4 颗，
   临界 4×86 = 344px**，正好卡在所有真机之下，所以这是加渠道新引入的坑。

   不整体调字号：11.5px 已经在可读性下限附近。只做两件事 —— */
@media(max-width:430px){
  /* ① 其余 4 条只收字距，字号不动。收完最长的是 WHATSAPP ≈ 57px，
        5 × 57 = 285px，360 的机器上每颗 72px，富余 15px。 */
  .rail a span{letter-spacing:.02em}

  /* ② 只有最长那条换短标签。用 CSS 换而不是改 HTML —— 21 个文件的 rail
        目前逐字节一致，为了 3 个字母在 21 个文件里分叉不值得。
        原文字用 font-size:0 隐掉，**没有从无障碍树里删掉**，
        何况这颗本来就有 aria-label="Get a quote"，读屏读到的始终是全称。
        ⚠️ 这条依赖 ::after 的 content —— content 是普遍支持的属性，
           但属于"看着像 hack"的写法，所以理由留在这里。 */
  .rail a.rq span{font-size:0}
  .rail a.rq span::after{content:"Quote";font-size:var(--fs-2xs)}
}

/* ---------- 认证标志墙（首页 Certifications） ----------
   六个认证标志 + 每块底下配真文字。标志图在 assets/certs/marks/，
   是从公司资料里切出来的透明底 webp，不是画出来的。
   ⚠️ 标志本身是图，AI 读不到 —— 所以每块牌子底下**必须**有真文字
   （标准名 / 覆盖范围 / 发证机构 + 编号）。只有标志没有字，这一墙等于白挂：
   这跟"把参数表做成图片"是同一个错误。 */
.badges{display:grid;grid-template-columns:repeat(6,1fr);gap:var(--s-5)}
.badge{
  display:flex;flex-direction:column;text-align:center;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r-md);
  padding:var(--s-5) var(--s-4) var(--s-4);
  transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease);
}
.badge:hover{border-color:var(--line-2);box-shadow:var(--sh-1)}
.badge .mark{height:64px;display:grid;place-items:center;margin-bottom:var(--s-4)}
.badge .mark img{max-height:64px;max-width:100%;width:auto;display:block}
.badge b{display:block;font-family:var(--font-mono);font-weight:600;
  font-size:var(--fs-sm);color:var(--ink);letter-spacing:.01em;line-height:1.3}
.badge span{display:block;font-size:var(--fs-xs);color:var(--ink-2);
  line-height:1.45;margin-top:var(--s-2)}
.badge .num{display:block;font-size:var(--fs-2xs);
  font-family:var(--font-mono);margin-top:var(--s-3);
  /* 第 43 轮下半场改：原来吃 --deep-500(#343B42) —— 那是**面**令牌，
     深色主题里当**字**用等于隐形（旧卡底 #1A1F23 上只有 1.5:1）。
     换 --mute(#8D959D)：压 --card-umber 5.10:1，压 --card-jade 4.89:1。 */
  color:var(--mute)}
.badge .scan{display:inline-block;margin-top:auto;padding-top:var(--s-3);
  font-size:var(--fs-2xs);font-family:var(--font-mono);color:var(--jade-600);
  text-decoration:underline;text-underline-offset:3px}
.badge .scan.off{color:var(--mute);text-decoration:none}
/* 同 .card / .tile 的规矩：进了 alt 版块要退回 --tint-50，不然白底白卡 */
section.alt .badge{background:var(--tint-50)}
@media(max-width:1080px){.badges{grid-template-columns:repeat(4,1fr)}}
@media(max-width:760px){.badges{grid-template-columns:repeat(3,1fr)}}
@media(max-width:520px){.badges{grid-template-columns:repeat(2,1fr)}}

@media(max-width:1020px){
  /* 页头折行断点。1020 是 2026-10-14 第 20 轮**重量**出来的，两个数一起看：

       · 单行版式（logo 和导航并排）**在 968 撑不住** —— 导航自己折成两行
         （geo_check/nav_bp_probe.py：把这一块对页头的覆盖临时压回桌面值，逐档试。
           同一个脚本档位不动，只把顶层项从 5 改回 5 就能复现上一轮的 900）
       · 双行版式下，导航**要到 500 才折**（同脚本扫 500–620 不折）

     卡住 1020 的是**前一个数**，不是后一个。1020 = 968 + 52 余量。
     余量留给：字体回退（macOS/Linux 落不到同一套字族，字宽会变）、
     将来导航再加项、用户装了会加宽字体的扩展。

     ⚠️ **别拿 500 那个数把断点往下挪** —— 500 说的是"折成两行之后导航还能
        撑多宽"，是另一回事。挪到 500 附近的话，968~1020 这段就变成
        "页头两行 + 导航自己再折第二行"，三行叠在一起。

     ⚠️ 历史：第 4 轮 6 项时取 940；第 7 轮加到 7 项、实测文档宽 1002 起折，
        取 1050 抢在它前面。第 12 轮顶层收到 5 项（Home + 3 组 + CTA），
        **抢的对象变成了单行版式**，数从 1050 掉到 950。第 20 轮 FAQ 提到顶层
        （Home + 3 组 + FAQ + CTA = 6 项），实测单行版式在文档宽 968 起折，
        数从 950 抬到 1020。以后再动导航（加项、改字号、改 padding）
        都要回来重量，不能凭感觉调一档。 */
  header.site .wrap{height:auto;padding-top:var(--s-3);padding-bottom:var(--s-3);flex-wrap:wrap}
  /* logo 跟着页头一起收：46px 到 375px 手机上要占掉 63% 屏宽，太重。
     38px 正好是老 .mark 方块的边长，页头比例回到原来的感觉。 */
  .brand .logo{height:38px}
  nav.main{width:100%;justify-content:flex-start;gap:2px;margin-top:var(--s-2)}
  nav.main a{padding:var(--s-2) var(--s-3);font-size:var(--fs-sm)}
  nav.main a.cta{margin-left:0}
  /* ④ 触屏没有 hover —— 抽屉在窄屏直接关掉。
     不关的两个后果，都是硬的：
       · `:focus-within` 会在手指点标签时把抽屉弹出来；
       · 此时 nav.main 是 width:100%、分组靠左，而 .nav-panel 是 right:0，
         面板会从分组右缘向左铺满 760px —— **整个溢出视口左边**。
     父链接还在，所以入口没丢：窄屏点一下直接进栏目页。 */
  .nav-panel{display:none}
  /* 抽屉关了，chevron 却还在的话，就是在暗示一个点不开的下拉 —— 一起去掉，
     留一枚指向上方的箭头反而更让人以为能点。 */
  .chev{display:none}
}

@media(max-width:760px){
  :root{--fs-3xl:clamp(29px,7.2vw,40px)}
  /* 页头的其它覆盖在 1020 那一块，这里只再往下压一档字号 */
  nav.main a{font-size:var(--fs-xs)}
  .hero{padding:var(--s-11) 0 var(--s-9)}
  .hero h1{max-width:100%}
  section{padding:var(--s-10) 0}
  section.loose{padding:var(--s-11) 0}
  .row2{grid-template-columns:1fr}
  .split{grid-template-columns:1fr;gap:var(--s-6)}
  /* ⚠️ 这条不能省。上面那条 `.split` 是 (0,1,0)，而 1407 行有一条
     `.split.form{grid-template-columns:1.15fr .85fr}` 是 (0,2,0) —— 权重更高，
     与先后无关，所以光靠上面那条压不住它：contact 页在手机上**一直是两栏**，
     右侧卡片被挤到 145px 宽，里面 242px 宽的联系表就溢出到 x=544
     （实测 420px 下文档宽 464，多出 44px）。改 .split 的花样时要连这条一起看。 */
  .split.form{grid-template-columns:1fr}
  footer.site .cols{grid-template-columns:1fr;gap:var(--s-6)}
  .wrap{padding:0 var(--s-5)}
}

/* ============================================================
   首页动态互动（2026-10-15 第 25 轮）
   ------------------------------------------------------------
   用户要求「在主页加一些动态互动」。全站铁律零 JS，所以只有两条路可走：
   **滚动时间线**（CSS 原生 view()）+ **:has() 悬停联动**。

   作用域只挂 .home（首页 <body class="home">，另外 20 页 DOM 一字没动）。
   用 <body> 上一个类当开关，而不是靠"哪些类只在首页出现"去凑 ——
   .tiles / .shotrow / .promises 在别的页面上也有，凑不干净。

   ⚠️ 四条硬约束，改之前先读：
     ① 整段包在 @supports (animation-timeline: view()) 里 —— 不支持的浏览器
        **完全没有这一段样式**，退化成静态页，而不是"坏掉的动画"。
     ② 再包一层 @media (prefers-reduced-motion: no-preference)。⚠️ **别指望**
        文件末尾那条全局 `animation-duration:.01ms`：一旦设了
        animation-timeline，animation-duration 就被忽略，那条对滚动时间线
        **完全无效**，动画照跑。包在 no-preference 里才是真的关掉。
     ③ 动画的 transform 必须落在**和 hover 不同的元素**上：.tile:hover /
        .pcard:hover 自己就有 translateY(-2px)，同一元素上再叠一个动画的
        transform，动画结束时的填充值会把 hover 顶掉（动画来源压过普通声明）。
        所以 .tile / .pcard 的入场动画写在它们的**子元素**上。
     ④ **不加 will-change**：首页二十多个元素一起上，常驻合成层只会拖滚动。

   ⚠️ 错开用 animation-range，**不用 animation-delay**：滚动时间线里 delay
      的语义是"进度轴上的偏移"，各实现不一致；animation-range 是时间线
      原生属性，靠得住。
   ⚠️ 顺序也是坑：`animation:` 简写会把 animation-timeline 一起重置成 auto，
      所以简写写在前面，animation-timeline / animation-range 写在后面。

   ⚠️ 截图不受影响：全页截图时窗口高 = 文档高，整个文档都在视口里，
      每一条时间线都算"已经走完"，元素一律是终态。所以验收用的全页图
      永远拍的是最终样子，不会拍到半透明的那一帧。

   不动的地方：.rail / .nav-panel（fixed / sticky，动它们等于动视口坐标）、
   .boardrail（它自己在跑）。
   ⚠️ 第 28 轮往这里**减**了两样，别照着旧记录去找：
      · .hero 那两层照片的交叉淡入**整组搬走了** —— 它改成了时间驱动
        （12s 循环），不再是滚动时间线，所以不在这个 @supports 块里，
        而是写在前面的首屏那一段（`@keyframes hero-xfade`）。
        ⚠️ 同名但**不是同一个东西**：第 27 轮那个 `hero-xfade` 是
        from 1 / to 0 的滚动淡出，第 28 轮这个是三段式的循环。旧的那条已删。
      · header.site 从"不许动"变成"首页那一档会动"（透明 → 白条），
        但它动的是颜色和 backdrop-filter、不是位置，视口坐标没变；
        而且整块挂在 `@supports (animation-timeline: scroll())` + ≥1021px 里，
        别的 20 页一个字节都碰不到。见"首页页头叠加档"那一段。 */

/* 入场：淡入 + 上浮 14px。只写 from —— to 取元素自己的计算值，
   所以"入场之后长什么样"永远由组件决定，这里不重复声明一遍。 */
@keyframes home-rise{from{opacity:0;transform:translateY(14px)}}
/* 首屏数字带右上角那颗状态灯的呼吸（只在鼠标指着那一格时动） */
@keyframes home-led{50%{box-shadow:0 0 0 7px rgba(18,128,77,.10)}}

@supports (animation-timeline: view()){
@media (prefers-reduced-motion: no-preference){

  .home .promises li,
  .home .badges .badge,
  .home .shotrow,
  .home .feature .fgrid{
    animation:home-rise linear both;
    animation-timeline:view();
    animation-range:entry 0% entry 45%;
  }

  /* ⚠️⚠️ 瓦片/卡片这两组**不能**用 view()，得用具名时间线 —— 第 25 轮实测抓到的坑：
     .tile 和 .pcard 上都有 overflow:hidden（圆角要裁插图和图片），而任何
     overflow 非 visible 的盒子**本身就是一个滚动容器**。view() 量的是"元素相对
     它**最近的滚动容器**的位置"，于是 .art 的参照物变成了 .tile —— 而 .art 永远
     填满 .tile，进度被钉死在 1：**入场动效静默失效**（没有任何报错，元素只是
     一直不透明）。当时探针在同一时刻读到：.promises / .badges 全透明、
     .tiles / .pcards 进度=1 且已就位，两组差别只在"祖先有没有裁切"。
     修法：把时间线**具名声明在 .tile / .pcard 上**（它们自己的最近滚动容器才是
     根滚动条），子元素按名字取用。⚠️ 别改回 view()，别把这两组塞回上面那一段。 */
  @supports (view-timeline-name: --rise){
    .home .tiles .tile,
    .home .pcards .pcard{view-timeline-name:--rise}

    .home .tiles .tile > .art,
    .home .tiles .tile > .body,
    .home .pcards .pcard > figure,
    .home .pcards .pcard > .body{
      animation:home-rise linear both;
      animation-timeline:--rise;
      animation-range:entry 0% entry 45%;
    }
  }

  /* ---------- ⚠️ 这里原来是"首屏两层滚动交叉淡入"（2026-10-15 第 27 轮）----------
     第 28 轮**整组删掉、搬去前面首屏那一段了**，换成 12 秒的时间循环。
     留一句话记账，免得下一个人照着 git 之外的旧文档到处找：
     那一版是 `scroll(root)` + `animation-range:0 420px`，注释里记着两条试错
     （① .hero 的 overflow:hidden 会把子孙的"最近滚动容器"顶替掉、进度钉死在 1；
      ② view() 的 exit 相位量的是**视口**高而不是首屏高，落点随窗口高漂）。
     **那两条结论对滚动时间线仍然有效**，只是现在首屏不用滚动时间线了 ——
     真正的原因写在首屏那段的 @keyframes hero-xfade 上面：参考站的淡入
     本来就是时间驱动，跟着它走就不必绕这些坑。 */

  /* 错开半拍：每往右一格（3 栏版式）晚一点起步，看上去是"一张一张铺上来"。
     1 栏版式（≤720px）下 3n 的错开变成纯级联，同样好看，不用另写一套。 */
  .home .tiles .tile:nth-child(3n+2) > .art,
  .home .tiles .tile:nth-child(3n+2) > .body,
  .home .pcards .pcard:nth-child(2) > figure,
  .home .pcards .pcard:nth-child(2) > .body,
  .home .badges .badge:nth-child(3n+2),
  .home .promises li:nth-child(even){animation-range:entry 8% entry 53%}

  .home .tiles .tile:nth-child(3n) > .art,
  .home .tiles .tile:nth-child(3n) > .body,
  .home .pcards .pcard:nth-child(3) > figure,
  .home .pcards .pcard:nth-child(3) > .body,
  .home .badges .badge:nth-child(3n){animation-range:entry 16% entry 61%}

  /* 悬停联动：同组里只亮被指着的那一格，其余压暗、降饱和。
     ⚠️ 目标必须是**自己没有入场动画的元素** —— .shotrow / .fgrid 这两个
        容器在上一段里当整体做入场，里面的 figure 是干净的，正好接这条。
     ⚠️ 不给照片补 figcaption：第 23 轮已定"客户的设计不是我们该命名的"，
        这里也不借悬停偷偷塞字。 */
  .home .fgrid:has(figure:hover) figure:not(:hover),
  .home .shotrow:has(figure:hover) figure:not(:hover){
    opacity:.82;filter:saturate(.78);
    transition:opacity var(--dur-2) var(--ease),filter var(--dur-2) var(--ease);
  }

  /* 瓦片：照片落地之后，鼠标指上去照片轻轻推近一点
     （.art 的 overflow:hidden 和 img 的 transition 在 2 层组件那段里备着） */
  .home .tile:hover .art img{transform:scale(1.03)}

  /* 首屏数字带：指着哪一格，那颗状态灯呼吸一下 —— 和 .stats-band .stats 那句
     "板上一颗绿点亮着就是一个通了"是同一句话，只是这里只在被指的时候亮。
     ⚠️ 第 28 轮数字带搬出 .hero 了，选择器跟着改成 .stats-band，别改回去。 */
  .home .stats-band .stats div:hover::after{animation:home-led 1.4s var(--ease) infinite}
}}

/* ============================================================
   第 5 层 · 组件 —— 首页「深色高端」一整套（2026-10-16 第 33 轮）
   ------------------------------------------------------------
   用户给了一张整页参考稿（同一家公司，另一套设计语言），原话是
   「帮我把首页往这种设计改变调整走高端感不要清新感」。
   已问过一轮，选的是**最克制的那一档**，四条：
     1. --tint-* 去彩度（第 1 层，21 个页面共用，见 1.1 节）
     2. 首页加 5 段深色带，做成"深 / 浅大块交替"
     3. 首页的卡片压平（去投影、圆角收到 --r-xs）
     4. 首屏加重（标题加大、栏宽收紧、照片底缘溶进深色带）
   **版式与文案一个字没动**：居中的 .sec-head 照旧，没有搬参考稿那种
   "左栏标题 + 右内容"的版头；没有新增任何文案；没有新增任何颜色令牌。
   ============================================================ */

/* ---------- 深色带 ----------
   作用域写在 .home 上：另外 20 个页面一个字节不受影响。
   ⚠️ 必须写成 .home .dark，不能图省事写成裸 .dark ——
      裸 .dark 是 (0,1,0)，和 .band 的 (0,1,0) **打平手**，谁赢看源码顺序。
      下一个人把哪一节前后挪一行，深底就静默没了（而且不报错、console 干净）。
   用的令牌全是现成的：底 --deep-900，字 --on-dark*（第 24 轮为页脚开的四档），
   深底强调色 --jade-300。**一个颜色都没新增。**
   对比度全部在 geo_check\contrast_r33_math.py 里算过，下面逐条标了比数。 */
.home .dark{
  background:var(--deep-900);
  color:var(--on-dark-3);
  border-color:var(--on-dark-line);
}

/* 标题与正文。
   ⚠️ h1~h4 在排版层写死了 color:var(--ink)，**光靠继承改不动**，必须逐条压过去。 */
.home .dark h2,
.home .dark h3{color:var(--on-dark)}                 /* 15.04:1 */
.home .dark .sub,
.home .dark p,
.home .dark .lead,
.home .dark li{color:var(--on-dark-2)}               /* 11.86:1 */
/* 眉标：深底上的强调色**只许用 --jade-300**（6.84:1）。
   --jade-600 压 --deep-900 只剩 2.4:1 一档，读不出来。 */
.home .dark .eyebrow{color:var(--jade-300)}
.home .dark .eyebrow::before{background:var(--jade-300)}
/* 深底上的链接比正文亮（和页脚同一条规矩，层次别反过来）。
   ⚠️ :not(.btn) 不是啰嗦 —— .btn.primary 自己是"绿底白字"，另外算过 5.33:1，
      被这条按白字重算虽然结果一样，但下一个人把按钮换成别的底就说不清了。 */
.home .dark a:not(.btn){color:var(--on-dark)}
.home .dark a:not(.btn):hover{color:var(--jade-300)}

/* 数字带。它是 .stats 的基类（白卡 + 1px 网格 + 圆角裁切），
   五处都写死了浅底颜色，逐条盖掉（少盖一条就是白底白字或暗底暗字）。 */
.home .dark .stats{
  background:var(--on-dark-line);   /* 1px 的网格线：gap 里透出来的就是它 */
  border-color:var(--on-dark-line);
}
.home .dark .stats div{background:rgba(255,255,255,.06)}
/* ⚠️ 数字从 --jade-600 换 --jade-300：它本来就是"深底上的亮强调"那个角色
   （第 31 轮压暗时特意没压它，理由就是页脚那一处）。 */
/* ⚠️ 数字用 --jade-200，不是 --jade-300 —— 这里是**量出来的**，不是推出来的。
   --jade-300 压 --deep-900 是 6.84:1（第 24 轮定的深底强调色），但数字带这几个数
   **不是**压在 --deep-900 上：`.stats` 容器自己有一层 `--on-dark-line`(.16 白)，
   格子再叠 .06 白 —— 合成出来是 **20.7% 的白**，不是我以为的 6%。
   实测最差 3.56:1（30px 大字按 WCAG 够，但站上一直是按 4.5 卡的，不能破例）。
   换 --jade-200 #A9E2C9 之后 5.38:1 ✓。
   教训：**"某个令牌压某个底色是多少"这句话离开具体合成层就是错的** ——
   卡片叠在容器上、容器又叠在节上，两层半透明一乘，白就是 20% 而不是 6%。 */
.home .dark .stats b{color:var(--jade-300)}
.home .dark .stats span{color:var(--on-dark-3)}      /*  7.81:1 */
/* 那颗状态灯 + 外面那圈光晕。原来那圈 rgba(18,128,77,.22) 是 --jade-400 的
   RGB 手写值，压在深底上等于没有，换成白色淡圈。 */
.home .dark .stats-band .stats div::after{
  background:var(--jade-300);
  box-shadow:0 0 0 3px rgba(255,255,255,.18);
}

/* ---------- 首屏证书条（2026-10-19 第 42c 轮） ----------
   用户的原话：「不让 AI 读得那么详细，但用户要一眼看得到我有这些证书」。
   所以这条**只出现四个名字** —— 编号、发证机构、有效期、覆盖范围一个都不上页面，
   统一落在 pcba-certifications-explained.html 那节 "available on request" 里。
   JSON-LD 那边同样只有这四个名字（#organization.hasCredential），2026-10-19 核过；
   **两边不许一边加细、一边不加**，否则又变成"注释是假的"。
   ⚠️ 配色**照抄上面 .stats 那一套**（6% 白卡片 + --on-dark-line 边）。
      这支深青底上已经叠了好几层半透明，再调一个"差不多"的值就会和旁边五张卡差一档 ——
      第 28 轮那颗数字的颜色就是这么栽的（见上面 .stats b 那段实测）。 */
.home .dark .cred{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:var(--s-2) var(--s-5);
  margin-top:var(--s-5);padding:var(--s-4) var(--s-5);
  border:1px solid var(--on-dark-line);border-radius:var(--r-md);
  background:rgba(255,255,255,.06);
}
.home .dark .cred b{
  font-family:var(--font-mono);font-size:var(--fs-xs);font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--on-dark-3);                                  /*  7.8:1 */
}
.home .dark .cred span{
  font-size:var(--fs-sm);font-weight:600;
  color:var(--on-dark);white-space:nowrap;                 /* 15.0:1 */
}
/* 深底上的强调色**只许 --jade-300**（6.84:1），这条和页脚 / 眉标是同一条规矩。
   ⚠️ 选择器带 :not(.btn)、并且写在 .home .dark a 那几条之后 ——
      同权重时靠源码顺序决定，别把这一块挪到文件前半截去。 */
.home .dark .cred a:not(.btn){margin-left:auto;font-size:var(--fs-xs);color:var(--jade-300)}
.home .dark .cred a:not(.btn):hover{color:var(--on-dark)}

@media(max-width:720px){
  /* 窄屏这一行必然换行；链接独占一行，比被挤在半截好看 */
  .home .dark .cred a{margin-left:0;flex-basis:100%}
}

@media print{
  /* 深底不印背景色 —— 同第 24 轮页脚、第 34 轮尺寸面板那两条理由。
     白字落在白纸上会整块消失，所以这里退回深字。 */
  .home .dark .cred{background:none;border-color:#BBB}
  .home .dark .cred b,.home .dark .cred span,
  .home .dark .cred a:not(.btn){color:#000}
}

/* 跑马灯：图的描边跟着翻。🔎 两头那层 mask 渐隐是渐到
   **transparent**（不是渐到白），所以深底上照样成立，一个字不用改。 */
.home .dark .boardrail img{border-color:var(--on-dark-line)}

/* 色带里的那张照片：浅底上要白卡外壳和细边，深底上要的是"照片自己浮出来"。 */
.home .dark .band-split img{border-color:var(--on-dark-line)}

/* 三张检查卡 */
.home .dark .pcard{
  background:rgba(255,255,255,.06);
  border-color:var(--on-dark-line);
}
.home .dark .pcard:hover{border-color:var(--on-dark-3)}
.home .dark .pcard li::before{border-color:var(--jade-300)}   /* 那个 L 形勾 */
/* .pcard .n 那颗编号牌**不动**：--jade-300 实底 + --deep-900 字，
   底色不透明，6.84:1 在深底和浅底上同样成立。 */

/* 表单 */
.home .dark .form-card{
  background:rgba(255,255,255,.06);
  border-color:var(--on-dark-line);
}
.home .dark .field label{color:var(--on-dark-2)}
/* 必填星号：--red-600 压深底读不出来，换站上**已有**的 --red-200（8.46:1）。
   不新增令牌 —— 这一轮一个颜色都不加。 */
.home .dark .field .req{color:var(--red-200)}
.home .dark .field input,
.home .dark .field select,
.home .dark .field textarea{
  background:rgba(255,255,255,.07);
  border-color:var(--on-dark-line);
  color:var(--on-dark);                              /* 12.28:1 */
}
.home .dark .field input:hover,
.home .dark .field select:hover,
.home .dark .field textarea:hover{border-color:var(--on-dark-3)}
.home .dark .field input:focus,
.home .dark .field select:focus,
.home .dark .field textarea:focus{
  border-color:var(--jade-300);
  box-shadow:0 0 0 3px rgba(255,255,255,.20);
}
/* ⚠️ placeholder **必须显式写**：它不继承父级颜色，浏览器自带那档是中灰，
   压在深底上基本看不见（7.37:1 是换过之后的值）。 */
.home .dark .field input::placeholder,
.home .dark .field textarea::placeholder{color:var(--on-dark-3)}
/* ⚠️ color-scheme 是给**原生下拉弹层**用的：select 展开之后那张列表是浏览器
   自己画的，CSS 碰不到它。不加这一条，深底表单收起时是深的、一展开弹出一张白列表。 */
.home .dark form.inquiry{color-scheme:dark}
.home .dark .form-note{color:var(--on-dark-3)}

/* ---------- 首页卡片压平 ----------
   参考稿的卡片是"无投影 + 近乎方角"。全站照搬到 21 页太猛，**只压首页**：
   形状靠 --r-xs(4px) + 一根 1px 描边撑，不再靠投影。
   ⚠️ 这一条会让首页的卡片比另外 20 页"平"一档 —— 是刻意的（用户只要首页）。
      真觉得别扭，下一轮把 --r-* 整体降一档就行，不用回头动这里。
   ⚠️ .hero 里没有任何圆角面；.pcard .n / .tile .num 是圆形小牌、.badge .mark
      里是透明底标志图 —— 这三处**不在**压平范围内，它们是版式的一部分。 */
.home .tile,
.home .pcard,
.home .badge,
.home .promises li,
.home .form-card,
.home .feature .fcard,
.home .stats,
.home .band-split img,
.home .boardrail img,
.home .shotrow img,
.home .feature .fgrid img,
.home .btn{border-radius:var(--r-xs);box-shadow:none}
/* 折叠问答的面板：它静息是 1px 边，只有展开时才上一道 --sh-1 —— 一起收掉 */
.home .faq details,
.home .faq details[open]{border-radius:var(--r-xs);box-shadow:none}
/* 悬停：只换边框色。**不再抬手、不再加深投影**（参考稿的卡片是不动的）。
   ⚠️ 这几条必须比各自的 :hover 基类更具体或更靠后，否则那个 translateY(-2px)
      会漏回来 —— 它们都是 (0,3,0) 打 (0,2,0)，稳的。 */
.home .tile:hover,
.home .pcard:hover,
.home .badge:hover,
.home .promises li:hover,
.home .faq details:hover{box-shadow:none;transform:none}

/* ---------- 打印退路 ----------
   ⚠️ 第 24 轮页脚那次踩过：浏览器默认**不印背景色**，深底上的白字落在白纸上
      整块消失。第 33 轮新增了 5 段深底，所以这几条是**必须**的。
   ⚠️ 一律写 background:none，**不要**再填一个白 —— 用户要是勾了"背景图形"，
      填白只会印出一条白带盖住字（页脚那条注释记过同一个理由的。 */
@media print{
  .home .dark{background:none;color:#333}
  .home .dark h2,.home .dark h3,.home .dark a,.home .dark .stats b{color:#000}
  /* ⚠️⚠️ 下面这一条是**打印预览量出来的**，不是推出来的：
     第一版只点了"标题 + 表单控件"，打出来整页只剩标题、按钮和 select 里
     那行 "Select one..."，**正文几乎全白**。因为深底节的正文走的是
     --on-dark-2 / --on-dark-3（两个都是白的），而它们**不在**上面那条里。
     教训：深底上"不是标题、也不是表单控件"的文字，得一条条点，
     别以为写了 `color:#333` 在 `.dark` 上就继承下去了 —— 那条被
     `.home .dark .sub` 那些更具体的选择器按住了（它们本来就写着白色）。
     同理还有必填星号（--red-200 是极浅的粉）、placeholder、输入框描边。 */
  .home .dark p,.home .dark .sub,.home .dark .lead,.home .dark li,
  .home .dark .stats span,.home .dark .form-note,
  .home .dark .field label,.home .dark .field .req{color:#333}
  .home .dark .field input,.home .dark .field select,
  .home .dark .field textarea{color:#000;background:none;border-color:#999}
  .home .dark .field input::placeholder,
  .home .dark .field textarea::placeholder{color:#666}
  .home .dark .pcard,.home .dark .form-card,.home .dark .stats,
  .home .dark .stats div,.home .dark .band-split img,
  .home .dark .boardrail img{border-color:#BBB}
  .home .dark .pcard,.home .dark .form-card,.home .dark .stats div{background:none}
  /* 卡上那个勾是 --jade-300（浅绿），纸面上偏淡，压深一档 */
  .home .dark .pcard li::before{border-color:#333}
  .home .dark .eyebrow,.home .dark .eyebrow::before{color:#000;background:#000}
  .home .dark .stats-band .stats div::after{display:none}
  /* 首屏照片底缘那层渐隐（本轮新增）：它本身是背景色，纸上是看不见的，
     而且勾了"背景图形"的人会印出一条黑带。 */
  .hero-media::before{display:none}
}

/* 尊重系统的"减少动效"设置
   ⚠️ 第 25 轮踩到的坑：下面那条 `animation-duration:.01ms` **拦不住滚动时间线
      动画** —— 一旦设了 animation-timeline，animation-duration 就被忽略，
      动画照跑。所以首页那几段入场动效不写在这里，而是整段包在
      @media(prefers-reduced-motion:no-preference) 里（见上面那一段）。
      以后往站里再加滚动时间线动画，照这个办法做，别以为这条兜得住。 */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
  .btn:hover,.card:hover,.gallery figure:hover,.tile:hover{transform:none}
}

/* 打印时不要那张浮在纸上的联系条 —— 纸上是固定不住的，
   而且它会被每一页都印一遍 */
@media print{
  .rail{display:none}
  /* 2026-10-15 第 24 轮：页脚改深底之后，浏览器**默认不印背景色** ——
     白字落在白纸上，整块页脚会当场消失（21 页全中）。所以打印时退回深字。
     ⚠️ 写 `background:none` 而不是再填一个白：用户要是在打印设置里勾了
        "背景图形"，填白也只会印出一条白带盖住字。 */
  footer.site{background:none;color:#333}
  footer.site h4,footer.site a{color:#000}
  footer.site .bot{border-top-color:#BBB}
  footer.site a:hover{color:#000}
  /* 2026-10-19 第 34 轮：尺寸面板是深底，打印时同样退回深字 ——
     浏览器默认不印背景色，白字会落在白纸上整块消失（同第 24 轮页脚那条）。 */
  /* 尺寸线靠 --dim-line 那支白画（三层背景），所以这里换的是**变量**，
     不是 background —— 直接写 background:#999 会把三层背景图一起清掉，
     线当场消失（而且不报错）。 */
  .sizepanel{--dim-line:#999;background:none;color:#333;border:1px solid #BBB}
  .sizepanel h3,.sizepanel .sz-dim,.sizepanel .sz-types b{color:#000}
  .sizepanel p,.sizepanel .size-note,.sizepanel figcaption{color:#333}
  .sizepanel .eyebrow,.sizepanel .eyebrow::before{color:#000;background:#000}
  .sizepanel .size-note{border-top-color:#BBB}
  .sizepanel .size-note svg{stroke:#000}
}


/* ============================================================
   节带三色 · 板块底色交替（第 43 轮下半场）
   ------------------------------------------------------------
   首页那四节 .alt 各挂一个时类，其余 22 页一个字节不受影响。

   ⚠️ **这一块必须写在文件靠后**：下面每条和
      section.alt(.card/.promises/.docs/.tile/.form-card)、.badge、
      section.svc-sec .tile、section.alt .process li::before
      全是**同权重打平手**，谁赢看源码顺序。写在 1323 那一块之前
      会被静默盖掉，而且不报错、console 干净。
   ⚠️ `.svc-sec` 的 background 是**带照片的简写**，所以重写它那条必须用
      section.svc-sec.band-jade（多一个类），不能用裸 section.band-jade ——
      后者会把照片和纱一起删掉，露出一张没压纱的板微距。
   ⚠️ 纱的明度沿用原来的 .88，只换色相。别顺手调暗 —— 那个数是
      geo_check\contrast_r27.py 逐像素量出来的，不是看着调的。 */

section.band-jade{background:var(--band-jade)}
section.band-indigo{background:var(--band-indigo)}
section.band-umber{background:var(--band-umber)}

/* What we do：底纹照片还在，只把纱从灰换成同色的绿 */
section.svc-sec.band-jade{
  background:
    linear-gradient(rgba(15,36,29,.88),rgba(15,36,29,.88)),
    url("img/hero-pcb-macro.jpg") center/cover no-repeat var(--band-jade);
}

/* 带上的卡面：跟着带子换色相，否则像彩色纸上贴灰卡 */
section.band-jade .card,
section.band-jade .promises li,
section.band-jade .docs li,
section.band-jade .tile,
section.band-jade .form-card,
section.band-jade .badge{background:var(--card-jade)}
section.band-indigo .card,
section.band-indigo .promises li,
section.band-indigo .docs li,
section.band-indigo .tile,
section.band-indigo .form-card,
section.band-indigo .badge{background:var(--card-indigo)}
section.band-umber .card,
section.band-umber .promises li,
section.band-umber .docs li,
section.band-umber .tile,
section.band-umber .form-card,
section.band-umber .badge{background:var(--card-umber)}

/* 悬停：同色相再抬一档。**只抬这一点**，卡面再亮 figcaption 就破 4.5 */
section.band-jade .card:hover,
section.band-jade .promises li:hover,
section.band-jade .docs li:hover,
section.band-jade .tile:hover{background:var(--card-jade-h)}
section.band-indigo .card:hover,
section.band-indigo .promises li:hover,
section.band-indigo .docs li:hover,
section.band-indigo .tile:hover{background:var(--card-indigo-h)}
section.band-umber .card:hover,
section.band-umber .promises li:hover,
section.band-umber .docs li:hover,
section.band-umber .tile:hover{background:var(--card-umber-h)}

/* 折叠问答的卡壳是 .faq details（不是 .card），第一版漏了它 ——
   渲出来是一张灰卡贴在绿带上。summary 的悬停 / 展开底色也要跟着换，
   否则一展开就跳回中性 --tint-50。 */
section.band-jade .faq details{background:var(--card-jade)}
section.band-indigo .faq details{background:var(--card-indigo)}
section.band-umber .faq details{background:var(--card-umber)}
section.band-jade .faq summary:hover,
section.band-jade .faq details[open] summary{background:var(--card-jade-h)}
section.band-indigo .faq summary:hover,
section.band-indigo .faq details[open] summary{background:var(--card-indigo-h)}
section.band-umber .faq summary:hover,
section.band-umber .faq details[open] summary{background:var(--card-umber-h)}

/* 服务瓦片里那个 icon 底窝：基础规则吃 --tint-50（中性、还比卡面亮），
   放在绿卡上就是一块亮灰补丁。这里让它跟着带子走。
   ⚠️ 首页眼下 6 张瓦片全是 .art.photo，**这一条目前不生效** ——
      留着是给"没有实拍、只有 icon"的瓦片兜底，那种瓦片以后还会加回来。 */
section.svc-sec.band-jade .tile .art:not(.photo){background:var(--band-jade)}

/* 流程圆点外面那圈"镂空"底色必须跟着带子走，否则白圈会露馅。
   这四个节现在都没有 .process，留着是给以后加节的人兜底。 */
section.band-jade .process li::before{box-shadow:0 0 0 4px var(--band-jade)}
section.band-indigo .process li::before{box-shadow:0 0 0 4px var(--band-indigo)}
section.band-umber .process li::before{box-shadow:0 0 0 4px var(--band-umber)}


/* ============================================================
   页头叠加档 · 几何（第 43 轮）
   ------------------------------------------------------------
   首屏 / 内页页头往上一格，钻到页头背后。相邻兄弟的上下外边距合拢，
   实际位移就是负的那 84px；多出来的 84px 补回上内边距，文字落点不动。

   ⚠️ **这一块必须留在文件最末尾**。它压的是 `.hero`（约 1040 行）
      和 `.page-head`（约 2320 行）的基础规则，两条同权重 —— 写在前面
      就会被后面那条 padding-top 盖掉，结果是"照片上去了、文字没让开"，
      页头直接压住眉标。第 43 轮第一版就是这么排的，渲出来一眼看到。
   ⚠️ 第 43 轮之前这两条写在 @supports (animation-timeline) 里 ——
      不支持的浏览器上首屏直接掉到页头下面。现在无条件生效。 */
@media(min-width:1021px){
  .hero,
  .page-head{margin-top:calc(-1 * var(--header-h))}
  .hero{padding-top:calc(var(--header-h) + var(--s-13))}
  .page-head{padding-top:calc(var(--header-h) + var(--s-10))}
}
