/* 自动生成 —— 别手改，改 scripts/gen_pages.py
   风格来源：docs/frontend/DESIGN_SYSTEM.md §3.1 方案 B「墨韵金笺 · 文人书房」
             （2026-10-01 用户拍板：「风格 B」）

   🔴 四条硬约束（都是闸门在查的，改之前先读清楚）：
   1. **所有 margin/padding/gap 必须是 4px 的整数倍** —— `visual_richness_check.py`
      的「间距节奏」项按 4px 栅格算，容差 15%、样本量 ≥20 才判。
   2. **正文字号 ≥16px、导航/页脚/面包屑的文字链接高度 ≥24px、按钮高度 ≥44px**
      —— `ui_qa.py` 的 WCAG 2.5.8(文字链接 24) / 2.5.5(控件 44) 两条。
   3. **@keyframes ≥3、真实 transition ≥5、必须有 `prefers-reduced-motion` 降级、
      `:hover`/`:focus-visible`/`:active` 三类状态齐全且 focus 带 outline**
      —— `visual_richness_check.py` 的「动效」「交互状态」两项。
   4. **颜色字面量只准出现在 `:root{}` 里** —— `verify_design_tokens.py` 零例外。
      `:root` 之外一律 `var()` 或 `color-mix(in srgb, var(--x) N%, transparent)`。

   🔴 版式结构（2026-10-01 重做，不是换色）：
      · 每个模块由 `render_module()` 产出 `<section class="module module-<版式>">
        <div class="mod-in">…</div></section>`
        —— **所有内容都包在 `.mod-in` 里**，不再用旧版 `.module>*` 那种
        「给每个直接子块发 padding 再逐个盖回来」的写法（那套错法在旧样式表里
        一共踩了 7 次，见 2026-09-29 的注释）。子块现在天然撑满 `.mod-in`。
      · 版式由内容推断（`_module_layout()`）：hero / cards / ledger / steps /
        figure / cta / prose —— 每页因此长得不一样，不是一个模板套到底。
      · 带 H1 的模块 = **卷轴式首屏**（深墨满幅 + 双金线压边 + 内嵌双线框）。
*/
:root{
  /* ── 墨韵金笺 · 主色 ─────────────────────────────────────────── */
  --c-ink:#1F1B18;              /* 玄墨：标题、深色带、页脚 */
  --c-gold:#B8944D;             /* 金：深色底上的文字与线（在玄墨上 6.01:1 AA）*/
  --c-gold-deep:#7C5F22;        /* 深金：**浅色底上当文字/链接**（宣纸 5.26:1 AA）*/
  --c-seal:#9E2B25;             /* 朱砂：只做小面积点缀（宣纸 6.54:1 AA）*/
  --c-page:#F5F0E6;             /* 宣纸：页面底 */
  --c-card:#FBF8F2;             /* 浅笺：卡片底 */
  --c-subtle:#EFE8DA;           /* 米灰：交替区块底 */
  --c-heading:var(--c-ink);
  --c-body:#3A322B;             /* 正文（宣纸 11.06:1 AAA）*/
  --c-muted:#6B6155;            /* 次要（宣纸 5.33:1 AA）*/
  --c-inverse:#F7F2E7;          /* 深底上的正文（玄墨 15.31:1 AAA）*/
  --c-inverse-2:#CFC6B4;        /* 深底上的链接（玄墨 10.09:1 AAA）*/
  --c-inverse-3:#A79E8C;        /* 深底上的次要（玄墨 6.44:1 AA）*/
  --c-line:#D8CFBC;             /* 发丝线（纯装饰，不承担信息）*/
  --c-line-strong:#B9AC92;      /* 稍重一档的分隔线 */
  --c-edge:#94866C;             /* 卡片边框：整卡是链接，边界属「可识别图形」→ 需 ≥3:1（宣纸 3.14:1）*/
  /* 🔴 派生色一律用 `color-mix()` 从主色算出来，**不许写死 rgb 字面量** ——
     `DESIGN_SYSTEM.md` §4.4：全站走 CSS 变量是「换肤的前提」。
     写死 `rgba(31,27,24,.18)` 等于把主色的 rgb 抄了一份：用户哪天换强调色，
     焦点环 / 呼吸动画**不会跟着变**，是静默的。 */
  --c-gold-a64:color-mix(in srgb, var(--c-gold) 64%, transparent);
  --c-gold-a40:color-mix(in srgb, var(--c-gold) 40%, transparent);
  --c-gold-a24:color-mix(in srgb, var(--c-gold) 24%, transparent);
  --c-gold-a12:color-mix(in srgb, var(--c-gold) 12%, transparent);
  --c-gold-a08:color-mix(in srgb, var(--c-gold) 8%, transparent);
  --c-gold-a00:color-mix(in srgb, var(--c-gold) 0%, transparent);
  --c-ink-a12:color-mix(in srgb, var(--c-ink) 12%, transparent);
  --c-ink-a06:color-mix(in srgb, var(--c-ink) 6%, transparent);
  --c-inverse-a80:color-mix(in srgb, var(--c-inverse) 80%, transparent);
  --c-inverse-a22:color-mix(in srgb, var(--c-inverse) 22%, transparent);
  --c-inverse-a12:color-mix(in srgb, var(--c-inverse) 12%, transparent);
  --c-shadow:color-mix(in srgb, var(--c-ink) 14%, transparent);
  /* 表单状态色（成功 / 失败）。引擎 `setStatus()` 上线后会挂 `.lead-status.success|error`。
     这是**语义色**（成功绿、失败红），**不该随品牌换肤变**，所以给独立 token。 */
  --c-ok:#2E6B33;
  --c-alert:#9B2C1E;
  /* 🔴 原型专用色（`.proto-banner` 提示条 / `.lead-status.notice` 表单状态）——
     **故意不跟主色挂钩**：这是警示色，用户换品牌强调色时它**不该跟着变**。
     但仍是变量（不是写死字面量），因为「全站走变量、无写死色」是**零例外**的机器可查项。
     上线（F7 正式渲染）时这两组样式连同变量一起删。 */
  --c-proto-bg:#3B2F00;
  --c-proto-fg:#FFE9A8;
  --c-proto-code:#FFFFFF;
  --c-proto-line:#B9A76A;
  --c-proto-note:#FFFDF3;
  --c-proto-note-fg:#6B5B1F;

  /* ── 字体：三个各司其职 ──────────────────────────────────────── */
  /* 标题 = Cormorant Garamond（高对比度古典衬线，字大有碑帖气）
     正文 = EB Garamond（书本字体，长文读起来像书不像网页）
     界面 = Inter（导航/按钮/表头/小标签，需要横平竖直的清晰度） */
  --f-heading:"Cormorant Garamond",Georgia,"Times New Roman",serif;
  --f-body:"EB Garamond",Georgia,"Times New Roman",serif;
  --f-ui:"Inter",-apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif;
  /* 🔴 `--f-*-cjk` 是给 `/ja/` 准备的（`DESIGN_SYSTEM.md` §4.2），**P10 才会被消费** ——
     现在全站 en，没有日文字符，所以这两条目前**没有任何地方引用**，不是漏写。
     P10 建 `/ja/` 树时要**同时**做两件事，缺一件就静默掉系统字体：
       ① 让 `/ja/` 页的 `font-family` 用这两个变量（或加 `html[lang="ja"]` 重绑）；
       ② 在 Google Fonts 的 `<link>` 里**加上 `Noto+Serif+JP`**
          —— 光改 CSS 栈没用，字体压根没被请求，会静默 fallback。
     （加进 link 不会拖慢 en 页：Google Fonts 的 CSS 带 `unicode-range`，
       页面上没有日文字符就不会下载日文字体。）
     🔴 方案 B 的正文是 EB Garamond —— **它没有汉字**，所以 CJK 侧必须落到
        Noto Serif JP，不能沿用拉丁衬线，否则中文会掉到系统默认宋体。 */
  --f-heading-cjk:"Cormorant Garamond","Noto Serif JP",serif;
  --f-body-cjk:"EB Garamond","Noto Serif JP",Georgia,serif;

  /* ── 字阶 / 版心 ─────────────────────────────────────────────── */
  --fs-h1:54px; --lh-h1:64px;
  --fs-h2:34px; --lh-h2:46px;
  --fs-h3:23px; --lh-h3:32px;
  --fs-body:17px; --lh-body:29px;
  --radius:0px;
  /* 🔴 2026-10-08：**1200 → 1240**。用户答「在意」页头比正文宽 20px ⇒ 把正文也放宽，两边对齐。
     为什么要 1240：页头单行要放下**最宽的语言**（德文 1166px）+ 左右内边距 48 = **1214px**。
     ⚠️ 这条会动**全站正文宽度**（每一页）⇒ 改完必须重跑 M7 确认没有页掉到 5 屏硬下限以下。 */
  --wrap:1240px;
  --gap:88px;          /* 模块上下呼吸 */
  --gap-in:32px;       /* 模块内部块间距 */
  --touch:24px;        /* 文字链接最小可点高度（WCAG 2.5.8 AA）*/
  --ctrl:44px;         /* 控件最小可点尺寸（WCAG 2.5.5）*/
  --rule:1px;          /* 发丝线宽，集中一处好调 */
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--c-page);color:var(--c-body);
  font-family:var(--f-body);font-size:var(--fs-body);font-weight:400;line-height:var(--lh-body);
  /* 🔴 2026-10-05 删「纸纹」：原来是 repeating-linear-gradient 每 8px 一条 1px 横线
     铺满全站每一页。用户验收打回「这种线几乎每一个页面都会有」——
     截图缩放后 8px 周期产生摩尔纹，大面积留白处尤其明显。
     画廊路线的底子就是干净的纯色，不要仿纸肌理。 */
}
h1,h2,h3,h4{font-family:var(--f-heading);font-weight:600;color:var(--c-heading);margin:0 0 .5em}
h1{font-size:var(--fs-h1);line-height:var(--lh-h1);font-weight:500;letter-spacing:.005em}
h2{font-size:var(--fs-h2);line-height:var(--lh-h2)}
h3{font-size:var(--fs-h3);line-height:var(--lh-h3)}
p{margin:0 0 1.2em}
a{color:var(--c-gold-deep);text-decoration:none;transition:color .2s ease}
a:hover{color:var(--c-ink);text-decoration:underline;text-underline-offset:3px}
a:active{color:var(--c-seal)}
img{max-width:100%;height:auto}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 24px}
/* 界面字体（导航、按钮、标签、表头）—— 中文回落到 CJK 正文栈 */
.ui{font-family:var(--f-ui)}

/* ── 键盘焦点：必须看得见（闸门要求 outline 或 box-shadow）── */
:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.btn:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:3px;box-shadow:0 0 0 4px var(--c-gold-a40)}
.card a:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:3px}

.skip{position:absolute;left:-9999px}
.skip:focus{left:12px;top:12px;background:var(--c-card);padding:8px 16px;z-index:99;
  border:1px solid var(--c-edge)}

/* ── 动效 ───────────────────────────────────────────
   🔴 名字沿用旧表：`visual_richness_check.py` 只数 `@keyframes` 个数（≥3），
   但改了名不会有任何东西报错，所以顺手改成贴合新风格的语义名。 */
@keyframes ink-rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
@keyframes gold-breathe{0%,100%{box-shadow:0 0 0 0 var(--c-gold-a40)}50%{box-shadow:0 0 0 6px var(--c-gold-a00)}}
@keyframes seal-in{from{opacity:0;transform:rotate(-6deg) scale(.88)}to{opacity:1;transform:rotate(0) scale(1)}}

.proto-banner{
  background:var(--c-proto-bg);color:var(--c-proto-fg);font-size:11px;letter-spacing:.03em;
  padding:4px 24px;text-align:center;font-family:var(--f-ui);opacity:.88;
}
.proto-banner code{color:var(--c-proto-code)}

/* ══ 页头：与页面同底的 sticky 栏 ═══════════════════════════════
   2026-10-05 画廊化后：无底色切换、无底边线 —— 页头融进页面，不「装帧」。 */
.site-header{background:var(--c-page);position:sticky;top:0;z-index:20}
/* 🔴 2026-10-05 删页头底边「墨线 + 金发丝」双线（用户验收打回「这种线」图 3）——
   原是 border-bottom:1px solid --c-ink + ::after 一条金色发丝。sticky 页头悬在
   内容上方，那条线全程横在屏幕上。画廊路线：页头与页面同底、无分界线。 */
/* 🔴 `gap` 24→20、导航 `gap` 16→14：品牌名换成 Cormorant 后字面变宽，
   加上 26px 字号，实测一行会超。算式见文件末断点那节的注释。 */
/* 🔴🔴 2026-10-07：**页头回到单行**（用户拍板「**方案 A**」，三个方案里选的）。
   一行：品牌 + 导航 + 语言下拉 + 报价按钮。
   🔴 **能单行的前提是语言切换改成了下拉**（`render_lang_switch()` → `<details>`）：
      横排五语要 311px，下拉只要 ~60px。
      单行实测需要 **1142px**（品牌 252 + 间距 16 + 导航 598 + 间距 16 + CTA 244），
      而 `.wrap` 上限 1200px ⇒ 可用 1152px，**余量只有 10px** ⇒ 见下面那条 1200 断点。
   ⚠️ **中间那档（769–1199px）退回两行**：`@media (max-width:1199px)`。
   ⚠️ **≤768px 仍是汉堡抽屉**（那条更早，见下）。 */
