/* ═══════════════════════════════════════════════════════════════════════════
   Workhorse — workhorseapi.com 全站样式表
   ───────────────────────────────────────────────────────────────────────────
   视觉依据：母版 05《企业服务 / 数据平台 / AI 增长中台》
     正式约定见 docs/建站-视觉规范-母版05落地.md（视觉正主）

   ⚠️ 本文件是从 index.html 的内联 <style> 抽出来的。抽出来的原因有两个：
      1. 多页站点要共用同一套样式，内联等于每页抄一遍，改一处漏十处；
      2. placeholder_scan.py 会把内联 <style> 里的 CSS 文本当页面正文扫，
         于是 `input::placeholder` 这种正常写法被误判成「占位符残留」。
         抽成外部文件后扫描器只读 .html，误报消失，且不是靠加白名单绕过去的。

   ⚠️ 间距纪律（visual_richness 闸会逐条验）：margin / padding / gap 一律落在
      4px 栅格上。所有 --sN 变量都是 4 的倍数，新增规则请直接用变量，别写死 14px 这种。
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══ 设计 Token（照抄母版 05 §4 风格变量表）═══ */
:root{
  /* 背景多档近黑深蓝 */
  --bg-base:#070A14; --bg-section:#0B0F1F; --bg-elevated:#111733; --bg-deep-blue:#0A1230;
  /* 品牌主色 */
  --brand-violet:#7C5CFF; --brand-blue:#3B82F6;
  /* 🔴 2026-10-04 改：渐变两个端点各压暗 7% 明度（色相/饱和度不动）。
     为什么：`.btn-primary` 是**白字**，而白字压在原渐变上对比度**全线不够**
     —— 实测（真浏览器 getComputedStyle）紫端 **4.35** / 中间 **4.11** / 蓝端 **3.68**，
     WCAG AA 正文要求 **4.5**。
     字号：按钮实际有**三档** —— 16px（`.btn` 基础）/ 14px（`.btn-sm`）/
     13px（`.nav-cta`，窄屏导航）。
     ⚠️ **三档全都远低于「大字」门槛**（大字 = ≥24px，或 ≥18.66px 且 ≥700 粗），
        所以**每一档都按 4.5 算**，没有一档能套 3.0 的宽松线。
     （2026-10-04 我一开始只写了「`.btn` 是 16px」—— 数对、说法不全，去浏览器量了才发现还有 14/13px。）
     全站 **798 处** `.btn-primary`（其中 384 处是 `.btn-sm`）—— 这是线索型站最重要的转化元素。
     改后：紫端 **6.01** / 中间 **5.56** / 蓝端 **4.67**，三处都过（真浏览器实测）。
     ⚠️ **只改这个渐变，没动 `--brand-violet` / `--brand-blue`** ——
        那两个是品牌色，还用在光晕、描边、表单聚焦环上（那些是 UI 元件，3.0 就够）。
     关联：`.lang-menu .on` 也吃这个渐变，白字同样受此修复保护。
     复核办法见 `scripts/check_contrast.py`（静态尺子）+ 浏览器 getComputedStyle（终审）。 */
  --grad-primary:linear-gradient(135deg,#5F38FF 0%,#196CF4 100%);
  --grad-hero:radial-gradient(120% 80% at 50% 0%,#1A2350 0%,#0A1230 45%,#070A14 100%);
  --grad-neon:linear-gradient(90deg,#22D3EE 0%,#7C5CFF 50%,#FF49C0 100%);
  /* 霓虹点缀 */
  --neon-cyan:#22D3EE; --neon-magenta:#FF49C0;
  /* 文字 */
  --text-strong:#F2F5FB; --text-body:#C7CEDB; --text-muted:#8A93A8;
  /* 玻璃拟态 */
  --glass-bg:rgba(255,255,255,.04); --glass-bg-hover:rgba(255,255,255,.07);
  --glass-blur:blur(18px) saturate(140%);
  --glass-border:1px solid rgba(255,255,255,.10);
  --glass-inner-light:inset 0 1px 0 rgba(255,255,255,.12);
  /* 发光阴影 */
  --shadow-card:0 8px 32px rgba(0,0,0,.45);
  --glow-violet:0 0 24px rgba(124,92,255,.45);
  --glow-cyan:0 0 20px rgba(34,211,238,.40);
  --glow-strong:0 0 40px rgba(124,92,255,.55);
  /* 圆角 */
  --r-sm:8px; --r-md:16px; --r-lg:20px; --r-xl:28px; --r-pill:999px;
  /* 间距 8pt */
  --s2:8px; --s3:12px; --s4:16px; --s6:24px; --s8:32px;
  --s10:40px; --s12:48px; --s16:64px; --s20:80px; --s30:120px;
  --maxw:1200px;
}
*,*::before,*::after{box-sizing:border-box}
/* 🔴 `color-scheme:dark` 不是装饰，是**告诉浏览器「这一页是深色的」**。
   不写的话，浏览器按浅色渲染**原生控件**（勾选框 / 单选 / 下拉弹层 / 滚动条 /
   自动填充底色）—— 实测本站的同意勾选框因此渲染成一个**刺眼的白盒子**，
   而它就在提交按钮正上方（线索型站最关键的转化点上）。

   为什么现在才补：2026-10-04 量「手指点得到吗」时顺带发现的。
   ⚠️ 它**只管原生控件**，不改变任何我们自己的颜色（`body` 的背景是显式写死的，
      所以画布颜色不会变）—— 实测 A/B 对比过，页面其余部分零变化。

   ⚠️ 打印时必须压回浅色，见文件末尾 `@media print` 里的 `color-scheme:only light`。
      不加那一句，纸上的勾选框会是深色的。 */
html{scroll-behavior:smooth;scroll-padding-top:88px;color-scheme:dark}
body{
  margin:0;background:var(--bg-base);color:var(--text-body);
  /* Inter 现在是本站自托管的（见 fonts.css），后面那串是字体没加载出来时的兜底。
     ⚠️ 原来这里还写着 "Sora"，2026-09-27 去掉了，两个原因：
       ① 它排在 Inter 后面，Inter 一直在，所以它**从来没生效过**；
       ② Sora **没有西里尔字母**（只有拉丁），俄语页的标题用它就会掉回别的字体。
       留着只会让人以为标题用的是 Sora —— 那是假的。 */
  font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  font-size:16px;line-height:1.65;-webkit-font-smoothing:antialiased;overflow-x:hidden;
}
h1,h2,h3,h4{margin:0;color:var(--text-strong);line-height:1.15;letter-spacing:-.02em;font-weight:600}
p{margin:0}
a{color:inherit;text-decoration:none}
ul{margin:0;padding:0;list-style:none}
.mono{font-family:"JetBrains Mono","Roboto Mono",SFMono-Regular,Consolas,monospace;font-variant-numeric:tabular-nums}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 24px}
@media(max-width:1023px){.wrap{padding:0 16px}}

/* 跳转到主内容（可访问性） */
.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--bg-elevated);
  color:var(--text-strong);padding:12px 20px;border-radius:0 0 var(--r-md) 0}
.skip:focus{left:0}

/* ═══ 焦点态（母版 §7/§10：青色 2px + 外圈，深色下必须可见）═══ */
:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px;
  box-shadow:0 0 0 4px rgba(34,211,238,.25);border-radius:var(--r-sm)}

/* ═══ 按钮（母版 §6.1）═══ */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  min-height:44px;padding:16px 28px;border-radius:var(--r-md);border:0;
  font-family:inherit;font-size:16px;font-weight:600;cursor:pointer;white-space:nowrap;
  transition:box-shadow 180ms ease-out,transform 220ms cubic-bezier(.22,1,.36,1),background 180ms ease-out,border-color 180ms ease-out}
.btn-primary{background:var(--grad-primary);color:#fff;box-shadow:var(--glow-violet)}
.btn-primary:hover{box-shadow:var(--glow-strong);transform:translateY(-2px)}
.btn-primary:active{transform:translateY(1px);box-shadow:0 0 12px rgba(124,92,255,.35)}
.btn-secondary{background:var(--glass-bg);border:var(--glass-border);color:var(--text-strong);
  backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur)}
.btn-secondary:hover{border-color:rgba(124,92,255,.45);box-shadow:var(--glow-violet)}
.btn-sm{min-height:44px;padding:8px 16px;font-size:14px}

/* ═══ 导航（母版 §6.2：72px，初始透明，滚过 80px 变玻璃态）═══ */
.nav{position:fixed;top:0;left:0;right:0;z-index:100;height:72px;
  display:flex;align-items:center;background:transparent;border-bottom:1px solid transparent;
  transition:background 240ms ease-out,box-shadow 240ms ease-out,border-color 240ms ease-out}
.nav.solid{background:rgba(11,15,31,.72);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border-bottom:1px solid rgba(255,255,255,.08);box-shadow:var(--shadow-card)}
.nav-in{display:flex;align-items:center;gap:var(--s8);width:100%}
.brand{display:flex;align-items:center;gap:12px;font-size:17px;font-weight:700;
  color:var(--text-strong);letter-spacing:-.02em}
.brand svg{filter:drop-shadow(0 0 8px rgba(124,92,255,.55))}
.nav-links{display:flex;align-items:center;gap:var(--s6);margin-left:auto;font-size:14px}
.nav-links > a{position:relative;color:var(--text-body);padding:8px 4px;transition:color 180ms ease-out}
.nav-links > a::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:var(--grad-neon);transform:scaleX(0);transform-origin:left;
  transition:transform 200ms ease-out;border-radius:2px}
.nav-links > a:hover{color:var(--text-strong)}
.nav-links > a:hover::after{transform:scaleX(1)}

/* 🔴 2026-10-02：顶层导航标签**永远不许折行**。
   实测：俄语导航自然宽度只差 **0.24px** 就放不下，浏览器就把最长的
   「Как это работает」压成了三行，页眉从 44px 涨到 62px。
   根因不是「差得多」，是 `.nav-links` 的宽度**恒等于内容宽度**
   （`flex-grow:0`，见上一行）⇒ 它**永远贴着边界**，任何一次亚像素舍入
   都可能让最长的那个标签折行 —— 英文这次只是运气好。
   而且折行本身从来不是想要的效果：标签放不下应当去减入口/减字，
   不该让「How it works」竖着排。
   ⚠️ 只能加给**顶层**这两个选择器，**不能**加在 `.nav-links` 上 ——
      下拉面板里的说明文字（`.menu-panel a span`）本来就要折行。 */
.nav-links > a,
.nav-links .menu-trigger{white-space:nowrap}

/* ═══ 导航下拉菜单 ═══
   产品/方案有 10 个真实子页，全平铺导航会挤爆，所以做成两个下拉。
   🔴 为什么用真 <a> 而不是 JS 弹窗：爬虫和质检脚本都要能顺着链接爬到详情页
      （render_reality AF-4 查的就是「商品卡有没有真详情页路由」）。
   🔴 键盘可达：trigger 是 <button>，:focus-within 展开，Esc 收起，触屏点按切换。 */
.menu-item{position:relative}
.menu-trigger{display:inline-flex;align-items:center;gap:4px;min-height:44px;padding:8px 4px;
  background:none;border:0;cursor:pointer;font-family:inherit;font-size:14px;font-weight:400;
  color:var(--text-body);transition:color 180ms ease-out}
.menu-trigger:hover{color:var(--text-strong)}
.menu-trigger .chev{transition:transform 200ms ease-out;stroke:currentColor}
.menu-item[data-open="true"] .menu-trigger{color:var(--text-strong)}
.menu-item[data-open="true"] .chev{transform:rotate(180deg)}
.menu-panel{position:absolute;top:calc(100% + 12px);left:-16px;z-index:120;
  min-width:280px;padding:var(--s3);
  background:rgba(11,15,31,.96);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border:var(--glass-border);border-radius:var(--r-lg);
  box-shadow:var(--shadow-card),var(--glass-inner-light);
  display:grid;gap:2px;
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity 180ms ease-out,transform 180ms ease-out,visibility 180ms ease-out}
.menu-item[data-open="true"] .menu-panel,
.menu-item:focus-within .menu-panel{opacity:1;visibility:visible;transform:none}
.menu-panel a{display:block;padding:10px 12px;border-radius:var(--r-sm);
  font-size:14px;color:var(--text-body);
  transition:background 180ms ease-out,color 180ms ease-out}
.menu-panel a:hover{background:var(--glass-bg-hover);color:var(--text-strong)}
.menu-panel a span{display:block;font-size:12px;color:var(--text-muted);margin-top:2px;line-height:1.5}

/* ═══ 语言切换（导航右上 / 抽屉 / 页脚 三处共用）═══
   🔴 2026-10-03 从「EN | RU 两颗药丸」改成**下拉菜单**：语种要从 2 种加到 17 种。
      平铺两颗 = 66px；平铺 17 颗 ≈ 17×24 + 16×2 + 6 = 446px ——
      而导航栏在 1024 那一档总共只有 1009px，光语言就吃掉 44%，直接挤爆。
   🔴 用 `<details>/<summary>` **原生**折叠，不依赖 JS —— 关掉 JS 也能切语言。
   🔴 必须放导航里 —— 只放页脚等于没有（页脚在 9400px 处，没人翻到底）。
   ⚠️ 三处的展开方式**故意不同**（各自的覆盖规则见下面各处）：
      导航 = 悬浮在下方 ｜ 页脚 = 悬浮在上方 ｜ 抽屉 = 原地撑开。
      抽屉那份必须原地撑开：`.drawer` 是 position:fixed + overflow-y:auto，
      它同时是绝对定位后代的**包含块** —— 悬浮面板会被它裁掉。
   ⚠️ `display` 必须**只在 [open] 时给 grid**：`<details>` 收起时靠 UA 样式
      `details:not([open])>*:not(summary){display:none}` 隐藏面板，
      而作者样式**无条件压过** UA 样式（跟选择器权重无关）——
      直接给 `.lang-menu{display:grid}` 会让面板在收起时也显示出来。 */
.lang-switch{position:relative;display:inline-flex;flex:none}
.lang-switch>summary{display:inline-flex;align-items:center;gap:6px;flex:none;
  min-height:44px;padding:0 12px;cursor:pointer;list-style:none;
  background:var(--glass-bg);border:var(--glass-border);border-radius:var(--r-pill);
  backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  font-size:13px;font-weight:600;letter-spacing:.04em;color:var(--text-body);
  transition:color 180ms ease-out,background 180ms ease-out}
.lang-switch>summary::-webkit-details-marker{display:none}
.lang-switch>summary:hover{color:var(--text-strong);background:var(--glass-bg-hover)}
.lang-switch>summary .chev{flex:none;transition:transform 200ms ease-out}
.lang-switch[open]>summary .chev{transform:rotate(180deg)}
.lang-menu{display:none;position:absolute;top:calc(100% + 10px);right:0;z-index:130;
  grid-template-columns:repeat(2,minmax(0,1fr));gap:2px;
  min-width:min(360px,calc(100vw - 32px));
  max-height:min(64vh,420px);overflow-y:auto;overscroll-behavior:contain;
  padding:var(--s3);
  background:rgba(11,15,31,.96);
  backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border:var(--glass-border);border-radius:var(--r-lg);
  box-shadow:var(--shadow-card),var(--glass-inner-light)}
.lang-switch[open] .lang-menu{display:grid}
/* 面板滚动条调细、调暗。⚠️ 不改的话深色底上会挂一条**亮白色粗杠**（系统默认配色），
   是整页最扎眼的东西 —— 2026-10-03 在 1024px 实截图上看到的。
   ⚠️ 两套写法都要写：Firefox 只认 scrollbar-width/color，Chrome/Safari 只认 ::-webkit-scrollbar。 */
.lang-menu{scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) transparent}
.lang-menu::-webkit-scrollbar{width:8px}
.lang-menu::-webkit-scrollbar-track{background:transparent}
.lang-menu::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:99px}
.lang-menu::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,.34)}
.lang-menu>*{display:flex;align-items:center;min-height:44px;padding:0 10px;
  border-radius:var(--r-sm);font-size:14px;font-weight:600;color:var(--text-muted);
  transition:background 180ms ease-out,color 180ms ease-out}
.lang-menu a:hover{background:var(--glass-bg-hover);color:var(--text-strong)}
.lang-menu .on{background:var(--grad-primary);color:#fff;box-shadow:0 0 12px rgba(124,92,255,.45)}

/* ═══ 移动端抽屉（母版05 §6.2 / §9 / §12.2）═══
   🔴 为什么必须有：
     母版05 §3.3 断点表规定 Mobile ≤599 用「抽屉导航」、Tablet 600–1023「导航可折叠」；
     B2B页面标准 §54 规定「移动端汉堡→抽屉」，且产品/方案细分入口要「移动端抽屉中直接展示」。
     2026-09-27 实测发现：原先手机上 .nav-links 直接 display:none，
     结果是**手机用户从导航里够不到任何产品页/方案页**（英文站有 10 个这样的子页）。
     而公司质检 nav_qa 只读 HTML 文本，display:none 是 CSS，它看不见 —— 所以照样 PASS。
   🔴 为什么收起态用 visibility 而不是 display:none：
     要做「从右侧滑入 + 逐项 stagger」的动效，元素在 DOM 里必须有盒子；
     display:none 没有盒子，做不了过渡。visibility:hidden 既不可点、也不进无障碍树。
   🔴 抽屉里的链接必须 ≥44px 高：ui_qa.py 的触控断言只看 rect 是不是 0，
     不跳过 visibility:hidden 的元素，矮了会 FAIL —— 手机上本来也该这么大。 */
.burger{display:none;align-items:center;justify-content:center;flex:none;
  width:44px;height:44px;padding:0;cursor:pointer;
  background:var(--glass-bg);border:var(--glass-border);border-radius:var(--r-sm);
  color:var(--text-strong);
  transition:background 180ms ease-out,border-color 180ms ease-out}
.burger:hover{background:var(--glass-bg-hover);border-color:var(--glass-border-glow)}

.drawer-scrim{position:fixed;inset:0;z-index:190;
  background:rgba(4,6,14,.62);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);
  opacity:0;visibility:hidden;
  /* 同 .drawer：visibility 用 0s+延迟，否则淡入的头 240ms 遮罩根本还没显示 */
  transition:opacity 240ms ease-out,visibility 0s linear 240ms}
.drawer-scrim[data-open="true"]{opacity:1;visibility:visible;
  transition:opacity 240ms ease-out,visibility 0s linear 0s}

.drawer{position:fixed;top:0;right:0;bottom:0;z-index:200;width:100%;
  display:flex;flex-direction:column;padding:var(--s4);
  background:rgba(11,15,31,.94);
  backdrop-filter:blur(12px) saturate(140%);-webkit-backdrop-filter:blur(12px) saturate(140%);
  border-left:var(--glass-border);box-shadow:var(--shadow-card);
  overflow-y:auto;overscroll-behavior:contain;
  transform:translateX(100%);visibility:hidden;
  /* 🔴 visibility 必须用「0s + 延迟」，不能写成 `visibility 280ms`：
     写成 280ms 就变成「用 280ms 慢慢从 hidden 过渡到 visible」，
     而**过渡期间的元素真的不可聚焦** —— 抽屉打开时把焦点给关闭按钮会静默失效，
     读屏用户完全不知道抽屉开了。2026-09-27 实测：`visibility 280ms` 下
     同步重算样式后 focus()、setTimeout(0) 后 focus() 全都失败，
     要等到第 2 帧（约 32ms）才生效。
     改成 0s+延迟后：打开时**当帧立即**变 visible（可聚焦），
     关闭时延迟 280ms 才变 hidden（滑出动画照样看得见）。 */
  transition:transform 280ms cubic-bezier(.32,.72,0,1),visibility 0s linear 280ms}
.drawer[data-open="true"]{transform:none;visibility:visible;
  transition:transform 280ms cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}

.drawer-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);
  padding-bottom:var(--s4);border-bottom:1px solid rgba(255,255,255,.08)}
.drawer-x{display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:44px;height:44px;padding:0;cursor:pointer;
  background:var(--glass-bg);border:var(--glass-border);border-radius:var(--r-sm);
  color:var(--text-strong);transition:background 180ms ease-out}
.drawer-x:hover{background:var(--glass-bg-hover)}

.drawer-nav{display:flex;flex-direction:column;gap:2px;padding:var(--s4) 0;flex:1}
.drawer-nav a{display:flex;align-items:center;min-height:44px;padding:0 var(--s3);
  border-radius:var(--r-sm);font-size:16px;color:var(--text-body);
  opacity:0;transform:translateX(12px);
  transition:opacity 240ms ease-out,transform 240ms ease-out,
             background 180ms ease-out,color 180ms ease-out}
.drawer[data-open="true"] .drawer-nav a{opacity:1;transform:none}
.drawer-nav a:hover{background:var(--glass-bg-hover);color:var(--text-strong)}
.drawer-group{padding:var(--s4) var(--s3) var(--s2);font-size:13px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted)}
.drawer-foot{padding-top:var(--s4);border-top:1px solid rgba(255,255,255,.08)}
/* 🔴 抽屉里的语言面板**原地撑开**，不悬浮 —— 原因见上面 `.lang-switch` 那段：
   `.drawer` 是 position:fixed + overflow-y:auto，它同时是绝对定位后代的包含块，
   悬浮面板会被它裁掉（面板有一半在抽屉外面）。原地撑开则跟着抽屉一起滚，不会丢。
   ⚠️ `.lang-switch` 必须从 `inline-flex` 改成 `block`：它是**横排**的 flex 容器，
      面板 `position:static` 后会变成它**并排的兄弟项**，跑到触发钮右边去 ——
      2026-10-03 在 375px 抽屉里实截到：触发钮被挤成一个圆球，面板挤在它右边。
      改成 block 后，触发钮按内容宽（inline-flex），面板在它**下面**独占一行。
   ⚠️ `min-width` 要清掉：默认那个 min(360px, 100vw-32px) 是按「悬浮面板」定的，
      在抽屉里会撑出横向滚动条。改成 block 后面板宽度自动 = 抽屉内容宽（375-32=343px），
      两栏各 ~160px，最长的「Bahasa Indonesia」也放得下。 */
.drawer .lang-switch{display:block}
.drawer .lang-menu{position:static;min-width:0;max-width:none;max-height:none;
  overflow:visible;margin-top:var(--s3);box-shadow:none}

/* 逐项 stagger 进入（母版05 §6.2：每项延迟 40ms）。
   封顶在 8 项：抽屉里 17 项，全按 40ms 累加最后一项要等 680ms，太拖。 */
.drawer[data-open="true"] .drawer-nav > *:nth-child(1){transition-delay:0ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(2){transition-delay:40ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(3){transition-delay:80ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(4){transition-delay:120ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(5){transition-delay:160ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(6){transition-delay:200ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(7){transition-delay:240ms}
.drawer[data-open="true"] .drawer-nav > *:nth-child(n+8){transition-delay:280ms}

/* Tablet 600–1023 用右侧面板而不是全屏，全屏在平板上太笨重 */
@media(min-width:600px){.drawer{width:min(86vw,420px)}}

/* 🔴 断点是 1199 而不是 1023 —— 2026-09-27 实测定的，不是随手写的。
   加了「应用领域 / 关于我们 / 联系我们」三项之后，8 个入口 + 语言切换 + CTA
   的自然宽度约 1175px（用 iframe 逐档量过：1170 折行，1180 不折）。
   1280 那一档只收紧间距就能放下；**1024~1174 这一档再怎么收都放不下**，
   硬放的结果就是「How it works」被 flex 压成 48px 宽、换行成两行（高 85px）。
   → 这一档改用抽屉。**没有丢任何入口**：抽屉里 14 条链接一条不少，
     而且抽屉本来就是这一档该用的形态（600~1023 那段注释里已说明）。
   ⚠️ 为什么不动 .wrap 的 1023 断点（见上方 .wrap 那行）：≤1023 内容宽度 =
     视口 − 32px 是母版05 §3.3 的硬规定，跟导航装不装得下是两回事，不能一起改。 */
@media(max-width:1199px){
  /* 折叠 = 收进抽屉，不是 display:none —— 见上方「移动端抽屉」整段说明 */
  .nav-links{display:none}
  .nav-in{gap:var(--s3)}
  .nav-cta{padding:8px 16px;font-size:13px}
  .burger{display:inline-flex}
  /* 🔴 语言切换**留在导航里**，靠右跟 CTA 作伴。
     2026-09-28（第三十八段）改的。原先这行是 `.nav-in > .lang-switch{display:none}`，
     跟 .nav-links 一起被藏了 —— 但两者被藏的理由**毫无关系**：
     .nav-links 是因为 8 个入口在 1024~1174 挤不下；而它一藏，**反而空出 775px**。
     实测 1024px：导航栏 1009px 宽，品牌+切换+CTA+汉堡一共只要 527px，**空着 482px**。
     后果：1024 这一档页眉里没有语言切换，只剩页脚那一个 ——
     而本文件上面刚写过「只放页脚等于没有（页脚在 9400px 处，没人翻到底）」。
     ⚠️ 自动左边距从 .nav-cta 挪到 .lang-switch 上，否则切换会贴着 Logo 站在最左边。 */
  .nav-in > .lang-switch{margin-left:auto}
}

/* ═══ 手机档：语言切换压成「只显示语种代码」，但**仍留在导航里**（420~599px）═══
   🔴 为什么要压：改成下拉后，全称版触发钮（「English」）约 97px ——
      比旧的两颗药丸（66px）**更宽**，会把下面 420 那条断点继续往右推。
      代码版（「EN」）约 49px，比旧版还窄，断点才有可能往左收。
   🔴 凭什么能小于 44px —— 24px 是 WCAG 2.5.8 的硬下限，这里取 28 留余量
      （贴着 24 写，子像素一舍入就成 23.99 判不合格）。
   ⚠️ 这里写的是**我们自己的尺度**，不是公司闸给的：`质检脚本/ui_qa.py` 第 35、46、50 行
      的 `clickable = 'a,button,[role=button],input[type=submit]'` **不包含 <summary>**，
      也就是这把尺子**量不到**这个控件。量不到不等于可以不守 —— 所以按 WCAG 2.5.8 自愿守 24。
   ⚠️ 页脚那份和抽屉那份**不跟着缩**：那两处地方宽裕，保持 44px 好点。 */
@media(max-width:599px){
  .nav-in > .lang-switch>summary{min-height:28px;padding:0 8px;gap:3px}
  .nav-in > .lang-switch .lang-cur{display:none}
  /* ⚠️ 代码走 `attr(data-code)`，**不是写死在 CSS 里** —— 语种是动态的
     （俄文页要显示 RU、葡语页要显示 PT-BR），CSS 不可能知道当前是哪一种。
     ⚠️ 用 ::before 不是 ::after：summary 是 flex 容器，::before 天然排在箭头**前面**；
     ::after 会跑到箭头后面，变成「⌄ EN」这种倒过来的样子。 */
  .nav-in > .lang-switch>summary::before{content:attr(data-code);
    font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase}
}

/* 🔴 408 以下放不下，切换收进抽屉（页脚那份还在，抽屉那份也在）。
   🔴 这个数是**量出来的，不是拍的**，而且第一版拍错过一次：
      第一版写的是 399，因为拿**英文页**的数字算：品牌 112 + 切换 66 + CTA 96 + 汉堡 44
      + 三个间隙 24 + 左右内衬 32 = 358px，看着 400 够用。
      2026-09-28 在 400px 档一量：汉堡按钮右边缘在 **404px**，戳出视口 4px。
      原因：**俄文页的按钮字更长**（«Запросить расчёт» 142px vs 英文 "Get a quote" 96px），
      俄文页实际需要 **420px**。两种语言的按钮宽度不一样，**必须按宽的那个定断点**。
   🔴 2026-10-03 重量了一次：语言切换从「两颗药丸 66px」换成「下拉触发钮 51px」，
      俄文页最低可用宽度 420 → **404.984px**（在 500px 视口上逐件量自然宽再加出来：
      112.359 + 51.109 + 141.516 + 44 + 三个间隙 24 + 左右内衬 32）。
      ⚠️ 断点取 **408** 而不是 405：留 3px 余量 —— 字体渲染在不同机器/缩放上会有
         零点几像素的出入，贴着 405 写，赶上往上那次舍入就会戳出去。
      ⚠️ 受益的是 **412 / 414** 这两档（Pixel、iPhone Plus）：
         它们**重新拿回了页眉里的语言切换**。360 / 375 / 390 / 393 仍然放不下，维持原样。
   ⚠️ 这个几像素的戳出，`documentElement.scrollWidth` **看不见** ——
      导航是 position:fixed，fixed 元素不参与页面滚动宽的计算。
      所以「页面没有横向滚动条」不能证明导航没戳出去（同一坑见 scripts/browser_reality_check.py）。
   ⚠️ 而且我自己的检查脚本当时也没抓到：它只测 320/360/375/768/1280/1440，
      **400~413 正好是它六个档位之间的空档** —— 新断点恰好落在量具的盲区里。
      已补 420 一档进 VIEWPORTS。
   ⚠️ 51px 已经接近切换钮的物理下限：里面是「两个字母 + 箭头 + 左右内衬 8×2 + 间隙 3」。
      想再小就得砍品牌或砍 CTA，那两样都不该为它让路。
   ⚠️ 这一档**不是「没有」**：页脚那个（全称、44px）一直可见，抽屉里那个也是完整 44px。 */
@media(max-width:407px){
  .nav-in > .lang-switch{display:none}
  .nav-cta{margin-left:auto}
}

/* 🔴 2026-10-04：**西语单独一档** —— 上面那个 407 号称是「按最宽的语种」定的，
   但那个「最宽」当时**只量了俄语**。这次六个语种全量了一遍
   （自然宽 = 品牌 112.36 + 切换钮 + CTA + 汉堡 44 + 三个间隙 8×3；最低视口 = 自然宽 + 32 内衬）：

     en  Get a quote             327.4  ⇒  359.4
     ru  Запросить расчёт        373.0  ⇒  405.0   ← 407 这一档原本是按它定的
     de  Angebot anfordern       373.9  ⇒  405.9
     fr  Demander un devis       375.1  ⇒  407.1
     pt  Solicitar orçamento     377.0  ⇒  409.0   （超 1px，肉眼不可见，不动）
     es  Solicitar presupuesto   387.7  ⇒  419.7   ← **最宽的是西语，不是俄语**

   ⇒ 408~419 这一整段，**西语页头放不下**。412（Pixel）实测：汉堡右边缘 403.72，
     而内容栏右边界应当是 396（母版05 §3.3：视口 − 32px）⇒ 吃进右留白 7.72px，
     右边只剩 8.28px 而左边还有 16px，**左右不对称**；408 那一档最差，吃进 11.72px。
   ⚠️ 412 / 414（Pixel、iPhone Plus）正是 407 这一档当初想照顾的机型，
      所以**不能靠把 407 往上提**来解决 —— 那会把 en/ru/de/fr 在 412/414 好不容易
      拿回来的页眉语言切换又收走（见上面 321-329 行那段论证）。
   ⇒ 只给**放不下的那个语种**单独设一档：es 需要 419.7px，按本文件惯例留 3px 余量 ⇒ 423
      （即 ≤422 藏、≥423 显示）。
   ⚠️ 藏起来**不等于没有**：抽屉里那份（完整 44px）和页脚那份都还在，汉堡一点就到。
   ⚠️ 将来加语种（第二波 ko/id/nl/ja）**必须重新量各自 CTA 文案的自然宽**，
      超过 408 的就照这条加进 `:is(...)` 列表 —— 别默认「跟英语差不多长」。
   ⚠️ 为什么不用 `.nav-in` 的 `scrollWidth` 判这件事：内容溢出到**内衬区**时仍在
      padding box 之内，不产生 scrollable overflow ⇒ scrollWidth 恒等于 clientWidth，
      **报 0**。这次实测撞到了：408px 它报「408/408，没溢出」，而同一时刻汉堡已经站在
      403.72。凡是「内容挤进内衬」的缺陷，都得逐元素量边缘，不能看容器的 scrollWidth。 */
@media(max-width:422px){
  html:is([lang|="es"]) .nav-in > .lang-switch{display:none}
  /* 切换钮一藏，自动左边距必须挪到 CTA 上，否则整组会贴着 Logo 站在最左边 */
  html:is([lang|="es"]) .nav-cta{margin-left:auto}
}

/* 1200 以上，导航放得下但很紧。
   🔴 2026-09-27 实测：8 个入口自然宽约 775px（24px 间距），而可用宽度只有 1152px ——
      flex 会去压最长的那个，于是「How it works」被压成 48px 宽、**换行成两行**
      （高 85px，正常是 39px）。不是「看着挤」，是真的折行了。
   🔴 **这条不能写成 max-width:1319 那种区间**：`.wrap` 的 --maxw 是 1200，
      内容宽度在 1200 以上就**锁死在 1152px 不再变**，所以 1440、1920 跟 1280 一样紧。
      （第一版就写成了区间，1280 好了、1440 又折行 —— 是量出来的，不是看出来的。）
   ⚠️ 只收紧间距，**不删入口、不缩字号** —— 删入口就是规格 §2.3 说的「删项凑数」，
      字号动了下限就跟着变（规格 §2.1 正文字号是硬线）。
   ⚠️ 1180 以下连收间距也不够，那一档已经走抽屉（见上面 1199 那个断点）。 */

/* 🔴 2026-10-04：间距再从 16 收到 12。上面 2026-09-27 那次是按**英俄两个语种**量的，
   10-03/04 加进来的 4 个小语种**从没在桌面档重量过** —— 一补量就露了：
   `.wrap` 封顶 1200，所以 1200 以上可用宽度**恒为 1152px**，而德语那一行自然宽 1159.5px
   ⇒ **每一个 ≥1200 的宽度都戳出 7.5px**，窗口再宽也不缓解（1200~1600 逐档量过，恒定 7.5）。
   ⚠️ 判据不是「谁的 CTA 最长」：西语 CTA 最宽（176.1px vs 德语 160.3px），但它导航链接短
      （660.9px vs 德语 724.6px），整行反而放得下 —— **要量整行，不能只量按钮**。
   ⚠️ 第二波 ko/id/nl/ja 进来必须**重新量整行自然宽**，别默认「跟英语差不多」。 */
@media(min-width:1200px){
  .nav-in{gap:var(--s3)}
  .nav-links{gap:var(--s3)}
}

/* 小屏收紧。
   ⚠️ 不能靠缩小 .wrap 的左右留白来腾地方 —— 母版05 §3.3 规定 ≤1023 内容宽度 = 视口 − 32px。
   🔴 2026-10-04：间隙 8→6、按钮左右内衬 12→10（`gap` 用原值 6px，间距表里没有 --s1）。
      360px 是**真实机型宽度**（Galaxy S 系、一批千元安卓机），西语正好在这一档放不下：
      品牌 112.36 + 两个间隙 16 + CTA 157.8 + 汉堡 44 = 330.16px，可用只有 328px ⇒ 戳出 2.13px。
      ≤359 品牌文字会折叠所以没事、362 以上也够 —— **只有 360、361 这两档**，正好落在量具空档里。
      省下 8px 后余量约 5.9px（本文件惯例留 3px 抗字体渲染差异）。
   ⚠️ 为什么不上调「品牌折叠」那一档（≤359）去躲：360/361 是常用机型，
      不该为 2px 把品牌文字收掉。 */
@media(max-width:599px){
  .nav-in{gap:6px}
  .brand{font-size:15px;gap:var(--s2)}
  .nav-cta{padding:8px 10px}
}

/* 320px 这类超窄屏：连品牌文字都放不下了，收起来只留 Logo 图标。
   无障碍名还在 —— .brand 上的 aria-label 就是「Workhorse」。 */
@media(max-width:359px){
  .brand{font-size:0;gap:0}
}

/* ═══ 通用区块（母版 §2.3：桌面 120 / 平板 88 / 移动 64）═══ */
section{padding:var(--s30) 0;position:relative}
@media(max-width:1023px){section{padding:88px 0}}
@media(max-width:599px){section{padding:64px 0}}
.eyebrow{display:inline-block;font-size:12px;font-weight:500;letter-spacing:.12em;
  text-transform:uppercase;color:var(--neon-cyan);margin-bottom:var(--s4)}
/* 博客里每篇笔记标题下面那行小字（「笔记 · 迁移」这种署名行）。
   ⚠️ 这条规则是 2026-10-04 补的：de/es/fr/pt 四个语种的博客页一直在用
   class="meta-line"，但 CSS 里**从来没有这个类** —— 那行小字一直是按普通正文渲染的。
   🔴 2026-10-04 改成 `display:inline-block` 的**标签**（配 `<span>` 用），
   理由：它跟页首那颗 `.eyebrow` 是**同一个角色**（小号、大写、带字距的标签），
   英语/俄语页的 `.eyebrow` 本来就是 `<span>`。原先四个语种把它写成 `<p>`，
   于是被 §2.1 那把尺子（只认 `<p>` 里的字当「正文」）量成「正文字号 12px」→ 假红。
   ⚠️ **别改回 `<p>`** —— 那会让四个语种的博客页在正主凭证上红掉；
      也**别为了过闸把这行抬到 16px** —— 同一个页面上的 `.eyebrow` 是 12px，
      两行同样角色的标签一个大一个小，才是真的排版坏掉。 */
.meta-line{display:inline-block;margin:0 0 var(--s4);font-size:12px;font-weight:500;
  letter-spacing:.12em;text-transform:uppercase;color:var(--text-muted)}
/* 🔴 2026-10-04 晚：**820px 保留不动** —— 我一度想把它压到 600px（跟新的正文列对齐），
   核完数据后否掉了，理由记在这里，免得以后又有人翻出来重开：
   ① **本文件里的限宽是一条「一层比一层窄」的阶梯**，不是各改各的：
        h1 满宽 1152px(64px) / h1 1152px(44px) / **h2 820px** / `.sub` 660px / 正文 600px。
        把 h2 压到 600 会跟正文**同宽**，层级被压平，视觉上「标题=正文」。
   ② **「60–80 字符/行」是正文的规矩，不能搬给标题**。192 页真量（Range.getClientRects 数真行）：
        44px 的 h1 每行 27–36 字 · 36px 的 h2 每行 15–40 字 —— 都在标题的舒适区里。
        标题字号大，每行字数本来就该少；拿正文的尺子量它会得出「标题太宽」的假结论。
   ③ **真正会换行的只有 24 个**（568 个 h2 里 544 个只有一行 —— 一行标题不存在「超宽」）。
        这 24 个里 10 个下面是 `.sub`(660px)、14 个下面是 600px 正文。
   ④ **压窄会更难看**：实测 /de/ 那条从 2 行变 3 行，末行只剩一个词
        （「Spitzenmodell-Preise für Arbeit, die / ein Flash-Modell genauso gut / erledigt.」）。
        德语/西语/法语/葡语都是长词语言，压窄只会逼出更多「孤字行」。
   ⑤ 页面里**没有可见的容器边框**跟这个 820 对齐（h2 直接在 `.wrap` 里，1200px，无底色无描边），
        所以「标题比正文宽」不会被看成「框没对齐」—— 只是右边缘各自参差，本来就是左对齐排版的常态。 */
h2{font-size:36px;max-width:820px}
@media(max-width:1023px){h2{font-size:28px}}
@media(max-width:599px){h2{font-size:26px}}
/* 🔴 2026-10-05 补 `margin-top`：正文里的 `h2` **头顶原来是 0 空白**。
   病根：第 79 行 `h1,h2,h3,h4{margin:0}` 是统一归零，`.prose h2` 后来自己补了
   `margin-top:var(--s12)`，但**正文（非 prose）的 h2 一直没人补** ——
   它的间距全靠「前一个兄弟碰巧带了 margin-bottom」，碰到就 16px，碰不到就 0。
   实测（14 页、30 个「上下都有兄弟」的 h2）：**30 / 30 个的上间隙 ≤ 下间隙** ——
   全站**没有一个是靠近自己那组内容的**，标题全贴在上一块上。
   最差的一类是「列表后面跟 h2」：组内两个 `li` 的视觉间隙 24px，而
   「最后一个 li → 下一个 h2」只有 12px（全靠 li 自己的 padding）——
   标题离上一组比上一组内部还近，读起来像上一组多出来的一项。
   `/sitemap/` 六个分组连排时最刺眼（「Trust & resources」贴着「vs Google Cloud Vision」）。
   ✅ 取值 `--s16`(64px)：正好是下方最常见的 32px 的整 2 倍，又明显小于
      120px 的 section 边界，中段标题不会跟分节抢戏。
      A/B 注入比过 64 与 80（不重生成页面，直接注 CSS 截图），64 已足够。
   ⚠️ `:first-child` 必须归零 —— 首子 h2 靠 `section{padding:var(--s30) 0}`(120px) 撑开，
      不归零会变成 184px，跟分节边界打架。页脚 66 个 h2 全是 `.foot-col` 首子，
      也靠这条保持原样（实测：注入后页脚 **0 个**变化）。
   ⚠️ `.eyebrow + h2` 必须归零 —— eyebrow 是「小标签 + 标题」的紧贴配对，
      间距靠 eyebrow 自己的 `margin-bottom:var(--s4)`(16px)。不归零会变成 80px，把配对拆散。
   ⚠️ **只管 h2，别顺手管 h3**：实测 h3 共 53 个，**52 个本来就是「上 > 下」**
      （卡片内 24px 上 / 8~28px 下；`.step` 内 16 上 / 12 下），没这个毛病。
   ⚠️ 抽屉（`#drawer`）里**一个标题都没有**（实测：真打开汉堡菜单数过，n=0），
      所以这条碰不到抽屉；`.prose` 有自己更专一的 `.prose h2` 规则（48px），也不受影响。 */
h2{margin-top:var(--s16)}
h2:first-child{margin-top:0}
.eyebrow + h2{margin-top:0}
.sub{margin-top:var(--s4);font-size:18px;color:var(--text-muted);max-width:660px;line-height:1.6}
.grad-text{background:var(--grad-neon);-webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent}

/* ═══ 玻璃卡（母版 §6.3）═══ */
.glass{background:var(--glass-bg);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border:var(--glass-border);border-radius:var(--r-lg);
  box-shadow:var(--shadow-card),var(--glass-inner-light);padding:var(--s8);
  transition:background 180ms ease-out,border-color 180ms ease-out,box-shadow 180ms ease-out,transform 220ms cubic-bezier(.22,1,.36,1)}
.glass:hover{background:var(--glass-bg-hover);border-color:rgba(124,92,255,.45);
  box-shadow:var(--shadow-card),var(--glass-inner-light),var(--glow-violet);transform:translateY(-4px)}
@media(max-width:599px){.glass{padding:var(--s6)}}

/* 图标容器（母版 §6.4：56px 圆角方块，玻璃底） */
.ic{width:56px;height:56px;border-radius:var(--r-md);display:flex;align-items:center;justify-content:center;
  background:var(--glass-bg);border:var(--glass-border);margin-bottom:var(--s6);
  transition:box-shadow 180ms ease-out}
.ic svg{stroke:var(--neon-cyan);filter:drop-shadow(0 0 6px rgba(34,211,238,.5))}
.glass:hover .ic{box-shadow:var(--glow-cyan)}

/* ═══ 英雄区（母版 §5.1 第 1 屏）═══ */
.hero{padding:calc(72px + var(--s20)) 0 var(--s20);background:var(--grad-hero);overflow:hidden}
.hero::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.55;
  background-image:linear-gradient(rgba(124,92,255,.06) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(124,92,255,.06) 1px,transparent 1px);
  background-size:48px 48px;animation:grid 20s linear infinite}
@keyframes grid{to{background-position:48px 48px,48px 48px}}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(1.35)}}
@keyframes sweep{0%{background-position:-140% 0}100%{background-position:240% 0}}
.hero-in{position:relative;display:grid;grid-template-columns:1.02fr .98fr;gap:var(--s16);align-items:center}
@media(max-width:1023px){.hero-in{grid-template-columns:1fr;gap:var(--s12)}}
.pill{display:inline-flex;align-items:center;gap:8px;font-size:12px;font-weight:500;
  color:var(--text-body);background:var(--glass-bg);border:var(--glass-border);
  backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  padding:8px 16px;border-radius:var(--r-pill);margin-bottom:var(--s6)}
.pill .dot{width:6px;height:6px;border-radius:50%;background:var(--neon-cyan);
  box-shadow:var(--glow-cyan);flex:none;animation:pulse 2.4s ease-in-out infinite}
/* overflow-wrap:anywhere 是**兜底**，只在某个词真的一行放不下时才生效。
   2026-10-04 实测：德语 /dpa/ 的 h1「Auftragsverarbeitungsvertrag.」
   是个 27 字符不可拆的复合词，360px 下要 425px、内容栏只有 328px
   ⇒ 整行顶出屏幕右边 97px（截图 质检证据/文字被裁-探针发现/5_德语dpa标题_精确.png）。
   用 anywhere 而不是 break-word：anywhere 会**同时**压低 min-content 宽度
   （break-word 不会），跟行 725 那条注释是同一个理由。
   全语种都挂上 —— 它只在放不下时才动手，放得下时一个字节都不动。 */
/* 🔴 2026-10-05 补 `text-wrap:balance`：治**标题孤字** ——
   两行的标题里第二行只剩两三个词，视觉上像没写完（也白占一行高度）。
   实测（真浏览器、三档宽度、10 个页面，逐行量 Range.getClientRects）：
     孤字页（末行 < 最长行的 30%）：现状 **4 页** → balance **0 页**
       /product/deepseek-api/ 1440px  178/1074 = 17%
       /about/                1440px  184/1107 = 17%
       /get-started/          1440px  182/967  = 19%（1024px 下也是 19%）
       /get-started/           375px  124/340  = 36%
     balance 之后这四处的末行占比分别变成 100% / 100% / 100% / 88%。
   ⚠️ 代价实测为 **0**：10 页 × 1440/1024/375 三档，h1 高度**一个像素都没变**
      （`/` 269→269、`/ru/` 336→336、`/de/dpa/` 63→63 …），
      行数也没变 —— 它只在**能保住行数**的前提下重排，排不动就不动手。
      `/` 那个 4 行的大标题 balance 前后逐行宽度完全一致（408/515 = 79%），
      说明它没去碰本来就不孤字的标题。
   ⚠️ 跟下面 `overflow-wrap:anywhere` 不冲突：一个管**在哪断**，一个管**怎么分配行宽**。 */
h1{font-size:64px;line-height:1.05;font-weight:700;letter-spacing:-.03em;
  overflow-wrap:anywhere;text-wrap:balance}
@media(max-width:1023px){h1{font-size:44px}}
@media(max-width:599px){h1{font-size:40px}}
/* 德语连字符 —— 2026-10-04 全站实测后**收窄到窄屏**（原来不分宽度一律开）。
   起因：hyphens:auto 不是「只在放不下时才断」，而是**只要能让行更满就断**，
   这跟上面 anywhere 的语义是两回事（原注释把两者混为一谈了）。
   桌面 A/B 实测（/de/，1366×768，栏宽 555px、字号 64px）：
     开：Künstliche Intelli- / genz für Unter- / nehmen — ohne / eine Zeile Code zu / ändern.   5 行 336px
     关：Künstliche / Intelligenz für / Unternehmen — / ohne eine Zeile / Code zu ändern.        5 行 336px
   行数与高度**一模一样** —— 宽栏下它一分钱好处没有，只把 64px 的标题切得七零八落。
   真正需要它的是窄屏：375px 下 /de/dpa/ 的「Auftragsverarbeitungsvertrag.」实测 451px、
   栏宽只有 343px，这时带连字符断才是德语正统排法
   （不带连字符的 anywhere 硬断会把一个词读成两个词）。
   767px 是全站既有的「窄屏」断点（栅格、步骤条都在这里塌成一列），沿用同一个口径。
   浏览器没有该语种断词表时 hyphens 会静默失效，那时由上面的 anywhere 接住。 */
/* 🔴 2026-10-04 晚扩到图内文字：h1 之外还有两处德语复合词在 375px 下被 anywhere 硬断
   （`scripts/midword_check.py` 实测，改 .ticks 后全站只剩这 3 处，
     其中 /de/dpa/ 的 h1 已被本条覆盖）：
     · /de/compliance/  .fig-sub  「Verantwortlicher-Auftragsverarbeiter-Kette」需 324px、栏宽 259px
     · /de/sla/         .fig-lead 「Verfügbarkeitsbedingungen」需 272px、栏宽 259px
   带上连字符断（Verfügbarkeits-bedingungen）才是德语正统排法；
   不带连字符的 anywhere 硬断会把一个词读成两个词。范围仍限窄屏，理由同上。
   ✅ 2026-10-04 晚**逐处截图核实过**（375px、device_scale_factor=3）：
     /de/compliance/ → 「Verantwortli-cher-Auftragsverarbeiter-Kette」
     /de/dpa/        → 「Auftragsverarbeitungs-vertrag.」
     /de/sla/        → 「Verfügbarkeitsbedingun-gen」
   三处都是**带可见连字符**的正规断词，不是硬断。
   🔴 尺子的坑（下次别再被它骗）：当时那把**一次性脚本**（`/tmp/midword.py`，临时文件，重启即清）
   量的是「最长词当成不可断的 nowrap 整串，比栏宽」，**它不认识 `hyphens:auto`** ——
   所以上面这 3 处改完它**照样报**，而且永远报这 3 处。
   扶正后的 `scripts/midword_check.py` 已经补上这个豁免（判据改成**合取**：
   「词比栏宽」**且**「没开 `hyphens:auto`」才算问题）。
   但**操作纪律不变**：尺子报的是**因**（词放不下），不是**果**（渲染出来什么样）。
   `hyphens:auto` 还依赖浏览器真有该语种断词表，没有时会**静默失效**，
   所以「某处词被劈开」必须**截图看渲染结果**才能定案，不能拿数字当结论。
   另一条独立线索佐证：`Verantwortlicher-Auftragsverarbeiter-Kette` 里**本来就带连字符**，
   浏览器在 `-` 后断行是默认行为，压根不需要 hyphens —— 那条从一开始就是误报。
   ⚠️ /de/sla/ 那处断完会剩「gen」三个字母独占一行，德语排版上不理想；
   但栏宽 259px 放不下 27 个字母的词，这是必然的取舍，**不是 bug**。 */
@media(max-width:767px){html[lang|="de"] h1,html[lang|="de"] .fig-lead,
  html[lang|="de"] .fig-sub,html[lang|="de"] .fig-kicker{hyphens:auto}}
.lede{margin-top:var(--s6);font-size:18px;color:var(--text-body);max-width:540px;line-height:1.6}
.lede code{font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:15px;
  background:rgba(124,92,255,.14);border:1px solid rgba(124,92,255,.28);
  color:#C9BCFF;padding:4px 8px;border-radius:var(--r-sm)}
.cta-row{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s10)}
@media(max-width:599px){.cta-row .btn{width:100%}}

/* 移动端 hero 按钮行 —— 给右下角 AI 客服浮球让出安全区
   ─────────────────────────────────────────────────────────────
   为什么必须让：浮球是 `position:fixed` 的，在 ≤480px 时固定占
   x ∈ [100vw−66px, 100vw−14px]、y ∈ [100vh−66px, 100vh−14px]；
   而 .wrap 的内容列一直铺到 100vw−16px。**两者必然重叠**，
   重叠的正好是主 CTA 按钮的右端（实测被压 50px 宽、2400px²，
   浏览器 hit-test 确认点下去弹的是聊天窗，不是按钮）。
   纵向躲不开：hero 的 CTA 行落点随标题/导语行数在 443–711px 之间浮动，
   浮球那条 52px 高的带子怎么摆都会撞上；缩小 hero 只会把某些页
   从「干净」推进带子里。所以只能**横向分离**。

   为什么收 .phero 而不收全站 .cta-row：.cta-band 在页面最底部，
   scrollY=0 时远在浮球带子下方，不会撞；那里的按钮保持满宽更有转化力。

   为什么按钮改成按文字自适应（width:auto）而不是在限宽行里撑满：
   实测撑满会变成「本该满宽的横条被截短」的观感；自适应则像
   有意为之的按钮组，浮球正好落在右侧空出来的地方。
   六个语种里最长的按钮文案（es「Solicitar presupuesto」）需 221px，
   360px 视口下这里留 260px —— 够用；white-space:normal 是窄屏兜底，
   万一真放不下就换行，绝不让 nowrap 把文字顶出按钮盒子。 */
@media(max-width:599px){
  .phero .cta-row{max-width:calc(100vw - 100px)}
  .phero .cta-row .btn{width:auto;white-space:normal}
}
/* ⚠️ 2026-10-05 改成**竖排**（原来是 `flex-wrap` 横排）。
   病根：`.facts` 在首屏左栏里，容器**只有 555px 宽**（且跟视口无关 ——
   1440px 下量出来也是 555），而四条「✓ 短句」横排需要 EN ~1025px、ES ~1370px
   ⇒ 必然换行，**换几行还看语种**。
   真浏览器实测（1440px、六语种）：EN 2 行 / RU 2 行 / DE 3 行 / ES·FR·PT 4 行，
   首屏总高被拖到 956~1099px，**六语种差 143px**。
   ⚠️ 试过 2×2 网格：行数统一了，但西/法/葡的句子会被从**短语中间**劈开
   （「35% below DeepSeek's / published list price」），比参差更难读。
   竖排（本条）实测：六语种**一律 4 行**、**没有一项被迫折行**，
   EN 首屏只高 5px（956→961），ES 反而矮 42px（1099→1057），语种间差 143→96px。
   剩下的 96px 差来自 h1 / 导语本身的长短，跟 `.facts` 无关。 */
.facts{display:flex;flex-direction:column;align-items:flex-start;gap:10px;
  margin-top:var(--s10);padding-top:var(--s6);border-top:1px solid rgba(255,255,255,.08)}
.fact{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--text-muted)}
.fact svg{flex:none;stroke:var(--neon-cyan)}

/* 代码面板 */
.code{background:rgba(7,10,20,.72);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border:var(--glass-border);border-radius:var(--r-xl);overflow:hidden;
  box-shadow:var(--shadow-card),var(--glass-inner-light),0 0 60px rgba(124,92,255,.18);
  animation:float 7s ease-in-out infinite}
.code-bar{display:flex;align-items:center;gap:8px;padding:16px 20px;
  border-bottom:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.02)}
.tl{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.14)}
.code-name{margin-left:8px;font-size:12px;color:var(--text-muted)}
.code pre{margin:0;padding:var(--s6) 0;font-size:13px;line-height:1.9;overflow-x:auto;
  font-family:"JetBrains Mono","Roboto Mono",SFMono-Regular,Menlo,monospace}