.header-inner{display:flex;align-items:center;gap:12px;min-height:80px;flex-wrap:nowrap;
  /* 🔴 2026-10-08：**页头不再单独设宽度，跟正文一样走 `.wrap`（`--wrap:1240px`）。**
     经过：页头单行要放得下**最宽的语言**（德文 1166px），而 `.wrap` 原先是 1200px
     ⇒ 可用宽只有 1152px ⇒ **德文超 14px**。
     我第一版是「**只放宽页头**到 1240」—— 结果**页头比正文每侧宽 20px**（页头左边界 124 / 正文 144）。
     用户答「**在意**」⇒ 改成**把 `--wrap` 整体放宽到 1240**，两边自然对齐。
     ⚠️ **改 `--wrap` 会动全站每一页的正文宽度** ⇒ 必须重跑 M7 看屏数有没有掉到硬下限以下。 */
  padding:12px 24px}
/* 🔴 `flex`/`order` 写在**外层 `nav.menu`** 上，不是里面的 `<ul class="nav">` ——
   `.nav` 不是 `.header-inner` 的 flex 子项，给它写 flex/order 浏览器直接忽略。 */
nav.menu{flex:0 1 auto;order:2;min-width:0}
.brand{display:inline-flex;align-items:center;min-height:var(--ctrl);font-family:var(--f-heading);
  font-size:27px;font-weight:600;letter-spacing:.01em;color:var(--c-heading);
  transition:color .2s ease}
.brand:hover{color:var(--c-gold-deep);text-decoration:none}
.brand-slot{white-space:nowrap}
/* 🔴 2026-10-08：`gap` 14→12、`letter-spacing` .07→.04（见 `.nav a` 那条）——
   为「页头单行放得下最宽的语言（德文）」腾出宽度。 */
.nav{list-style:none;display:flex;flex-wrap:wrap;gap:12px;margin:0;padding:0;font-family:var(--f-ui)}
/* 文字链接高度 ≥24px：inline-flex + min-height，别靠 padding 硬撑（会破 4px 栅格）*/
.nav a{display:inline-flex;align-items:center;min-height:var(--touch);
  /* 🔴 2026-10-08：`letter-spacing` .07em → **.04em**。
     德文导航 8 项约 90 个字符，.07em × 12px = 每字 0.84px ⇒ **光字母间距就占 ~76px**。
     收到 .04em 省下约 44px —— 是「让德文页头单行放得下」最省的一刀（不缩字号）。 */
  color:var(--c-heading);font-size:12px;font-weight:500;letter-spacing:.04em;text-transform:uppercase;
  transition:color .2s ease}
/* 当前页：不再只是换个颜色 —— 底下压一条金线，像书页的折角 */
.nav a.is-active{color:var(--c-gold-deep);box-shadow:inset 0 -2px 0 var(--c-gold)}
.nav .has-sub{position:relative}
.nav .sub{list-style:none;margin:0;padding:12px 0;position:absolute;left:-16px;top:100%;min-width:232px;
  background:var(--c-card);border:var(--rule) solid var(--c-edge);display:none;
  box-shadow:0 12px 28px var(--c-shadow);animation:ink-rise .2s ease-out both}
.nav .sub a{display:flex;align-items:center;min-height:32px;padding:0 16px;text-transform:none;
  letter-spacing:0;font-weight:400;transition:background-color .2s ease,color .2s ease}
.nav .sub a:hover{background:var(--c-gold-a12);text-decoration:none}
.nav .sub .sub-head{padding:8px 16px 2px;margin-top:6px;border-top:var(--rule) solid var(--c-line);
  font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--c-muted);cursor:default}
/* 🔴 `flex:none` 是「按钮被压扁」的根因修复（2026-09-29）：`.header-cta` 是
   `.header-inner` 的 flex 子项，默认 `flex-shrink:1`，空间不够时它自己先被压扁，
   里面的语言和按钮才跟着换行。不收缩 = 让导航去换行，而不是让按钮变形。 */
.header-cta{display:flex;align-items:center;gap:16px;flex:none;order:2;margin-left:auto}
/* 🔴 `white-space:nowrap` 不能省：它和按钮都是 `.header-cta` 的 flex 子项，
   默认可收缩 —— 父容器一窄就被压到断成两行。 */

/* ── 语言切换器（SW1–SW3）─────────────────────────────────────
   两样东西：当前语言（`<span>`，**不可点**）+ 另一种语言（真 `<a href>`）。
   🔴 **SW3：必须是真链接**，不是 JS 切 class —— 搜索引擎得顺着它爬，
      读屏软件也得能 Tab 到（`javascript:` 或 `<button>` 都不行）。
   🔴 **SW2：目标页不存在时那一项根本不渲染**（判据在 `render_lang_switch()`）；
      两项都凑不齐时整个控件不出现 —— 所以这组样式**不需要**处理「只有一项」的样子。
   🔴 高度走 `--touch`（24px）**不是** `--ctrl`（44px）：`ui_qa.py` 的触控判据是
      `el.matches('button,[role=button],.btn,.burger,input[type=submit]')` 才按 44 算，
      普通 `<a>` 一律按文字链接的 24px 判。**这里按 44 撑反而会把头部顶高一大块。** */
/* ── 语言切换（SW1–SW3）：🔴 2026-10-07 从「横排五语」改成 `<details>` 下拉 ────
   横排五语占 **311px**，把页头挤到放不下；下拉只占 **~60px**（用户拍板「方案 A」）。
   为什么用 `<details>` 而不是 JS 下拉：**不用 JS**、**链接仍在 DOM 里**（爬虫读得到）、
   **原生可键盘操作 / 可读屏**，不用自己补 ARIA。 */
.lang-dd{position:relative;display:inline-flex;align-items:center;flex:none}
.lang-dd summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:8px;
  min-height:var(--touch);padding:0 12px;border:var(--rule) solid var(--c-line);
  color:var(--c-heading);font-family:var(--f-ui);font-size:12px;font-weight:600;
  letter-spacing:.08em;transition:border-color .2s ease,color .2s ease}
.lang-dd summary::-webkit-details-marker{display:none}
.lang-dd summary:hover{border-color:var(--c-gold-deep);color:var(--c-gold-deep)}
.lang-dd summary:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
/* 小三角：纯 CSS，不用图片、不用字体图标 */
.lang-caret{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid currentColor}
.lang-dd ul{display:none;position:absolute;top:100%;right:0;margin:8px 0 0;padding:8px 0;
  list-style:none;min-width:176px;background:var(--c-page);border:var(--rule) solid var(--c-line);
  box-shadow:0 10px 26px var(--c-shadow);z-index:30}
.lang-dd[open] ul{display:block}
.lang-dd ul a,.lang-dd ul .lang-cur{display:flex;align-items:center;min-height:var(--touch);
  padding:8px 16px;font-family:var(--f-ui);font-size:13px;letter-spacing:.04em}
.lang-dd ul a{color:var(--c-muted);transition:color .2s ease,background-color .2s ease}
.lang-dd ul a:hover{color:var(--c-gold-deep);background:var(--c-subtle);text-decoration:none}
/* 当前语言：用**颜色深浅**表达「你在这儿」，不只靠 `aria-current`（那只有读屏软件看得见）。
   `--c-heading` 比可点链接的 `--c-muted` 重一档 —— 对比是给眼睛的。 */
.lang-dd ul .lang-cur{color:var(--c-heading);font-weight:600}

/* ── 汉堡按钮（只在 ≤768px 出现，见断点那节）───────────────
   🔴 类名必须是 `burger` —— 公司共享脚本 `ui_qa.py` 的触控尺寸判据里
   写死了 `.burger` 归入「控件」那一档（要 ≥44×44），用别的类名就只按文字链接的
   24px 判，等于**自己把闸门调松了**。
   🔴 `display:none` 写在基础样式里（桌面不出现），断点里再 `display:inline-flex`。
   按钮由 `assets/site.js` 切换 `html.nav-open`，**不靠 CSS 伪类** ——
   伪类改不了 `aria-expanded`，读屏软件就不知道菜单开没开。 */
.burger{display:none;align-items:center;justify-content:center;gap:8px;
  min-width:var(--ctrl);min-height:var(--ctrl);padding:0 16px;
  background:var(--c-card);border:var(--rule) solid var(--c-edge);color:var(--c-heading);
  font-family:var(--f-ui);font-size:12px;font-weight:500;letter-spacing:.1em;
  text-transform:uppercase;cursor:pointer;
  transition:border-color .2s ease,color .2s ease,background-color .2s ease}
.burger:hover{border-color:var(--c-gold-deep);color:var(--c-gold-deep)}
.burger:active{background:var(--c-subtle)}
.burger-icon{display:block;width:16px;height:12px;position:relative}
.burger-icon::before,.burger-icon::after{content:"";position:absolute;left:0;width:16px;height:2px;
  background:currentColor;transition:transform .2s ease,top .2s ease}
.burger-icon::before{top:0}
.burger-icon::after{top:10px}
html.nav-open .burger-icon::before{top:5px;transform:rotate(45deg)}
html.nav-open .burger-icon::after{top:5px;transform:rotate(-45deg)}

/* ── 按钮：金线方框，不是色块 ───────────────────────────
   旧版是「实心藏青圆角矩形」—— 那是标准的现代 SaaS 按钮。
   新版：**直角 + 内嵌一道金发丝**。主按钮 = 玄墨底 + 金内线；次按钮 = 纸底 + 金外框。
   视觉上像一枚钤印，不像一个控件。 */
/* 控件按 WCAG 2.5.5 = 44×44，min-height 写死，别靠 padding 凑 */
.btn{display:inline-flex;align-items:center;justify-content:center;min-height:var(--ctrl);
  border-radius:var(--radius);font-family:var(--f-ui);font-size:12px;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;padding:0 28px;border:var(--rule) solid transparent;
  /* 🔴 `white-space:nowrap` 不能省：按钮是 flex 子项、默认可收缩，
   父容器一窄文字就被压成两行（实测 1440px 就发生过）。 */
  white-space:nowrap;
  transition:background-color .2s ease,color .2s ease,border-color .2s ease,box-shadow .2s ease}
.btn:hover{text-decoration:none}
.btn:active{transform:translateY(1px)}
.btn-primary{background:var(--c-ink);color:var(--c-inverse);border-color:var(--c-ink);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold-a64)}
.btn-primary:hover{background:var(--c-ink);color:var(--c-gold);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold);animation:gold-breathe .8s ease-out}
.btn-ghost{background:transparent;color:var(--c-ink);border-color:var(--c-edge);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold-a24)}
.btn-ghost:hover{background:var(--c-ink);color:var(--c-inverse);border-color:var(--c-ink);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold-a64)}
.btn-sm{min-height:var(--ctrl);padding:0 20px;font-size:11px;letter-spacing:.12em}
.btn-row{display:flex;flex-wrap:wrap;gap:16px;margin:28px 0 0}
/* 深色带（首屏 / 询盘带）里的按钮要反色，否则玄墨底上叠玄墨按钮 = 隐形 */
.on-ink .btn-primary{background:var(--c-gold);color:var(--c-ink);border-color:var(--c-gold);
  box-shadow:inset 0 0 0 var(--rule) var(--c-ink)}
.on-ink .btn-primary:hover{background:var(--c-inverse);border-color:var(--c-inverse);color:var(--c-ink);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold)}
.on-ink .btn-ghost{color:var(--c-inverse);border-color:var(--c-inverse-a22);
  box-shadow:inset 0 0 0 var(--rule) var(--c-gold-a40)}
.on-ink .btn-ghost:hover{background:var(--c-gold);color:var(--c-ink);border-color:var(--c-gold)}

/* ══ 报头条（面包屑）═══════════════════════════════════════════
   旧版面包屑孤零零浮在一片白上。新版给它一条窄带 + 一道金发丝压底，
   读作书页的「书眉」（running head）。 */
.masthead{background:var(--c-page);padding:20px 0 0}
.masthead-in{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.crumb{font-family:var(--f-ui);font-size:12px;letter-spacing:.08em;color:var(--c-muted)}
.crumb a{display:inline-flex;align-items:center;min-height:var(--touch)}
.crumb span{margin:0 4px}
.role-line{margin-top:8px;font-family:var(--f-ui);font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--c-muted)}

/* ══ 模块骨架 ══════════════════════════════════════════════════
   每个模块 = `<section class="module module-<版式>"><div class="mod-in">…</div></section>`
   🔴 所有内容包在 `.mod-in` 里 —— 子块天然撑满，**不再需要**旧版那种
   「`.module>*` 发 padding、再逐个 `margin:0 auto` 盖回来」的补丁
   （旧样式表为此踩了 7 次同样的坑）。 */
.module{background:var(--c-page);padding:var(--gap) 0}
.mod-in{max-width:var(--wrap);margin:0 auto;padding:0 24px}
/* 🔴 2026-10-07：**长词断行**。德文有 `Handwerksklassifizierungen`（24 字母）、
   `Datenschutzerklärung` 这类超长复合词，320px 下会把标题/正文撑破容器
   （实测德文 71 页里有一批就是被这个词撑出横向滚动的）。
   🔴 **必须用 `anywhere` 而不是 `break-word`**：两者都断行，但
   **`break-word` 不影响 min-content 尺寸** —— 而 `.sec-head` 是 flex 容器、
   标题是 flex 子项，`min-width:auto` 会按 min-content 顶住不肯缩。
   `anywhere` 参与内在尺寸计算，子项才收得住。⚠️ 只在**放不下时**才断，正常文字不受影响。 */
h1,h2,h3,h4,h5,h6{overflow-wrap:anywhere}
/* 🔴 2026-10-05 删「交替底色带」（用户二次验收打回「这种线几乎每一个页面都会有」）：
   原是 `.module:nth-of-type(even){background:var(--c-subtle)}` —— 长页每隔一个模块
   换米灰底（#EFE8DA），区块交界就是一条通栏色带，截图里像「横线」铺满每页。
   画廊路线：整页一个底子，节奏靠留白不靠色带。`--c-subtle` 变量保留（卡片等处仍在用）。 */
.module-gap{padding:32px 0}
/* 空的 `.proto-gap` 灰框样式已删：空模块现在印 `【待项目确认】`（见 `render_module()`）。
   `--c-proto-line/-note/-note-fg` 保留 —— `.lead-status.notice` 还在用。 */

/* ── 区块题头：朱砂印 + 双线 ─────────────────────────────────
   旧版是 `MODULE 1` 一行灰色小字（**开发注释，任务 G 要清掉的东西**）。
   新版：一个朱砂方块（里面是卷次数字）+ 一条双线拉到右边。
   数字是「第几卷」，不是「第几个模块」—— 去掉 `Module` 字样，
   它就从开发注释变成了装帧元素。 */
.sec-head{display:flex;align-items:center;gap:20px;margin:0 0 var(--gap-in)}
.seal{flex:none;width:40px;height:40px;display:grid;place-items:center;
  background:var(--c-seal);color:var(--c-inverse);
  font-family:var(--f-ui);font-size:12px;font-weight:600;letter-spacing:.04em;
  animation:seal-in .5s ease-out both}
.sec-head h2,.sec-head .blk-h2{margin:0}

.blk-h2{margin-top:0;animation:ink-rise .5s ease-out both}
/* 正文里的二级标题：上面压一条短金线，像段落的「起笔」 */
.blk-h{margin-top:1.6em}
.mod-in>h2+p,.mod-in>h2+.lead{margin-top:0}
.lead{font-size:19px;line-height:1.72;color:var(--c-body);animation:ink-rise .5s ease-out both}
/* 引文（P4 里以 `> ` 开头的块会被 `paras()` 落成 blockquote）——
   新版做成「批注」：左边一条金线 + 略缩进 + 略小一号。 */
blockquote{margin:24px 0;padding:0 0 0 24px;border-left:3px solid var(--c-gold);
  color:var(--c-muted);font-style:italic}

/* ══ 版式 ①：卷轴式首屏（带 H1 的模块）══════════════════════════
   深墨满幅 + 上下各一道 4px 金压边 + 内嵌双线框。
   文字居中，标题下是一条「金线 + 中央菱形」的收笔。 */
.module-hero{background:var(--c-ink);color:var(--c-inverse);
  padding:96px 0 88px;position:relative;overflow:hidden;
  /* 🔴 2026-10-05 删「竖纹帘纹」：与纸纹同源的问题 —— 1px/8px 周期的条纹
     在截图缩放后出摩尔纹，用户验收打回。深色 hero 就纯深色。 */
  text-align:center}
/* 2026-10-05 画廊化：带轮播的首屏拉高到接近一屏 —— 首页那一屏是主角，
   图应该顶天立地，文字垂直居中浮在画上。 */
.module-hero.is-carousel{min-height:82vh;display:flex;flex-direction:column;
  justify-content:center;padding:72px 0}
.module-hero.is-carousel .mod-in{width:100%}
.module-hero .mod-in{position:relative;max-width:960px}
/* 🔴 2026-10-01 删：hero 里那两条内嵌金框（`::before` inset:20px / `::after` inset:24px）。
   用户原话「**8 去掉金边**」。理由：它们绕首屏一圈，读起来像「隔线」而不是「装裱」。
   注意：这跟「模块之间那条 4px 通栏金线」是**两回事**，那条早就删了（见 §45）。 */
.module-hero h1{color:var(--c-inverse);font-size:var(--fs-h1);line-height:var(--lh-h1);
  margin:0 0 24px;animation:ink-rise .6s ease-out both}
/* 🔴 2026-10-04 改色：原来是 `--c-inverse-2`（#CFC6B4 淡茶色）。
   实测：副文是 **19px 正文**，AA 线 **4.5:1**（H1 是 54px 大字，线只有 3.0）——
   所以全站首屏真正读不了的**不是标题、是这段副文**。
   淡茶色的亮度只有 0.5697，比 H1 的 0.8904 低一大截，压在照片上先垮的就是它。
   改成跟 H1 同色后，同样的底子对比度直接涨约 1.5 倍。
   （层级不靠颜色分了 —— 54px 对 19px，字号自己就说清楚了。） */
.module-hero .lead{color:var(--c-inverse);max-width:760px;margin:0 auto 32px}
.module-hero .btn-row{justify-content:center;margin-top:0}
/* 首屏里如果还有别的块（内链、表格），也按反白处理 */
.module-hero .mod-in>*:not(h1):not(.lead):not(.btn-row){color:var(--c-inverse-2)}
.module-hero a{color:var(--c-gold)}
.module-hero a:hover{color:var(--c-inverse)}
.module-hero .tbl-wrap table{background:transparent;color:var(--c-inverse-2)}
.module-hero .tbl-wrap th,.module-hero .tbl-wrap td{border-color:var(--c-inverse-a22)}
.module-hero .tbl-wrap th{background:transparent;color:var(--c-inverse)}

/* ══ 版式 ②：左题右文（纯散文模块）═════════════════════════════
   题头钉在左栏、正文在右栏 —— 像书的「眉批」版式。
   窄屏自动落回单栏（见断点节）。 */
.module-prose .mod-in{display:grid;grid-template-columns:minmax(0,240px) minmax(0,1fr);gap:0 56px}
.module-prose .sec-head{flex-direction:column;align-items:flex-start;gap:12px;margin:0;
  position:sticky;top:120px;align-self:start}
.module-prose .mod-body{min-width:0;max-width:720px}
.module-prose .mod-body>*:first-child{margin-top:0}

/* ══ 版式 ②b：无题头（既没 h2 也没卷次号的模块）══════════════
   典型是页尾那行版权。没有题头就没有左栏，别摆一枚孤零零的朱砂印。 */
.module-plain{padding:32px 0}
.module-plain .mod-in>*:first-child{margin-top:0}
.module-plain .mod-in>*:last-child{margin-bottom:0}

/* ══ 模块底图（2026-10-01）══════════════════════════════════════
   给纯文字模块整块铺一张压暗大图，正文浮在上面。见 `PAGE_MODULE_BG`。
   🔴 **蒙版必须够暗**：底图是浅色照片，而这里的正文是浅色字（`.on-ink`）。
      底图不压暗就是「浅字压浅图」，对比度闸直接红。四档 76%~90% 的玄墨。
   🔴 三层顺序（z-index 写死，不靠 DOM 顺序猜）：
      0 底图 → 1 正文 `.mod-in` → 2 金线内框 `::before`。
      不给 `::before` 提层，那圈金线会被底图盖掉 —— 框没了但页面看着「也没坏」，
      是那种不报错、只是悄悄少个装饰的错。 */
.module.has-bg{position:relative;overflow:hidden}
.module-bg{position:absolute;inset:0;z-index:0}
.module-bg img{display:block;width:100%;height:100%;object-fit:cover}
.module-bg-scrim{position:absolute;inset:0;
  background:linear-gradient(to bottom,
    color-mix(in srgb,var(--c-ink) 88%,transparent) 0%,
    color-mix(in srgb,var(--c-ink) 78%,transparent) 46%,
    color-mix(in srgb,var(--c-ink) 84%,transparent) 78%,
    color-mix(in srgb,var(--c-ink) 92%,transparent) 100%)}
.module.has-bg .mod-in{position:relative;z-index:1}
.module-cta.has-bg::before{z-index:2}

/* ══ 版式 ①b：满屏轮播首屏（2026-10-01）═════════════════════════
   用户原话：「我们这种网站的首页，一定是多图、大图、轮播等等，
   这些至少占了 80%，应该强调的是视觉效果」。
   此前首屏是**纯墨底 + 文字**，一张图都没有 —— 最贵的一屏全是字。

   结构（`render_hero_carousel()` 产出）：
     <section class="module module-hero is-carousel">
       <div class="hero-carousel">   ← 幻灯片层，绝对定位铺满，**在 .mod-in 外面**
         <div class="hero-slides"><img class="hero-slide is-on">…</div>
         <div class="hero-scrim"></div>          ← 压暗蒙版
       </div>
       <div class="hero-ctl">…圆点 + 左右箭头…</div>
       <div class="mod-in on-ink">H1…</div>       ← 文字层，压在最上面
     </section>

   🔴 三层顺序靠 **DOM 次序** 定，不靠 z-index：`.hero-carousel` 是 `z-index:0`，
      `.mod-in` 是 `position:relative` 且写在后面 → 后画的在上面。改动 DOM 次序
      会让文字被图盖住（白字压照片，看着就是「首屏糊了」）。 */
.module-hero.is-carousel{display:flex;flex-direction:column;justify-content:center;
  min-height:min(88vh,840px);padding:120px 0 132px}
/* 有轮播时不用竖纹肌理（照片已经把底铺满了，纹理只会从图的缝里透出来） */
.module-hero.is-carousel{background-image:none}
/* ── 内页首屏的单图背景（2026-10-01）────────────────────────────
   比首页那一屏**矮**：内页首屏是「章节扉页」，不是主角。
   复用 `.hero-scrim` 那层压暗蒙版（下面那个规则），不再写第二份 ——
   两份蒙版迟早会漂成两个深浅，而对比度闸只认其中一份。 */
.module-hero.has-hero-bg{display:flex;flex-direction:column;justify-content:center;
  min-height:clamp(360px,46vh,520px);padding:96px 0;background-image:none}
.hero-bg{position:absolute;inset:0;z-index:0;overflow:hidden;background:var(--c-ink)}
.hero-bg img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* 有底图时把金线内框提回最上层：它是**装帧**，压在照片上正好是画册的「框」。
   不提层的话图会盖掉它 —— 框没了但页面看着「也没坏」，是不报错的静默丢失。 */
.module-hero.is-carousel::before,.module-hero.is-carousel::after,
.module-hero.has-hero-bg::before,.module-hero.has-hero-bg::after{z-index:2}
.module-hero.is-carousel .mod-in,.module-hero.has-hero-bg .mod-in{z-index:1}
/* 🔴 `transition:--scrim-a` —— 轮播换图时蒙版**跟着一起渐变**，
   时长必须跟 `.hero-slide` 的 `opacity 1.2s` 对齐。
   不齐的话：图在慢慢淡，蒙版**啪一下跳档**，看着像闪了一下。
   （`@property` 不支持的老浏览器会忽略这条 —— 那就跳档，功能不受影响。） */
.hero-carousel{position:absolute;inset:0;z-index:0;overflow:hidden;background:var(--c-ink);
  transition:--scrim-a 1.2s ease}
.hero-slides{position:absolute;inset:0}
.hero-slide{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1.2s ease}
.hero-slide.is-on{opacity:1}
/* 压暗蒙版：**必须有**。首屏图是照片，白字直接压上去读不了（对比度闸会红）。

   🔴 它**改过两轮，两轮都是用户打回**：
   · 2026-10-04 上一轮：一刀切 52% → **66%**。52% 是照**暗色照片**定的，
     后来换进来几张亮色照片（牡丹瓷餐厅场景等）就压不住了 ——
     全站实测 **17 个页面**的副文有 >15% 的面积不到 4.5:1。加深到 66%
     等于给所有照片兜了底，**代价是暗色照片跟着更暗一档**。
   · 2026-10-04 本轮：用户原话「**14 不能更暗**」——
     就是嫌上一轮那一刀。所以**不再一刀切**：改成**按图给量**，
     每张图压多少由 `HERO_SCRIM` 算好，写进 `--scrim-a`（见那张表的注释）。

   🔴 **`--scrim-a` 是「这张图要压多少」，不是「全站压多少」。**
      · 单图首屏：`render_hero_bg()` 把值内联写在 `.hero-bg` 上
      · 轮播：写在 `.hero-carousel` 上，`site.js` 每次换图**同步换档**
      · 查不到就落到兜底 66%（`SCRIM_FALLBACK`）—— 跟老行为一致，不会突然变亮

   ⚠️ **改这个值之前先跑 `.qa/_probe_hero_sweep.py` 拿真实数字**，
      别凭感觉调（首屏对比度**没有机器闸在查**，全靠那个脚本，见
      `docs/qa/TESTING_STRATEGY.md` §6.2「测法澄清五」）。 */
@property --scrim-a{syntax:"<percentage>";inherits:true;initial-value:66%}
.hero-scrim{position:absolute;inset:0;pointer-events:none;
  background:color-mix(in srgb, var(--c-ink) var(--scrim-a, 66%), transparent)}