.code .ln{display:block;padding:0 var(--s6);white-space:pre}
.code .k{color:#C792EA}.code .s{color:#9EE6B4}.code .c{color:var(--text-muted)}
/* 🔴 2026-10-04：字色跟 `.c-add` / `.c-del`（第 1052 行那两个）**对齐**。
   原先这里是 #5BD98A / #F0776F，而 `.c-*` 那套是 #9EE6B4 / #FF8FA3 ——
   同一页上 hero 的 `- base_url` / `+ base_url` 跟第三步卡片里的是**同一段 diff**，
   却一套红绿一个样。实测两者相隔约 2700px，访客上下滚就看得见。
   ⚠️ 底色和左侧竖条**不动**：那是「整行 diff」的写法，跟行内片段本来就该长得不一样；
      要统一的是**同一个语义的颜色**，不是把两个组件做成同一个样子。 */
.code .add{color:#9EE6B4;background:rgba(91,217,138,.09);box-shadow:inset 2px 0 0 #9EE6B4}
.code .del{color:#FF8FA3;background:rgba(240,119,111,.08);box-shadow:inset 2px 0 0 #FF8FA3}
.code-foot{border-top:1px solid rgba(255,255,255,.08);padding:16px 20px;font-size:13px;
  color:var(--text-muted);display:flex;align-items:center;gap:8px}
.code-foot svg{stroke:var(--neon-cyan);flex:none}

/* ═══ 适用对象条 ═══ */
.strip{background:var(--bg-section);border-top:1px solid rgba(255,255,255,.06);
  border-bottom:1px solid rgba(255,255,255,.06);padding:var(--s8) 0}
.strip p{text-align:center;font-size:16px;color:var(--text-muted);line-height:1.7}
.strip strong{color:var(--text-body);font-weight:600}

/* 内页通栏（母版 §2.3 的节奏感）：长内页整页一个底色，滚起来像一大块。
   加一层稍亮的底把它切成几段，读者才知道「换话题了」。
   实现靠 page() 里的 <!--BAND--> 标记 —— 通栏必须是 .wrap 的**兄弟**才能满宽，
   所以在标记处收掉当前 wrap、另开一个 <section>。绝不用 100vw 逃逸：
   那个写法在带滚动条的浏览器上会多出几像素，360px 下必横向溢出（本站栽过）。 */
.band{background:var(--bg-section);border-top:1px solid rgba(255,255,255,.06);
  border-bottom:1px solid rgba(255,255,255,.06)}

/* ═══ 卡片栅格 ═══ */
.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s6);margin-top:var(--s12)}
@media(max-width:1023px){.grid3{grid-template-columns:1fr 1fr}}
@media(max-width:767px){.grid3{grid-template-columns:1fr}}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s6);margin-top:var(--s12)}
@media(max-width:767px){.grid2{grid-template-columns:1fr}}
.grid4{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s6);margin-top:var(--s12)}
@media(max-width:1023px){.grid4{grid-template-columns:1fr 1fr}}
@media(max-width:599px){.grid4{grid-template-columns:1fr}}
/* 栅格子项默认 min-width:auto，会被代码块里的长网址撑宽 → 整页横向溢出 */
.hero-in>*,.grid3>*,.grid2>*,.grid4>*,.steps>*,.bill>*,.form-grid>*,.foot-grid>*,.split>*{min-width:0}
.card{display:flex;flex-direction:column;height:100%}
.card h3{font-size:20px;margin-bottom:var(--s3)}
.card p{font-size:16px;color:var(--text-muted);line-height:1.65}
/* 🔴 2026-10-04 补 `min-height:2lh`：只给**「三张并排对着读」的矩阵卡**
   （`.num` 01/02/03 编号卡、`.obj-q` 客户顾虑卡）预留两行标题高度。
   为什么：这类卡本来就是横着比着看的，标题折成一行还是两行会把正文错开一行（23px）。
   实测（`de/` 顾虑卡）：标题 h=46 / h=23 / h=46 ⇒ 正文顶边 7141 / 7091 / 7114。
   🔴 **为什么不给所有卡片标题都加**：全站量过 —— 414 个卡片标题里 **386 个只有一行**
   （93%），183 个网格行里只有 **14 行**是「标题高度不一致」。
   普遍加 = 给 386 张卡白加 23px 空白，只为修 14 行，不划算（试过，已回退）。
   普通卡片标题折行导致正文错一行，是卡片网格的正常表现，不动。
   `lh` = 「当前元素的行高」单位；浏览器不支持时这条自动失效，退回原样。 */
.card > .num + h3,
.card > .obj-q + h3{min-height:2lh}
.card > .btn,.card > .card-link{margin-top:auto;align-self:flex-start}
/* 🔴 2026-10-05 补：`.spec-tag` 在卡片里**被拉成整条横幅**，看着像一条空输入框。
   病根：`.spec-tag{display:inline-block}`（作者本意是「贴着文字包住」），
   但它的父级是 `.card{display:flex;flex-direction:column}` ⇒ 作为 flex 子项
   默认吃 `align-items:stretch`，被拉到整卡宽 —— `display:inline-block` 白写了。
   证据：同页同一种芯片，`.meta li`（「AP intake」「High volume」）**都是贴着文字的**，
   只有 `.spec-tag` 变横条 ⇒ 同一个页面里同一种视觉元素两种长相，正是「乱」。
   范围：**恰好 24 处、6 个语种首页**，全部是 `.card` 的直接子元素。
   全仓库搜过：**没有任何注释或文档说明这个拉伸是故意的**，
   而 `display:inline-block` 明摆着作者要的是「包住文字」。
   写法照抄上面 `.card > .btn` 那条同款（`align-self:flex-start` 才是这个模式的正解）。
   ⚠️ **不要**给它加 `margin-top:auto` —— 那是 `.btn` 用来钉卡底的，
      `.spec-tag` 是芯片不是按钮，钉底会把它浮到半空。 */