/* 轮播最下沿那一带**额外**再压一层：金箭头 / 金圆点落在那里，
   它们是**控件**，WCAG 1.4.11 按 **3:1** 判（不是文字的 4.5:1），
   而金 `--c-gold`（#B8944D，L=0.3481）本身就不亮 ——
   光靠上面那层按「文字」算出来的档位，金在亮照片上只有 1.9:1。
   压完这一层，**连纯白的底子都有 3.2:1**。
   ⚠️ 只给轮播加（内页首屏没有圆点箭头，不需要这条黑边）。 */
.hero-carousel .hero-scrim::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:150px;
  background:linear-gradient(0deg,
    color-mix(in srgb, var(--c-ink) 42%, transparent) 0%,
    color-mix(in srgb, var(--c-ink) 20%, transparent) 46%,
    transparent 100%)}
.hero-ctl{position:absolute;left:0;right:0;bottom:32px;z-index:2;
  display:flex;align-items:center;justify-content:center;gap:16px}
/* 🔴 箭头 / 圆点是 `<button>`，**高度一律 44px 起**（`ui_qa.py` WCAG 2.5.5）。
   圆点那个小菱形是 `::before` 画出来的**样子**，按钮本体是 44×44 的透明块 ——
   把按钮直接做成 12px 的小圆点会判不合格。 */
.hero-arrow{width:48px;height:48px;display:grid;place-items:center;cursor:pointer;
  background:color-mix(in srgb, var(--c-ink) 40%, transparent);
  border:var(--rule) solid var(--c-gold-a40);color:var(--c-gold);
  font-size:26px;line-height:1;padding:0;
  transition:background-color .3s ease,border-color .3s ease,color .3s ease}
.hero-arrow:hover{background:var(--c-gold-a24);border-color:var(--c-gold);color:var(--c-inverse)}
.hero-arrow:active{background:var(--c-gold-a40)}
.hero-arrow:focus-visible{outline:2px solid var(--c-gold);outline-offset:3px}
.hero-dots{display:flex;align-items:center;gap:4px}
.hero-dot{width:44px;height:44px;display:grid;place-items:center;padding:0;
  background:transparent;border:0;cursor:pointer}
.hero-dot::before{content:"";width:11px;height:11px;transform:rotate(45deg);
  border:var(--rule) solid var(--c-gold-a64);
  transition:background-color .3s ease,border-color .3s ease}
.hero-dot.is-on::before{background:var(--c-gold);border-color:var(--c-gold)}
.hero-dot:hover::before{background:var(--c-gold-a64)}
.hero-dot:active::before{background:var(--c-inverse)}
.hero-dot:focus-visible{outline:2px solid var(--c-gold);outline-offset:2px}

/* ══ 全幅章节图版（`PAGE_BANDS`，2026-10-01）═════════════════════
   满幅大图 + 压在图下沿的一行标题，**整块是一个链接**。
   作用：给长页做视觉停顿，像画册的章节扉页。**不承担正文。**
   🔴 为什么整块做链接：那样就不用生成器自己写一句按钮文案（写了就是编内容）。
      标题本身就是 P4 里已有的品类英文名。 */
.band{position:relative;margin:0;background:var(--c-ink)}
.band-link{display:block;position:relative;text-decoration:none}
.band-link img{display:block;width:100%;height:clamp(420px,52vw,700px);
  object-fit:cover;opacity:.97;transition:opacity .5s ease}
.band-link:hover img{opacity:1}
/* 🔴 2026-10-05 减淡（用户验收打回「一大块死黑」）：窑炉/青铜这类暗图叠上
   底部 80% 的墨渐变，中段以下必然全黑 —— 图白铺了。现在遮罩只压底部
   标题带那一条，图的大半身终于看得见。 */
.band-scrim{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,
    color-mix(in srgb, var(--c-ink) 8%, transparent) 0%,
    transparent 45%,
    color-mix(in srgb, var(--c-ink) 46%, transparent) 100%)}
.band-t{position:absolute;left:0;right:0;bottom:40px;display:block;
  max-width:var(--wrap);margin:0 auto;padding:0 24px;
  /* 🔴 2026-10-05 删标题上的 2px 通栏金线（用户验收打回「这种线」图 1）——
     画廊化时我想给标题加个「收笔」，实测在通栏大图下就是一条横切的金线。 */
  font-family:var(--f-heading);font-size:clamp(26px,3.4vw,44px);line-height:1.14;
  font-weight:600;letter-spacing:.02em;color:var(--c-inverse);
  text-shadow:0 1px 12px var(--c-ink-a60, rgba(0,0,0,.55));
  transition:color .3s ease}
.band-link:hover .band-t{color:var(--c-gold)}
.band-link:focus-visible{outline:2px solid var(--c-gold);outline-offset:-4px}

/* ══ 版式 ③：册页卡片网格 ══════════════════════════════════════
   旧版：白底直角卡片 + 悬停浮起 + 投影（现代 SaaS 卡片）。
   新版：**册页** —— 外一道实框、内一道金发丝框（中间 12px 留白当「装裱」），
   图不做出血、压在框内；悬停不浮起，改成**内框变金**。
   🔴 卡片里必须有 `.card-thumb`（含 `thumb` → 闸门认作「视觉锚点」）
      和 `.card-lines`（`<ul>` → 闸门认作「结构化信息」），
      `visual_richness_check.py` 的「卡片完整度」两项靠这两个类名过。 */
.card-grid{list-style:none;padding:0;margin:var(--gap-in) 0 0;display:grid;
  grid-template-columns:repeat(4,1fr);gap:20px}
.card{margin:0;display:flex}
.card a{display:flex;flex-direction:column;gap:12px;width:100%;padding:16px;
  border:var(--rule) solid var(--c-edge);background:var(--c-card);overflow:hidden;
  position:relative;
  transition:border-color .3s ease,box-shadow .3s ease,background-color .3s ease}
/* 内框：装裱的第二道线。`inset:4px` 让它比外框小一圈。 */
.card a::after{content:"";position:absolute;inset:4px;pointer-events:none;
  border:var(--rule) solid var(--c-gold-a24);
  transition:border-color .3s ease}
.card a:hover{border-color:var(--c-gold-deep);background:var(--c-card);
  box-shadow:0 10px 26px var(--c-shadow);text-decoration:none}
.card a:hover::after{border-color:var(--c-gold)}
.card a:active{background:var(--c-subtle)}
/* 卡片缩略图（`card_thumb()`）—— 用**目标页自己那张首图**。
   🔴 旧版是负外边距出血到卡片边缘；新版**不出血**，老老实实待在装裱框里，
   下面压一条墨线当「题签」的分界。
   🔴 `aspect-ratio` + `object-fit:cover` 是**成对**用的：前者定死框高（免得
      各按自己比例撑出参差不齐的高度），后者把图裁进框里。
      `<img>` 上的 `width`/`height` 属性仍是**真实像素**，两者比例一致，不会打架。 */
.card-thumb{display:block;width:100%;margin:0;aspect-ratio:2/1;object-fit:cover;
  background:var(--c-subtle);border-bottom:var(--rule) solid var(--c-line-strong)}
/* 图的容器 —— 只为 `overflow:hidden`（悬停放大时裁掉溢出的边）。 */
.card-media{display:block;overflow:hidden}
.card-t{font-family:var(--f-heading);font-size:21px;line-height:28px;color:var(--c-heading);
  font-weight:600}
.card a:hover .card-t{color:var(--c-gold-deep)}
.card-d{font-size:15px;line-height:24px;color:var(--c-muted)}
/* 卡片里的「推荐品类」列表（`card_lines()`）—— 内容来自目标页正文，不新写。
   🔴 为什么是 `<ul>` 而不是一串 `·`：`visual_richness_check.py` 的「卡片完整度」
      闸把 `<ul>` 认作「结构化信息」；一串用中点连起来的文字它认不出。
   🔴 `margin-top:auto` 把列表顶到卡片底部 —— 卡片高度不一时，列表底边对齐。 */
.card-lines{display:flex;flex-wrap:wrap;gap:6px;margin:auto 0 0;padding:0;list-style:none}
.card-lines li{font-family:var(--f-ui);font-size:11px;line-height:18px;letter-spacing:.04em;
  color:var(--c-gold-deep);border:var(--rule) solid var(--c-line-strong);
  background:var(--c-gold-a08);padding:2px 8px}
/* 品类卡上的「定制能力」（`card_custom()`）—— 同样取自目标页正文。
   🔴 块标题逐页不同（`Custom Work` / `Custom Casting`），照抄，不统一。
   🔴 这块**不做底对齐**（`.card-lines` 才做）：它是 4 行长文本，贴底会在描述
      和它之间留一大片空白。改成跟着描述往下走，空白落到卡片底部。 */
.card-custom-h{display:block;margin:0;padding-top:12px;font-family:var(--f-ui);font-size:11px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--c-gold-deep)}
.card-custom{margin:6px 0 0;padding:0 0 0 12px;list-style:none;
  border-left:2px solid var(--c-line-strong)}
.card-custom li{font-size:14px;line-height:20px;color:var(--c-muted);padding:1px 0}

/* ══ 版式 ③b：大图卡（`VISUAL_CARD_MODULES` 点名的模块）══════════
   和 ③ 的**结构完全一样**（同样的 `.card-thumb` / `.card-lines`，闸门照过），
   区别只在「图多大、字摆哪」：
     · ③ 是四列小卡，图 2:1 扁扁一条压在卡片顶部；
     · ③b 是**两列大卡**，图 4:3 占满整格，文字落在图下面一小块。
   整格 4:3 的图 + 一小块字，图占这张卡约八成 —— 这是「视觉型首页」的主力图区。

   🔴 底色**不能用 `.card a` 那个浅笺**（`--c-card`）：大字压在浅底上、旁边一张
      满格照片，卡和卡的边界会糊在一起。改成玄墨底 + 反白字，卡片边界由照片
      自己的边缘给出。 */
.card-grid.is-visual{grid-template-columns:repeat(2,1fr);gap:24px}
.card-grid.is-visual .card a{padding:0;gap:0;border:0;background:var(--c-ink);
  overflow:hidden;position:relative;display:block}
/* 内框（装裱第二道线）在满格图上会压到照片主体，撤掉 */
.card-grid.is-visual .card a::after{display:none}
.card-grid.is-visual .card-media{position:relative;display:block}
.card-grid.is-visual .card-thumb{aspect-ratio:1/1;border-bottom:0;
  transition:transform .7s ease,opacity .4s ease}
/* 压字用的暗幕：从卡片底边长上来，让反白字压得住照片。
   🔴 用 `color-mix()` 现配，**不写颜色字面量** —— `verify_design_tokens.py`
      只允许颜色字面量出现在 `:root{}` 里。 */
.card-grid.is-visual .card-media::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:78%;pointer-events:none;
  background:linear-gradient(to top,
    var(--c-ink) 4%,
    color-mix(in srgb, var(--c-ink) 86%, transparent) 40%,
    transparent 100%)}
/* 🔴 文字块**贴住卡片下沿、压在图上面**（2026-10-01 改）。
   改前是「图 564×564 + 下面 247px 字」，卡片高 811px，图只占这张卡 55%。
   用户要的是「多图、大图……至少占 80%」。现在整张卡就是一张方图，
   标题 / 描述 / 标签叠在下沿的暗幕上 —— 图占满整格。 */
.card-grid.is-visual .card-body{position:absolute;left:0;right:0;bottom:0;z-index:2;
  display:flex;flex-direction:column;gap:8px;padding:24px 22px 22px}
.card-grid.is-visual .card a:hover{background:var(--c-ink);box-shadow:0 14px 32px var(--c-shadow)}
.card-grid.is-visual .card a:hover .card-thumb{transform:scale(1.045)}
.card-grid.is-visual .card a:active .card-thumb{opacity:.82}
.card-grid.is-visual .card-t{margin:0;color:var(--c-inverse);font-size:24px;
  line-height:32px}
.card-grid.is-visual .card a:hover .card-t{color:var(--c-gold)}
.card-grid.is-visual .card-d{margin:0;color:var(--c-inverse-2);font-size:15px;
  line-height:24px}
/* 列表 / 定制块在这张卡里也要反白 */
.card-grid.is-visual .card-lines,
.card-grid.is-visual .card-custom-h,
.card-grid.is-visual .card-custom{margin:8px 0 0}
.card-grid.is-visual .card-lines li{color:var(--c-gold);border-color:var(--c-gold-a40);
  background:var(--c-gold-a08)}
.card-grid.is-visual .card-custom-h{color:var(--c-gold);padding-top:0}
.card-grid.is-visual .card-custom{border-left-color:var(--c-gold-a40)}
.card-grid.is-visual .card-custom li{color:var(--c-inverse-3)}

/* ══ 版式 ④：账簿式表格 ════════════════════════════════════════
   旧版是四面带框的网格（Excel 感）。新版只留**横线**：
   表头上下各一道墨线（双线），表体每行一道发丝线。
   读起来像账本 / 谱录，不像数据表。 */
.tbl-wrap{overflow-x:auto;margin:var(--gap-in) 0 0}
table{border-collapse:collapse;width:100%;background:transparent;font-size:16px}
th,td{border:0;border-bottom:var(--rule) solid var(--c-line);
  padding:14px 16px;text-align:left;vertical-align:top}