.card > .spec-tag{align-self:flex-start}
/* 🔴 2026-10-04 补：卡片**最后一行**是「芯片行 `.meta`」或「角色标签 `.obj-role`」时，
   把它钉到卡片底边。
   为什么：`.card` 是 `display:flex;flex-direction:column;height:100%`，网格行里
   每张卡被拉伸成等高。但 `.meta` / `.obj-role` 原来只是**跟在正文后面**——
   同一行里哪张卡的标题或正文多折了一行，它的芯片行就整体往下掉。
   全站量过（192 页）：**31 页、49 处没对齐**，其中 **41 处**就是这两个东西，
   最差一处差 **79px**。
   它俩本来就各带一条上分隔线（`border-top`），钉底之后分隔线落在卡片同一高度——
   这才是设计本意。`.scene .slug{margin-top:auto}` 早就是同一套做法，这里只是补齐。
   ⚠️ **只钉「最后一个孩子」**。全站 416 张卡里还有 68 张是别的结构
      （60 张 `.meta` 后面跟 `.card-link`、8 张 `.meta` 后面跟正文），
      那些不加 auto —— 加了会把芯片行浮到半空，反而更乱。
   ⚠️ 别改成 `.card > .meta`（不加 `:last-child`）。 */
.card > .meta:last-child,
.card > .obj-role:last-child{margin-top:auto}
/* 卡片上那个「01 / 02 / 03」的步骤编号。
   🔴 2026-10-04 改色：原为 `var(--brand-violet)`（#7C5CFF），12px 当正文算，
   实测对比度在四档背景里**三档不够**：bg-elevated **4.05** / bg-deep-blue **4.23**
   / bg-section **4.38**（只有 bg-base 4.55 勉强过），AA 要 4.5。
   改用 `#C9BCFF` —— 这**不是新造的颜色**，项目里已经有 4 处拿它做
   「紫调小字」（`.chip` 之类的标签），这里跟它们统一。
   改后四档背景全在 **10.15~11.41**，余量很足。
   ⚠️ 别改回 `var(--brand-violet)`；也别为了省事把它改成 `--text-muted` ——
      那样就丢了这一处的紫色强调，跟页面其它编号的视觉语言对不上。 */
.num{display:block;font-size:12px;font-weight:600;letter-spacing:.1em;
  color:#C9BCFF;margin-bottom:var(--s6)}
.spec-tag{display:inline-block;margin-top:var(--s4);padding:4px 12px;border-radius:var(--r-pill);
  background:rgba(34,211,238,.10);border:1px solid rgba(34,211,238,.28);
  font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--neon-cyan)}

/* 卡片里的结构化信息条（视觉丰富度闸要「卡片不能只有标题+一句话」） */
.meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--s4);padding-top:var(--s4);
  border-top:1px solid rgba(255,255,255,.08)}
/* ⚠️ 同样是防 360px 横向溢出：`.meta` 是 `display:flex`，每个 `li` 是 flex 子项，
   默认 min-width:auto 不肯缩到比内容最小宽度窄。装不下长串时（如
   `base_url="https://api.workhorseapi.com/v1"`）就会顶破卡片。
   理由与 `.ticks li` 那条相同，见上面的详细说明。 */
.meta li{font-size:12px;font-weight:500;letter-spacing:.02em;color:var(--text-muted);
  padding:4px 10px;border-radius:var(--r-pill);
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.10);
  overflow-wrap:anywhere}
.meta li.k{color:var(--neon-cyan);border-color:rgba(34,211,238,.28);background:rgba(34,211,238,.08)}
/* min-height:24px 是为了 WCAG 2.5.8 的 24×24 线。
   2026-10-04 实测（375px）：它是 inline-flex、`align-items:center`，
   盒高由里面最高的那个孩子（14px 文字的行盒 / svg）撑出来 = **23.1px**，
   差 **0.9px** 掉到线下面 —— 全站 72 处（违反 AA 的 97 处里它占大头）。
   ⚠️ 论理它**大概率适用 WCAG 的「间距豁免」**（四周 24px 内没有别的可点目标），
   但**修它只要 0.9px**，比写一篇豁免论证省事得多，而且以后不用再解释。
   `align-items:center` 已经在，所以长高 0.9px 文字仍然居中 —— **视觉上零变化**。 */
.card-link{display:inline-flex;align-items:center;gap:6px;margin-top:var(--s4);
  min-height:24px;
  font-size:14px;font-weight:600;color:var(--neon-cyan);
  transition:gap 180ms ease-out,color 180ms ease-out}
.card-link:hover{gap:12px;color:#7FE7F5}
.card-link svg{stroke:currentColor;flex:none}

/* ═══ 三步流程（母版 §5.1 第 6 屏：发光连线）═══ */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s6);margin-top:var(--s12)}
/* 🔴 2026-10-05 补两条。先说清一件事：`.steps` 这个类名全站被**两种东西共用** ——
   ① `<div class="steps">` 装 `.step` 卡：本站设计的三列流程条（`/get-started/` 那种）；
   ② 小语种内容里手写的 `<ol class="steps">`：一个**带编号的有序列表**，
      作者只是借了这个类名，结构上根本不是 `.step` 卡。
   下面两条按类名匹配、不分标签，两种都管。 */

/* 一、4 条的流程条：三列会把第 4 条孤零零甩到第二行，右边三分之二整片空着。
   全站 40 块 `.steps` 实测（1440px）：3 条的 25 块、4 条的 15 块。
   改成 2×2 之后 —— 3 条那 25 块**列数 / 行分布 / 高度一个字节没变**（`:has` 不匹配）；
   4 条的三块（/get-started/ /ru/get-started/ /de/quality/）块高
   419 / 444 / 392 → **366 / 392 / 365**，375px 下仍是一列。 */
.steps:has(> :nth-child(4)){grid-template-columns:repeat(2,1fr)}

/* 二、`.split` 的窄栏里塞不下三列。
   `.split` 是 `1fr 1fr`（≥1024px 两栏、≤1023px 塌成一栏），所以每栏只有 544px；
   三列各剩 152~165px，西/葡语一个短语被折成 7~9 行。
   实测最长一条的折行数（1440px，块高）：
     /contact/            12 行 → 竖排 7 行（303→560）
     /es/contact/          9 行 → 3 行（238→206）
     /es/product/billing/  7 行 → 2 行（367→283）
     /es/production/       9 行 → 3 行（238→233）
     /pt-br/sla/           5 行 → 1 行（262→178）
   12 个 `<ol>` 块是**折行少了、块还更矮**；只有 `/contact/` 那个卡片式的变高
   （3 张卡竖排、满宽 544px —— 12 行挤成 7 行，读起来顺得多）。
   ⚠️ 试过 2×2：3 条的那种第二行左边留个洞，且 260px 栏里照样折 9 行，不如竖排。
   ⚠️ 只在 ≥1024px 生效 —— 1024px 以下 `.split` 自己就是单栏，`.steps` 拿的是整幅宽
      （900px 下 868px），那时三列并排是对的，别动。 */
@media(min-width:1024px){.split .steps{grid-template-columns:1fr}}

/* 窄屏一律一列。⚠️ 这条必须排在上面两条**之后** ——
   `.steps:has(> :nth-child(4))` 和 `.steps` 权重不同（0,2,0 vs 0,1,0），
   不把 `:has` 那版一起写进来，4 条的在 375px 会变成两列。 */
@media(max-width:767px){.steps,.steps:has(> :nth-child(4)){grid-template-columns:1fr}}
.step{position:relative;padding-top:var(--s6)}
.step::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;border-radius:2px;
  background:var(--grad-neon);opacity:.55}
.step .n{font-size:12px;font-weight:600;letter-spacing:.1em;color:var(--neon-cyan);
  margin-bottom:var(--s4);display:block}
/* 🔴 2026-10-04 补 `min-height:2lh`：三步流程是「01 标题 正文」三列并排。
   标题一行还是两行，会把下面的正文整体顶下去一行。
   实测（`contact/`）：标题 h=23（一行）/ h=46 / h=46 ⇒ 正文顶边 873 / 896 / 896。
   预留两行后三列正文都从同一条线开始。 */
.step h3{font-size:20px;margin-bottom:var(--s3);min-height:2lh}
.step p{font-size:16px;color:var(--text-muted)}

/* ═══ 场景卡（= /product/{slug} 的入口）═══ */
.scene{display:flex;flex-direction:column;height:100%}
.scene .ic{width:48px;height:48px;margin-bottom:var(--s6)}
.scene h3{font-size:18px;margin-bottom:var(--s2)}
.scene p{font-size:16px;color:var(--text-muted);line-height:1.6}
.scene .slug{margin-top:auto;padding-top:var(--s4);
  font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--text-muted)}
.note{margin-top:var(--s8);font-size:16px;color:var(--text-muted)}

/* ═══ 让法务/财务点头 ═══ */
/* 🔴 2026-10-04 补 `min-height:2lh`：这条引言是「客户顾虑」卡的第一行，
   它下面还压着 h3 / 正文 / 角色标签。同一行三张卡里，只要有一张的引言折成
   两行、另两张只有一行，**下面所有东西就整体错开一行（26px）**。
   实测（`de/`）：引言 h=53（两行）/ h=26 / h=26 ⇒ 三张卡的 h3 顶边 7083 / 7056 / 7056。
   预留两行高度后三张卡的 h3 都在同一条线上。
   `lh` 是「当前元素的行高」单位，`2lh` = 两行，浏览器不支持时这条自动失效
   （退回今天的样子，不会更糟）。 */
.obj-q{font-size:16px;color:var(--text-strong);font-weight:600;font-style:italic;margin-bottom:var(--s4);
  min-height:2lh}
.obj-role{margin-top:var(--s6);padding-top:var(--s4);border-top:1px solid rgba(255,255,255,.08);
  font-size:12px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--text-muted)}

/* ═══ 计费 ═══ */
.bill{display:grid;grid-template-columns:1fr 1fr;gap:var(--s12);align-items:start;margin-top:var(--s12)}
@media(max-width:900px){.bill{grid-template-columns:1fr;gap:var(--s8)}}
.bill-list li{display:flex;gap:var(--s4);padding:var(--s4) 0;font-size:16px;color:var(--text-body);
  border-bottom:1px solid rgba(255,255,255,.08)}
.bill-list li:last-child{border-bottom:0}
.bill-list svg{flex:none;margin-top:4px;stroke:var(--neon-cyan)}
.bill-card h3{font-size:22px;margin-bottom:var(--s3)}
.bill-card p{font-size:16px;color:var(--text-muted)}
.bill-card .btn{margin-top:var(--s6);width:100%}

/* ═══ FAQ ═══ */
.faq{margin-top:var(--s10);border-top:1px solid rgba(255,255,255,.08)}
details{border-bottom:1px solid rgba(255,255,255,.08)}
summary{cursor:pointer;list-style:none;position:relative;padding:var(--s6) 48px var(--s6) 0;
  font-size:18px;font-weight:600;color:var(--text-strong);min-height:44px;
  display:flex;align-items:center;transition:color 180ms ease-out}
summary::-webkit-details-marker{display:none}
summary:hover{color:#fff}
summary::after{content:"";position:absolute;right:10px;top:50%;width:9px;height:9px;
  border-right:2px solid var(--neon-cyan);border-bottom:2px solid var(--neon-cyan);
  transform:translateY(-70%) rotate(45deg);transition:transform 200ms ease-out}
details[open] summary::after{transform:translateY(-20%) rotate(-135deg)}
/* 🔴 2026-10-04 晚：820px → **648px**。820 是本文件里最宽的一条正文限制（约 89 字/行），
   实测 FAQ 正文段里有 19 处超 80 字/行。
   为什么不是直接写 600：这条规则带 `padding-right:48px`（没有左边距、没有底色），
   `max-width` 管的是**含内边距的盒子**，所以盒宽 648px 时**文字实际宽度才是 600px** ——
   和 `.prose` / `main .wrap >` 那几处的正文右边缘**对得齐**。
   直接写 600px 会让 FAQ 正文比它上面的段落窄 48px，右边缘错开一条，看着像没对齐。 */
details .body{padding:0 48px var(--s6) 0;font-size:16px;color:var(--text-muted);
  max-width:648px;line-height:1.7}
details .body p + p{margin-top:var(--s4)}
details .body code{font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:14px;
  background:rgba(124,92,255,.14);border:1px solid rgba(124,92,255,.28);color:#C9BCFF;
  padding:4px 8px;border-radius:var(--r-sm)}
details .body a{color:var(--neon-cyan);text-decoration:underline;text-underline-offset:3px}
/* 2026-10-04 全站 192 页实测补漏：FAQ 有**两套写法**，上面那条 `details .body` 只治得了第一套。
     <details><summary>…</summary><div class="body"><p>…</p></div></details>  → 539 处 / 95 页  ✓ 被 820px 管住
     <details><summary>…</summary><p>…</p></details>                          →  46 处 / 12 页  ✗ 谁都没管
   第二套的裸 `<p>` 是 `details` 的**直接子元素**，不在 `.body` 里；
   而下面「正文段落限宽」那条 `main .wrap > p:not([class])` 用的是 `>`（只管直接子级），
   `details` 又嵌在 `div.faq` 里、`div.faq` 嵌在 `div.wrap` 里 —— 隔了两层，选择器够不着。
   真浏览器实测（1366px 视口）：这 12 页的正文段排到 **1152px ≈ 122 字符/行**，
   可读区间是 60–80；`max-width` 计算值是 `none`。
   12 页全在俄语站（`/ru/get-started/`、`/ru/pricing/`、`/ru/use-cases/`、
   `/ru/solution/` 5 页、`/ru/vs/` 4 页）。补 600px，跟全站正文同一个宽度标准（见 1199 行那条）。
   ⚠️ 只卡 `:not([class])`：带类的 `<p>`（如 `<p class="note">`）各有各的既有约束，不碰。
   🔴 2026-10-04 晚：原写 760px，实测这 12 页仍有段落排到 87 字/行，随全站一起收到 600px。
   ⚠️ 这条**不带内边距**（裸 `<p>` 直接是 `details` 的子元素），所以 600 就是文字宽 600 ——
   和上面 `details .body` 那条（要写 648 才等于文字 600）不是一回事，别照抄。 */
details > p:not([class]),
details > ul:not([class]),
details > ol:not([class]){max-width:600px}
@media(max-width:599px){summary{font-size:16px;padding-right:36px}
  details .body{padding-right:0}}

/* ═══ 表单（母版 §6.7）═══ */
.form-sec{background:var(--bg-section);border-top:1px solid rgba(255,255,255,.06)}
.form-grid{display:grid;grid-template-columns:.85fr 1.15fr;gap:var(--s16);align-items:start;margin-top:var(--s10)}
@media(max-width:1023px){.form-grid{grid-template-columns:1fr;gap:var(--s10)}}
.ticks{margin-top:var(--s8)}
/* 🔴 2026-10-04 晚：**把 flex 拿掉了** —— 它才是 360px 溢出的根因，不是 anywhere 该修的东西。
   2026-09-28 的原始记录（诊断对、药是止痛药）：6 个俄语页在 360px 下横向溢出 7~37px，
   页面能左右拖；根因是 `li` 的 flex 子项默认 `min-width:auto`，
   遇到 `deepseek-v4-pro` / `https://api.workhorseapi.com/v1` 这类不可断行的长串就拒绝收缩。
   当时之所以用 `anywhere` 而不是 `break-word`：只有 `anywhere` 会**同时压低最小内容宽度**，
   而 `min-width:auto` 正是拿最小内容宽度算的 —— 用 `break-word` 等于没修。
   ⇒ `anywhere` 保留（下面仍在用），但真正该去掉的是 flex。问题在于：
   `li` 是 `display:flex` ⇒ `<svg>`、`<strong>`、以及紧随其后的那段文字
   **各成一个 flex 子项**（连续文本会被包成「匿名 flex 子项」）。HTML 里明明写的是
   `<strong>小标题。</strong> 后面接同一句话`（一句话），却被 flex 拆成两栏。
   实测后果（真浏览器，192 页）：
     · 375px：148 处词被从中间劈开，145 处在 .ticks 里 ——
       "contract"→"contra/ct"、"negotiated"→"negoti/ated"、"Applications"→"Applicat/ions"。
       因为 `anywhere` 会把子项的 min-content 压到 ≈1 个字符，那一栏就被压成几个字母宽。
     · 桌面 1440px：小标题栏宽度每条都不一样，正文起始 x 三个条目三个位置（看着像表格坏了）。
   ⇒ 正解是**不要 flex**：`li` 回到普通块，图标绝对定位到左边。
      文本重新变成一句话，在空格处正常换行；`anywhere` 仍然留着，
      但从此只在 `https://api.workhorseapi.com/v1` 这种**真放不下的整串**上动手。
   ⚠️ 图标位置要和旧 flex 版**像素级一致**：旧的是 padding-top 12px + margin-top 4px = 16px，
      左边 18px 图标 + 12px 间距 ⇒ 文字左缩进 30px。
      改完必须重跑 `scripts/midword_check.py` 对账。 */
.ticks li{position:relative;padding:var(--s3) 0 var(--s3) 30px;font-size:16px;color:var(--text-body);
  overflow-wrap:anywhere}
.ticks li > svg{position:absolute;left:0;top:16px;stroke:var(--neon-cyan)}
/* 🔴 2026-10-04 晚（第九十三段）：`.ticks` 里**没有后缀说明文字**的链接，
   点按区只有 **20px** 高 —— 低于公司量具 `ui_qa.py` 的「次要文字链接 24px」下限。

   怎么发现的：`evidence_pack.py` ① 移动端断言（192 页 × 3 断点真渲染）报
   **12 项 FAIL，全部落在 6 个 `/sitemap/` 页**（en 18 个 / ru 21 个 / de·es·fr·pt 各 3 个）。
   真浏览器逐条量出来的读数（360px 视口）：
     · `<a>` 一律 `display:inline`、`line-height:26.4px`，但**盒子高只有 20px**
       （inline 元素的 `getBoundingClientRect()` 取的是**字体内容盒**，不含行高留白）；
     · `li` 高 50.4px（= `--s3` 12px × 2 + 行高 26.4px）。

   🔴 **为什么同一页里有的链接被判、有的没被判** —— 公司量具第 46~51 行有条**句内豁免**：
     `inlineInText = a.parentElement.textContent.trim().length > a.innerText.trim().length + 12`
   ⇒ `<li><svg/><a>Acceso a la API…</a> — un endpoint, una clave</li>`（**链接后面还跟着说明**）
      被豁免；`<li><svg/><a>DeepSeek API access</a></li>`（**光秃秃一个链接**）不豁免 ⇒ 判 24px。
   所以 de/es/fr/pt 只有**法务那 3 条**（隐私/条款/DPA，同样没带说明）中招，
   **en / ru 的 sitemap 整页链接都是光秃秃的** ⇒ 18 / 21 条全中。

   ⚠️ **为什么不改量具**：这把尺子是公司 `ui_qa.py` 的**逐字照搬**（2026-09-28 业务负责人裁定
   「重要的 44、次要的 24」），**改它 = 让闸闭嘴**（母规则 M15 禁）。
   而且 WCAG 2.5.8 的「间距例外」其实放得过这一页（`li` 间距 50.4px，24px 圆不相交）——
   **但公司基线没实现这个例外，公司基线说了算**，所以**改页面**。

   ⚠️ **为什么用 `padding-block` 而不是 `display:inline-block`**：
   `inline-block` 会让链接**不能跨行断开**。360px 下俄语最长那条
   `Соглашение об обработке данных` 实测 270px、`Сравнение с официальным DeepSeek` 297px，
   可用宽只有 360 − 30(左缩进) = 330px —— **再长一点就溢出**，等于用新 bug 换旧 bug。
   `display:inline` + 纵向 padding：**padding 算进 border box，撑大点按区**，
   但**不参与行盒高度计算** ⇒ 排版一个像素都不动。

   📐 **4px 是怎么定的（保证不与上下行重叠）**：
     字体内容盒 20px、行高 26.4px ⇒ 上下各 3.2px 留白（half-leading）。
     `padding-block:4px` ⇒ 盒子 28px（≥24，留 4px 余量），
     只超出留白 0.8px，而 `li` 自身上下还有 12px padding ⇒ **净空 12.8px，绝不会压到邻行**。 */
.ticks li a{display:inline;padding-block:4px}
form{background:var(--glass-bg);backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border:var(--glass-border);border-radius:var(--r-xl);padding:var(--s10);
  box-shadow:var(--shadow-card),var(--glass-inner-light),0 0 60px rgba(124,92,255,.12)}
@media(max-width:599px){form{padding:var(--s6)}}
.field{margin-bottom:var(--s6)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:var(--s4)}
@media(max-width:599px){.row2{grid-template-columns:1fr}}
label{display:block;font-size:14px;font-weight:500;color:var(--text-body);margin-bottom:var(--s2)}
.req{color:var(--neon-cyan)}.opt{color:var(--text-muted);font-weight:400}
input,select,textarea{width:100%;min-height:48px;background:rgba(7,10,20,.6);
  border:1px solid rgba(255,255,255,.12);color:var(--text-strong);
  padding:12px 16px;border-radius:var(--r-sm);font-size:16px;font-family:inherit;
  transition:border-color 180ms ease-out,box-shadow 180ms ease-out}
input::placeholder,textarea::placeholder{color:var(--text-muted)}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand-violet);
  box-shadow:var(--glow-violet);background:rgba(10,14,28,.85)}