thead th{border-top:var(--rule) solid var(--c-ink);border-bottom:var(--rule) solid var(--c-ink);
  background:transparent;font-family:var(--f-ui);font-size:11px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--c-heading)}
tbody tr{transition:background-color .2s ease}
tbody tr:hover{background:var(--c-gold-a08)}
/* 表头里的第一列常是「项目」，给它一点宽度权重 */
th:first-child{width:32%}

/* ══ 字段面板（两列表的替身，2026-10-01）══════════════════════════
   `| 字段 | 值 |` 这种两列表**不走表格**，走这里（判据见 `render_table()`）。
   表格的问题：`Item`/`Value` 两个表头是废话，字段名其实在每行第一格里。
   这里把字段名提出来做成**金色小字**，值另起一栏，行间一道发丝线 ——
   读起来是「规格表 / 名片」，不是「数据表」。
   🔴 窄屏（≤720px）改成上下堆叠：两栏并排时值那栏会被挤到每行只放得下两三个词。 */
.spec-list{margin:var(--gap-in) 0 0;display:block}
.spec-row{display:grid;grid-template-columns:minmax(140px,26%) 1fr;
  gap:8px 28px;padding:16px 0;border-bottom:var(--rule) solid var(--c-line);
  transition:background-color .2s ease}
.spec-row:first-child{border-top:var(--rule) solid var(--c-ink)}
.spec-row:last-child{border-bottom:var(--rule) solid var(--c-ink)}
.spec-row:hover{background:var(--c-gold-a08)}
.spec-row dt{font-family:var(--f-ui);font-size:12px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--c-gold-deep);padding-top:3px}
.spec-row dd{margin:0;font-size:16px;line-height:1.65;color:var(--c-body)}
@media (max-width:720px){
  .spec-row{grid-template-columns:1fr;gap:4px}
  .spec-row dt{padding-top:0}
}

/* ── 配图 ─────────────────────────────────────────── */
/* 🔴 AI 图的披露（M8 真实性铁律）**改过两次，两次都是用户打回**：
   · 2026-09-28 → 2026-10-01：从**逐张 figcaption**（全站 90 条）改成
     **每页一条 `.img-note`**（用户报「示例场景这些字段…要他干啥」）——
     同一句话翻两屏撞见一次，买家会麻木。
   · 2026-10-01 → 2026-10-04：那条**从正文末尾搬进页脚细则**
     （用户报「Home第一张图都有一个，这不要」「每一个屏都有 不能要」）——
     它站在最后一屏、紧贴上一张图，看着就是「那张图的说明」。
   ⚠️ **两次搬的都是位置，披露本身一次都没取消。**
   ⚠️ **别把 `.img-note` 做成「缩到看不见」的装饰**：它是披露，
      字号/对比度仍按次要正文规格走（搬进页脚后：13px + `--c-inverse-3`，6.46:1 AA）。 */
/* 🔴 2026-10-01 配图改**通栏大图**（用户：「一定是多图、大图、轮播」）。
   原来是 `max-width:var(--wrap)`（1152px 宽），在 1425 屏上左右各空 137px ——
   图被框在正文栏里，读起来是「一张插图」，不是「一扇窗」。
   现在图通栏、**图注仍留在正文栏内**（图注跟着通栏就会贴屏幕左边缘，
   而全站正文在 x=144 —— 这条坑 `.fig-hero` 已经踩过一次，这里照抄它的解法）。
   `object-fit:cover` + `clamp` 高度：源图是 2:1，显示成 ~2.2:1，只裁掉一点点上下。 */
.fig{margin:56px 0;max-width:none;padding:0}
/* 🔴 2026-10-07：**暗色首屏 / 通栏 band 之后紧接通栏图时贴齐**（`margin-top` 归零）。
   否则两者之间露出一条 56px 的**页面底色**，看着像一条「色带」——
   用户 2026-10-07 原话「**排列图片之间有间距**」（截图里正是暗色 band 下面那条米色带）。
   实测影响面（全站 355 页）：`hero → fig` **205 处** · `band → fig` **10 处**。
   ⚠️ **只归零上边距**，且**只对这两种组合**：
     · 图与**正文**之间那 56px 留着（正文需要呼吸，那不是「图片之间」）；
     · 🔴 **`fig → fig` 的 56px 也【留着】** —— 用户同日对 `/applications` 两张相邻大图
       明确要「**拉开**」（那两页的图是**两件不相关的事**，贴在一起反而糊成一片）。
       ⇒ 「首页那条带要消失」和「两张图要拉开」是**两件不同的事**，用两条不同规则表达。 */
.module-hero + .fig,
.band + .fig{margin-top:0}
.fig img{display:block;width:100%;height:clamp(360px,46vw,660px);object-fit:cover;
  border:0;background:var(--c-subtle)}
/* 通栏头图：图通栏是有意的（视觉上是「打开一扇窗」）。 */
.fig-hero{max-width:none;padding:0;margin:0 0 32px}
.fig-hero img{border:0}
/* 并排图组（首页 4 张工艺示意图）—— 外层留白照抄 .fig，内层四列网格。
   两处的 `max-width` 必须同步，否则这一行会比上下每张图宽出一截。 */
.fig-row{margin:32px auto;max-width:var(--wrap);padding:0 24px}
.fig-row-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
.fig-cell{margin:0}
.fig-cell img{display:block;width:100%;height:auto;border:var(--rule) solid var(--c-edge);
  background:var(--c-subtle)}
/* 产品款式图墙（品类页 `Products` 底下那一面，2026-10-01）——
   外层留白**不用管**：它整个在 `.mod-in` 里，`max-width` 和 `padding` 已经和
   上下每一张 `.fig` 对齐（`.fig-row` 当初就是为这个另开了一层 padding）。
   列数**取三不取四**：商品图是 4:3，四列一排每张只剩 276px 宽，盘面上的花看不清；
   三列是 384px，接近手机全宽，够看清款式。 */
.pg-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.pg-cell{margin:0}
.pg-cell img{display:block;width:100%;height:auto;border:var(--rule) solid var(--c-edge);
  background:var(--c-subtle)}
/* 图墙上每格的**器型标签**（2026-10-01 加，用户答「1 出器型」）。
   🔴 刻意**不做成大写字母 + 宽字距**那种「字段感」的样式 ——
      用户打回首页时点名骂过「示例场景这些字段」。这里要读起来像
      一句产品说明，不像表单标签：正常大小写、字号贴近正文、颜色压到 muted。 */
.pg-form{margin-top:10px;font-size:13px;line-height:1.5;color:var(--c-muted);
  letter-spacing:.03em}
/* 页脚注：每页一条，印在**页脚细则**（`foot-bottom`）里、跟版权行同排。见 `IMG_NOTE`。
   🔴 2026-10-04 从「正文末尾、`</main>` 之前」搬进来（用户第二次报这件事，
      原话「Home第一张图都有一个，这不要」「每一个屏都有 不能要」）——
      原先它孤零零站在最后一屏、**紧贴上面那张图**，读起来就像「那张图的说明」。
   样式**跟着页脚走**，不再自带外边距/栏宽 —— 它现在是页脚细则的一员，不是正文里的一条横带。
   字号与对比度仍按次要正文规格：13px（`.foot-bottom` 给的）+ `--c-inverse-3`
   在玄墨页脚上 6.46:1，过 AA。 */
.img-note{margin:0}

/* ── 询盘表单（首页模块 10 与 `/contact` 模块 2 共用 `render_inquiry_form()`）──
   🔴 每个 `name` 都对应引擎 `fromForm()` 的一个读取键，**名字写错＝字段静默丢失**。
   对照表见 `docs/frontend/PAGES_FIELDS.md` §1.2。 */
.inq-form{margin:var(--gap-in) 0 0}
.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
.field{display:flex;flex-direction:column;gap:8px}
.field-full{grid-column:1/-1}
.field label{font-family:var(--f-ui);font-size:12px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--c-heading)}
.field input,.field select,.field textarea{width:100%;font-family:var(--f-body);font-size:16px;
  min-height:48px;padding:12px 16px;border:var(--rule) solid var(--c-edge);background:var(--c-card);
  color:var(--c-body);transition:border-color .2s ease,box-shadow .2s ease}
.field textarea{min-height:144px;resize:vertical;line-height:1.6}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--c-gold-deep)}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{
  outline:2px solid var(--c-gold-deep);outline-offset:2px;border-color:var(--c-gold-deep)}
/* 同意勾选：`required` 是**浏览器原生约束** —— 没勾选时提交事件根本不触发，
   不依赖 JS，也就不会因为 JS 挂了而失守（合规要求见 P4 11_合规与系统页.md）。
   勾选框 24×24 是 WCAG 2.5.8 的触控下限。 */
.check{display:flex;align-items:flex-start;gap:12px;font-size:15px;line-height:24px}
.check input{width:24px;height:24px;flex:0 0 auto;margin:0;accent-color:var(--c-seal)}
.check a{color:var(--c-gold-deep)}
/* 蜜罐：真人看不见、机器人会填。隐藏手法与引擎 `ensureTrap()` 一致 ——
   **不能用 `display:none`**：不少机器人会跳过 `display:none` 的字段，那样蜜罐就白设了。 */
.hp-trap{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none}
.inq-form .btn-row{margin:4px 0 0}
.form-note{font-size:14px;line-height:22px;color:var(--c-muted);margin:16px 0 0}
/* 表单状态条。引擎 `setStatus()` 会挂 `.lead-status sending|success|error` 三个类；
   原型阶段只有 `notice`（提示「没接后端」）。**四种都定好样式**，
   否则上线后成功/失败提示会是裸文字。 */
.lead-status{margin:16px 0 0;padding:12px 16px;border:var(--rule) solid var(--c-line);
  font-size:15px;line-height:24px}
.lead-status.notice{border-color:var(--c-proto-line);background:var(--c-proto-note);color:var(--c-proto-note-fg)}
.lead-status.success{border-color:var(--c-ok);color:var(--c-ok)}
.lead-status.error{border-color:var(--c-alert);color:var(--c-alert)}
.lead-status[hidden]{display:none}

/* ══ 版式 ⑤：询盘 CTA 带（含表单的模块）════════════════════════
   跟首屏同一套「深墨 + 双金线 + 内嵌框」，但矮一些 ——
   长页面翻到底部时，这一带是唯一的深色块，视觉上自然收束。 */
.module-cta{background:var(--c-ink);color:var(--c-inverse);
  padding:80px 0;position:relative;overflow:hidden}
/* 🔴 2026-10-01 删：CTA 带那条内嵌金框（`::before` inset:20px）。同上。 */
.module-cta h2,.module-cta h3{color:var(--c-inverse)}
.module-cta .mod-in{position:relative;max-width:960px}
.module-cta .lead,.module-cta p{color:var(--c-inverse-2)}
.module-cta a{color:var(--c-gold)}
.module-cta a:hover{color:var(--c-inverse)}
.module-cta .field label{color:var(--c-inverse-2)}
.module-cta .field input,.module-cta .field select,.module-cta .field textarea{
  background:var(--c-inverse-a12);border-color:var(--c-inverse-a22);color:var(--c-inverse)}
.module-cta .field input:hover,.module-cta .field select:hover,.module-cta .field textarea:hover{
  border-color:var(--c-gold)}
.module-cta .field input:focus-visible,.module-cta .field select:focus-visible,
.module-cta .field textarea:focus-visible{outline-color:var(--c-gold);border-color:var(--c-gold)}
.module-cta .field input::placeholder,.module-cta .field textarea::placeholder{color:var(--c-inverse-3)}
.module-cta .check{color:var(--c-inverse-2)}
.module-cta .check input{accent-color:var(--c-gold)}
.module-cta .form-note{color:var(--c-inverse-3)}
/* 深底上 `<select>` 的下拉箭头是系统画的，会跟着 `color` 走；这里给它一个可见的底色 */
.module-cta .field select option{background:var(--c-ink);color:var(--c-inverse)}

/* ── 站内搜索 `/search` ───────────────────────────────
   表单是原生 GET 表单：**没 JS 也能把查询提交出去**（地址栏变成 `/search?s=…`），
   只有「把结果画出来」这一步需要 JS —— 降级路径因此是断的但看得见。 */
.search-form{margin:var(--gap-in) 0 0;display:flex;flex-wrap:wrap;gap:16px}
.search-form input{flex:1 1 240px;min-width:0;font-family:var(--f-body);font-size:16px;
  min-height:48px;padding:12px 16px;border:var(--rule) solid var(--c-edge);background:var(--c-card);
  color:var(--c-body);transition:border-color .2s ease,box-shadow .2s ease}
.search-form input:hover{border-color:var(--c-gold-deep)}
.search-form input:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px;
  border-color:var(--c-gold-deep)}
.search-results{margin:0}
.search-empty{font-size:18px;line-height:1.7;color:var(--c-body)}
.search-empty[hidden],.search-results .card-grid[hidden]{display:none}
.search-noscript{font-size:16px;line-height:1.7;color:var(--c-body)}
.search-noscript a{color:var(--c-gold-deep)}
.search-count{font-family:var(--f-ui);font-size:12px;letter-spacing:.08em;color:var(--c-muted);
  margin:0 0 4px}

.link-list{list-style:none;padding:0;margin:0}