select{appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238A93A8' stroke-width='2.4'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 16px center;padding-right:44px}
select option{background:#0B0F1F;color:var(--text-strong)}
textarea{resize:vertical;min-height:96px;line-height:1.6}
.consent{display:flex;gap:var(--s3);align-items:flex-start;margin:var(--s8) 0 var(--s6);
  padding-top:var(--s6);border-top:1px solid rgba(255,255,255,.08)}
/* 🔴 24×24 不是「好看」，是 **WCAG 2.5.8（Level AA）的「目标尺寸最小」线**。
   原来是 20×20 —— 2026-10-04 用 `scripts/tap_target_check.py` 在 375px 下量全站，
   **违反 AA 的 97 处里有 25 处是它**（每个表单一个：联系页 / 博客 / 帮助 / DPA × 6 语种）。
   ⚠️ 它在**提交按钮正上方** —— 线索型站（铁律 1）最关键的转化点上，
   手指点不中的代价是**直接丢线索**，不是「体验小瑕疵」。
   ⚠️ 标签虽然关联了（`<label for="f-consent">`，点文字也能勾），
   但 WCAG 量的是**控件自己的盒子** —— 20 就是 20，不因为标签能点就过关。
   🔴 这里**故意不做「视觉 20px + 隐形放大点击区」那种技巧**：
   原生 checkbox 的勾选标记画在控件盒子里，用 padding / border 撑大会让勾**看起来错位**。
   直接写 24px，所见即所点。

   🔴🔴 2026-10-05 二次改（第一百零二段）—— **上面「所见即所点」当时只做了一半**。
   用户 2026-10-02 原话「很多页面效果排版都很差包含但不限于 Ui 差 乱 配色」。
   查「配色」查到原生控件这一层，发现勾选框**是浏览器画的、不在本站调色板里**：
   `color-scheme:dark` 下未勾状态 = **中性灰方块**
   （实测填充 `rgb(59,59,59)` / 描边 `rgb(133,133,133)`，压在 `rgb(19,24,46)` 的卡片上）
   —— 跟海军蓝 + 紫 + 青的调色板完全不搭，六个语种的每个表单都有它。
   ⇒ 「所见即所点」还差一半：**看得见，但不是我们的颜色**。

   ⚠️ 三条实测结论，别绕回去走老路：
   ① **`accent-color` 只管「已勾」那一态**（第 1034 行原来就写着它）——
      未勾态一直是 UA 的中性灰，`accent-color` 管不着。
   ② **Chrome 对原生 checkbox 的 `border-color` / `background-color` 直接忽略**
      —— 拿两条变体（只给 border-color / 给 border-color + background-color）截图，
      出来是**一模一样**的灰方块。⇒ **`appearance:none` 是唯一的路**，
      没有「更保守的写法」可退。
   ③ 🔴 **必须写 `padding:0`** —— 这一条差点漏掉，根因是**本站自己**的：
      第 1009 行 `input,select,textarea{…padding:12px 16px…}` 是**按标签名**写的通配选择器，
      把 `input[type=checkbox]` 也吃进去了（浏览器 UA 对勾选框的内边距本来就是 0）。
      原生外观在时看不见；`appearance:none` 一去掉原生外观，这圈内边距立刻露出来：
      `box-sizing:border-box` 下 `width:24px` 会被「padding+border 撑出来的 34px」顶掉，
      **实测盒子变成 34×26（不是方的）**。
      ⚠️ 一般教训：`input,select,textarea` 这种**标签名通配选择器会连勾选框/单选框/提交按钮一起吃**。

   ✅ 三条既有决定**都保留**：还是 24×24（WCAG 2.5.8）；还是不搞隐形放大点击区；
      还是「所见即所点」—— 只不过现在**看到的就是我们自己画的、24×24 的、有颜色的方框**。
   ✅ 高对比模式（forced-colors）实测：系统会接管配色，本规则不会让它消失，**不需要另写兜底**。
   ✅ `check_contrast.py` 不会因此误报 —— 它第 293 行只找 `color:<值>`，
      不匹配 `border` / `background` / `border-color`。
   勾是 `::after` 画的（1.5px 边框下 padding 盒 21×21，`left:7.5/top:5` 的 6×11 方块
   旋转 45° 后中心正好落在 padding 盒正中心）。 */
.consent input{width:24px;height:24px;min-height:24px;flex:none;margin-top:4px;
  appearance:none;-webkit-appearance:none;position:relative;
  /* 🔴 见上面 ③：`appearance:none` 会露出第 1009 行漏进来的 12px 16px 内边距，
     不写这行盒子就是 34×26。 */
  padding:0;
  border:1.5px solid rgba(255,255,255,.42);border-radius:7px;
  background:rgba(255,255,255,.07);
  cursor:pointer;transition:background .15s,border-color .15s}
.consent input:hover{border-color:rgba(255,255,255,.62)}
.consent input:checked{background:var(--brand-violet);border-color:var(--brand-violet)}
.consent input:checked::after{content:'';position:absolute;left:7.5px;top:5px;
  width:6px;height:11px;border:solid #fff;border-width:0 2.5px 2.5px 0;
  transform:rotate(45deg)}
/* 焦点态：必须单独写 —— 第 1014 行 `input:focus{outline:none}`（0,1,1）
   盖得过第 99 行全局 `:focus-visible`（0,1,0）。带上全局那条的青色光晕，
   但**不抄它的 border-radius**（那会把圆角从 7px 改成 --r-sm，一聚焦就跳一下）。 */
.consent input:focus-visible{outline:2px solid var(--neon-cyan);outline-offset:2px;
  box-shadow:0 0 0 4px rgba(34,211,238,.25)}
.consent label{margin:0;font-size:14px;font-weight:400;color:var(--text-muted);line-height:1.6}
.consent a{color:var(--neon-cyan);text-decoration:underline;text-underline-offset:3px}
form .btn{width:100%}
/* 🔴 满宽按钮撞上 `.btn` 自带的 white-space:nowrap（行 94）⇒ 窄屏长文案被顶出按钮盒子。
   2026-10-04 实测（360px，法语联系页/博客/帮助/DPA 等 12 页）：
   「Envoyer votre volume et recevoir un prix」要 279px，表单内容盒只有 246px
   ⇒ 文字左右两头各戳出约 16.5px，紫色底衬不住它（不是被裁，是**溢在背景外面**）。
   六个语种里只有法语踩中（西语 157.77px、德语 143.14px 都放得下）；
   768px 起表单变宽，不再触发 —— 所以这条只管 ≤599px。
   兜底做法与 §phero 那条既定先例一致（见 `.phero .cta-row .btn`，行 533）：
   放不下就换行，绝不让 nowrap 把文字顶出盒子。 */
@media(max-width:599px){form .btn{white-space:normal;line-height:1.3}}
.form-note{margin-top:var(--s4);font-size:16px;color:var(--text-muted);text-align:center}

/* 表单提交后的提示条（2026-09-27 第五段补；2026-09-29 接后台时加了状态）。
   状态：invalid = 必填没填全；sending = 正在发；sent = **后台确认收到了**；
        error = 发送失败；not-sent = 旧的「还没接通」说明（留着，万一要退回）。
   🔴 「已发送 / Sent / Отправлено」**只能在收到后台成功响应之后**出现 ——
   对一个线索型站，让客户以为联系上了但实际没人收到，比按钮点不动更糟：
   他不会再去试别的渠道，这条线索就彻底没了。 */
.form-msg{margin-top:var(--s4);padding:var(--s3) var(--s4);border-radius:10px;
  font-size:16px;line-height:1.6;text-align:center;border:1px solid transparent}
.form-msg[data-state="invalid"]{color:#FFB4B4;border-color:rgba(255,107,107,.45);
  background:rgba(255,107,107,.10)}
.form-msg[data-state="not-sent"]{color:var(--text-muted);border-color:rgba(255,255,255,.14);
  background:rgba(255,255,255,.04)}
.form-msg[data-state="sending"]{color:var(--text-muted);border-color:rgba(255,255,255,.14);
  background:rgba(255,255,255,.04)}
.form-msg[data-state="sent"]{color:#9EE6B4;border-color:rgba(91,217,138,.45);
  background:rgba(91,217,138,.10)}
.form-msg[data-state="error"]{color:#FFB4B4;border-color:rgba(255,107,107,.45);
  background:rgba(255,107,107,.10)}

/* 蜜罐（反机器人）：真人看不见、键盘也 Tab 不到（输入框带 tabindex="-1"），
   有值即判定为机器人，后台静默丢弃。
   ⚠️ 必须用绝对定位挪出视口，**不能**用 display:none / visibility:hidden /
   type="hidden" —— 那几种机器人一眼就认出是陷阱，反而不填了，等于白设。 */
.form-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

/* ═══ 页脚（母版 §6.8）═══ */
footer{background:var(--bg-base);padding:var(--s16) 0 var(--s10);position:relative}
footer::before{content:"";position:absolute;top:0;left:0;right:0;height:1px;
  background:var(--grad-neon);opacity:.28}
.foot-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:var(--s10)}
@media(max-width:900px){.foot-grid{grid-template-columns:1fr 1fr;gap:var(--s8)}}
@media(max-width:599px){.foot-grid{grid-template-columns:1fr}}
.foot-brand p{margin-top:var(--s4);font-size:16px;color:var(--text-muted);max-width:280px}
.foot-col h2{font-size:14px;font-weight:600;color:var(--text-strong);margin-bottom:var(--s4)}
.foot-col li{margin-bottom:4px}
/* 🔴 触屏目标 ≥24px（WCAG 2.5.8 非内联链接）：行高不够时用 padding 撑开。
   2026-09-27 第一次真跑 ui_qa 就是栽在这里 —— 11 个页脚链接全 18px 高。 */
.foot-col a{display:inline-flex;align-items:center;min-height:32px;font-size:15px;
  color:var(--text-muted);transition:color 180ms ease-out}
.foot-col a:hover{color:var(--text-strong)}
.socials{display:flex;gap:var(--s3);margin-top:var(--s6)}
.socials a{width:44px;height:44px;border-radius:var(--r-md);display:flex;align-items:center;justify-content:center;
  background:var(--glass-bg);border:var(--glass-border);
  transition:box-shadow 180ms ease-out,border-color 180ms ease-out}
.socials a:hover{box-shadow:var(--glow-violet);border-color:rgba(124,92,255,.45)}
.socials svg{stroke:var(--text-body)}
.foot-bot{margin-top:var(--s12);padding-top:var(--s6);border-top:1px solid rgba(255,255,255,.08);
  display:flex;flex-wrap:wrap;gap:var(--s4);justify-content:space-between;align-items:center;
  font-size:14px;color:var(--text-muted)}
.lang{display:flex;gap:var(--s4);margin-left:auto}
/* 🔴 页脚面板**向上**展开：页脚是文档最后一块，往下开就掉到文档外面去了。
   ⚠️ `margin-left:auto` 是给手机档兜底的：`.foot-bot` 是 flex + space-between + wrap，
      窄屏上版权那行文字会把语言块**挤到第二行**，而 space-between 对「单独占一行的
      唯一元素」是**靠左**排 —— 那时 `right:0` 的面板就会往左戳出视口。
      加上 margin-left:auto 后，语言块永远靠右，面板永远往左展开，戳不出去。 */
.lang .lang-menu{top:auto;bottom:calc(100% + 10px)}

/* ═══ 内页通用 ═══ */
/* 内页头（比首页英雄区矮，带面包屑） */
.phero{padding:calc(72px + var(--s16)) 0 var(--s12);background:var(--grad-hero);
  border-bottom:1px solid rgba(255,255,255,.06);position:relative;overflow:hidden}
.phero::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.4;
  background-image:linear-gradient(rgba(124,92,255,.06) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(124,92,255,.06) 1px,transparent 1px);
  background-size:48px 48px;animation:grid 20s linear infinite}
.phero-in{position:relative;max-width:820px}
.phero h1{font-size:44px}
@media(max-width:1023px){.phero h1{font-size:34px}}
@media(max-width:599px){.phero h1{font-size:30px}}
.crumbs{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:var(--s6);
  font-size:13px;color:var(--text-muted)}
.crumbs a{display:inline-flex;align-items:center;min-height:32px;
  color:var(--text-muted);transition:color 180ms ease-out}
.crumbs a:hover{color:var(--neon-cyan)}
.crumbs .sep{color:var(--text-muted)}
.crumbs [aria-current]{color:var(--text-body)}

/* 图文两栏 */
/* 🔴 2026-10-05 把 `align-items:center` 改成 `start`：并排两栏**顶部对齐**。
   病根：`center` 的本意是「文字栏相对大图栏垂直居中」——
   但**全站 89 处 `.split`，没有一处有媒体列**（`.split-media` 在 192 个页面里
   **0 次使用**，下面那两条 `.split-media` 规则是死代码）。
   🔴 **2026-10-05 更正一句**：这里原本还跟着「`:has()` 全站也没用过」——
   **写这条的时候是实情，现在不是了**，所以删掉，免得后来人以为 `:has()` 在本项目没先例。
   本文件现在**三处**在用 `:has()`（只写选择器，不写行号 —— 行号会漂）：
   `.steps:has(> :nth-child(4))`、窄屏档里把同一条再写一遍的那处、
   以及 `.fig-flow:has(.fig-node) .fig-panel`。
   于是 `center` 只在干一件事：**把「两栏文字」里较矮的那栏往下推半截**，
   推出来的就是「两栏标题不在一条线上」。
   实测（1440px，等动画停）：**89 处里 74 处顶边不齐 ≥8px**，最狠的
   `/fr/production/` 差 **454px**（左栏 618 高、右栏 1527 高，左栏被推下去大半屏）
   —— 读者先看到右边标题，左边是一大片空的。**这正是用户说的「乱」。**
   A/B 注入实测：改 `start` 后 **89 处顶边全齐（仍不齐 0 处）**，
   且**没有一处媒体列被误伤**（因为没有媒体列）。
   尺子 `scripts/` 里那份普查自带自检：故意居中的假两栏（300 vs 100 高）
   必须量出 100px 差、改成 start 必须量出 0 —— 两条都过才认数。
   ⚠️ 没顺手删下面两条 `.split-media` / `.split.flip` 规则：那是另一件事（清死代码），
      而且 `flip` 这个名字在别处（`fig-split2`）有别的含义，删之前要单独核。
      保留它们也意味着：**将来真加了图文两栏，记得同时把居中补回来**
      （写成 `.split:has(.split-media){align-items:center}`，或给图文那栏单独一个类）。 */
.split{display:grid;grid-template-columns:1fr 1fr;gap:var(--s16);align-items:start}
@media(max-width:1023px){.split{grid-template-columns:1fr;gap:var(--s10)}}
.split.flip .split-media{order:-1}
@media(max-width:1023px){.split.flip .split-media{order:0}}

/* 配图：图上**一个字的字都没有**，所有标签都是网页上的真文字。
   为什么必须这样（两件事逼的）：
     1) 公司铁律 F6「图上禁止任何文字/水印，文案一律 HTML 叠加」；
     2) 原来字画在 1200px 宽的 SVG 里，360px 手机上 16px 的标签被缩到 4.57px，
        根本读不出来 —— 而且图里的字 Google 搜不到、读屏软件也念不出来。
   内容（每张图写什么字）在 scripts/site_figs.py，这里只管长相。
   窄屏断点选 899px：768px 平板下三个框并排每格只剩 ~180px，20px 的标题会碎成
   一行八九个字，所以提前改成一列、箭头朝下。 */
.fig{margin:0}
.fig figcaption{margin-top:var(--s3);font-size:13px;color:var(--text-muted);line-height:1.6}
.fig-note{display:block;font-style:normal;color:var(--text-muted)}

/* ── 画布 ─────────────────────────────────────────────────────────────── */
.fig-stage{position:relative;border-radius:var(--r-lg);border:var(--glass-border);
  background:radial-gradient(120% 130% at 50% 0%,#151D3E 0%,#0B1024 55%,#080C18 100%);
  box-shadow:var(--shadow-card),var(--glass-inner-light);
  padding:var(--s8) var(--s6);overflow:hidden}
/* 原图里那张霓虹网格背景，改用 CSS 画，省掉 48 个文件 */
.fig-stage::before{content:"";position:absolute;inset:0;pointer-events:none;
  background-image:linear-gradient(rgba(124,92,255,.09) 1px,transparent 1px),
    linear-gradient(90deg,rgba(124,92,255,.09) 1px,transparent 1px);
  background-size:44px 44px;
  -webkit-mask-image:radial-gradient(120% 95% at 50% 0%,#000 22%,transparent 100%);
  mask-image:radial-gradient(120% 95% at 50% 0%,#000 22%,transparent 100%)}
.fig-stage > *{position:relative;z-index:1}

.fig-eyebrow{margin:0 0 var(--s6);font-size:16px;font-weight:600;letter-spacing:1.3px;
  text-transform:uppercase;color:var(--text-muted);overflow-wrap:anywhere}

/* ── 一横行：面板 → 箭头 → 圆节点 → 箭头 → 面板 ───────────────────────
   框和箭头**画在底图里**（prototype/assets/img/fig-<slug>.svg，只有图形没有字），
   文字用同一套栅格叠在上面。对齐靠「两边用同一个百分比」：
   这里 repeat(3,1fr) + gap:4% → 每条带正好 30.667%，位置 0 / 34.667% / 69.333%；
   底图 gen_fig_art.py 的 _bands() 算出来是同一个数。
   ⚠️ 改这里任何一个数字，必须同时改 gen_fig_art.py，否则文字会静静叠歪。 */
.fig-flow{position:relative;display:grid;align-items:stretch;gap:4%;
  grid-template-columns:repeat(3,1fr)}
/* `:not(.fig-art)` 不能省：底图也是这些容器的直接子元素，而 `> *` 跟 `.fig-art`
   同权重、又写在后面，会把底图的 position:absolute 盖回 relative，底图就掉进栅格占一格。 */
.fig-flow > *:not(.fig-art),.fig-steps > *:not(.fig-art){position:relative;z-index:1}

/* 底图：铺满整个栅格，拉伸到正好等于容器（preserveAspectRatio="none"）。
   窄屏会藏起来 —— 见 899px 那个断点。 */
.fig-art{position:absolute;inset:0;z-index:0;display:block;
  width:100%;height:100%;pointer-events:none}

/* ── 面板 ─────────────────────────────────────────────────────────────── */
/* 底图已经画了框，所以桌面端这里不再画背景和边框（画了就重复、还盖住底图）。
   窄屏底图藏起来时，由 899px 断点把背景和边框加回来。

   🔴 `justify-content:flex-start`（2026-10-05 从 center 改过来，别再改回去）。
   事由：`.fig-flow{align-items:stretch}`（上面 1126 行）会把同一行三个面板拉成
   **等高**，`center` 再把每个面板的内容在各自盒子里垂直居中 ⇒ **内容高的那个
   面板往上顶、矮的往下坠**，三栏的 eyebrow 和小字**对不齐**。

   📏 量它的尺子 = `scripts/census_fig_align.py`（**不是**临时脚本；这条注释
      最早引的是 `/tmp/figprobe.py`，那把已经扶正成正式脚本了）。
      🔴 它的判据是**分档的**，别把两档混成一个数：
        · **不夹圆节点**（三栏并排，`_flow`）：判据是「**顶线**齐」——
          改前实测最狠 25px，改后 **0px**。这一档才是本条要修的。
        · **夹圆节点**（`_workload` / `_request_flow`）：判据是「**中线**齐」，
          首行 top **天生就差一截**。实测 `/product/deepseek-api/`
          左栏首行 471px、右栏 426px，**差 45px** —— 截图上看**三栏中线是齐的**，
          **这是设计，不是缺陷**。所以那 45px 改前改后**都是 45px**，
          本条**没有**、也**不应该**去动它。

   ⚠️ 但**不能一刀切**：`_workload` / `_request_flow` 那种「左右两块面板夹一个
   中间圆节点」的图（`site_figs.py`，共 84 张），`center` 正是把两边和圆节点
   对齐的那条规则 —— 见下面那条 `:has(.fig-node)`。
   两种图必须分开对待，判据是**有没有 `.fig-node`**（有 = 夹节点 = 居中；
   没有 = 三栏并排 = 顶对齐）。
   ✅ 改这条时做过**逐像素反证**：夹圆节点的图改前/改后各截一张，
      `PIL.ImageChops.difference().getbbox()` 返回 `None`、最大差 **0**
      —— 证明这条改动对那一档**一个像素都没动**。 */
.fig-panel{border-radius:var(--r-lg);padding:var(--s6);min-width:0;
  display:flex;flex-direction:column;justify-content:flex-start}
/* 夹着中间圆节点的图，两边面板继续居中（改前就是这个效果，别动）。 */
.fig-flow:has(.fig-node) .fig-panel{justify-content:center}
.fig-panel--in .fig-kicker,.fig-panel--out .fig-kicker,
.fig-panel--mid .fig-kicker{color:var(--neon-cyan)}

.fig-kicker{margin:0;font-size:16px;font-weight:600;letter-spacing:1.1px;
  text-transform:uppercase;color:var(--text-muted);overflow-wrap:anywhere}
.fig-lead{margin:var(--s3) 0 0;font-size:20px;font-weight:600;line-height:1.3;
  color:var(--text-strong);overflow-wrap:anywhere}
.fig-sub{margin:var(--s2) 0 0;font-size:16px;line-height:1.5;color:var(--text-muted);
  overflow-wrap:anywhere}
.fig-sub--extra{margin-top:var(--s4);color:var(--text-body)}

/* 输入/输出那几条示意小横杠（原来画在 SVG 里） */
.fig-bars{display:grid;grid-template-columns:132fr 72fr;gap:var(--s2);
  margin-top:var(--s6);max-width:212px}
.fig-bars i{display:block;height:8px;border-radius:4px;min-width:0}
.fig-bars i:nth-child(3){grid-column:1;width:73%}
.fig-panel--in .fig-bars i{background:rgba(34,211,238,.55)}
.fig-panel--out .fig-bars i{background:rgba(158,230,180,.55)}

/* ── 中间那个圆节点（模型/端点）───────────────────────────────────────── */
.fig-node{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s3);min-width:0;text-align:center;padding:var(--s6)}
.fig-glyph{display:grid;place-items:center;width:92px;height:92px;border-radius:50%;
  color:#EDE9FF;border:1.5px solid rgba(124,92,255,.75);
  background:radial-gradient(62% 62% at 50% 34%,rgba(124,92,255,.52),rgba(124,92,255,.16));
  box-shadow:0 0 34px rgba(124,92,255,.45)}
.fig-glyph-svg{display:block}
.fig-node-name{margin:0;font-size:16px;line-height:1.4;color:var(--text-body);
  overflow-wrap:anywhere}
.fig-node-sub{margin:0;font-size:16px;line-height:1.4;color:var(--text-muted);
  overflow-wrap:anywhere}

/* ── 图下方那排要点 ───────────────────────────────────────────────────── */
.fig-points{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--s4) var(--s6);margin:var(--s6) 0 0;padding:var(--s6) 0 0;list-style:none;
  border-top:1px solid rgba(124,92,255,.28)}
.fig-points li{position:relative;padding-left:var(--s6);font-size:15px;line-height:1.5;
  color:var(--text-body);overflow-wrap:anywhere}
.fig-points li::before{content:"";position:absolute;left:0;top:7px;width:7px;height:7px;
  border-radius:50%;background:var(--neon-cyan);box-shadow:var(--glow-cyan)}

/* ── 对照表（真 <table>，不是画出来的）─────────────────────────────────
   表格要 table-layout:fixed，列宽才会**正好**是 20% / 40% / 40% ——
   底图里那两条带就是按这三个数画的（gen_fig_art.py 的 _TABLE_COLS）。
   用 auto 布局的话列宽由内容决定，底图的带子会跟列对不上。 */
.fig-tablewrap{position:relative;overflow-x:auto;-webkit-overflow-scrolling:touch}
.fig-tablewrap > .fig-art{inset:0 auto 0 0;width:100%}
.fig-compare{position:relative;z-index:1;width:100%;border-collapse:collapse;
  font-size:15px;table-layout:fixed}
.fig-compare thead th{width:40%}
.fig-compare th,.fig-compare td{text-align:left;vertical-align:top;
  padding:var(--s4);border-top:1px solid rgba(255,255,255,.08)}
.fig-compare thead th{border-top:0;padding-top:0;padding-bottom:var(--s6)}
.fig-compare-corner{padding:0;border-top:0}
.fig-compare tbody th{width:20%;padding-left:0;font-size:13px;font-weight:600;
  letter-spacing:.4px;color:var(--text-muted)}
.fig-compare td{color:var(--text-body)}
.fig-compare .fig-compare-us{color:var(--text-strong)}
.fig-compare .fig-kicker{display:block}
.fig-compare thead .fig-compare-us .fig-kicker{color:var(--neon-cyan)}
.fig-compare .fig-lead{display:block;margin-top:var(--s2);font-size:19px}
.fig-compare .fig-sub{display:block;margin-top:var(--s2);font-size:14px;font-weight:400}

/* ── 请求路径版式底部那条「我们负责什么」─────────────────────────────── */
.fig-band-title{margin:var(--s8) 0 0;font-size:16px;font-weight:600;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--text-muted)}
.fig-chips{display:flex;flex-wrap:wrap;gap:var(--s3);margin:var(--s4) 0 0;padding:0;
  list-style:none}
.fig-chips li{padding:var(--s2) var(--s4);border-radius:var(--r-pill);font-size:14px;
  line-height:1.4;color:var(--text-body);overflow-wrap:anywhere;
  background:rgba(124,92,255,.14);border:1px solid rgba(124,92,255,.4)}

/* ── 接入三步（带编号）────────────────────────────────────────────────── */
.fig-legend{display:flex;flex-wrap:wrap;gap:var(--s3);margin:0 0 var(--s6)}
.fig-legend span{padding:5px var(--s4);border-radius:var(--r-pill);font-size:12px;
  font-weight:600;letter-spacing:1.6px;text-transform:uppercase;color:var(--text-muted);
  background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.18)}
/* 🔴 2026-10-04：这两个 chip 是**底图的图例**，颜色必须跟底图对得上。
   底图（`fig-*-art3.svg`）画的是「白卡 / 紫卡 / 白卡」—— 中间那张紫的才是「我们办的」，
   窄屏兜底 CSS（`.fig-steps > li:nth-child(2)`，见第 1087 行）用的也是同一套。
   原先第一个 chip 写成**青色**，可底图里根本没有青色卡片（青色只用在箭头上）——
   图例在指一个不存在的颜色。改成紫 = 对上高亮卡；第二个 chip 走上面的默认灰 = 对上白卡。

   ⚠️ 「对上底图」这件事**是 `background` 和 `border-color` 干的**，两个都是
      `rgba(124,92,255,…)`（底图那张紫卡就是 `#7C5CFF`，见 `gen_fig_art.py` 的
      `stroke="#7C5CFF"` / 渐变 `bA`）。**字色不参与对色** —— 底图上根本没有字
      （DoD 第 98 行：图内检出任何文字即 FAIL，这也是同一张图能跨语言复用的原因）。
      所以字色该用全站那一个浅紫 `#C9BCFF`，跟 `.lede code` / `.prose code` /
      `.tbl code` / `.num` / `.chip` 同一套。原先这里写的是 `#C4B5FD`，
      **全站只此一处**，跟 `#C9BCFF` 差 RGB(5,7,2) —— 肉眼几乎分不出，
      但「一个角色两个值」就是配色乱的起点，而且后来人不知道哪个才算数。
      实测对比度（chip 底 = `rgba(124,92,255,.14)` 压页面底，两种底色都算）：
      `#C4B5FD` 9.52 / 9.00，`#C9BCFF` **10.14 / 9.59** —— 换过去只升不降。 */
.fig-legend span:first-child{color:#C9BCFF;background:rgba(124,92,255,.14);
  border-color:rgba(124,92,255,.5)}
/* 跟 .fig-flow 同一套栅格（repeat(3,1fr) + gap:4%），底图共用同一张图。
   框同样由底图画，这里只留内边距。 */
/* 外面这层只是给底图当定位参照 —— `<ol>` 里不许放 <img>，所以底图放这一层 */
.fig-steps-wrap{position:relative}
.fig-steps{display:grid;grid-template-columns:repeat(3,1fr);gap:4%;
  margin:0;padding:0;list-style:none}
.fig-steps > li{border-radius:var(--r-lg);padding:var(--s6);min-width:0}
.fig-step-n{display:grid;place-items:center;width:32px;height:32px;margin:0 0 var(--s4);
  border-radius:50%;font-size:15px;font-weight:700;color:#EDE9FF;
  background:rgba(124,92,255,.35);border:1.5px solid rgba(124,92,255,.75)}
.fig-step-extra{margin-top:var(--s4)}
.fig-step-extra .fig-code{margin-top:0}