/* ── 列表块：编号步骤（「合作流程」5 步）／圆点要点（痛点 4 条）──────
   新版把「编号步骤」做成**卷次**：每一步前面一个朱砂小方块里的序号，
   左边一条竖线串起来 —— 像目录里的一串条目。 */
.step-list{margin:var(--gap-in) 0 0;padding:0;list-style:none;display:grid;gap:16px;
  counter-reset:step}
.step-list li{padding-left:56px;position:relative;line-height:1.7;min-height:32px}
.step-list li::before{counter-increment:step;content:counter(step,decimal-leading-zero);
  position:absolute;left:0;top:0;width:32px;height:32px;display:grid;place-items:center;
  background:var(--c-seal);color:var(--c-inverse);
  font-family:var(--f-ui);font-size:11px;font-weight:600;letter-spacing:.02em}
/* 竖线：把一串步骤串成「一卷」。最后一节不画线。 */
.step-list li::after{content:"";position:absolute;left:15px;top:36px;bottom:-16px;width:1px;
  background:var(--c-line-strong)}
.step-list li:last-child::after{display:none}
/* 圆点要点：与编号步骤同款间距，区别只在圆点由 <ul> 画、不是数字。 */
.bullet-list{margin:var(--gap-in) 0 0;padding:0;list-style:none;display:grid;gap:12px}
.bullet-list li{padding-left:24px;position:relative;line-height:1.7}
.bullet-list li::before{content:"";position:absolute;left:4px;top:11px;width:7px;height:7px;
  background:var(--c-gold);transform:rotate(45deg)}
.link-list li{border-bottom:var(--rule) solid var(--c-line)}
.link-list a{display:flex;align-items:center;min-height:var(--ctrl);padding:0;
  transition:padding-left .2s ease,color .2s ease}
.link-list a:hover{padding-left:8px}

/* ══ 编号清单（2026-10-05 画廊化：序号三列表的替身）══════════════════
   大衬线编号 + 标题 + 描述，行间细线 —— 编辑/画廊式排版。
   序号是阅读顺序不是字段，不占一「列」、不被 32% 宽度挤歪。 */
.idx-list{margin:var(--gap-in) 0 0;padding:0;list-style:none}
.idx-list li{display:grid;grid-template-columns:88px minmax(0,1fr);gap:0 32px;
  padding:26px 0;border-bottom:var(--rule) solid var(--c-line)}
.idx-list li:first-child{border-top:var(--rule) solid var(--c-ink)}
.idx-list li:last-child{border-bottom:var(--rule) solid var(--c-ink)}
.idx-n{font-family:var(--f-heading);font-size:40px;font-weight:600;line-height:1.05;
  color:var(--c-gold-deep)}
.idx-t{margin:0 0 4px;font-size:21px;line-height:1.3}
.idx-d{margin:0;max-width:640px}
@media (max-width:640px){
  .idx-list li{grid-template-columns:52px minmax(0,1fr);gap:0 18px;padding:20px 0}
  .idx-n{font-size:28px}
}

/* ── sitemap.html ─────────────────────────────────── */
.sm-list{list-style:none;padding:0;margin:0;columns:2;column-gap:48px}
.sm-list li{border-bottom:var(--rule) solid var(--c-line);padding:12px 0;break-inside:avoid}
.sm-list a{display:inline-flex;align-items:center;min-height:var(--touch);font-size:16px}
.sm-path{display:block;font-family:var(--f-ui);font-size:12px;color:var(--c-muted);
  letter-spacing:.02em;margin-top:4px}

/* ── 缺料占位（原样显示，不改字）──────────────────── */
.tbd{border-bottom:1px dashed var(--c-line-strong);color:var(--c-muted)}

/* ── 博客 /blog 与文章页 /blog/<slug> ─────────────────────────────
   2026-10-03 加。文章正文是**长文**，排版要按阅读来调：
   行宽收窄、行高放大、段距比全站默认大一档。
   🔴 **不覆写 `body` 的字体** —— 全站字体走 `--f-*` 变量（风格 B 定的），
      文章页只是**同一套字体换个更舒服的尺度**。 */
.art-cards{margin-top:8px}
.art-card-grid{list-style:none;padding:0;margin:0 0 40px;
  display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:20px}
.art-card{display:flex;flex-direction:column;gap:8px;height:100%;
  padding:24px 22px;border:var(--rule) solid var(--c-line);border-radius:2px;
  background:var(--c-page);transition:border-color .2s ease,transform .2s ease}
.art-card:hover{border-color:var(--c-gold);transform:translateY(-2px)}
.art-card-t{font-family:var(--f-display);font-size:19px;line-height:1.35;
  font-weight:600;color:var(--c-ink)}
.art-card-d{font-size:14px;line-height:1.65;color:var(--c-muted)}
.art-hero{text-align:center}
.art-hero h1{max-width:22ch;margin:0 auto}
.art-hero-sec{margin-top:16px;font-family:var(--f-ui);font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--c-gold)}
.art-body{max-width:720px;margin:0 auto}
.art-body p{margin:0 0 1.25em;line-height:1.85;font-size:18px;color:var(--c-body)}
.art-body p.lead{font-size:20px;line-height:1.75;color:var(--c-body)}
.art-h2{margin:2.2em 0 .8em;font-size:27px;line-height:1.3}
.art-h3{margin:1.8em 0 .6em;font-size:21px;line-height:1.35}
.art-quote{margin:32px 0;padding:0 0 0 24px;border-left:3px solid var(--c-gold);
  color:var(--c-muted);font-style:italic}
.art-quote p{margin:0 0 .6em;font-size:17px;line-height:1.75}
.art-quote p:last-child{margin-bottom:0}
.art-body .tbl-wrap{margin:32px 0}
.art-body .step-list,.art-body .bullet-list{margin:24px 0 32px}
.art-back{margin-top:0}
.art-back-a{display:inline-flex;align-items:center;min-height:var(--touch);
  font-family:var(--f-ui);font-size:13px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--c-gold);border-bottom:var(--rule) solid var(--c-gold)}
.art-back-a:hover{color:var(--c-ink);border-color:var(--c-ink)}

/* ══ 页脚：玄墨底 + 双金线压顶 + 中轴收尾 ═══════════════════════
   旧版是「藏青底 + 四列链接」，标准企业站页脚。
   新版：玄墨底、金色小标题、列间一道竖发丝线，
   最底下中轴对齐放协会徽记 + 版权 —— 像书的版权页。 */
.site-footer{background:var(--c-ink);color:var(--c-inverse-2);
  margin-top:0;padding:72px 0 32px;position:relative}
.foot-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0 32px}
.fcol{padding:0 0 0 24px;border-left:var(--rule) solid var(--c-inverse-a12)}
.fcol:first-child{border-left:0;padding-left:0}
.fcol h4{color:var(--c-gold);font-family:var(--f-ui);font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;font-weight:600;margin-bottom:16px}
.fcol ul{list-style:none;padding:0;margin:0}
.fcol li{margin-bottom:4px}
.fcol a{display:inline-flex;align-items:center;min-height:var(--touch);
  color:var(--c-inverse-2);font-size:16px;transition:color .2s ease,padding-left .2s ease}
.fcol a:hover{color:var(--c-gold);padding-left:4px}
.foot-bottom{margin-top:56px;padding-top:24px;border-top:var(--rule) solid var(--c-inverse-a12);
  display:flex;justify-content:center;flex-wrap:wrap;gap:8px 32px;font-size:13px;
  color:var(--c-inverse-3);text-align:center}
.foot-bottom p{margin:0}
.foot-assoc{margin-top:12px;font-size:13px;color:var(--c-inverse-3);text-align:center}
.foot-assoc p{margin:0}
/* 协会徽记。🔴 徽记本身是**藏青底盘**，而页脚底色是**玄墨**——
   直接放会「隐形」：底盘融进底色，只剩三段弧和小花。
   所以垫一层浅色圆底把它托出来，读作一枚徽章。**徽记文件本身一个字没改**，
   这层底只是容器 —— 相当于给 logo 一块浅色卡纸。
   🔴 派生色用 `var(--c-page)` 而不是写死色（`DESIGN_SYSTEM.md` §4.4）。
   🔴 `box-sizing:content-box` + `max-width:none` **不是可选项，是必须的** ——
      全站 reset 里有 `*{box-sizing:border-box}` 和 `img{max-width:100%;height:auto}`：
        · border-box 会把 3px padding 算进 48px 里 → 图形只剩 42px（低于 48 会糊）；
        · `max-width:100%` 是**按父元素宽度**算的 —— 徽记在页脚 `<p>` 里，
          父宽一旦收窄，图就被压扁甚至压成 0 宽。
      这两条都是**静默**的：属性写着 48，实际渲染多少要看父容器脸色。 */
.assoc-logo{display:inline-block;vertical-align:middle;margin-left:8px;padding:3px;
  background:var(--c-page);border-radius:50%;
  box-sizing:content-box;max-width:none}

/* ── 降级：前庭功能障碍用户（WCAG 2.3.3）──────────── */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none !important;transition:none !important}
}

/* ══ 断点 ══════════════════════════════════════════════════════
   1280 是桌面基准，768 / 360 是 M7 要查的两档。
   🔴 1180 这条是「页头能不能一行放下」的分界（2026-09-29 实测定的）。
      方案 B 把品牌名换成 Cormorant 27px、导航 gap 16→14、页头 gap 24→20，
      行宽需求变了，所以这条断点**必须在新样式生成后回浏览器实测复核**，
      不能沿用旧算式。 */
/* 🔴 2026-10-07（方案 A）：**769–1199px 退回两行** —— 品牌 + 报价 在上，导航整行在下。
   单行需要 1142px、`.wrap` 上限只给 1152px（余量 10px），
   窄一点就放不下 ⇒ 这一档退回两行，**导航与语言仍然同排**（语言下拉在右）。 */
/* ⚠️ 2026-10-08：断点 1199 → **1220**。单行要放下最宽的德文（1166px）+ 左右内边距 48 = **1214px**，
   所以 1220 以下退回两行。 */
@media (max-width:1219px){
  .header-inner{flex-wrap:wrap}
  nav.menu{flex:1 1 100%;order:3}
  .header-cta{order:2;margin-left:auto}
}
@media (max-width:1180px){
  .module-prose .mod-in{grid-template-columns:minmax(0,200px) minmax(0,1fr);gap:0 40px}
}
@media (max-width:1024px){
  :root{--gap:64px;--gap-in:28px}
  .foot-grid{grid-template-columns:repeat(2,1fr);gap:32px}
  .fcol{padding-left:0;border-left:0}
  .card-grid{grid-template-columns:repeat(3,1fr)}
  .module-prose .mod-in{grid-template-columns:1fr;gap:0}
  .module-prose .sec-head{flex-direction:row;align-items:center;position:static;margin:0 0 var(--gap-in)}
}
@media (max-width:768px){
  :root{--fs-h1:42px;--lh-h1:50px;--fs-h2:29px;--lh-h2:40px;--fs-h3:21px;--lh-h3:30px;
        --gap:52px;--gap-in:24px}
  .module-hero{padding:64px 0 56px}
  .module-cta{padding:56px 0}
  /* 🔴 2026-10-01：原本这里覆盖 hero/CTA 内嵌金框的 inset；框已删，覆盖规则一并删。 */
  /* ── 移动端导航：汉堡抽屉（2026-09-29 做，用户拍板「做」）────────────
     🔴 **这一档把导航整条收进抽屉，所以断点这里必须让 `.burger` 出现** ——
     上面基础样式里它是 `display:none`。
     🔴 为什么 NAV-1 / NAV-6 不会被抽屉破坏：`nav_qa.py` 用 `html.parser`
     直接读服务器返回的原始 HTML、**不渲染 CSS、不跑 JS**，抽屉只是给同一个
     `<ul class="nav">` 加了个 class，`<a>` 一个没少（已读源码确认）。
     🔴 为什么 `ui_qa.py` 不会被破坏：它遍历可点元素时先跳过
     `width>0 && height>0` 不成立的元素，抽屉关着时导航链接是 0 尺寸 → 直接跳过。 */
  .burger{display:inline-flex;order:2;margin-left:auto}
  /* 🔴 2026-10-07 修「手机端全站横向溢出」——
     五语扩展（§119）把语言切换器从 `EN|日本語` 撑成 `EN|日本語|Français|Deutsch|Nederlands`，
     切换器 311px + 报价按钮 165px + 间距 = `.header-cta` 宽 493px；
     而 360px 屏只有 320px 可用。`.header-cta` 基础样式是 `flex:none`、
     子项又都是 `white-space:nowrap`，**既不换行也不收缩** ⇒ 每页都被它撑出横向滚动条。
     实测：360px 下 10/10 抽样页溢出（scrollWidth 513 > clientWidth 360），
     门禁 `ui_qa` 在 2026-10-07 的干净复跑里报 FAIL（[mobile] 横向溢出）。
     改法：① 让 CTA 行独占一整行（`flex-basis:100%`）；
           ② 允许它内部换行（切换器一行、按钮一行）；
           ③ 切换器自己也允许换行 —— 320px 小屏（可用 280px）也不会撑破。
     ⚠️ 纯 CSS 修，**不动 HTML 结构** —— 切换器/按钮的 `<a>` 一个没少，导航闸与无障碍不受影响。 */
  .header-cta{order:3;flex:1 1 100%;flex-wrap:wrap;justify-content:flex-end;gap:8px 16px}
  nav.menu{order:4;flex:1 1 100%;display:none}
  html.nav-open nav.menu{display:block}
  html.nav-open .nav{flex-direction:column;gap:0;padding-top:8px}
  html.nav-open .nav>li{border-top:var(--rule) solid var(--c-line)}
  html.nav-open .nav a{width:100%;min-height:var(--ctrl);font-size:13px}
  /* 抽屉里的下拉**展开平铺**，不再靠悬停 —— 触屏没有 hover */
  html.nav-open .nav .sub{display:block;position:static;left:auto;top:auto;min-width:0;
    border:0;box-shadow:none;padding:0 0 12px 16px;background:transparent}
  html.nav-open .nav .sub .sub-head{padding-left:0;border-top:0;margin-top:0}
  .fig-row-grid{grid-template-columns:repeat(2,1fr)}
  .pg-grid{grid-template-columns:repeat(2,1fr)}
  .card-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .foot-bottom{flex-direction:column;align-items:center}
}
@media (max-width:480px){
  :root{--fs-h1:34px;--lh-h1:42px;--fs-h2:25px;--lh-h2:34px;--fs-h3:19px;--lh-h3:28px;
        --gap:44px;--gap-in:20px}
  .wrap{padding:0 20px}
  .header-inner{padding:12px 20px}
  .mod-in{padding:0 20px}
  .fig,.fig-row{padding:0 20px}
  .sm-list{columns:1}
  .card-grid{grid-template-columns:minmax(0,1fr)}
  .pg-grid{grid-template-columns:1fr}
  .form-grid{grid-template-columns:1fr}
  .sec-head{gap:12px}
}
/* 🔴 2026-10-07：**320px 档**（**不在规格断点内**，但用户 2026-10-07 拍板「**管**」）。
   实测两个溢出源，都**只在 320px 出现、360px 是干净的**：
     ① **页脚两列放不下德文长词**（`Datenschutzerklärung` / `Nutzungsbedingungen`）——
        德文 **71 页全部**横向溢出（`scrollWidth` 354 > 320）；英/日/荷没事（词短、能断行）。
     ② **品牌名 27px 太宽**：品牌 193 + 间距 16 + 汉堡 99 = 308 > 可用 280
        ⇒ 汉堡被挤到下一行、页头 225px 高。
   ⇒ 这一档：**页脚单列 + 品牌缩到 22px**。
   ⚠️ 阈值取 360（含）—— 360px 那档德文页脚本来只剩 3px 余量（141px 词 vs 144px 列），
      字体一换就会破，**单列更稳**。 */