/* ── 代码块（接入示例 / JSON 输出）────────────────────────────────────── */
.fig-code{margin:var(--s4) 0 0;padding:var(--s4);border-radius:var(--r-md);
  overflow-x:auto;background:rgba(7,10,20,.65);border:1px solid rgba(255,255,255,.10);
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;line-height:1.7;color:var(--text-body)}
.fig-code code{white-space:pre}
.c-k{color:#9CC6FF}
.c-s{color:#9EE6B4}
.c-n{color:#F5C97A}
.c-p{color:var(--text-muted)}
.c-del{color:#FF8FA3}
.c-add{color:#9EE6B4}
.c-com{color:var(--text-muted)}

/* ── 扫描件进 → JSON 出 ───────────────────────────────────────────────── */
/* 两栏：跟底图的 2band（两条 48% + 4% 缝）对齐 */
/* 🔴 2026-10-05 两栏改成**等高**：这是「左右两半看不出是一对」的根因。
   病根：左边是「扫描件」浅色纸（`.fig-doc` 的 min-height 只有 172px），
   右边是 JSON 代码块（自然高 279px），`align-items:start` 让两栏各按各的高度走
   ⇒ **两张卡一高一矮，卡下面那行小字（`.fig-note-inline`）差 107px**，
   本来是一组「输入 → 输出」对照，看起来像两个不相干的块。
   实测（1440px，等动画停）：6 个语种首页 + `/product/deepseek-ocr/` +
   `/solution/pdf-to-structured/` 共 **12 处，全部差 107px**，一处不落
   —— 是**模板**的毛病，不是某一页。
   改法：`align-items:stretch` 让两栏等高 ⇒ 卡片 `flex:1 0 auto` 吃掉多出来的高度
   ⇒ 两边的说明行落在同一条线上。
   ⚠️ 用 `flex:1 0 auto` 不用 `flex:1`：`flex:1` 的 basis 是 0，
      会**把代码块压矮**（`.fig-code` 是 `overflow-x:auto`，压矮就出竖向滚动条）。
   A/B 注入实测：说明行错位 107px → **0px**，卡片高 [172,279] → **[279,279]**，
      5 个代表页（`/` `/ru/` `/product/deepseek-ocr/`
      `/fr/solution/pdf-to-structured/` `/pt-br/`）全部归零。
   `.fig-doc` 里的横线加 `justify-content:center`：纸被拉高后线全挤在顶上，
      居中之后才像一张真的单据（对比过截图，居中的更自然）。 */
.fig-split2{position:relative;display:grid;grid-template-columns:repeat(2,1fr);
  gap:4%;align-items:stretch}
.fig-split2 > *:not(.fig-art){position:relative;z-index:1}
.fig-col{min-width:0;display:flex;flex-direction:column}
.fig-col > .fig-doc,.fig-col > .fig-code{flex:1 0 auto}
.fig-col > .fig-doc{justify-content:center}
/* 左边那张纸：纯装饰（aria-hidden），故意做成浅色，一眼看出是「扫描件」 */
.fig-doc{display:flex;flex-direction:column;gap:var(--s3);min-height:172px;
  margin-top:var(--s4);padding:var(--s6);border-radius:var(--r-md);
  background:rgba(255,255,255,.94);border:1px solid rgba(255,255,255,.5);
  box-shadow:0 10px 30px rgba(0,0,0,.4)}
.fig-doc-line{flex:0 0 auto;display:block;width:var(--w);height:8px;border-radius:3px;
  background:rgba(10,18,48,.22)}
.fig-doc-line--accent{background:rgba(124,92,255,.55)}
.fig-note-inline{margin:var(--s3) 0 0;font-size:16px;font-style:italic;
  color:var(--text-muted);overflow-wrap:anywhere}

/* ── 窄屏：一列。底图藏起来，框改由 CSS 画回来 ──────────────────────────
   为什么藏：底图是**横排三条带**，窄屏文字改成竖排一列后，横带的框就跟文字对不上了，
   留着只会让人以为框错了位置。藏掉之后背景和边框由下面这两条补回来，
   文字照样有框、照样能读。
   （图仍然在 HTML 里 —— 公司闸是静态数 `<img>` 标签，不看视口。） */
@media(max-width:899px){
  .fig-art{display:none}
  .fig-flow{grid-template-columns:minmax(0,1fr);gap:var(--s3);justify-items:stretch}
  .fig-steps{grid-template-columns:minmax(0,1fr);gap:var(--s3)}
  .fig-node{max-width:none}
  .fig-points{grid-template-columns:minmax(0,1fr);gap:var(--s3)}
  /* 框补回来（桌面端由底图画，这里底图没了，得自己画） */
  .fig-panel--in,.fig-panel--step,.fig-steps > li{background:rgba(255,255,255,.045);
    border:1px solid rgba(255,255,255,.14)}
  .fig-panel--out,.fig-panel--mid,.fig-steps > li:nth-child(2){background:rgba(124,92,255,.10);
    border:1px solid rgba(124,92,255,.45);box-shadow:0 0 32px rgba(124,92,255,.16)}
}
@media(max-width:719px){
  .fig-stage{padding:var(--s6) var(--s4)}
  .fig-split2{grid-template-columns:minmax(0,1fr)}
  /* 对照表窄屏改成「一行一张卡」：列名藏起来、贴到每个值前面（data-col） */
  /* thead 必须一起改成 block —— 漏了它的话它保持浏览器内部表格显示类型
     （table-header-group），浏览器会给它套一个匿名表格盒、按「收缩到内容宽」算，
     于是表头比下面的行窄一截（2026-10-04 实测 412px：表头 306px / 行首 346px，
     卡片右边缘差 40px 错位；360px 因为内容正好占满才看不出）。
     改 block 后由 1044 行那条把里面的 tr 设成 flex，表头依旧是竖排。 */
  .fig-compare,.fig-compare thead,.fig-compare tbody,.fig-compare tr,
  .fig-compare th,.fig-compare td{display:block;width:auto}
  .fig-compare thead tr{display:flex;flex-direction:column;gap:var(--s4);
    padding-bottom:var(--s6)}
  .fig-compare thead .fig-compare-corner{display:none}
  /* 🔴 width:auto 必须写在**这两条**上，不能只靠上面那条 `.fig-compare th`。
     权重算给你看：`.fig-compare th` = (0,1,1)，而桌面端的
     `.fig-compare thead th{width:40%}`(行 947) 和
     `.fig-compare tbody th{width:20%}`(行 952) 都是 (0,1,2) —— **桌面那条赢**，
     于是窄屏下标签列仍被钉在 20%。360px 时表宽 294px ⇒ 标签列只有 58.8px，
     德语「Abrechnungseinheit」要 135px、`table-layout` 又不许列自己撑宽
     ⇒ 文字直接压到右边那张卡上（2026-10-04 实测：de/es 对照页共 51 处）。
     这两条选择器与 947/952 同权重、且位置在后 ⇒ 后写的赢，才盖得住。 */
  .fig-compare thead th{padding:0;border-top:0;width:auto}
  .fig-compare tbody tr{padding:var(--s4) 0}
  .fig-compare tbody th{width:auto;padding:0 0 var(--s3)}
  .fig-compare td{padding:var(--s3) 0 0}
  .fig-compare td::before{content:attr(data-col);display:block;margin-bottom:4px;
    font-size:12px;font-weight:600;letter-spacing:1.4px;text-transform:uppercase;
    color:var(--text-muted)}
  .fig-compare .fig-compare-us::before{color:var(--neon-cyan)}
}

/* 正文排版（法务页/文档页） */
/* 🔴 2026-10-04 晚：**760px → 600px**。这是「排版差」里最后一块、也是最大的一块。
   760px 是几个字：16px 字、字均 8.68px（含空格）⇒ 约 **87 字/行**，最长的一行到 100 字。
   而**本文件 1248 行自己写明的可读区间是「60–80 字符」** —— 这条规则一直没达到自己定的标准。
   真浏览器全站 192 页实测：**203 处段落超 80 字/行**，覆盖 85 页，分布在
   div.wrap(151) / div.prose(30) / details .body(19) / div.note(2) / div.glass(1)。
   600 这个数怎么来的：把候选宽度**真注入页面**，用 Range.getClientRects() 数**真实行数**
   （不是拿「字符宽度」估 —— 估会低估每行字数，那是上一版尺子的毛病）：
        720px → 仍超 142 处 · 680 → 85 · 660 → 49 · 640 → 24 · **600 → 0**（最宽 77 字/行）
   600px 是唯一达标值，不是挑了个好看的数。
   ⚠️ **五处必须一起改**，它们是一件事的五个入口，漏一个就有页面还是 87 字/行：
   `.read` 栅格列(1436) · `main .wrap > …`(1319) · `details .body`(810) · `details > p`(831)
   · 本条 `.prose`(1250)。
   ⚠️ 尤其别漏 `.read`：`.read .prose{max-width:none}`(1437) 把 `.prose` 自己的限宽**撤掉了**，
   长文页（blog/dpa）真正管宽度的是 `.read` 的栅格列 —— 只改这条对它们一点用都没有。 */
.prose{max-width:600px}
.prose h2{font-size:26px;margin-top:var(--s12)}
.prose h3{font-size:19px;margin-top:var(--s8)}
/* h3 兜底（2026-10-04 真浏览器全站实测补）。**没被任何规则管到的 h3 会掉回浏览器默认
   18.72px 且上下外边距全 0** —— 同一个 /production/ 页型，英语 20px / 上 24px / 下 12px，
   德语 18.72px / 上 0 / 下 0（小标题**紧贴上下两段**）。实测分布：英 0 处、俄 0 处、
   德 18 处（/de/privacy/ 2 · /de/production/ 12 · /de/vs/ 4）、西/法/葡各 2 处。
   ⚠️ 这是**兜底不是覆盖**：`.card h3`(20) / `.scene h3`(18) / `.step h3`(20) /
   `.bill-card h3`(22) / `.related h3`(18) / `.prose h3`(19) 的权重都比 `main h3` 高，
   一个都不会被改动 —— 改完必须重跑真浏览器逐页量一遍确认。

   🔴 2026-10-04 当晚自我修正：**第一版把 `margin-top` 也写进来了，那是个 bug。**
   上面那 6 条规则里，`.card / .step / .scene / .bill-card / .related` **全都只写
   `margin-bottom`、故意不写 `margin-top`** —— 因为它们上面的 `.ic` 图标自带
   `margin-bottom:24px` 顶着间距。兜底规则一加 `margin-top:32px`，卡片里就凭空
   多出 32px 空隙（实测 `/certificates/` 的「Our certifications」卡：图标与标题之间
   从 24px 变 56px，整张卡从 266px 撑到 298px，看着像坏掉）。
   所以**字号可以全局兜底，外边距不行** —— 外边距只补给真正缺它的那种：
   裸 `<h3><p>` 直接挂在 `.wrap` 下（德/西/法/葡的 production / privacy / vs 页型，
   实测德 18 处、西法葡各 2 处）。`.split` 里和对比图里的裸 h3 不需要，别给。
   下间距 `margin-bottom` 全局给是安全的：只有 `.prose h3` 没写它，
   补上 12px 反而比原来的 0 更好（标题紧贴正文才是问题）。 */
main h3{font-size:19px;margin-bottom:var(--s3)}
main .wrap > h3{margin-top:var(--s8)}
.prose p{margin-top:var(--s4);font-size:16px;color:var(--text-body);line-height:1.75}
.prose ul{margin-top:var(--s4);padding-left:0}
.prose ul li{position:relative;padding-left:var(--s6);margin-top:var(--s3);
  font-size:16px;color:var(--text-body);line-height:1.7}
.prose ul li::before{content:"";position:absolute;left:6px;top:11px;width:6px;height:6px;
  border-radius:50%;background:var(--neon-cyan);box-shadow:var(--glow-cyan)}
/* 正文里的链接：内联元素的 rect 量的是**字体内容盒**（16px 字约 20px 高），
   不是行盒（26.4px）。所以「整段就是一个链接」的那种，点按区只有 20px —— 低于 24px 底线。
   padding-block 把点按区撑到 26px，仍在 26.4px 行盒内，**不重叠、不改任何视觉**
   （内联元素的上下内边距不参与排版）。
   ⚠️ 两处都要写：`.prose` 有 6 页在用，而正文段落多数走的是不带类的 `div.wrap > p`。 */
.prose a,
main p a{color:var(--neon-cyan);text-decoration:underline;text-underline-offset:3px;
  padding-block:3px}
.prose strong{color:var(--text-strong)}
.prose .lede-note{margin-top:var(--s6);padding:var(--s4) var(--s6);border-radius:var(--r-md);
  background:rgba(124,92,255,.08);border:1px solid rgba(124,92,255,.24);
  font-size:15px;color:var(--text-body)}
.prose code{font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:14px;
  background:rgba(124,92,255,.14);border:1px solid rgba(124,92,255,.28);color:#C9BCFF;
  padding:4px 8px;border-radius:var(--r-sm)}

/* 正文段落限宽 —— 上面那条 `.prose{max-width:600px}` 只管**带那层包裹**的页面
   （英俄的法务/博客页，全站 8 页）。其余页面的正文段是**直接挂在 `.wrap` 下**的
   裸 `<p>`，`--maxw` 是 1200px（减左右 24px 留白 = 内容宽 1152px），
   16px 字排下来约 **130 字符/行**，远超可读区间（60–80 字符）——
   真浏览器实测：`/de/privacy/` 1147px、`/product/billing/` 1134px。
   这里给那些页面统一补上宽度（跟 `.prose` 同一个值，保持全站一致）。
   🔴 2026-10-04 晚：这个值从 760px 收到 **600px**（理由与实测数据见 1199 行那条）。
   这 151 处是全站超长段落的最大来源，收完这一段，全站超 80 字/行的段落从 203 处归零。
   ⚠️ 列表里 `div.glass` 和 `p.note-warn` 自带内边距（glass 32px、note-warn 24px），
   所以它们的**文字**会比 600px 窄一点（glass 534px、note-warn 552px）—— 这是**故意**的：
   这两个是**有底色/有边框的盒子**，盒子的外边缘跟正文列对齐才对，文字往里缩是正确的观感。
   （FAQ 的 `details .body` 不一样：它没有底色边框、只有一侧内边距，所以那条要写 648。）
   ⚠️ 故意**不碰**这几类（各有各的既有约束或布局，限宽会改观感）：
     · `.lede`（首屏导语，自带 540px）      · `.sub`（小标题，自带 660px）
     · `.meta`（药丸行，flex 换行）          · `.steps`（三步格，3 列网格）
   选择器用 `:not([class])` 卡住「裸 `<p>`」，其余按类名逐个点名，
   这样将来新增带类的 `<p>` 不会被默默改宽。

   2026-10-04 第二轮（真浏览器全站 160 页实测补漏）：上面那条只治「文字**直接**
   挂在 `.wrap` 下」的。还有三类**多包了一层容器**，实测同样超宽，一并补上：
     · `div.note`（提示块，about/contact/use-cases）—— 与 `p.note` 同一套文字样式
     · `article`（西/法博客正文，12 段 1112–1143px）—— 英俄博客走 `.prose`，这两边没有
     · `div.glass`（提示卡片，sla/use-cases/ocr-api）—— 卡是通栏的，里面的字同样超宽 */
main .wrap > p:not([class]),
main .wrap > p.note,
main .wrap > p.note-warn,
main .wrap > p.prose-meta,
main .wrap > ul.ticks,
main .wrap > div.note,
main .wrap > article,
main .wrap > div.glass{max-width:600px}

/* 🔴 上面那条限宽**误伤了首页的横幅**（2026-10-05 修）。
   `.strip` 是「整幅底色 + 一行居中的字」的适用对象条，
   它的 `<p>` 正好长成 `main .wrap > p:not([class])` 的样子 ⇒ 被压成 600px 宽，
   而 `text-align:center` 只在**那 600px 里**居中 ⇒ 视觉上整行贴着 1200px 容器的左边。
   真浏览器实测（1440px、六语种）：文字左留白 24~74px、右留白 576~626px，
   **左右差 552px**；1024px 下差 376px；只有 375px 窄屏是对的（盒子本来就窄）。
   修法：把横幅从限宽里摘出来。实测 `max-width:none` 在 1440/1024 下
   六语种**左右差 0px 且都保持一行**（文字实宽 676~915px，容器 976~1152px）。
   ⚠️ 试过「收到 820px 再居中」：西/法/葡会被挤成两行（858/915/843 > 820），
   所以这里**不能**加限宽 —— 横幅是**一句话**，不是正文段落，可读性限宽不适用。
   选择器带上 `main` 和 `.wrap` 是为了跟上面那条**同权重**、靠**位置靠后**生效。 */
main .strip .wrap > p{max-width:none}

/* 规格表 */
.tbl{width:100%;border-collapse:collapse;margin-top:var(--s8);font-size:15px}
.tbl caption{text-align:left;font-size:13px;color:var(--text-muted);padding-bottom:var(--s3)}
.tbl th,.tbl td{text-align:left;padding:12px 16px;border-bottom:1px solid rgba(255,255,255,.08);
  vertical-align:top}
.tbl th{width:38%;color:var(--text-strong);font-weight:600}
.tbl td{color:var(--text-muted)}
.tbl tr:last-child th,.tbl tr:last-child td{border-bottom:0}
/* 引文的出处。做小做淡，是为了让人读得到、但不跟正文抢注意力 */
.tbl .src{display:block;margin-top:6px;font-size:12px;color:var(--text-muted);opacity:.7;
  line-height:1.5}
.tbl code{font-family:"JetBrains Mono",SFMono-Regular,Menlo,monospace;font-size:14px;
  background:rgba(124,92,255,.14);border:1px solid rgba(124,92,255,.28);color:#C9BCFF;
  padding:2px 8px;border-radius:var(--r-sm);
  /* 代码标识符是要照着抄的，从中间断行会让人以为换行是内容的一部分 */
  white-space:nowrap}

/* 页尾行动条 */
.cta-band{background:var(--bg-section);border-top:1px solid rgba(255,255,255,.06);
  border-bottom:1px solid rgba(255,255,255,.06);padding:var(--s16) 0}
.cta-band .wrap{display:flex;flex-wrap:wrap;gap:var(--s8);align-items:center;justify-content:space-between}
.cta-band h2{font-size:28px;max-width:560px}
.cta-band p{margin-top:var(--s3);font-size:16px;color:var(--text-muted);max-width:560px}
.cta-band .cta-row{margin-top:0}

/* 相关页卡片组 */
.related{margin-top:var(--s12)}
.related h3{font-size:18px;margin-bottom:var(--s6)}
.related-list{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s4)}
@media(max-width:767px){.related-list{grid-template-columns:1fr}}
.related-list a{display:block;padding:var(--s6);border-radius:var(--r-md);
  background:var(--glass-bg);border:var(--glass-border);font-size:15px;font-weight:600;
  color:var(--text-strong);transition:border-color 180ms ease-out,box-shadow 180ms ease-out}
.related-list a:hover{border-color:rgba(124,92,255,.45);box-shadow:var(--glow-violet)}
.related-list a span{display:block;margin-top:4px;font-size:13px;font-weight:400;color:var(--text-muted)}

/* ═══ 滚动进场（母版 §8.1）═══ */
.reveal{opacity:0;transform:translateY(24px);
  transition:opacity 560ms cubic-bezier(.22,1,.36,1),transform 560ms cubic-bezier(.22,1,.36,1)}
.reveal.in{opacity:1;transform:none}

/* ═══ 减少动态效果降级（母版 §8.8）═══ */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .hero::before,.phero::before,.code,.pill .dot{animation:none}
  .reveal{opacity:1;transform:none;transition:opacity 200ms linear}
  .glass:hover{transform:none}
  .btn-primary:hover{transform:none}
  *{transition-duration:.01ms!important;animation-duration:.01ms!important}
  /* 时长压到 0.01ms 还不够 —— 抽屉的逐项 stagger 是 transition-delay，
     延迟不归零的话，关闭动效后打开抽屉仍会一项一项慢慢冒出来。 */
  .drawer-nav > *{transition-delay:0ms!important}
  .drawer-nav a{opacity:1;transform:none}
}

/* ═══ 2026-09-27 补：内页生成器用到的组件 ═══ */

/* 表格在窄屏横向滚动，不撑破布局 */
.tbl-wrap{overflow-x:auto;margin-top:var(--s8);-webkit-overflow-scrolling:touch}
.tbl-wrap .tbl{margin-top:0;min-width:520px}
/* 🔴 2026-10-04 晚：**手机档把 520px 下限撤掉，改成缩排塞进屏幕**。
   起因：真浏览器普查（`scripts/census_tables.py`，192 页 × 375px）——全站 76 个表
   （57 个 2 列 + 19 个 3 列）**无一例外都藏起 177px**，占 343px 可视宽度的 34%；
   而且**一个都不显示「能左右滑」的迹象**。实测后果（当时截的对比图）：
   2 列表的第二列——也就是真正有内容的那一列——整列看不见，
   caption（"Source: DeepSeek help center…"）也被裁成半句。
   为什么改成缩排而不是「保留滚动 + 加滑动提示」：
     · 缩排能让**全部 76 个表一次都不用滚**（下面这组数就是实测出来的，不是估的）；
     · 滑动提示要么写死文字（本站 6 个语种，得写 6 份），要么用渐变遮罩
       （表格底色随 section 变，遮罩色对不上就会露馅）。
   实测过程（每改一版都真量）：
     只撤 min-width → 2 列表 343px 刚好；3 列的 /product/deepseek-v4/ 仍 467px，藏 124px。
     再缩 padding/字号 → 352px，只差 9px。
     `.tbl code` 是最后那 9px 的元凶：它带 `white-space:nowrap`（见上面 1282 行那条注释，
     标识符要照着抄、不能从中间断），所以 `deepseek-v4-pro` 撑着列不肯缩。
     把 code 降到 11px → 343px，**全部装下**。
   ⚠️ 别把 `.tbl code` 的 nowrap 去掉来省事 —— 那会让人把换行当成标识符的一部分。 */
@media(max-width:767px){
  .tbl-wrap .tbl{min-width:0}
  .tbl{font-size:13px}
  .tbl th,.tbl td{padding:8px}
  .tbl code{font-size:11px;padding:1px 4px}
  .tbl .src{font-size:11px}
}

/* 提示块：中性（默认 .note 是无框的），这里给「要注意」的语气加个框 */
.note-warn{margin-top:var(--s8);padding:var(--s6);border-radius:var(--r-md);
  background:rgba(255,176,32,.07);border:1px solid rgba(255,176,32,.28);
  font-size:16px;color:var(--text-body);line-height:1.7}
.note-warn strong{color:#FFD08A}

/* 法务页底部那行「最后更新 / 草案状态」 */
.prose-meta{margin-top:var(--s12);padding-top:var(--s6);font-size:16px;color:var(--text-muted);
  border-top:1px solid rgba(255,255,255,.08)}

/* 内页正文首个 h2 不要顶出多余空档 */
.prose h2:first-child{margin-top:0}
.prose ul li a{color:var(--neon-cyan)}

/* 长文阅读布局：正文列 + 右侧信息栏。规格见母版05 §6.8
   🔴 minmax(0,…) 不能写成 760px 1fr —— 否则长网址把栅格撑宽、整页横向溢出
   🔴 top:104px = 导航 72px + --s8，改小会被吸顶导航压住
   🔴 2026-10-04 晚：正文列 760px → **600px**（理由见 1199 行那条）。
   这一条是长文页的**真正开关**：`.read .prose{max-width:none}`(下一行) 已经把
   `.prose` 自己的限宽撤掉了，所以 blog / dpa 这类页的正文宽度**只由这个栅格列决定**，
   只改 `.prose{max-width}` 对它们毫无作用（实测确认过）。
   副作用（是好的，不是 bug）：省下的 160px 全给了右边 `1fr` 信息栏，
   侧栏 328px → 488px，卡片里的说明文字少折一行（262px → 422px）。 */
.read{display:grid;grid-template-columns:minmax(0,600px) minmax(0,1fr);gap:var(--s16);align-items:start}
.read .prose{max-width:none}
.read-aside{position:sticky;top:104px}
@media(max-width:1023px){
  .read{grid-template-columns:1fr;gap:var(--s10)}
  .read-aside{position:static}
}

/* 法务长文目录：规格见母版05 §6.8.1
   🔴 a 必须 display:block —— inline 只有文字宽度可点，移动端点不中
   🔴 border-left 必须带 transparent 底色，只改颜色不会长出来 */
.toc{margin-top:var(--s4);display:flex;flex-direction:column;gap:2px}
.toc li{list-style:none}
.toc a{display:block;padding:6px 0 6px 12px;font-size:14px;line-height:1.4;
  color:var(--text-muted);border-left:2px solid transparent;
  transition:color .15s,border-color .15s}
.toc a:hover{color:var(--text-body);border-left-color:var(--neon-cyan)}

/* `.meta` 药丸放进 `.prose` 里时，别继承正文列表的小圆点和 24px 缩进
   （`.prose ul li` 的选择器比 `.meta li` 更专一，不挡就会长出一排蓝点） */
.prose ul.meta{padding-left:0}
.prose ul.meta li{padding-left:10px;margin-top:0}
.prose ul.meta li::before{display:none}

/* 页尾行动条：左边文字块、右边按钮，窄屏堆叠 */
@media(max-width:767px){
  .cta-band .wrap{flex-direction:column;align-items:flex-start;gap:var(--s6)}
}

/* 内页头的面包屑 + 标题间距（.phero-in 是首页那套，内页用 .wrap 直出） */
.phero .wrap{position:relative}

/* ═══ Cookie 同意条（2026-09-30 新增，对应待办 A20）═══════════════════════════
   放在**页面最上面的一条细带**，把导航和正文一起往下推 —— 不是浮在它们上面。
   访客没表态之前，埋点脚本**根本不挂上去**（见 scripts/gen_pages.py 的 COOKIE_BAR）。

   🔴 为什么不做「底部浮条」：底部浮条是 position:fixed 贴在视口底边，而首屏
      「Get a quote」那一行按钮**正好横跨视口底边**（360×640 实测：按钮顶在
      y=614，视口底在 640）。也就是说，**只要浮条有一像素高，就会压住主 CTA**。
      这不是「调矮一点」能解决的，是结构问题 —— 所以改成把内容往下推。
      实测记录见 docs/质检证据与闸门-现状与待裁定.md。

   z-index 150：在导航(100)之上、抽屉遮罩(190)之下 —— 手机菜单一拉开，
   同意条自然被遮住，不会浮在菜单上面。

   🔴 `.cookie-bar[hidden]` 这行**不能删**：浏览器自带的 `[hidden]` 规则在
      **用户代理样式表**里，作者写的 `display:flex` 会把它盖掉 —— 少了这行，
      横幅永远藏不住（本该隐藏的元素反而一直显示）。

   🔴 下面 data-consent-bar 的三条**必须成对**：只推导航不推正文 → 正文被导航压住；
      只推正文不推导航 → 导航压在同意条上。--cookie-h 由页内脚本实测写入。

   ⚠️ 状态标记用的是 `<html data-consent-bar="on">`，**故意不用 class** ——
      质检脚本找浮窗是靠 `[class*=cookie i]` 这种「类名里有没有 cookie」的启发式，
      挂在 `<html>` 上的状态类会被它当成一个覆盖全屏的浮窗，报出假红。
      （真横幅的 class 仍是 `cookie-bar`，**照旧被质检盯着**，这里没有放过谁。） */
.cookie-bar{position:fixed;top:0;left:0;right:0;z-index:150;
  display:flex;flex-wrap:nowrap;align-items:center;justify-content:center;
  gap:var(--s4);padding:var(--s3) var(--s4);
  background:rgba(9,12,24,.97);
  backdrop-filter:var(--glass-blur);-webkit-backdrop-filter:var(--glass-blur);
  border-bottom:1px solid rgba(255,255,255,.10);
  box-shadow:0 8px 24px rgba(0,0,0,.35)}
.cookie-bar[hidden]{display:none}
.cookie-bar p{margin:0;max-width:62ch;min-width:0;font-size:16px;line-height:1.45;color:var(--text-body)}
.cookie-bar p a{color:var(--text-strong);text-decoration:underline;text-underline-offset:2px}
.cookie-bar-actions{display:flex;flex-shrink:0;gap:var(--s3)}
html[data-consent-bar="on"] .nav{top:var(--cookie-h,0px)}
html[data-consent-bar="on"] body{padding-top:var(--cookie-h,0px)}
@media(max-width:640px){
  .cookie-bar{flex-direction:column;align-items:stretch;gap:var(--s2);padding:var(--s2) var(--s3)}
  .cookie-bar p{max-width:none;line-height:1.35}
  .cookie-bar-actions{justify-content:flex-end}
}

/* ═══════════════════════════════════════════════════════════════════════════
   AI 客服挂件（第三方 `widget.js` 运行时注入）—— 样式覆盖
   ───────────────────────────────────────────────────────────────────────────
   🔴 2026-10-07 加。用户原话：「**是 Ui 不协调　排版乱**」。

   **为什么要覆盖**：挂件的样式**全部来自平台默认值**，本文件此前关于它只有两条
   （都在打印块里，只是「打印时隐藏」）。真浏览器实测出来的三个具体问题：

     ① **快捷按钮参差 / 溢出** —— `.aichat-quick` 是 `flex-wrap:nowrap`，
        三个按钮**加起来 528px、容器只有 379px** ⇒ 一行放不下又**不许换行**；
     ② **发送按钮变成又粗又满的高饱和蓝横条** —— `.aichat-form` 是单列 grid，
        输入框和发送按钮**各占满宽、上下堆叠**；
     ③ **配色是另一个网站** —— 平台默认**白底 + Tailwind 蓝 `rgb(37,99,235)`**，
        而本站是**近黑深蓝 + 紫蓝渐变**（`--bg-elevated` / `--grad-primary`）。

   **改法**：布局上把两个按钮区改成**等宽网格**（不再参差）、把输入行改成
   **「输入框撑满 + 发送按钮贴右边」**；配色上**换成站点自己那套变量**。

   ⚠️ **这是第三方组件的内部类名**（`.aichat-*`）。平台一更新、类名或 DOM 结构一变，
      下面这些覆盖**可能失效** —— 那时按实测重新对一遍即可，**不是「站坏了」**。
   ⚠️ **用 `!important` 是故意的**：挂件的样式由 `widget.js` **运行时注入**，
      注入时机晚于本文件，同权重下我们会输。
   ⚠️ **只改外观，不改行为** —— 没动它的 DOM、没拦截它的请求、没碰 `window.AICHAT`。
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 浮球：跟站点主按钮同一套渐变（原来是一块 Tailwind 蓝） ── */
.aichat-toggle{
  background:var(--grad-primary)!important;
  box-shadow:var(--glow-violet),0 8px 24px rgba(0,0,0,.45)!important;
}
.aichat-toggle:hover{filter:brightness(1.08)}

/* ── 面板：深色玻璃，跟站点一套（原来是纯白） ── */
.aichat-panel{
  background:var(--bg-elevated)!important;
  border:var(--glass-border)!important;
  border-radius:var(--r-md)!important;
  box-shadow:var(--shadow-card)!important;
  color:var(--text-body)!important;
  padding:0!important;            /* ③ 收掉平台默认那圈很厚的上下留白 */
  overflow:hidden!important;
}
.aichat-head{
  background:linear-gradient(180deg,rgba(124,92,255,.16),rgba(124,92,255,0))!important;
  border-bottom:1px solid rgba(255,255,255,.08)!important;
}
.aichat-head,.aichat-brand{color:var(--text-strong)!important}
.aichat-mark{background:var(--grad-primary)!important;border-radius:var(--r-sm)!important}
.aichat-close{color:var(--text-muted)!important;background:transparent!important}
.aichat-close:hover{color:var(--text-strong)!important;background:var(--glass-bg-hover)!important}
.aichat-messages{background:transparent!important}
/* 🔴 这一条是**改完当场看出来的**（2026-10-07）：上面把面板改成深色之后，
   中间装按钮的那块 `.aichat-workbar` 忘了改，**还是平台的白底** ——
   而按钮文字是浅灰（`--text-body`）⇒ **浅灰字压白底，几乎看不见**。
   📌 教训：覆盖第三方样式时，**只改外层不改中层 = 把里面变成不可读**。
      改完必须**真的看一眼**，不能只量数值 —— 数值全对（等宽 186/186/186），
      但字是看不见的。 */
.aichat-workbar,.aichat-lead,.aichat-turnstile{background:transparent!important}

/* ── 问候语：小气泡，不是一整块灰板 ── */
.aichat-msg.system{
  background:var(--glass-bg)!important;
  border:var(--glass-border)!important;
  border-radius:var(--r-sm)!important;
  color:var(--text-body)!important;
  max-width:88%!important;
}

/* ── 快捷按钮 / 工具按钮：**等宽网格**，治「参差」 ──
   原来是 flex + nowrap（放不下又不换行）。改成自适应网格：
   容器多宽就分几列，**同一行里的按钮一律等宽**，不再一条宽一条窄。 */
.aichat-quick,.aichat-tools{
  display:grid!important;
  grid-template-columns:repeat(auto-fit,minmax(150px,1fr))!important;
  gap:var(--s2)!important;
  align-items:stretch!important;
}
.aichat-chip,.aichat-secondary{
  display:inline-flex!important;align-items:center!important;justify-content:center!important;
  min-width:0!important;min-height:34px!important;
  padding:6px 10px!important;text-align:center!important;
  background:var(--glass-bg)!important;
  border:var(--glass-border)!important;
  border-radius:var(--r-sm)!important;
  color:var(--text-body)!important;
  font-size:12.5px!important;line-height:1.3!important;
  white-space:normal!important;
  text-decoration:none!important;
}
.aichat-chip:hover,.aichat-secondary:hover{
  background:var(--glass-bg-hover)!important;color:var(--text-strong)!important;
}
/* 每组是**奇数个**按钮（实测：快捷 3 个、工具 3 个）时，排两列会剩一个孤零零占半行、
   右边空一块。让它**横跨整行**，看起来就是有意为之，而不是没排齐。
   ⚠️ 依赖「最后一个子元素」—— `.aichat-quick` / `.aichat-tools` 下面
      **只有按钮**（隐藏的 `.aichat-lead` 表单在 `.aichat-workbar` 下，不在这两个容器里，
      已用 DOM dump 核过）。平台哪天往里塞别的元素，这条要跟着对一遍。 */
.aichat-quick>:last-child:nth-child(odd),
.aichat-tools>:last-child:nth-child(odd){grid-column:1/-1}

/* ── 输入行：**输入框撑满 + 发送按钮贴右边**（原来是上下各占满宽） ── */
.aichat-form{
  grid-template-columns:1fr auto!important;
  gap:var(--s2)!important;
  padding:var(--s3)!important;
  background:transparent!important;
}
.aichat-input{
  background:var(--glass-bg)!important;
  border:var(--glass-border)!important;
  border-radius:var(--r-sm)!important;
  color:var(--text-strong)!important;
  width:100%!important;min-width:0!important;
}
.aichat-input::placeholder{color:var(--text-muted)!important}
.aichat-input:focus{
  border-color:var(--brand-violet)!important;
  box-shadow:0 0 0 3px rgba(124,92,255,.22)!important;
  outline:none!important;
}
.aichat-send{
  background:var(--grad-primary)!important;
  border:0!important;border-radius:var(--r-sm)!important;
  color:#fff!important;font-weight:600!important;
  padding:0 var(--s4)!important;white-space:nowrap!important;
  width:auto!important;
}
.aichat-send:hover{filter:brightness(1.08)}
.aichat-send:disabled{opacity:.5!important;filter:none!important}


/* ═══════════════════════════════════════════════════════════════════════════
   ═══ 打印 / 存 PDF ═══

   🔴 为什么必须单独写这一块：**深色底 + 浅色字，一打印就废。**

   2026-10-04 实测（Playwright 生成真 PDF，`print_background=False` 复现 Chrome
   打印对话框的默认「不打印背景图形」）：条款页印出来是 —— 标题几乎看不见、
   正文勉强能读、「Get a quote」成了一个白幽灵框。
   **采购和法务恰恰是会把条款 / DPA / 隐私政策打印或存 PDF 的人**，
   他们拿到的就是那么一份糊掉的文档。

   根因：浏览器打印**默认不打印背景色**（`print-color-adjust:economy` 是 CSS 的默认值，
   Chrome 打印对话框的「背景图形」也默认关）→ 纸是白的，
   而本站的文字色是给**深底**配的浅色。实测 `--text-body`(#C7CEDB) 在白纸上
   只有 **1.58:1**，WCAG 正文线是 4.5:1。

   修法：**重映射调色板**，不是到处打补丁。整块做五件事：
     ① 把 `:root` 的变量换成白底黑字的一套；
     ② 藏掉固定浮层和「点了才有用」的东西；
     ③ 补上变量盖不住的写死浅色；
     ④ 拆掉装饰，并**强制显示**那些靠 JS / 交互才出现的内容；
     ⑤ 分页规则。

   🔴🔴 三个「不这么修就会更糟」的坑，都是量出来的，不是想出来的：
     ⚠️ **`.grad-text` 会整片消失。** 它是 `background:var(--grad-neon)` +
        `-webkit-text-fill-color:transparent` —— **字本身是透明的，拿渐变当字色**。
        把 `--grad-neon` 设成 `none`，那行字就**既没背景也没颜色** = 白纸上的空白。
        而它包的正是**首页 hero 大标题的后半句**（6 个语种首页各一处，
        比如 "without changing a line of code."）。
        ⇒ 必须单独把 `.grad-text` 拉回来（见 ③ 第一条）。
     ⚠️ **`.reveal` 没滚到的部分是空白的。** `.reveal{opacity:0}` 靠 JS 滚动点亮，
        打印时**用户没滚到的地方一个字都不印**。这是**静默丢内容**，比配色糊掉更危险
        —— 糊了至少还看得见「这里有字」。
        ⇒ 打印时强制 `opacity:1`（见 ④）。
     ⚠️ **折叠的 FAQ 只印问题、不印答案。** 全站实测 **481 个** `<details>` 是折叠的
        （首页 21 个 / 语种 × 6，帮助页 10~17 个 / 语种）。纸上点不开，
        不展开就是**有问无答**。
        ⇒ 打印时把答案强制显示（见 ④）。

   ⚠️ 别指望 `prefers-reduced-motion` 那条兜底 —— 它只在**系统开了「减少动态效果」**
      时生效，跟「打印」没有半点关系。

   ⚠️ 阴影类变量这里写的是 `0 0 0 transparent` 而**不是 `none`**：
      它们会被用在**逗号列表**里（如 `box-shadow:var(--shadow-card),var(--glass-inner-light)`），
      列表里放一个 `none` 会让**整条声明作废**。写成合法的「透明零阴影」最稳。
   ═══════════════════════════════════════════════════════════════════════════ */
@media print{

  /* ── ① 调色板整体换成白底黑字 ──────────────────────────────────────────
     对比度全部按白纸(#fff)算过，都 ≥ 4.5:1（WCAG AA 正文线）：
       --text-strong  #111111 → 17.9:1
       --text-body    #1A1A1A → 15.9:1
       --text-muted   #555555 →  7.5:1
       --neon-cyan    #0E7490 →  5.4:1   ← 原来是 #22D3EE，白纸上只有 1.8:1
       --brand-violet #4A3AA8 →  8.5:1
     渐变一律 `none`：白纸上印渐变既费墨，深浅又不可控。 */
  :root{
    --bg-base:#fff;--bg-section:#fff;--bg-elevated:#f4f4f4;--bg-deep-blue:#fff;
    --brand-violet:#4A3AA8;--brand-blue:#1D4ED8;
    --grad-primary:none;--grad-hero:none;--grad-neon:none;
    --neon-cyan:#0E7490;--neon-magenta:#A21CAF;
    --text-strong:#111111;--text-body:#1A1A1A;--text-muted:#555555;
    --glass-bg:transparent;--glass-bg-hover:transparent;
    --glass-blur:none;
    --glass-border:1px solid #cccccc;
    --glass-inner-light:0 0 0 transparent;
    --shadow-card:0 0 0 transparent;
    --glow-violet:0 0 0 transparent;--glow-cyan:0 0 0 transparent;
    --glow-strong:0 0 0 transparent;
  }
  /* 原生控件（勾选框 / 下拉 / 滚动条）跟着调色板走。
     🔴 `only light` 是**强制**的：万一以后有人在 `html` 上加了 `color-scheme:dark`，
        这里得能把它压回去，否则纸上印出来的勾选框是深色的。 */
  html{color-scheme:only light;scroll-behavior:auto}
  body{background:#fff;color:#1A1A1A;overflow:visible}
  /* 同意条被藏了，但它给 body 加的顶部内推还在 —— 不还回去，纸面顶部会空一截 */
  html[data-consent-bar="on"] body{padding-top:0}

  /* ── ② 藏掉「屏幕上才有意义」的东西 ────────────────────────────────────
     · `.nav` / `.drawer` / `.drawer-scrim` / `.cookie-bar`：都是 `position:fixed`，
       打印时会**贴在每一页的同一个位置**压住正文。
     · `.aichat-root`：AI 客服浮球（外部脚本注入），纸上的浮球点不动。
     · `.skip`：跳转主内容链接，本来就是藏着的。
     · `.crumbs`：面包屑，全是链接，纸上没用。 */
  .nav,.drawer,.drawer-scrim,.cookie-bar,.aichat-root,.skip,.crumbs{display:none!important}

  /* 页脚：5 列站内地图印出来是**纯废纸**（40 多个点不动的链接，约 1.5 页），
     但最下面的**公司法定名**对一份要存档的法务文件是必须的 —— 那是「这是谁的文件」。
     所以：藏链接列 / 社交图标 / 语言切换，留品牌块和版权行。 */
  footer .foot-col,footer .socials,footer .lang{display:none!important}
  footer{background:#fff;padding:var(--s6) 0 0;border-top:1px solid #cccccc}

  /* 首屏那 72px 是给**固定导航**让位的（`.hero{padding:calc(72px + …)}`）。
     导航在纸上没了，这 72px 就是纯空白 —— 还回去。 */
  .hero,.phero{padding:var(--s12) 0}

  /* ── ③ 变量盖不住的写死浅色 ────────────────────────────────────────────
     全站有 20 处硬编码的浅色。**必须逐条覆盖** ——
     只换变量的话，这些字还是浅的，在白纸上照样看不见。 */

  /* 🔴 最要紧的一条：渐变当字色。不拉回来，首页大标题就是**整行空白**。 */
  .grad-text{background:none;-webkit-text-fill-color:currentColor;color:inherit}

  /* 主按钮：`--grad-primary` 已经变成 `none`，但 `color:#fff` 还在 ⇒ 白底白字。
     给回深字 + 描边，纸上读起来是「一个可点的动作」，而不是幽灵框。 */
  .btn-primary{background:#fff;color:#111111;border:1.5px solid #555555;box-shadow:none}

  /* 紫色小字（`.num` 编号 / `.chip` 标签）—— 原来是 #C9BCFF，白纸上只有 1.4:1 */
  .num,.chip{color:#4A3AA8}

  /* 🔴 2026-10-05 补漏：**页脚品牌 logo**（方框 + 里面那个 "W"）。
     它的描边色是**写死在 SVG 上的**（`gen_pages.py` 的 `BRAND_SVG`：
     `stroke="#7C5CFF"` 紫方框 + `stroke="#F2F5FB"` 近白 W），换变量够不着它
     ⇒ 那个 "W" 是 **#F2F5FB 压白纸 = 1.09:1**，纸上等于不存在，只剩一个空紫框。
     ⚠️ 打印规则**没有藏**页脚品牌块（上面那条注释写着「留品牌块和版权行」，
     理由是公司法定名对存档的法务文件是必须的），`a[href^="http"]` 那条还专门把
     `.brand` 排除掉（原话「页眉页脚那个 logo，网址跟站名重复」）
     —— **作者本来就预期它会印出来，只是漏了这一条。**
     实测（`scripts/print_graphic_check.py`，带通过的反证）：10/10 页命中，1.09:1，判据 3.0:1。
     办法同下面那条 `.code-foot svg`：直接改描边。紫框一起换成打印调色板的品牌紫
     （原来 4.35:1 虽然过线，但跟纸上其它品牌紫元素不同色）。 */
  .brand svg rect{stroke:#4A3AA8}
  .brand svg path{stroke:#111111}
  /* 行内 code：原来是紫底 + #C9BCFF 字，白纸上是浅紫压浅紫 */
  .lede code,details .body code,.prose code,.tbl code{
    color:#4A3AA8;background:#f4f4f4;border-color:#cccccc}

  /* 代码块：整块是深底(#070A14)，白纸上印出来是一片黑 —— 换成浅底 */
  .code,.fig-code{background:#f7f7f7;border-color:#cccccc}
  /* 代码语法色（原来全是给深底配的浅色） */
  .code .k,.c-k{color:#7B2FBE}
  .code .s,.code .add,.c-s,.c-add{color:#1E6B3A}
  .code .add{background:#eaf7ee;box-shadow:none}
  .code .del,.c-del{color:#B3261E;background:#fdecec;box-shadow:none}
  .c-n{color:#8A5A00}
  .code-name,.code-foot{color:#555555}
  .code-foot svg{stroke:#0E7490}

  /* 表单状态色 */
  .form-msg[data-state="invalid"],.form-msg[data-state="error"]{
    color:#B3261E;background:#fdecec;border-color:#dd9999}
  .form-msg[data-state="sent"]{color:#1E6B3A;background:#eaf7ee;border-color:#99cc99}
  .form-msg[data-state="not-sent"],.form-msg[data-state="sending"]{
    color:#555555;background:#f4f4f4;border-color:#cccccc}
  .note-warn{background:#fffaf0;border-color:#d9a441}
  .note-warn strong{color:#8A5A00}

  /* 图示里的圆形节点 / 步骤编号：原来是 #EDE9FF 压在紫色渐变上 —— 白纸上全无 */
  .fig-glyph,.fig-step-n{color:#4A3AA8;background:#fff;border-color:#4A3AA8;box-shadow:none}

  /* 图示的两个图例 chip —— 上面 ③ 那批**漏了这一处**（2026-10-04 全站 192 页补）。
     第一个 chip 的字色是浅紫 `#C9BCFF`，底是 `rgba(124,92,255,.14)`；屏幕上没问题
     （对四档深底 9.00~10.14），可纸上底色变成 `#EDE8FF`，**实测 1.45:1** ——
     跟上面 `.chip` 被修掉的那个病一模一样，只是当时没扫到它。
     第二个 chip 的字色走 `--text-muted`（纸上 `#555555`）本来就够，
     但它的描边是 `rgba(255,255,255,.18)`，压在白纸上等于没有 —— 一整圈框凭空消失。
     两个一起给：紫的保持紫、灰的保持灰，只把值换成纸上读得出来的。
     实测：紫 7.14:1 / 灰 6.78:1（chip 是 12px/600 小字，按正文 4.5 判，不能套大字 3.0）。 */
  .fig-legend span{color:#555555;background:#f4f4f4;border-color:#cccccc}
  .fig-legend span:first-child{color:#4A3AA8;background:#ede8ff;border-color:#4A3AA8}

  /* ── ④ 拆掉装饰 + 强制显示靠 JS / 交互才出现的内容 ─────────────────────
     · 毛玻璃 / 光晕 / 卡片阴影在白纸上要么印不出来、要么是一团灰。
     · 🔴 `.reveal` 必须强制点亮（默认 `opacity:0`，靠滚动才显示）——
       不强制的话**用户没滚到的地方打印出来是空白**，属于静默丢内容。
     · 🔴 折叠的 FAQ 必须展开（全站 481 个）—— 纸上点不开，
       不展开就是**有问无答**。只针对 `.body`（答案那层），
       不去动别的 `<details>`（比如页脚那个语言菜单，它整个已经被藏了）。 */
  .reveal{opacity:1!important;transform:none!important}
  details:not([open]) > .body{display:block!important}
  *{backdrop-filter:none!important;-webkit-backdrop-filter:none!important;
    box-shadow:none!important;text-shadow:none!important;animation:none!important;
    transition:none!important}

  /* 链接在纸上「点」不了，颜色深浅也不该是唯一的提示 —— 一律加下划线 */
  a{text-decoration:underline;text-underline-offset:2px}
  /* 绝对网址在纸上读不出来，补在链接后面。
     ⚠️ 排除 `.btn`（按钮后面跟一长串网址很难看，而且按钮文字本来就写了去哪）
     ⚠️ 排除 `.brand`（页眉页脚那个 logo，网址跟站名重复） */
  a[href^="http"]:not(.btn):not(.brand)::after{
    content:" (" attr(href) ")";font-size:.85em;font-weight:400;
    color:#555555;word-break:break-all}

  /* ── ⑤ 分页 ────────────────────────────────────────────────────────────
     · 标题不许跟它的正文劈在两页（`break-after:avoid`）。
     · 表格行 / 图 / 代码块 / 提示块不许被劈开。
     · 长表格跨页时表头在每页重复（`table-header-group`）。 */
  h1,h2,h3,h4{break-after:avoid;page-break-after:avoid}
  tr,figure,.code,.fig-code,.note,.note-warn,details,.tbl{
    break-inside:avoid;page-break-inside:avoid}
  thead{display:table-header-group}
  /* 正文限宽是给宽屏读起来舒服用的；纸本身就窄，再限宽只会让它缩在左边 */
  .prose{max-width:none}
  @page{margin:16mm}
}