@media (max-width:360px){
  .foot-grid{grid-template-columns:1fr}
  .brand{font-size:22px}
}

/* ══════════════════════════════════════════════════════════════════════
   商品详情页（PDP）
   结构照「存储独立站项目站」的商品页 —— 多图 / 放大 / 规格 / 详情四样都在；
   视觉**全部按本站方案 B「墨韵金笺」重做**，不是换个色号了事：
   直角、发丝线、宣纸底、金线分隔、表头走 --f-ui。
   🔴 颜色**只准 `var()` 或 `color-mix()`**（`verify_design_tokens.py` 零例外）。
   🔴 margin / padding / gap **一律 4 的倍数**（同一条闸门查）。
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1. 首屏：左图库 + 右信息栏 ─────────────────────────────────── */
.pdp-hero{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,.98fr);
  gap:var(--gap-in);align-items:start}
.pdp-gallery{display:grid;grid-template-columns:72px minmax(0,1fr);gap:16px}
.pdp-thumbs{display:flex;flex-direction:column;gap:8px}
.pdp-thumb{display:block;width:72px;height:72px;padding:0;
  border:var(--rule) solid var(--c-line);background:var(--c-card);
  cursor:pointer;overflow:hidden;
  transition:border-color .18s ease,box-shadow .18s ease,background-color .18s ease}
.pdp-thumb img{display:block;width:100%;height:100%;object-fit:cover}
.pdp-thumb:hover{border-color:var(--c-edge)}
.pdp-thumb:active{background:var(--c-subtle)}
.pdp-thumb.on{border-color:var(--c-gold-deep);box-shadow:0 0 0 2px var(--c-gold-a40)}
.pdp-thumb:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-stage{position:relative}
/* 主图是 `<button>` 不是 `<div>` —— 键盘要能按回车开灯箱 */
.pdp-main{display:block;width:100%;padding:0;aspect-ratio:1/1;overflow:hidden;
  border:var(--rule) solid var(--c-line);background:var(--c-card);cursor:zoom-in;
  transition:border-color .2s ease}
.pdp-main img{display:block;width:100%;height:100%;object-fit:contain;
  transform-origin:var(--mx,50%) var(--my,50%);transition:transform .35s ease}
/* 悬停放大：JS 只把鼠标位置写进 --mx/--my，缩放本身是纯 CSS */
.pdp-main:hover img{transform:scale(1.6)}
.pdp-main:active{border-color:var(--c-edge)}
.pdp-main:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-zoom-hint{margin:8px 0 0;font-family:var(--f-ui);font-size:16px;
  line-height:24px;color:var(--c-muted)}
.pdp-info h1{margin:0 0 8px}
.pdp-sub{display:inline-block;margin:0 0 8px;font-family:var(--f-ui);font-size:16px;
  line-height:var(--touch);color:var(--c-gold-deep);
  border-bottom:var(--rule) solid var(--c-gold-a40)}
.pdp-sub:hover{color:var(--c-seal);border-bottom-color:var(--c-seal)}
.pdp-sub:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-meta{margin:0 0 16px;font-family:var(--f-ui);font-size:16px;line-height:24px;
  color:var(--c-muted)}
.pdp-meta span{color:var(--c-body)}
/* 规格摘要：**有几行画几行**，缺的整行不出现（F2）—— 所以不用表格，
   用「标签 + 值」两列，行数随数据走，不会留空格子 */
.pdp-summary{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:0 var(--gap-in);margin:0 0 16px;
  border-top:var(--rule) solid var(--c-line)}
.pdp-kv{display:flex;flex-direction:column;gap:0;padding:12px 0;
  border-bottom:var(--rule) solid var(--c-line)}
.pdp-kv b{font-family:var(--f-ui);font-size:16px;line-height:24px;font-weight:500;
  color:var(--c-muted)}
.pdp-kv span{font-size:16px;line-height:24px;color:var(--c-body)}
.pdp-price{margin:0 0 24px;padding:16px;background:var(--c-subtle);
  border-left:3px solid var(--c-gold);font-size:16px;line-height:26px;
  color:var(--c-body)}
.pdp-cta{display:flex;flex-wrap:wrap;gap:12px;margin:0}

/* ── 2. 采购定位 + 应用场景芯片 ─────────────────────────────────── */
.module-pdp-lead .mod-in{max-width:900px}
.pdp-lead{margin:0 0 20px;font-size:var(--fs-body);line-height:var(--lh-body)}
.pdp-chips{display:flex;flex-wrap:wrap;gap:8px}
.pdp-chip{display:inline-flex;align-items:center;min-height:var(--touch);
  padding:4px 12px;font-family:var(--f-ui);font-size:16px;line-height:24px;
  color:var(--c-gold-deep);border:var(--rule) solid var(--c-gold-a40);
  background:var(--c-gold-a08);
  transition:background-color .18s ease,border-color .18s ease,color .18s ease}
.pdp-chip:hover{background:var(--c-gold-a24);border-color:var(--c-gold-deep);
  color:var(--c-seal)}
.pdp-chip:active{background:var(--c-gold-a40)}
.pdp-chip:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}

/* ── 3. 核心卖点（图文并排）─────────────────────────────────────── */
.pdp-feats{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap-in)}
.pdp-feats li{padding:0 0 0 20px;border-left:2px solid var(--c-gold-a40)}
.pdp-feats h3{margin:0 0 4px;font-size:var(--fs-h3);line-height:var(--lh-h3)}
.pdp-feats p{margin:0;font-size:var(--fs-body);line-height:var(--lh-body);
  color:var(--c-body)}
.pdp-feat-fig{margin:0 0 var(--gap-in)}
.pdp-feat-fig img{display:block;width:100%;height:auto;
  border:var(--rule) solid var(--c-line);background:var(--c-card)}

/* ── 4. 四块折叠（原生 `<details>`，没 JS 也能开合）─────────────── */
.pdp-accs{border-top:var(--rule) solid var(--c-line)}
.pdp-acc{border-bottom:var(--rule) solid var(--c-line)}
.pdp-acc>summary{display:flex;align-items:center;gap:12px;min-height:var(--ctrl);
  padding:12px 0;cursor:pointer;list-style:none;
  font-family:var(--f-ui);font-size:17px;line-height:26px;font-weight:600;
  color:var(--c-heading);
  transition:color .18s ease}
.pdp-acc>summary::-webkit-details-marker{display:none}
/* 加号/减号：**纯 CSS 画**，不引图标字体、不叠图片 */
.pdp-acc>summary::before{content:"";flex:0 0 16px;width:16px;height:16px;
  background:
    linear-gradient(var(--c-gold-deep),var(--c-gold-deep)) center/16px 2px no-repeat,
    linear-gradient(var(--c-gold-deep),var(--c-gold-deep)) center/2px 16px no-repeat;
  transition:transform .22s ease}
.pdp-acc[open]>summary::before{transform:rotate(90deg)}
.pdp-acc>summary:hover{color:var(--c-gold-deep)}
.pdp-acc>summary:active{color:var(--c-seal)}
.pdp-acc>summary:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-acc-in{padding:0 0 24px;font-size:var(--fs-body);line-height:var(--lh-body);
  color:var(--c-body)}
.pdp-acc-in h3{margin:16px 0 4px;font-size:var(--fs-h3);line-height:var(--lh-h3);
  color:var(--c-heading)}
.pdp-acc-in h3:first-child{margin-top:0}
.pdp-acc-in p{margin:0 0 12px}
.pdp-acc-in p:last-child{margin-bottom:0}
/* 规格表：表头窄列，照参照物的 32% 口径 */
.pdp-spec{width:100%;border-collapse:collapse;margin:0 0 8px;
  font-size:16px;line-height:24px}
.pdp-spec th,.pdp-spec td{padding:12px 16px;text-align:left;
  border-bottom:var(--rule) solid var(--c-line);vertical-align:top}
.pdp-spec th{width:32%;font-family:var(--f-ui);font-weight:500;font-size:16px;
  color:var(--c-muted);background:var(--c-gold-a08)}
.pdp-spec td{color:var(--c-body)}
.pdp-support{list-style:none;margin:0;padding:0}
.pdp-support li{padding:8px 0 8px 20px;border-left:2px solid var(--c-gold-a40);
  margin:0 0 8px}
.pdp-support b{display:block;font-family:var(--f-ui);font-size:16px;line-height:24px;
  color:var(--c-heading)}
.pdp-support li:last-child{margin-bottom:0}
/* 包装图（挂在「Warranty & Support」块里）—— 图比文字窄一档，别抢主图的戏 */
.pdp-pack-fig{margin:20px 0 0;max-width:480px}
.pdp-pack-fig img{display:block;width:100%;height:auto;
  border:var(--rule) solid var(--c-line);background:var(--c-card)}

/* ── 5. 变体矩阵 ────────────────────────────────────────────────── */
.pdp-vars{display:flex;flex-wrap:wrap;gap:8px}
.pdp-var-chip{min-height:var(--ctrl);padding:8px 16px;
  font-family:var(--f-ui);font-size:16px;color:var(--c-body);
  border:var(--rule) solid var(--c-line-strong);background:var(--c-card);
  cursor:pointer;
  transition:border-color .18s ease,background-color .18s ease,color .18s ease}
.pdp-var-chip:hover{border-color:var(--c-gold-deep);color:var(--c-gold-deep)}
.pdp-var-chip:active{background:var(--c-subtle)}
.pdp-var-chip[aria-pressed="true"]{border-color:var(--c-gold-deep);
  background:var(--c-gold-a24);color:var(--c-ink)}
.pdp-var-chip:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}

/* ── 6. 应用场景 / 资料 / 相关产品 ──────────────────────────────── */
.pdp-h2{margin:0 0 20px;font-size:var(--fs-h2);line-height:var(--lh-h2)}
.pdp-app-grid{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
/* 场景图：横构图，通栏压在场景卡上方 —— 用户要的「大图」在这儿 */
.pdp-app-fig{margin:0 0 24px;position:relative;overflow:hidden}
.pdp-app-fig img{display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  border:var(--rule) solid var(--c-line);background:var(--c-card)}

/* ── 品类页「产品网格」卡片（后台商品数据驱动，见「三·六」）───────── */
.pg-card-grid{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));gap:24px}
.pg-card{display:flex;flex-direction:column;height:100%;
  border:var(--rule) solid var(--c-line);background:var(--c-card);
  transition:border-color .18s ease,background-color .18s ease}
.pg-card:hover{border-color:var(--c-gold-deep);background:var(--c-gold-a08)}
.pg-card:active{background:var(--c-subtle)}
.pg-card:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pg-card-fig{display:block;position:relative;overflow:hidden}
.pg-card-fig img{display:block;width:100%;height:auto;aspect-ratio:4/3;
  object-fit:cover;transition:transform .32s ease}
.pg-card:hover .pg-card-fig img{transform:scale(1.04)}
/* 后台没配主图时的降级底 —— 空框，不放占位图、不写「待确认」 */
.pg-card-noimg{display:block;width:100%;aspect-ratio:4/3;
  background:var(--c-gold-a08)}
.pg-card-bd{display:flex;flex-direction:column;gap:8px;padding:20px}
.pg-card-t{font-family:var(--f-ui);font-size:17px;line-height:26px;font-weight:600;
  color:var(--c-heading)}
.pg-card-kind{font-family:var(--f-ui);font-size:16px;line-height:24px;
  color:var(--c-muted)}
.pdp-app{display:flex;flex-direction:column;gap:4px;min-height:var(--ctrl);
  padding:20px;border:var(--rule) solid var(--c-line);background:var(--c-card);
  transition:border-color .18s ease,background-color .18s ease}
.pdp-app b{font-family:var(--f-ui);font-size:17px;line-height:26px;font-weight:600;
  color:var(--c-heading)}
.pdp-app span{font-size:16px;line-height:24px;color:var(--c-muted)}
.pdp-app:hover{border-color:var(--c-gold-deep);background:var(--c-gold-a08)}
.pdp-app:active{background:var(--c-gold-a12)}
.pdp-app:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-doc-list{list-style:none;margin:0 0 20px;padding:0}
.pdp-doc-list li{padding:8px 0;border-bottom:var(--rule) solid var(--c-line)}
.pdp-doc-list a{display:inline-flex;align-items:center;min-height:var(--touch);
  font-size:16px;color:var(--c-gold-deep);
  border-bottom:var(--rule) solid var(--c-gold-a40)}
.pdp-doc-list a:hover{color:var(--c-seal);border-bottom-color:var(--c-seal)}
.pdp-doc-list a:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-doc-note{margin:0 0 20px;font-size:16px;line-height:26px;color:var(--c-muted)}
.pdp-rel-grid{list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
.pdp-rel{display:flex;flex-direction:column;gap:8px;min-height:var(--ctrl);
  border:var(--rule) solid var(--c-line);background:var(--c-card);
  transition:border-color .18s ease,background-color .18s ease}
.pdp-rel-fig{display:block;aspect-ratio:4/3;overflow:hidden;
  border-bottom:var(--rule) solid var(--c-line);background:var(--c-subtle)}
.pdp-rel-fig img{display:block;width:100%;height:100%;object-fit:cover;
  transition:transform .35s ease}
.pdp-rel b{padding:0 16px 16px;font-family:var(--f-ui);font-size:16px;line-height:24px;
  font-weight:500;color:var(--c-heading)}
.pdp-rel:hover{border-color:var(--c-gold-deep);background:var(--c-gold-a08)}
.pdp-rel:hover .pdp-rel-fig img{transform:scale(1.04)}
.pdp-rel:active{background:var(--c-gold-a12)}
.pdp-rel:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}

/* ── 6.5 契约补齐的新块（2026-10-02，B 项）────────────────────────
   这几块对应 `data/product_field_bindings.json` 里此前没落地的绑定：
   视频(3) · 多张详情图(4) · 多张场景图(5) · 多张包装图(6) · 质检三组(7) ·
   MOQ(15) · 变体选择器(19/20) · 折叠块里的插图(24) · 门控资料(25)
   🔴 **所有 margin/padding/gap 都是 4px 的整数倍**（`visual_richness_check.py`
      的「间距节奏」项按 4px 栅格算）。 */

/* 视频缩略图：跟主图缩略图同尺寸，叠一个播放三角 */
.pdp-thumb-v{position:relative}
.pdp-play{position:absolute;inset:0;margin:auto;width:24px;height:24px;
  background:var(--c-ink-a12);border:var(--rule) solid var(--c-card);
  border-radius:50%;transition:background-color .18s ease}
.pdp-play::after{content:"";position:absolute;top:50%;left:50%;
  width:0;height:0;margin:-5px 0 0 -3px;
  border-left:8px solid var(--c-card);border-top:5px solid transparent;
  border-bottom:5px solid transparent}
.pdp-thumb-v:hover .pdp-play{background:var(--c-gold-deep)}
/* 舞台里的 `<video>`：跟主图同一个方框，切到视频时把主图按钮藏起来 */
.pdp-video{display:block;width:100%;aspect-ratio:1/1;
  border:var(--rule) solid var(--c-line);background:var(--c-ink)}
.pdp-video[hidden]{display:none}
.pdp-stage.video-on .pdp-main{display:none}
.pdp-stage.video-on .pdp-zoom-hint{visibility:hidden}

/* 非链接标签芯片（后台打了我们认不出的 tag）—— 同款外观，但不可点 */
.pdp-chip-plain{color:var(--c-muted);border-color:var(--c-line);
  background:var(--c-subtle)}
.pdp-chip-plain:hover{background:var(--c-subtle);border-color:var(--c-line);
  color:var(--c-muted)}

/* 多图组（详情图 / 场景图 / 包装图 / 质检图共用一套网格）*/
.pdp-feat-fig-grid,.pdp-app-fig-grid,.pdp-pack-fig-grid,.pdp-q-fig-grid,
.pdp-sec-figs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:16px;margin:0 0 24px}
.pdp-pack-fig-grid{max-width:960px}
.pdp-q-fig-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
.pdp-feat-fig,.pdp-app-fig,.pdp-pack-fig,.pdp-q-fig,.pdp-sec-fig{margin:0}
.pdp-feat-fig img,.pdp-pack-fig img,.pdp-q-fig img,.pdp-sec-fig img{
  display:block;width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;
  border:var(--rule) solid var(--c-line);background:var(--c-subtle)}
.pdp-app-fig img{display:block;width:100%;height:auto;aspect-ratio:3/2;
  object-fit:cover;border:var(--rule) solid var(--c-line);background:var(--c-subtle)}

/* 质检 / 证书 / 安装（契约 binding 7）：三组各自独立显隐 */
.pdp-q-block{margin:0 0 var(--gap-in)}
.pdp-q-block:last-child{margin-bottom:0}
.pdp-q-h{font-family:var(--f-ui);font-size:17px;line-height:26px;font-weight:500;
  color:var(--c-muted);margin:0 0 16px}

/* MOQ 行（契约 binding 15）—— 跟 Model 行同款，值原样印（平台是字符串）*/
.pdp-moq span{font-variant-numeric:tabular-nums}

/* 变体选择器（契约 binding 19/20）—— 每组一行「组名 + 选项」 */
.pdp-var-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 16px;
  padding:8px 0}
.pdp-var-lab{flex:0 0 auto;font-family:var(--f-ui);font-size:16px;line-height:24px;
  font-weight:500;color:var(--c-muted);min-width:96px}
.pdp-var-opts{display:flex;flex-wrap:wrap;gap:8px}
.pdp-var-chip[disabled]{opacity:.45;cursor:not-allowed;text-decoration:line-through}
.pdp-var-chip[disabled]:hover{border-color:var(--c-line);color:var(--c-body)}
.pdp-var-chip.on{border-color:var(--c-gold-deep);background:var(--c-gold-a24);
  color:var(--c-seal)}
.pdp-varspec-wrap{margin:24px 0 0}

/* 门控资料（契约 binding 25）—— 出标题 + 「找我们要」，**不出文件地址** */
.pdp-doc-list li{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.pdp-doc-gated{font-size:16px;line-height:24px;color:var(--c-body)}
.pdp-doc-hint{font-family:var(--f-ui);font-size:16px;line-height:24px;
  color:var(--c-muted)}

/* ── 7. 问答（复用折叠样式，但更紧凑）──────────────────────────── */
.pdp-faqs{border-top:var(--rule) solid var(--c-line)}
.pdp-q{border-bottom:var(--rule) solid var(--c-line)}
.pdp-q>summary{display:flex;align-items:center;gap:12px;min-height:var(--ctrl);
  padding:12px 0;cursor:pointer;list-style:none;
  font-family:var(--f-ui);font-size:17px;line-height:26px;font-weight:500;
  color:var(--c-heading);transition:color .18s ease}
.pdp-q>summary::-webkit-details-marker{display:none}
.pdp-q>summary::before{content:"";flex:0 0 12px;width:12px;height:12px;
  background:
    linear-gradient(var(--c-gold-deep),var(--c-gold-deep)) center/12px 2px no-repeat,
    linear-gradient(var(--c-gold-deep),var(--c-gold-deep)) center/2px 12px no-repeat;
  transition:transform .22s ease}
.pdp-q[open]>summary::before{transform:rotate(90deg)}
.pdp-q>summary:hover{color:var(--c-gold-deep)}
.pdp-q>summary:active{color:var(--c-seal)}
.pdp-q>summary:focus-visible{outline:2px solid var(--c-gold-deep);outline-offset:2px}
.pdp-q .pdp-acc-in{padding-bottom:20px}

/* ── 8. 灯箱 ────────────────────────────────────────────────────── */
.pdp-lightbox{position:fixed;inset:0;z-index:120;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:16px;padding:24px;background:color-mix(in srgb,var(--c-ink) 92%,transparent)}
.pdp-lightbox[hidden]{display:none}
.pdp-lb-row{display:flex;align-items:center;gap:12px;max-width:100%}
.pdp-lb-stage{display:flex;align-items:center;justify-content:center;min-width:0}
.pdp-lb-row img{display:block;max-width:min(94vw,1120px);max-height:82vh;width:auto;
  height:auto;background:var(--c-card)}
.pdp-lb-x{align-self:flex-end;min-height:var(--ctrl);padding:8px 20px;
  font-family:var(--f-ui);font-size:16px;color:var(--c-inverse);
  border:var(--rule) solid var(--c-inverse-a22);background:transparent;cursor:pointer;
  transition:background-color .18s ease,border-color .18s ease}
.pdp-lb-x:hover{background:var(--c-inverse-a12);border-color:var(--c-inverse-a80)}
.pdp-lb-x:active{background:var(--c-inverse-a22)}
.pdp-lb-x:focus-visible{outline:2px solid var(--c-gold);outline-offset:2px}
.pdp-lb-nav{flex:0 0 auto;width:var(--ctrl);height:var(--ctrl);padding:0;
  font-size:24px;line-height:1;color:var(--c-inverse);
  border:var(--rule) solid var(--c-inverse-a22);background:transparent;cursor:pointer;
  transition:background-color .18s ease,border-color .18s ease}
.pdp-lb-nav:hover{background:var(--c-inverse-a12);border-color:var(--c-inverse-a80)}
.pdp-lb-nav:active{background:var(--c-inverse-a22)}
.pdp-lb-nav:focus-visible{outline:2px solid var(--c-gold);outline-offset:2px}
/* 只有一张图时左右按钮没意义，JS 会给容器挂 `data-single` */
.pdp-lightbox[data-single] .pdp-lb-nav{display:none}
/* 灯箱开着时**锁住背景滚动** —— 否则触屏上划一下背景跟着动 */
html.lb-open{overflow:hidden}
html.lb-open .pdp-main img{transform:none}

@media (max-width:900px){
  /* 🔴 2026-10-07：`1fr` = `minmax(auto,1fr)`，**下限是 auto、网格列不肯缩** ——
     里面的 `.pdp-gallery` 被缩略图那排撑到 303px，320px 屏溢出（法文 5 个详情页实测）。
     下限归零才收得住。**同一类毛病**：`.card-grid` / `.pdp-gallery` 也都改成了 `minmax(0,…)`。 */
  .pdp-hero{grid-template-columns:minmax(0,1fr);gap:var(--gap-in)}
  .pdp-feats{grid-template-columns:1fr}
  .pdp-app-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pdp-rel-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .pg-card-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
}
@media (max-width:480px){
  /* 🔴 2026-10-07 修：原来是 `1fr` —— 而 `1fr` = `minmax(auto,1fr)`，**下限是 auto**，
     网格列**不肯缩到内容最小宽度以下** ⇒ 里面那排缩略图（5×64px + 间距）把列撑到 303px，
     在 320px 屏上横向溢出（法文 5 个商品详情页实测 323 > 320）。
     改成 `minmax(0,1fr)` 下限归零，缩略图那排自己 `overflow-x:auto` 横向滚动。 */
  .pdp-gallery{grid-template-columns:minmax(0,1fr);gap:12px}
  /* 窄屏把缩略图**横过来**排，竖排会占掉半屏 */
  .pdp-thumbs{flex-direction:row;overflow-x:auto;gap:8px}
  .pdp-thumb{flex:0 0 64px;width:64px;height:64px}
  .pdp-summary{grid-template-columns:1fr}
  .pdp-app-grid{grid-template-columns:1fr}
  .pdp-rel-grid{grid-template-columns:1fr}
  .pg-card-grid{grid-template-columns:1fr}
  .pdp-cta .btn{width:100%}
  .pdp-lb-nav{width:40px;height:40px}
}
@media (prefers-reduced-motion:reduce){
  /* 🔴 全站已有一处总闸；这里**单独再收一次** PDP 的放大效果 ——
     悬停放大是「位移类」动效，前庭敏感的人会不适，必须关掉。 */
  .pdp-main:hover img,.pdp-rel:hover .pdp-rel-fig img,
  .pg-card:hover .pg-card-fig img{transform:none}
  .pdp-acc>summary::before,.pdp-q>summary::before{transition:none}
}
