/* Hallmark · genre: modern-minimal · macrostructure: Workbench · theme: LanZ (project-locked)
 * enrichment: E5 custom-illustration (Tier A CSS + Tier B SVG) · nav: N1b · footer: Ft1
 * tone: utilitarian · audience: 园区运营方 / 产业服务机构 / 资产持有方
 * paper-band: light · display-style: system-grotesk · accent-hue: cool-violet 288°
 * pre-emit critique: P5 H5 E5 S5 R4 V4
 * contrast: pass (40–41) · slop: pass (42–45) · honest: pass (46) · chrome: pass (47)
 * tokens: pass (48) · icons: pass (30) · mobile: pass (34, 49, 50–57)
 * studied: partial · DNA-source: url (www.vts.com,浏览器渲染读取;WebFetch 被 403 拦)
 *   实测对齐:paper #F5F7FA · ink #140532 · h1 85px/0.98/-1.7px —— 与项目原注释完全一致
 *   借用的结构:N5 悬浮胶囊导航 · 深色不等宽产品卡 · 颗粒纹理 · 渐变主 CTA
 *   借用的文案「架构」而非文案本身:标题 = [结果]+[手段],中位 6 词;数字只出现在产品截图内
 *   没有照搬:字体(Proxima Nova 是商业授权)、任何一句原文、任何一个数字
 *
 * 已知偏离 6 项,全部为用户知情后明确选定:
 *   gate 1  显示字体是系统栈 —— 项目明确不加载外部字体(国内服务器 + 中文 webfont 3–10MB)
 *   gate 38 outlier(mono)用在 12 个位置而非 ≤2 —— utilitarian 的标签 register,
 *           只标注一类角色(机器读的元数据),绝不用于正文与标题
 *   gate 23 强调色占比超过 5% —— 用户要求「紫色大面积色块」:
 *           CTA 通栏实心品牌紫 + about 段淡紫带 + 全站 2px 硬线走品牌紫
 *   motion  滚动视差 —— 通行守则里是禁止项;用视口驱动的 CSS 动画实现(合成线程),
 *           不挂 scroll 监听,不支持的浏览器整段跳过
 *   motion  循环动效 —— 同上属禁止项;只保留两处、且都有语义依据:
 *           已上线状态点脉冲(=此刻是活的)、引擎方块呼吸(=正在处理)
 *   gate 2  主 CTA 用了渐变 —— gate 2 禁的是「紫→蓝 / 青→品红」那种 AI 渐变;
 *           这里是同色相提亮(288°→292°,仅偏 4°),白字对两端 9.94:1 与 5.97:1
 *
 * 无论偏离与否,这几条一条没松:只动 transform/opacity、三个命名 easing、
 * 对比度全部实测达标、prefers-reduced-motion 下所有动效退回静态终态。
 * ============================================================
 * LanZ 官网 · 视觉体系
 * ------------------------------------------------------------
 * 【本轮 · 深色舞台】用户定调「绚丽第一位,真实截图排其次」。
 * 加的不是一堆特效,是一条版面规则:
 *
 *     深色 = 数据正在动的地方。
 *
 * 全站只有两处吃这条规则 —— hero(匹配正在发生)与「工作方式」
 * (数据在流)。其余要读字的版块一律保持浅底。深色不是主题,是状态标记;
 * 一旦每一节都深,这条规则就什么也没说了。
 *
 * hero 的 signature 是 <canvas> 匹配场(逻辑在 main.js):
 * 左边一个地块锚点,右边一片企业节点,光束按匹配顺序打过去 ——
 * 画的就是产品那一句「给一块地,找到最可能买它的人」,
 * 不是通用的粒子背景。真截图一张没删,它现在浮在场里、四周带辉光。
 *
 * 舞台上的三条硬规矩(和浅底那套是同一个推导,只是换了表面):
 *   · 品牌紫 #4B23A9 在 void 上只有 1.9:1 —— 深色表面一律换 --color-glow 系,
 *     和 --color-accent-on-dark 是同一个道理。别在深底上写 --color-accent。
 *   · 焦点环也得换:--color-focus 是深紫,在 void 上等于没有,
 *     舞台内改用 --color-glow-hi(12.5:1)。这条漏了键盘用户就瞎了。
 *   · 辉光只走 box-shadow / radial-gradient / drop-shadow,
 *     绝不用 filter:blur() 去糊承载文字的元素。
 *
 * ------------------------------------------------------------
 * 上一轮是密度收紧,不是换皮:配色、品牌紫的推导、直角卡片、
 * 胶囊按钮、全部文案、全部版块都保留。改的是节奏。
 *
 * 上一版为什么显得空 —— 三条,都已修掉:
 *
 *   1. 留白参数照搬了英文站(section 130px / H1 85px),
 *      但 H1 为中文压到 64px,留白没跟着降。字小了、留白没变 = 空。
 *      → --pad-y 拆成 tight(64)/ 默认(80)/ loose(88)三档,按内容密度分配。
 *
 *   2. max-width 用 ch。ch = 西文「0」的字宽 ≈ 0.5em,中文字是 1em 满宽,
 *      所以 40ch 只装得下约 20 个汉字,文字块仅占版心 37%,
 *      右侧 60% 永久空白。
 *      → 改用 em(中文 1em = 1 字),且 hero 与 section 头改双栏,
 *        右栏被引言占满,空白不再是漏出来的。
 *
 *   3. 六张能力卡里四张是空壳,三处等宽三列网格。
 *      → 2 张已上线放大承重,4 项规划中收成紧凑表行;
 *        适用场景改真表格;flow 破等宽。
 *
 * 排版分离
 *   拉丁字母与数字走 --font-body 里的 LanzLatin(可负字距、可等宽数字),
 *   中文走 --font-cn(字距 0),二者用 unicode-range 分工。
 *   页面满屏是面积、单价、匹配分——数字排版是主题本身,不是装饰。
 *
 * 界面示意图(.figure)为 CSS/SVG 绘制的风格化示意,非真实截图。
 * 不画假浏览器窗口(标题栏 + 三个红黄绿圆点)——用户的浏览器本身就是
 * 窗口,再画一个是把画框印在画框里。改用排版式帧:硬顶线 + 标签 + 底线。
 * 字段名取自 buyer / signal 的真实表单,结果区一律灰条占位,
 * 不出现任何编造的公司名或数值。
 * ============================================================ */

/* ── 拉丁字形分工 ───────────────────────────────────────── */
/* 只挑选系统里已有的西文字体,不加载任何外部字体文件:
   国内服务器 + 首屏速度 > 一套自托管中文 web font 的代价。 */
@font-face{
  font-family:'LanzLatin';
  src:local('SF Pro Display'), local('Segoe UI Variable Display'),
      local('Segoe UI'), local('Helvetica Neue'), local('Arial');
  unicode-range:U+0000-00FF, U+0131, U+2000-206F, U+2070-209F,
                U+20A0-20BF, U+2212, U+FB00-FB04;
  font-display:swap;
}

/* ── Reset ──────────────────────────────────────────────── */
*,*::before,*::after{ box-sizing:border-box; }

/* clip 而非 hidden:hidden 会新建滚动容器,破坏 position:sticky。 */
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  overflow-x:clip;
}
body{
  margin:0;
  overflow-x:clip;
  background:var(--color-paper);
  color:var(--color-ink);
  font-family:var(--font-body);
  font-size:var(--text-body);
  line-height:1.75;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img,svg{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
h1,h2,h3,p,ul,dl,dd,figure{ margin:0; }
ul{ padding:0; list-style:none; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }
table{ border-collapse:collapse; }

/* 标题永不因长词撑破容器(320px 下的硬底线)。 */
h1,h2,h3{ overflow-wrap:anywhere; min-width:0; }

/* 数字:等宽对齐 + 收紧字距。面积、单价、分数全走这里。 */
.num{
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1, "cv01" 1;
  letter-spacing:-.015em;
}

/* ── A11y ───────────────────────────────────────────────── */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
/* <symbol> 仓库。必须 display:none —— 否则那个空 <svg> 会在 body 顶端
   占一行,把整页往下推。 */
.brand-defs{ display:none; }

.skip-link{
  position:absolute; left:var(--space-md); top:0; z-index:var(--z-tooltip);
  padding:var(--space-sm) var(--space-lg);
  background:var(--color-ink); color:var(--color-ink-invert);
  border-radius:var(--radius-pill);
  /* transform 而非 top:top 会触发 layout。 */
  transform:translateY(-150%);
  /* 收回用 ease-in(离开加速),弹出用 ease-out(进入减速)。 */
  transition:transform calc(var(--dur-short) * .75) var(--ease-in);
}
.skip-link:focus{
  transform:translateY(var(--space-md));
  transition:transform var(--dur-short) var(--ease-out);
}

/* 焦点环即时出现——绝不给 outline 加过渡,否则键盘用户在过渡起点看不到指示。 */
:where(a,button,[tabindex]):focus-visible{
  outline:var(--rule-strong) solid var(--color-focus);
  outline-offset:3px;
}

/* ── Layout ─────────────────────────────────────────────── */
.wrap{
  width:100%;
  max-width:var(--wrap);
  margin-inline:auto;
  padding-inline:var(--pad-x);
}
.section{ padding-block:var(--pad-y); }

/* 纸张颗粒。叠加层压到 0.55,乘上贴图自身的 0.32 —— 最终强度约 0.18,
   在浅底上是"几乎看不见的纸感"。深色通栏不加:multiply 在深底上等于没有,
   而 screen 会把噪点变成亮斑。 */
.section, .hero{ position:relative; isolation:isolate; }
.section::after, .hero::after{
  content:"";
  position:absolute; inset:0; z-index:0;
  pointer-events:none;
  background-image:var(--grain);
  background-size:180px 180px;
  opacity:.55;
  mix-blend-mode:multiply;
}
.cta-band::after{ content:none; }
/* 内容必须盖在颗粒层之上 */
.wrap{ position:relative; z-index:1; }
.section--tight{ padding-block:var(--pad-y-tight); }
.section--loose{ padding-block:var(--pad-y-loose); }
.section--surface{ background:var(--color-surface); }
/* 淡紫带。全页唯一一处,压在深紫 CTA 之前,收尾处形成一段紫色渐强。
   overflow:hidden 是给 .split 的水印用的 —— 那枚标记比这一节的内容还高,
   不裁的话它会顶穿上下边界,在隔壁那节的浅底上露出一条来历不明的斜边。
   裁在 section 上而不是 .split 上:横向仍然出血到视口,只锁纵向。 */
.section--accent{ background:var(--color-accent-soft); overflow:hidden; }

/* ══ 浅底的空气 ═════════════════════════════════════════
   两段深色舞台之间原本是三块纯色(白 / 纸 / 淡紫),连着往下滑
   就是一大片空。这里给浅底也铺一层空气,和舞台共用同一套语汇 ——
   地籍网格 + 紫光晕 —— 但强度差一个数量级:

     舞台上的光是主角        alpha 0.30 ~ 0.50
     浅底上的光只是让空白有深度  alpha 0.04 ~ 0.12

   这几节是要读字的。底纹的活是把「空」变成「留白」,不是往里塞内容;
   一旦浓到能被单独看见,它就开始跟正文抢注意力了。

   网格间距 72px 和舞台那层完全一致 —— 同一张图纸滚下来,
   只是灯光换了。

   位置由 --field-x/--field-y 控制,三节各给一个方位:同一条滚动线上
   连着出现三团一模一样的光晕,比没有光晕更显得敷衍。 */
.section--field{
  --field-x:50%;
  --field-y:50%;
  background-image:
    /* 主光:冷紫 */
    radial-gradient(58% 62% at var(--field-x) var(--field-y),
      color-mix(in oklch, var(--color-accent) 12%, transparent) 0%,
      color-mix(in oklch, var(--color-accent) 4%,  transparent) 42%,
      transparent 72%),
    /* 补光落在主光的对角。
       ⚠ 这里刻意没有照搬 hero 那套「一冷一暖」:金在 void 上是光,
       在近白底上是污 —— 6% 的暖色叠在 #FFF 上会读成发黄的旧纸,
       而且和隔壁那节的冷底一交界就是一道脏边。实测过,直接退回同色相,
       只靠位置和浓度拉开层次。 */
    radial-gradient(46% 52% at calc(100% - var(--field-x)) calc(100% - var(--field-y)),
      color-mix(in oklch, var(--color-accent) 6%, transparent) 0%,
      transparent 70%);
}
/* 两节浅底相接时补一条发丝缝。各自有了光晕之后,交界处会出现一个
   说不清来历的色阶;补一条线,那个色阶就成了「两张纸叠着」。
   只在浅底挨着浅底时出现 —— hero 与「工作方式」自己带 2px 发光边,
   紧跟一条发丝线会变成双线。 */
.section--field + .section--field{
  border-top:var(--rule-hair) solid var(--color-rule);
}
/* 网格单独放伪元素:mask 是作用于整个元素的,和上面两团光晕
   共用一个盒子的话,光晕会被网格的遮罩一起裁掉。 */
.section--field::before{
  content:"";
  position:absolute; inset:0; z-index:0;
  pointer-events:none;
  background-image:
    linear-gradient(to right,  color-mix(in oklch, var(--color-accent) 8%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in oklch, var(--color-accent) 8%, transparent) 1px, transparent 1px);
  background-size:72px 72px;
  -webkit-mask-image:radial-gradient(70% 78% at var(--field-x) var(--field-y), #000 0%, #000 18%, transparent 74%);
          mask-image:radial-gradient(70% 78% at var(--field-x) var(--field-y), #000 0%, #000 18%, transparent 74%);
}
.section--field--tr{ --field-x:86%; --field-y:12%; }   /* 光源在右上 */
.section--field--bl{ --field-x:10%; --field-y:84%; }   /* 左下 */
.section--field--r { --field-x:96%; --field-y:46%; }   /* 正右 */
.section--field--tl{ --field-x:14%; --field-y:18%; }   /* 左上 */

/* ── Section 头:标题左 / 引言右 ────────────────────────
   原版每个 section 顶着一个 eyebrow(平台能力 / 产品 / 工作方式…),
   六个,而且和主导航的文字一字不差——重复了一遍,还各占一行高度。
   删掉,改成硬顶线 + 标题左 + 引言右:密度上去,右侧不再空。 */
.head{
  position:relative;
  display:grid;
  /* 不是 1:1 —— 中文标题比引言重得多,等分会让每条标题都掉一两个字下来。
     1.25:1 配合 --text-2xl 的上限,六条标题在桌面宽度下全部一行收得住。 */
  grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  gap:var(--space-md) clamp(var(--space-xl), 5vw, var(--space-3xl));
  align-items:end;
  padding-top:calc(var(--space-md) + var(--rule-strong));
}
/* 全站的 2px 硬线统一走品牌紫:六个 section 头 + 图框 + 表头 + 分栏,
   一条贯穿到底的紫色骨架,比零散的紫色小点更能立住品牌。
   改成伪元素是为了让它能被「描出来」(见揭示层的 ruleIn)——
   默认 scaleX(1),只有 JS 确认可用时才先收成 0,和全站一个规矩:
   内容默认可见,JS 只负责隐藏。 */
.head::before{
  content:"";
  position:absolute; top:0; left:0; right:0;
  height:var(--rule-strong);
  background:var(--color-accent);
  transform-origin:left;
}
.head__title{
  font-size:var(--text-2xl);
  font-weight:600;
  line-height:1.22;
  letter-spacing:0;
}
.head__intro{
  max-width:var(--measure-intro);
  color:var(--color-ink-2);
}

.prose{
  margin-top:var(--space-md);
  color:var(--color-ink-2);
  max-width:var(--measure-prose);
}

/* ── Buttons ────────────────────────────────────────────── */
/* 可点文字永不折行——两行的按钮读起来像样式坏了。 */
.btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--space-xs);
  padding:var(--space-sm) var(--space-xl);
  border:var(--rule-hair) solid transparent;
  border-radius:var(--radius-pill);
  font-size:0.875rem;
  font-weight:700;
  letter-spacing:.03em;
  white-space:nowrap;
  transition:background-color var(--dur-short) var(--ease-out),
             color var(--dur-short) var(--ease-out),
             border-color var(--dur-short) var(--ease-out),
             transform var(--dur-micro) var(--ease-out);
}
/* 同色相提亮的渐变,不是那种紫→蓝的 AI 渐变:色相只偏 4°,仍在靛紫带内。
   白字对渐变两端分别是 9.94:1 和 5.97:1,都过 4.5。 */
.btn--solid{
  position:relative;
  overflow:hidden;                 /* 关住下面那道扫光 */
  background-color:var(--color-accent);
  background-image:linear-gradient(135deg, var(--color-accent) 0%, var(--color-accent-lift) 100%);
  color:var(--color-ink-invert);
  box-shadow:0 10px 26px -14px color-mix(in oklch, var(--color-accent) 85%, transparent);
}
/* 悬停扫光。只动 transform,一次 620ms 走完,不循环 ——
   循环的扫光会一直在余光里闪,是最招人烦的一类装饰。 */
.btn--solid::after{
  content:"";
  position:absolute; inset:0;
  pointer-events:none;
  background:linear-gradient(105deg,
    transparent 38%,
    color-mix(in oklch, var(--color-ink-invert) 40%, transparent) 50%,
    transparent 62%);
  transform:translateX(-130%);
  transition:transform 620ms var(--ease-out);
}
.btn--solid:hover::after{ transform:translateX(130%); }
.btn--solid:hover{
  background-image:linear-gradient(135deg, var(--color-accent-lift) 0%, var(--color-accent) 100%);
  box-shadow:0 14px 34px -14px color-mix(in oklch, var(--color-accent) 95%, transparent);
}
.btn--solid:active{ background-image:none; background-color:var(--color-accent-deep); }
.btn--ghost{ border-color:var(--color-rule-strong); color:var(--color-ink); }
.btn--ghost:hover{ border-color:var(--color-ink); background:var(--color-surface); }
.btn--ghost:active{ background:var(--color-accent-soft); }
.btn:active{ transform:translateY(1px); }
.btn[aria-disabled="true"]{ opacity:.45; pointer-events:none; }
.btn--sm{ padding:var(--space-sm) var(--space-lg); font-size:0.8125rem; }
.btn--lg{ padding:var(--space-md) var(--space-xl); font-size:0.9375rem; }

/* ── Header · N5 悬浮胶囊 ───────────────────────────────
   圆角、脱离边缘、毛玻璃、浮在内容之上,由内容定宽而不是通栏。
   N5 的硬规矩:胶囊必须看得出是"浮"的;一旦被链接撑到接近满宽,
   它就只是个圆角通栏,失去意义。中文五项 + wordmark + CTA ≈ 600px,
   在 720px 的上限之内。 */
.site-header{
  position:fixed;
  inset:var(--space-md) 0 auto;
  z-index:var(--z-sticky);
  display:flex; flex-direction:column; align-items:center;
  gap:var(--space-xs);
  padding-inline:var(--pad-x);
  pointer-events:none;            /* 胶囊两侧的空白不拦下面的点击 */
}
.site-header__inner{
  pointer-events:auto;
  display:grid;
  grid-template-columns:auto auto auto;
  align-items:center;
  gap:var(--space-lg);
  height:var(--nav-h);
  max-width:100%;
  padding-inline:var(--space-lg) var(--space-xs);
  background:var(--color-surface-veil);
  backdrop-filter:blur(16px) saturate(160%);
  border:var(--rule-hair) solid var(--color-rule);
  border-radius:var(--radius-pill);
  /* 未滚动时胶囊浮在深色舞台上,给它一圈紫辉 —— 白胶囊 + 紫辉在 void 上
     是全页最高对比的一处,顺便解决了「白色元素贴在深底上像剪纸」。
     滚过 hero 之后底色变成浅纸,辉光收掉,换回原来的耳语级投影。 */
  box-shadow:0 12px 32px -24px var(--color-shadow),
             0 0 44px -14px color-mix(in oklch, var(--color-glow) 60%, transparent);
  transition:background-color var(--dur-short) var(--ease-out),
             box-shadow var(--dur-short) var(--ease-out);
}
.site-header.is-stuck .site-header__inner{
  background:var(--color-surface);
  box-shadow:0 18px 44px -26px var(--color-shadow);
}
.brand{ display:flex; align-items:center; gap:var(--space-xs); }
/* 真 logo 的标记:34 根圆头竖条排成菱形。几何是从官方 PNG 上用连通域
   标记逐条量出来的,再按左右镜像规整过(原图是画在网格上的,描图的
   亚像素抖动不该带进来)。全站只有 <symbol id="lanzMark"> 一份,
   顶栏 / 页脚 / 关于页水印都用 <use> 引它 —— 别再复制第二份几何。

   fill 走 currentColor:同一个标记要在白胶囊(深墨)、深色舞台(白)、
   页脚(深墨)三种表面上出现,颜色必须由上下文决定。
   宽高比 100:116.4 是量出来的,别写成正方形。 */
.brand__mark{
  height:24px;
  width:auto;
  flex:none;
  color:var(--color-ink);
}
.brand__word{ font-size:1.1875rem; font-weight:700; letter-spacing:-.02em; }

.nav{ display:flex; gap:var(--space-lg); }
.nav__link{
  position:relative;
  font-size:0.9375rem;
  line-height:1;
  white-space:nowrap;
  color:var(--color-ink-2);
  transition:color var(--dur-short) var(--ease-out);
}
.nav__link:active{ color:var(--color-accent); }
.nav__link::after{
  content:"";
  position:absolute; left:0; right:0; bottom:-3px;
  height:var(--rule-hair); background:var(--color-ink);
  transform:scaleX(0); transform-origin:left;
  transition:transform var(--dur-short) var(--ease-out);
}
.nav__link:hover{ color:var(--color-ink); }
.nav__link:hover::after{ transform:scaleX(1); }

.site-header__right{ display:flex; align-items:center; gap:var(--space-xs); }

.nav-toggle{ display:none; width:44px; height:44px; margin-right:calc(var(--space-xs) * -1); }
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after{
  display:block; width:20px; height:1.5px; background:var(--color-ink);
  transition:transform var(--dur-short) var(--ease-out), opacity var(--dur-micro) var(--ease-out);
}
.nav-toggle span{ margin-inline:auto; position:relative; }
.nav-toggle span::before{ content:""; position:absolute; top:-6px; }
.nav-toggle span::after{ content:""; position:absolute; top:6px; }
.nav-toggle[aria-expanded="true"] span{ background:transparent; }
.nav-toggle[aria-expanded="true"] span::before{ transform:translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after{ transform:translateY(-6px) rotate(-45deg); }

.mobile-nav{
  pointer-events:auto;
  display:none; flex-direction:column;
  width:min(100%, 22rem);
  padding:var(--space-xs) var(--space-lg) var(--space-md);
  background:var(--color-surface);
  border:var(--rule-hair) solid var(--color-rule);
  border-radius:1.25rem;
  box-shadow:0 18px 44px -26px var(--color-shadow);
}
.mobile-nav__link{
  padding-block:var(--space-sm);
  font-size:var(--text-body);
  color:var(--color-ink-2);
  border-bottom:var(--rule-hair) solid var(--color-rule);
}
.mobile-nav__link:active{ color:var(--color-accent); }
.mobile-nav .btn{ margin-top:var(--space-md); align-self:flex-start; }

/* ══ 深色舞台 ═══════════════════════════════════════════
   .stage 是可复用的深色底座:hero 与「工作方式」共用一套。
   背景分层放在 .stage__bg 里,整块 aria-hidden + pointer-events:none ——
   页面的信息一个字都不在这些层里,拿不到就是少一层气氛。 */
.stage{
  position:relative;
  isolation:isolate;
  background:var(--color-void);
  color:var(--color-ink-invert);
}
/* 纸张颗粒在 void 上 multiply 等于没有(乘一个接近 0 的底色)。
   换 screen 并把强度压到 .05 —— 目的是给大面积纯色一点胶片感,
   不是让人看见噪点。 */
.stage::after{ mix-blend-mode:screen; opacity:.05; }

.stage__bg{
  position:absolute; inset:0; z-index:0;
  overflow:hidden;
  pointer-events:none;
}
.stage__bg > *{ position:absolute; display:block; }

/* ① 极光。用 radial-gradient 的软停靠自然出边界,不上 filter:blur() ——
   blur 一个满屏元素每帧都要重新做模糊,而软渐变是一次性栅格化的。
   26s 一轮:环境光该慢到「看不出在动」,快到能被注意就说明太快了。 */
.stage__aurora{
  width:clamp(26rem, 66vw, 58rem);
  aspect-ratio:1.4;
  border-radius:50%;
  will-change:transform;
}
/* 往右挪:压暗层盖住左侧 1/4,极光的核心如果落在那底下就等于白画。 */
.stage__aurora--a{
  top:-24%; left:12%;
  background:radial-gradient(closest-side,
    color-mix(in oklch, var(--color-glow) 46%, transparent) 0%,
    color-mix(in oklch, var(--color-glow) 15%, transparent) 46%,
    transparent 74%);
  animation:auroraA var(--dur-aurora) var(--ease-in-out) infinite alternate;
}
/* 冷紫之外补一点暖金。比例压到 16% —— 再高就成了那种紫→橙的 AI 渐变;
   这个量级只是让右下角的空气有温度,而且金在本站本来就有语义(风险/次级信号)。 */
.stage__aurora--b{
  right:-18%; bottom:-34%;
  width:clamp(20rem, 46vw, 40rem);
  background:radial-gradient(closest-side,
    color-mix(in oklch, var(--color-glow-gold) 16%, transparent) 0%,
    color-mix(in oklch, var(--color-glow-gold) 5%, transparent) 48%,
    transparent 74%);
  animation:auroraB calc(var(--dur-aurora) * 1.35) var(--ease-in-out) infinite alternate;
}
.stage__aurora--c{
  top:-40%; left:50%;
  width:clamp(30rem, 78vw, 66rem);
  background:radial-gradient(closest-side,
    color-mix(in oklch, var(--color-glow) 30%, transparent) 0%,
    color-mix(in oklch, var(--color-glow) 9%, transparent) 46%,
    transparent 74%);
  animation:auroraC calc(var(--dur-aurora) * 1.15) var(--ease-in-out) infinite alternate;
}
@keyframes auroraA{
  from{ transform:translate3d(-4%, -3%, 0) scale(1);    }
  to  { transform:translate3d( 8%,  6%, 0) scale(1.18); }
}
@keyframes auroraB{
  from{ transform:translate3d( 6%,  4%, 0) scale(1.12); }
  to  { transform:translate3d(-7%, -6%, 0) scale(1);    }
}
@keyframes auroraC{
  from{ transform:translate3d(-52%, 0, 0) scale(1);    }
  to  { transform:translate3d(-44%, 5%, 0) scale(1.14); }
}

/* ② 地块网格。这一页从头到尾在谈地块与楼宇,底纹就用最朴素的
   地籍网格,不用那种发光六边形/电路板。mask 让它向外淡出,
   避免网格线一路顶到版心边缘变成表格。 */
.stage__grid{
  inset:0;
  background-image:
    linear-gradient(to right,  color-mix(in oklch, var(--color-glow) 12%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in oklch, var(--color-glow) 12%, transparent) 1px, transparent 1px);
  background-size:72px 72px;
  /* 前缀不是可有可无:Safari 15.4 以下只认 -webkit-mask-image,
     不带前缀那边的网格会一路铺到版心边缘,从「淡出的底纹」变成一张表格。 */
  -webkit-mask-image:radial-gradient(115% 92% at 46% 26%, #000 0%, #000 32%, transparent 78%);
          mask-image:radial-gradient(115% 92% at 46% 26%, #000 0%, #000 32%, transparent 78%);
}
.stage__grid--wide{
  background-size:96px 96px;
  -webkit-mask-image:radial-gradient(90% 120% at 50% 50%, #000 0%, #000 26%, transparent 74%);
          mask-image:radial-gradient(90% 120% at 50% 50%, #000 0%, #000 26%, transparent 74%);
}

/* ③ 匹配场。canvas 由 main.js 绘制,尺寸完全由 CSS 决定。
   canvas 逐帧要拼 rgba() 字符串,不适合每帧解析 oklch(),所以这里把
   三个发光色预先落成 sRGB 通道交给 JS。数值就是 tokens.css 里同名 token
   的精确转换结果 —— 全站唯一需要手工同步的一处,改 token 记得改这三行。 */
.stage__field{
  inset:0; width:100%; height:100%;
  --canvas-node: 162 136 251;   /* --color-glow      #A288FB */
  --canvas-hi:   216 198 252;   /* --color-glow-hi   #D8C6FC */
  --canvas-gold: 231 181  81;   /* --color-glow-gold #E7B551 */
}

/* ④ 压暗层。标题压在左上,底下要安静;但压过头整块场就白画了 ——
   白字在 void 上是 19.5:1,即便直接压在最亮的节点上也还有 6:1 以上,
   所以只需要把左 1/4 收一收,不需要压成纯色。
   纵向那一道是给下面那块白图框留呼吸,让它浮起来而不是贴在网格上。 */
.stage__scrim{
  inset:0;
  background:
    linear-gradient(100deg,
      color-mix(in oklch, var(--color-void) 92%, transparent) 0%,
      color-mix(in oklch, var(--color-void) 55%, transparent) 24%,
      transparent 58%),
    linear-gradient(to bottom,
      transparent 52%,
      color-mix(in oklch, var(--color-void) 62%, transparent) 100%);
}

/* 舞台内的焦点环换成发光紫 —— --color-focus 是深紫,在 void 上等于没有。 */
.stage :where(a,button,[tabindex]):focus-visible{ outline-color:var(--color-glow-hi); }

/* 舞台上的幽灵按钮:浅底那套描边色在 void 上看不见。 */
/* 60% 而不是随手一个暗色:按钮描边是 UI 组件边界,WCAG 1.4.11 要 3:1。
   glow 混到 60% 叠在 void 上是 #6456A3,实测 3.15:1 —— 刚过线。
   下面几条只是分隔用的发丝线,不受这一条约束,所以取 34~40%。 */
.stage .btn--ghost{
  border-color:color-mix(in oklch, var(--color-glow) 60%, transparent);
  color:var(--color-ink-invert);
}
.stage .btn--ghost:hover{
  border-color:var(--color-glow);
  background:color-mix(in oklch, var(--color-glow) 12%, transparent);
}
.stage .btn--ghost:active{ background:color-mix(in oklch, var(--color-glow) 22%, transparent); }

/* ── Hero · 深色舞台 + 双栏 ─────────────────────────────
   标题左、引言 + 行动 + 计数右。原版是单列左对齐,
   引言限宽 40ch,右侧 60% 版心永远空着——首屏「空」的主因。 */
/* 导航是 fixed,不占文档流 —— hero 顶部要把它的高度让出来。
   舞台底比浅底更吃留白(暗面显小),上下各再放一档。 */
.hero{
  padding-block:calc(var(--nav-h) + var(--space-lg) + clamp(2rem, 4vw, 3.25rem))
               clamp(3.5rem, 6.5vw, 5.5rem);
  /* 全站的「2px 硬线走品牌紫」骨架,在深色表面上的那一版。 */
  border-bottom:var(--rule-strong) solid color-mix(in oklch, var(--color-glow) 62%, transparent);
}
.hero__inner{
  display:grid;
  /* 1.55 而不是 1.45:标题放大到 4.75rem 之后,左栏要装得下
     「同一份产业数据,」这完整一行(8em),换算见 tokens.css 的 --text-display。 */
  grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);
  gap:clamp(var(--space-lg), 3.5vw, var(--space-xl));
  /* 顶对齐,不是底对齐。两栏高度不等时,底对齐会把较矮的标题整块往下推,
     标题上方留出一大片空洞——那正是这次要消掉的东西。标题锚住左上角,
     剩余落差落在右栏 CTA 下方,那里本来就不该有内容。 */
  align-items:start;
  /* 这一段留白不是随手放的:文字块与下面的白图框之间要留出一条
     看得见的场,地块锚点和光束就活在这条带子里(见 main.js 的 anchor.y)。
     收窄了锚点会被图框压住,首屏就只剩一层网格。 */
  padding-bottom:clamp(var(--space-xl), 5vw, var(--space-2xl));
  /* void-3(#182142)在 void 上只有 1.4:1,压在发光的匹配场上等于没画。
     结构线在深底上要带一点自发光才立得住。 */
  border-bottom:var(--rule-hair) solid color-mix(in oklch, var(--color-glow) 40%, transparent);
}
.hero__title{
  font-size:var(--text-display);
  font-weight:600;
  /* 深底 + 更大的字号,行距要跟着收 —— 1.1 在 5rem 上会散开成两块。 */
  line-height:1.06;
  letter-spacing:0;
  color:var(--color-ink-invert);
}
.hero__lead{ display:block; }
.rotator{ display:block; }
/* 「支撑」跟着轮播词同行,颜色仍是主文字色 —— 断行点由结构决定,
   不交给浏览器:中文可以在任意字之间断,「数据」被拆成两行很难看。 */
/* 「支撑」与轮播词现在是两个颜色,中间需要比原来更明确的一口气 ——
   .06em 在 76px 上只有 4.5px,「支撑AI」会挤成一个词。 */
.rotator__verb{ color:var(--color-ink-invert); margin-right:.14em; }
/* 辉光挂在 slot 上而不是 item 上:item 用了 background-clip:text,
   自身是透明的,text-shadow 透不出来;drop-shadow 作用于合成后的像素,
   渐变字才有光。 */
.rotator__slot{
  display:inline-grid;
  vertical-align:top;
  filter:drop-shadow(0 0 26px color-mix(in oklch, var(--color-glow) 42%, transparent));
}
.rotator__item{
  grid-area:1 / 1;
  color:var(--color-glow);
  opacity:0;
  transform:translateY(.26em);
  filter:blur(8px);
  transition:opacity var(--dur-long) var(--ease-out),
             transform var(--dur-long) var(--ease-out),
             filter var(--dur-long) var(--ease-out);
}
/* 渐变字。@supports 兜底很重要:color 是 transparent,
   一旦 background-clip:text 不生效,五个词会集体隐形。 */
@supports ((background-clip:text) or (-webkit-background-clip:text)){
  .rotator__item{
    background-image:linear-gradient(96deg,
      var(--color-glow) 0%, var(--color-glow-hi) 54%, var(--color-glow) 100%);
    -webkit-background-clip:text;
            background-clip:text;
    color:transparent;
  }
}
.rotator__item.is-active{ opacity:1; transform:none; filter:blur(0); }
/* 离场比进场快、且往反方向走 —— 两个词朝同一个方向滑会像列表在滚,
   反向才读得出是「换了一个」。 */
.rotator__item.is-out{
  opacity:0;
  transform:translateY(-.22em);
  filter:blur(8px);
  transition-duration:var(--dur-short);
  transition-timing-function:var(--ease-in);
}

.hero__aside{ min-width:0; }
.hero__sub{
  max-width:var(--measure-lede);
  font-size:var(--text-lede);
  color:var(--color-on-slate);
}
.hero__actions{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--space-sm); margin-top:var(--space-lg); }

/* 计数取自本页事实(开放入口 1 / 数据源 5 / 反馈 89),不是编出来的指标。
   上了舞台之后它们是首屏唯一的「硬数字」,值得放大一档并给一条发光顶线。 */
.hero__meta{
  display:flex; flex-wrap:wrap; gap:var(--space-xl);
  margin-top:var(--space-xl);
  padding-top:var(--space-md);
  border-top:var(--rule-hair) solid color-mix(in oklch, var(--color-glow) 40%, transparent);
}
.hero__meta > div{ position:relative; }
.hero__meta dt{
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  letter-spacing:.06em;
  color:color-mix(in oklch, var(--color-on-slate) 78%, transparent);
}
.hero__meta dd{
  margin:0;
  margin-top:var(--space-3xs);
  font-size:var(--text-xl);
  font-weight:600;
  line-height:1.15;
  color:var(--color-ink-invert);
  text-shadow:0 0 28px color-mix(in oklch, var(--color-glow) 55%, transparent);
}

/* ── 界面示意图 · 排版式帧 ──────────────────────────────
   硬顶线(2px ink)+ mono 标签行 + 细边框。没有假标题栏、没有三个圆点。 */
.figure{
  background:var(--color-surface);
  border:var(--rule-hair) solid var(--color-rule);
  border-top:var(--rule-strong) solid var(--color-accent);
}
/* 首屏图框浮在匹配场里。刻意保持白面 —— 深底上再放一块深卡就糊成一片,
   一块干净的白工作台反而是全页对比最强的一处,真界面也最清楚。
   辉光负责把它和背景「焊」起来,免得看着像贴上去的。 */
.figure--lead{
  margin-top:clamp(var(--space-xl), 4vw, var(--space-2xl));
  /* 顺序有讲究:border-color 是简写,会把 border-top-color 冲掉,必须写在它前面。 */
  border-color:color-mix(in oklch, var(--color-glow) 26%, transparent);
  border-top-color:var(--color-glow);
  box-shadow:var(--glow-panel), 0 44px 90px -56px oklch(0% 0 0 / .85);
}
.figure__head{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--space-md);
  padding:var(--space-xs) var(--space-md);
  border-bottom:var(--rule-hair) solid var(--color-rule);
  background:var(--color-surface-alt);
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  letter-spacing:.04em;
}
.figure__label{ font-weight:600; color:var(--color-ink); }
.figure__note{ color:var(--color-ink-3); text-align:right; }
.figure__body--split{
  display:grid;
  grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
}

.pane{ padding:clamp(var(--space-md), 2.2vw, var(--space-lg)); }
.pane--alt{
  background:var(--color-surface-alt);
  border-left:var(--rule-hair) solid var(--color-rule);
}
.pane__step{
  display:flex; align-items:baseline; gap:var(--space-xs);
  font-size:var(--text-sm); font-weight:600;
  color:var(--color-ink);
  margin-bottom:var(--space-sm);
}
.pane__step em{
  font-style:normal; font-weight:400;
  font-family:var(--font-mono); font-size:var(--text-xs);
  color:var(--color-ink-3);
}
.pane__idx{
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  font-weight:700;
  color:var(--color-accent);
  letter-spacing:.06em;
}
.pane__step--mt{ margin-top:var(--space-lg); }
.pane__btn{
  display:inline-block;
  margin-top:var(--space-md);
  padding:var(--space-sm) var(--space-lg);
  background:var(--color-ink); color:var(--color-ink-invert);
  font-size:var(--text-sm); font-weight:600;
  border-radius:var(--radius-pill);
}

/* 字段:标签与值同排。原版标签独占一行,纵向白吃掉一半高度。 */
.field--wide{
  display:grid;
  grid-template-columns:minmax(0,6em) minmax(0,1fr);
  gap:var(--space-sm);
  align-items:baseline;
  padding-block:var(--space-sm);
  border-bottom:var(--rule-hair) solid var(--color-rule);
}
.field__label{
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  letter-spacing:.04em;
  color:var(--color-ink-3);
}
.field__val{
  font-size:var(--text-sm);
  color:var(--color-ink-2);
  min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

/* 数字承重:面积、单价、等级放大成视觉锚。utilitarian 的核心动作。 */
.field-row{
  display:grid;
  grid-template-columns:repeat(3, minmax(0,1fr));
  border-bottom:var(--rule-hair) solid var(--color-rule);
}
.field-row--2{ grid-template-columns:repeat(2, minmax(0,1fr)); }
.field-row .field{
  padding:var(--space-sm) var(--space-md) var(--space-sm) 0;
  min-width:0;
}
.field-row .field + .field{
  padding-left:var(--space-md);
  border-left:var(--rule-hair) solid var(--color-rule);
}
.field-row .field__label{ display:block; margin-bottom:var(--space-3xs); }
.field__val--fig{
  display:flex; align-items:baseline; gap:.35em;
  white-space:nowrap; overflow:hidden;
  font-size:var(--text-figure);
  font-weight:600;
  line-height:1.15;
  color:var(--color-ink);
}
.field__val--fig i{
  font-style:normal;
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  font-weight:400;
  color:var(--color-ink-3);
}

.chips{ display:flex; flex-wrap:wrap; gap:var(--space-xs); }
.chip{
  padding:var(--space-2xs) var(--space-sm);
  border:var(--rule-hair) solid var(--color-rule-strong);
  border-radius:var(--radius-pill);
  font-size:var(--text-xs);
  white-space:nowrap;
  color:var(--color-ink-2);
}
.chip--on{
  background:var(--color-accent-soft);
  border-color:var(--color-accent-soft);
  color:var(--color-accent);
}

.bar{ display:block; height:9px; border-radius:var(--radius-pill); background:var(--color-rule-strong); }
.bar--w28{ width:28%; } .bar--w30{ width:30%; } .bar--w32{ width:32%; }
.bar--w36{ width:36%; } .bar--w40{ width:40%; } .bar--w44{ width:44%; }
.bar--w55{ width:55%; } .bar--w62{ width:62%; } .bar--w70{ width:70%; }

.result{ padding-block:var(--space-sm); border-bottom:var(--rule-hair) solid var(--color-rule); }
.result:last-child{ border-bottom:0; padding-bottom:0; }
.result__head{ display:flex; align-items:center; justify-content:space-between; gap:var(--space-sm); }
.result__head .bar{ flex:1; }
.result__score{ font-size:var(--text-md); font-weight:700; color:var(--color-accent); }
.result__tags{ display:flex; gap:var(--space-xs); margin-top:var(--space-xs); }
.result__tags .bar{ height:7px; background:var(--color-rule); }

/* meter 用 scaleX 而不是 width——width 会触发 layout,transform 走合成层。 */
.meter{ height:4px; background:var(--color-rule); margin-top:var(--space-xs); overflow:hidden; }
.meter i{
  display:block; height:100%;
  background:var(--color-accent);
  transform-origin:left center;
}
.meter--warn i{ background:var(--color-warn); }

.composer{ border:var(--rule-hair) solid var(--color-rule); padding:var(--space-sm); }
.composer__ph{ display:block; font-size:var(--text-sm); color:var(--color-ink-3); min-height:var(--space-xl); }
.composer__foot{ display:flex; flex-wrap:wrap; gap:var(--space-xs); }

.dim{
  display:grid;
  grid-template-columns:minmax(0,4.75em) minmax(0,1fr) auto;
  align-items:center; gap:var(--space-sm);
  margin-top:var(--space-sm);
}
.dim__label{
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  color:var(--color-ink-3);
  white-space:nowrap;
}
.dim__val{ font-size:var(--text-sm); font-weight:600; color:var(--color-accent); }
.dim__val--warn{ color:var(--color-warn-ink); }
.dim .meter{ margin-top:0; }

/* ── Hero 编排(signature) ─────────────────────────────── */
/* 页面唯一的一处编排动效:把产品的核心动作演一遍——
   录入地块 → 候选企业逐条落位 → 匹配分从 0 跳到结果。
   只跑一次,不循环;其余位置一律安静。 */
@keyframes riseIn{
  from{ opacity:0; transform:translateY(10px); }
  to  { opacity:1; transform:none; }
}
.hero-play #heroFigure .field,
.hero-play #heroFigure .pane__btn,
.hero-play #heroFigure .result{ opacity:0; }

.hero-play #heroFigure.is-playing .field,
.hero-play #heroFigure.is-playing .pane__btn,
.hero-play #heroFigure.is-playing .result{
  animation:riseIn var(--dur-long) var(--ease-out) forwards;
}
.hero-play #heroFigure.is-playing .field--wide{ animation-delay:.10s; }
.hero-play #heroFigure.is-playing .field-row .field:nth-child(1){ animation-delay:.20s; }
.hero-play #heroFigure.is-playing .field-row .field:nth-child(2){ animation-delay:.28s; }
.hero-play #heroFigure.is-playing .field-row .field:nth-child(3){ animation-delay:.36s; }
.hero-play #heroFigure.is-playing .pane__btn{ animation-delay:.48s; }
.hero-play #heroFigure.is-playing .result:nth-of-type(1){ animation-delay:.70s; }
.hero-play #heroFigure.is-playing .result:nth-of-type(2){ animation-delay:.84s; }
.hero-play #heroFigure.is-playing .result:nth-of-type(3){ animation-delay:.98s; }

/* 进度条生长:scale 全程由 JS 控制(HTML 里的 inline transform 是无 JS 时的
   正确降级,优先级高于任何 CSS 规则,所以这里只声明过渡,不声明 transform)。 */
.hero-play #heroFigure .meter i{ transition:transform .85s var(--ease-out); }

/* ── 平台能力 · 产品家族 ────────────────────────────
   一宽一窄的深藏青卡。底色 #13243B 取自 buyer 真实界面的顶栏,
   真截图嵌进同族底色才不打架;紫色退到卡外的结构线与 CTA 上。

   直角保留 —— 「全站唯一圆角是按钮」是这套系统的既定规矩。
   参照站的同类卡是 16px 圆角,想跟就改这一处的 border-radius。 */
.family{
  display:grid;
  grid-template-columns:minmax(0,1.55fr) minmax(0,1fr);
  gap:var(--space-md);
  margin-top:var(--space-xl);
}
.family__card{
  position:relative;
  background:var(--color-slate);
  overflow:hidden;
  /* --mx/--my 由 main.js 在 pointermove 时写入(CSSOM,不受 CSP 管辖)。
     没有 JS 就停在中心值,探照灯变成一枚静止的柔光,不会坏。 */
  --mx:50%; --my:50%;
  transition:transform var(--dur-short) var(--ease-out),
             box-shadow var(--dur-short) var(--ease-out);
}
/* 探照灯。这一节的动词是「发现」,光跟着指针走就是这个动作本身,
   不是随便挑的 hover 效果。 */
.family__card::before{
  content:"";
  position:absolute; inset:0; z-index:0;
  pointer-events:none;
  opacity:0;
  background:radial-gradient(24rem 24rem at var(--mx) var(--my),
    color-mix(in oklch, var(--color-accent-on-dark) 32%, transparent) 0%,
    color-mix(in oklch, var(--color-accent-on-dark) 10%, transparent) 38%,
    transparent 70%);
  transition:opacity var(--dur-short) var(--ease-out);
}
.family__card:hover::before{ opacity:1; }
.family__card:hover{
  transform:translateY(-5px);
  box-shadow:0 26px 56px -28px oklch(0% 0 0 / .55),
             0 0 0 1px color-mix(in oklch, var(--color-accent-on-dark) 42%, transparent);
}
.family__link{
  position:relative; z-index:1;    /* 压在探照灯之上,文字不被洗白 */
  display:flex; flex-direction:column;
  height:100%;
}

/* 截图带:定高裁切,只露界面左上角(深色顶栏 + 表单开头),
   而不是把整张 2:1 的图缩到看不清字。 */
.family__shot{
  margin:0;
  padding:var(--space-lg) 0 0 var(--space-lg);
  height:clamp(10rem, 17vw, 14.5rem);
  overflow:hidden;
}
.family__shot img{
  width:100%; height:100%;
  object-fit:cover; object-position:left top;
}

/* signal 有访问口令,截不到界面 —— 用手绘的研判维度条代替,
   不拿一张登录页截图充数。 */
.family__mark{ margin:0; padding:var(--space-xl) var(--space-lg) 0; }
.family__mark svg{ width:clamp(5.5rem, 44%, 8.5rem); height:auto; }
.family__mark-track{ fill:var(--color-slate-2); }
.family__mark-fill{ fill:var(--color-accent-on-dark); }
.family__mark-fill--warn{ fill:var(--color-warn); }

.family__text{
  margin-top:auto;
  padding:var(--space-lg);
  display:flex; flex-direction:column; align-items:flex-start;
}
.family__status{
  display:inline-flex; align-items:center; gap:var(--space-2xs);
  padding:var(--space-2xs) var(--space-sm);
  border-radius:var(--radius-pill);
  background:color-mix(in oklch, var(--color-accent-on-dark) 18%, transparent);
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:700; letter-spacing:.08em;
  color:var(--color-accent-on-dark);
}
.family__dot{
  position:relative;
  width:6px; height:6px; flex:none;
  border-radius:50%;
  background:var(--color-accent-on-dark);
}
.family__dot::after{
  content:"";
  position:absolute; inset:0;
  border-radius:50%;
  background:var(--color-accent-on-dark);
  animation:ping var(--dur-pulse) var(--ease-out) infinite;
}
.family__title{
  margin-top:var(--space-md);
  font-size:var(--text-xl); font-weight:600; line-height:1.28;
  color:var(--color-ink-invert);
}
.family__desc{
  margin-top:var(--space-xs);
  max-width:var(--measure-prose);
  font-size:var(--text-base); line-height:1.7;
  color:var(--color-on-slate);
}
/* 紫色下划线 —— 但必须是深色表面专用的那个紫。
   品牌紫 #4B23A9 在这个底上只有 1.57:1,等于隐形。 */
.family__cta{
  margin-top:var(--space-lg);
  padding-bottom:var(--space-2xs);
  border-bottom:var(--rule-strong) solid var(--color-accent-on-dark);
  font-size:0.875rem; font-weight:600;
  white-space:nowrap;
  color:var(--color-accent-on-dark);
}
.family__arrow{ display:inline-block; transition:transform var(--dur-short) var(--ease-out); }
.family__card:hover .family__arrow{ transform:translateX(5px); }

/* ── 入口已关闭的卡 ─────────────────────────────────
   展示照旧(宽卡 + 真截图都留着),只是不可点。
   所以:不抬升、不换指针、没有脉冲点、没有 CTA —— 一眼看得出它不是入口,
   但也不把它做灰做小,因为这东西是建成的,不是画饼。 */
.family__card--closed{ cursor:default; }
.family__card--closed:hover{ transform:none; box-shadow:none; }
/* 探照灯是「可以点」的信号,关闭的入口不给 —— 否则鼠标一扫过去
   它看起来又活了,等于骗一次点击。 */
.family__card--closed::before{ content:none; }
.family__status--closed{
  display:inline-flex; align-items:center;
  padding:var(--space-2xs) var(--space-sm);
  border:var(--rule-hair) solid color-mix(in oklch, var(--color-on-slate) 40%, transparent);
  border-radius:var(--radius-pill);
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:700; letter-spacing:.08em;
  color:var(--color-on-slate);
}

/* 产品详解里,入口关闭的那块用这句代替原来的箭头链接 */
.link-note{
  display:inline-block;
  margin-top:var(--space-lg);
  padding:var(--space-2xs) var(--space-sm);
  border:var(--rule-hair) solid var(--color-rule-strong);
  border-radius:var(--radius-pill);
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:600; letter-spacing:.08em;
  white-space:nowrap;
  color:var(--color-ink-3);
}

/* ── 数据源条 ───────────────────────────────────────── */
.sources{
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:var(--space-2xs) var(--space-md);
  margin-top:var(--space-xl);
  padding-top:var(--space-sm);
  border-top:var(--rule-hair) solid var(--color-rule);
  font-family:var(--font-mono);
  font-size:var(--text-xs);
}
.sources__label{ letter-spacing:.12em; color:var(--color-ink-3); }
.sources__list{ color:var(--color-ink-2); }
.sources__list i{ font-style:normal; color:var(--color-rule-strong); margin-inline:.55em; }

.soon{ margin-top:var(--space-xl); }
.soon__head{
  display:flex; align-items:baseline; gap:var(--space-xs);
  padding-bottom:var(--space-xs);
  border-bottom:var(--rule-strong) solid var(--color-accent);
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  letter-spacing:.1em;
  color:var(--color-ink-3);
}
.soon__count{ font-weight:700; color:var(--color-ink); }
.soon__count::after{ content:" 项"; font-weight:400; color:var(--color-ink-3); letter-spacing:0; }
.soon__row{
  position:relative;
  display:grid;
  grid-template-columns:minmax(0,13.5em) minmax(0,1fr);
  gap:var(--space-sm) var(--space-lg);
  align-items:baseline;
  padding-block:var(--space-sm);
  border-bottom:var(--rule-hair) solid var(--color-rule);
}
/* 悬停时从左往右扫一层淡紫,而不是整行瞬间换底色。
   position:absolute 是必须的 —— .soon__row 是 grid,
   在流内的伪元素会被当成 grid item 占掉一整个轨道,把两列挤歪。 */
.soon__row::before{
  content:"";
  position:absolute; inset:0; z-index:0;
  pointer-events:none;
  background:linear-gradient(90deg,
    var(--color-accent-soft) 0%,
    color-mix(in oklch, var(--color-accent-soft) 40%, transparent) 62%,
    transparent 100%);
  transform:scaleX(0); transform-origin:left;
  transition:transform var(--dur-short) var(--ease-out);
}
.soon__row:hover::before{ transform:scaleX(1); }
.soon__row > *{ position:relative; z-index:1; }
/* 胶囊右对齐成列,而不是跟着标题宽度飘 —— 四行对齐才像表格。 */
.soon__name{
  display:grid;
  grid-template-columns:minmax(0,1fr) auto;
  align-items:baseline;
  gap:var(--space-xs);
}
.soon__title{ font-size:var(--text-md); font-weight:600; color:var(--color-ink-2); }
/* 已上线是实底紫,规划中是描边紫——同一族颜色,不同的完成度。 */
.soon__tag{
  flex:none;
  padding:var(--space-3xs) var(--space-xs);
  border:var(--rule-hair) solid var(--color-accent);
  border-radius:var(--radius-pill);
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  font-weight:600;
  letter-spacing:.06em;
  white-space:nowrap;
  color:var(--color-accent);
}
.soon__desc{ font-size:var(--text-base); color:var(--color-ink-3); }

/* ── 产品详解 ───────────────────────────────────────────── */
.feature{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  gap:clamp(var(--space-lg), 4vw, var(--space-3xl));
  align-items:center;
  margin-top:clamp(var(--space-xl), 4.5vw, var(--space-2xl));
}
.feature--rev .feature__text{ order:2; }
.feature__kicker{
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:700; letter-spacing:.12em;
  color:var(--color-accent);
  margin-bottom:var(--space-xs);
}
.feature__title{ font-size:var(--text-xl); font-weight:600; line-height:1.3; }
.feature__desc{
  margin-top:var(--space-sm);
  max-width:var(--measure-prose);
  color:var(--color-ink-2);
}
/* 浅底上的图框也要有厚度。首屏那块是发着光浮在场里的,到了这一节
   如果只剩一个白方块,读起来像同一个东西掉了电。
   投影仍是「耳语」级(大模糊 + 大负扩散),不是那种四面均匀的卡片阴影。 */
.feature__visual{
  min-width:0;
  box-shadow:0 24px 56px -34px var(--color-shadow);
  transition:box-shadow var(--dur-short) var(--ease-out);
}
.feature__visual:hover{
  box-shadow:0 30px 66px -30px var(--color-shadow),
             0 0 0 var(--rule-hair) color-mix(in oklch, var(--color-accent) 24%, transparent);
}

.ticks{ margin-top:var(--space-md); display:grid; gap:var(--space-xs); }
.ticks li{
  position:relative;
  padding-left:var(--space-lg);
  font-size:var(--text-base);
  color:var(--color-ink-2);
}
.ticks li::before{
  content:"";
  position:absolute; left:0; top:.85em;
  width:12px; height:var(--rule-hair);
  background:var(--color-accent);
}

.link-arrow{
  display:inline-flex; align-items:center; gap:var(--space-xs);
  margin-top:var(--space-lg);
  font-size:var(--text-base); font-weight:600;
  white-space:nowrap;
  color:var(--color-accent);
}
.link-arrow span{ display:inline-block; transition:transform var(--dur-short) var(--ease-out); }
.link-arrow:hover span{ transform:translateX(5px); }
.link-arrow:active{ color:var(--color-accent-deep); }

/* ── 工作方式 · 第二块深色舞台 ──────────────────────────
   这一节的主语是「数据在流」,所以它和 hero 共用 .stage 的底座与语汇。
   下面这一组是把浅底组件搬到 void 上要换的色 —— 逐条都是对比度问题,
   不是审美偏好:凡是 --color-ink* / --color-accent / --color-rule 系,
   在 void 上都掉到 2:1 以下。 */
/* 底座由 .stage 提供(HTML 上两个类一起写:class="section section--stage stage"),
   .section--stage 只负责这一节自己的组件换色。 */
.section--stage{
  border-block:var(--rule-strong) solid color-mix(in oklch, var(--color-glow) 62%, transparent);
}
.section--stage .head__title,
.section--stage .flow__title{ color:var(--color-ink-invert); }
.section--stage .head__intro,
.section--stage .flow__desc{ color:var(--color-on-slate); }
.section--stage .head::before{ background:var(--color-glow); }
.section--stage .flow__node{ border-top-color:color-mix(in oklch, var(--color-glow) 34%, transparent); }
.section--stage .flow__node:first-child{ border-top-color:var(--color-glow); }
.section--stage .flow__idx{ color:var(--color-glow); }
.section--stage .sources{ border-top-color:color-mix(in oklch, var(--color-glow) 34%, transparent); }
.section--stage .sources__label{ color:color-mix(in oklch, var(--color-on-slate) 76%, transparent); }
.section--stage .sources__list{ color:var(--color-glow-hi); }
.section--stage .sources__list i{ color:color-mix(in oklch, var(--color-glow) 45%, transparent); }

/* ── 手绘信号链 ─────────────────────────────────────────
   Tier B 手写 SVG。原始数据是暗的,过引擎之后开始发光——
   颜色本身在说明「结构化」这件事,不是装饰。 */
.pipe{
  margin-top:var(--space-xl);
  padding-block:var(--space-md);
}
.pipe__svg{ width:100%; height:auto; }
.pipe__src path,
.pipe__tick path{ fill:none; stroke:var(--color-rule-strong); stroke-width:1.5; }
.pipe__tick path{ stroke-width:1; }
.pipe__out path{ fill:none; stroke:var(--color-accent); stroke-width:1.5; }
.pipe__engine rect{ fill:var(--color-surface-alt); stroke:var(--color-ink); stroke-width:1.5; }
.pipe__engine path{ fill:none; stroke:var(--color-ink-3); stroke-width:1.5; }
.pipe__block rect{ fill:var(--color-accent-soft); stroke:var(--color-accent); stroke-width:1; }

/* 舞台版信号链 */
.section--stage .pipe__src path,
.section--stage .pipe__tick path{ stroke:color-mix(in oklch, var(--color-on-slate) 34%, transparent); }
.section--stage .pipe__out path{
  stroke:var(--color-glow);
  filter:drop-shadow(0 0 6px color-mix(in oklch, var(--color-glow) 70%, transparent));
}
.section--stage .pipe__engine rect{ fill:var(--color-void-2); stroke:var(--color-glow-hi); }
.section--stage .pipe__engine path{ stroke:color-mix(in oklch, var(--color-on-slate) 60%, transparent); }
.section--stage .pipe__block rect{
  fill:color-mix(in oklch, var(--color-glow) 16%, transparent);
  stroke:var(--color-glow);
}

/* 流动的光点。pathLength="1" 把每条路径归一化,所以 dasharray 直接写成
   「4.5% 实 + 95.5% 虚」= 一颗沿路径跑的点,长短不一的五条线共用同一套参数。

   ⚠ 这里动的是 stroke-dashoffset,不是 transform —— 是本文件唯一一处
   越过「只动 transform/opacity」那条线的动画。理由:8 条短路径、
   1 个属性、纯 SVG 光栅化,实测不进主线程布局;而用 transform 沿曲线
   走点需要 offset-path,那同样不是合成属性,还多一层元素。
   代价可控且换来这一节唯一的「在流动」的证据,所以留着;
   prefers-reduced-motion 下整组隐藏 —— 静止的一排小点看着像渲染坏了。 */
.pipe__spark{ display:none; }
.section--stage .pipe__spark{ display:block; }
.pipe__spark path{
  fill:none;
  stroke:var(--color-glow-hi);
  stroke-width:2.5;
  stroke-linecap:round;
  stroke-dasharray:0.045 0.955;
  filter:drop-shadow(0 0 5px var(--color-glow));
  animation:spark var(--dur-flow) linear infinite;
}
/* 五条错开起跑,免得整齐划一像跑马灯。负延迟 = 一上来就已经在路上。 */
.pipe__spark path:nth-child(1){ animation-delay:0s; }
.pipe__spark path:nth-child(2){ animation-delay:-0.68s; }
.pipe__spark path:nth-child(3){ animation-delay:-1.36s; }
.pipe__spark path:nth-child(4){ animation-delay:-2.04s; }
.pipe__spark path:nth-child(5){ animation-delay:-2.72s; }
/* 输出侧慢一点、也更亮:结论比原料值钱。 */
.pipe__spark--out path{
  stroke-width:3;
  animation-duration:calc(var(--dur-flow) * 1.25);
}
.pipe__spark--out path:nth-child(1){ animation-delay:-0.4s; }
.pipe__spark--out path:nth-child(2){ animation-delay:-1.7s; }
.pipe__spark--out path:nth-child(3){ animation-delay:-3.0s; }
@keyframes spark{ from{ stroke-dashoffset:1; } to{ stroke-dashoffset:0; } }

/* 描线还没画完就有点在跑会很怪 —— 等揭示编排结束再淡入。
   和全站一个规矩:没有 reveal-ready(JS 挂了)时默认可见。 */
.reveal-ready .pipe .pipe__spark{ opacity:0; }
.reveal-ready .pipe.is-in .pipe__spark{
  opacity:1;
  transition:opacity var(--dur-long) var(--ease-out) 1.15s;
}

/* 线条一次性描出。pathLength="1" 把每条路径归一化,
   所以长短不一的路径可以共用同一个 dasharray。 */
.reveal-ready .pipe .pipe__src path,
.reveal-ready .pipe .pipe__out path{
  stroke-dasharray:1; stroke-dashoffset:1;
}
.reveal-ready .pipe.is-in .pipe__src path,
.reveal-ready .pipe.is-in .pipe__out path{
  animation:draw 1s var(--ease-out) forwards;
}
.reveal-ready .pipe.is-in .pipe__out path{ animation-delay:.35s; }
@keyframes draw{ to{ stroke-dashoffset:0; } }

.reveal-ready .pipe .pipe__tick,
.reveal-ready .pipe .pipe__engine,
.reveal-ready .pipe .pipe__block{ opacity:0; }
.reveal-ready .pipe.is-in .pipe__tick,
.reveal-ready .pipe.is-in .pipe__engine,
.reveal-ready .pipe.is-in .pipe__block{
  animation:fadeIn var(--dur-long) var(--ease-out) forwards;
}
.reveal-ready .pipe.is-in .pipe__engine{ animation-delay:.30s; }
.reveal-ready .pipe.is-in .pipe__block{ animation-delay:.85s; }
@keyframes fadeIn{ to{ opacity:1; } }

/* 编号保留:数据接入 → 研判 → 输出 是真实序列,顺序本身是信息。
   三列不等宽——等宽三列是最好认的 AI 特征网格。 */
.flow{
  display:grid;
  grid-template-columns:minmax(0,1.15fr) minmax(0,1fr) minmax(0,1fr);
  gap:clamp(var(--space-lg), 3vw, var(--space-2xl));
  margin-top:var(--space-md);
}
.flow__node{
  padding-top:var(--space-sm);
  border-top:var(--rule-hair) solid var(--color-rule);
}
.flow__node:first-child{ border-top-color:var(--color-accent); border-top-width:var(--rule-strong); }
.flow__idx{
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:700; letter-spacing:.12em;
  color:var(--color-accent);
}
.flow__title{ margin-top:var(--space-2xs); font-size:var(--text-md); font-weight:600; }
.flow__desc{ margin-top:var(--space-xs); font-size:var(--text-base); color:var(--color-ink-2); }

/* ── 适用场景 · 真表格 ──────────────────────────────────
   原版是三个等宽格子,每格只有一个标题加一句话,又矮又宽。
   拆成「角色 / 手上有什么 / 需要判断什么」三列——
   文案一字未改,只是从段落拆成了列。 */
/* 表格收进图框。原来它是裸排在纸底上的三行字,是全页最像「还没做完」
   的一块;装进和 .figure 同一套框(硬顶线 + 白面 + 发丝边 + 耳语级投影)
   之后它才是一个物件。框线语汇一律复用,不另起一套。 */
.who{
  width:100%;
  margin-top:var(--space-xl);
  background:var(--color-surface);
  border:var(--rule-hair) solid var(--color-rule);
  border-top:var(--rule-strong) solid var(--color-accent);
  box-shadow:0 24px 56px -40px var(--color-shadow);
}
.who th, .who td{
  text-align:left; vertical-align:top;
  padding:var(--space-md) var(--space-lg);
  border-top:var(--rule-hair) solid var(--color-rule);
}
/* 表头做成图框的标签行:凹面底 + mono 小字,和 .figure__head 一个道理。
   顶线已经挂在 <table> 上了,这里不能再来一条,否则 2px 变 4px。 */
.who thead th{
  padding-block:var(--space-xs);
  border-top:0;
  background:var(--color-surface-alt);
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:600; letter-spacing:.1em;
  color:var(--color-ink-3);
}
.who tbody tr{ transition:background-color var(--dur-short) var(--ease-out); }
.who tbody tr:hover{ background:var(--color-accent-soft); }
.who tbody th{ width:24%; font-size:var(--text-md); font-weight:600; }
.who tbody td{ font-size:var(--text-base); color:var(--color-ink-2); }
.who tbody td:last-child{ color:var(--color-ink); }

/* ── Split(关于 / 加入) ─────────────────────────────────
   全页文字密度最低的一块:两栏各一个标题加一段话,右侧大片见底。
   补一枚巨大的品牌标记当水印 —— 不是随便找个图形填空,它就是顶栏
   那个标记本身(同一个 <symbol id="lanzMark">,靠 <use> 引),放大到出血。
   这一版换成真 <svg> 元素而不是伪元素的 clip-path:全站的标记几何
   从此只有一份,改一处就三处都跟着变。 */
.split{
  position:relative;
  display:grid;
  grid-template-columns:repeat(2, minmax(0,1fr));
  gap:clamp(var(--space-xl), 5vw, var(--space-3xl));
}
.split__watermark{
  position:absolute; z-index:0;
  right:-4%; top:50%;
  height:min(30rem, 128%);
  width:auto;
  transform:translateY(-50%);
  pointer-events:none;
  /* 淡紫底上 9% 的紫:认得出形状,但压在它上面的正文照样好读。
     条状标记比原来那个实心多边形「稀疏」得多,同样浓度看着更淡,
     所以从 7% 提到 9%。 */
  color:color-mix(in oklch, var(--color-accent) 9%, transparent);
  fill:currentColor;
}
/* .split 在 .wrap 上(z-index:1,是个层叠上下文),定位过的水印
   会画到普通流内容之上 —— 两栏必须显式抬到 1,否则水印盖住正文。 */
.split__col{
  position:relative; z-index:1;
  padding-top:var(--space-md);
  border-top:var(--rule-strong) solid var(--color-accent);
}
.split__title{ font-size:var(--text-xl); font-weight:600; line-height:1.28; }

/* ── CTA band ───────────────────────────────────────────── */
/* 深墨 → 品牌紫。白标题 9.94:1、副文 5.63:1,都过 4.5。 */
.cta-band{ background:var(--color-accent); color:var(--color-ink-invert); overflow:hidden; }
/* 收尾这一带是全页紫色浓度最高的地方,给它一层极慢的极光,
   让实心色块有厚度。两团都是同色相的深浅,不引入第二个色相。 */
.cta-band::before{
  content:"";
  position:absolute; inset:-25%; z-index:0;
  pointer-events:none;
  background:
    radial-gradient(40% 54% at 20% 20%, color-mix(in oklch, var(--color-glow-hi) 30%, transparent) 0%, transparent 70%),
    radial-gradient(36% 48% at 82% 82%, color-mix(in oklch, var(--color-accent-deep) 78%, transparent) 0%, transparent 68%);
  animation:auroraA calc(var(--dur-aurora) * 1.4) var(--ease-in-out) infinite alternate;
}
.cta-band__inner{ text-align:center; }
.cta-band__title{ font-size:var(--text-2xl); font-weight:600; line-height:1.24; }
.cta-band__sub{
  margin-top:var(--space-sm);
  margin-inline:auto;
  max-width:var(--measure-prose);
  color:var(--color-on-dark-2);
}
/* 紫带上的按钮是反色的白底 —— 白色扫光在白底上等于没有,换成淡紫;
   紫色投影在紫底上也看不出来,换成黑投影撑起浮起感。 */
.cta-band .btn--solid{
  margin-top:var(--space-lg);
  background-image:none;
  background:var(--color-ink-invert); color:var(--color-ink);
  box-shadow:0 14px 34px -16px oklch(0% 0 0 / .45);
}
.cta-band .btn--solid:hover{ background:var(--color-accent-soft); }
.cta-band .btn--solid::after{
  background:linear-gradient(105deg,
    transparent 38%,
    color-mix(in oklch, var(--color-accent) 26%, transparent) 50%,
    transparent 62%);
}

/* ── Footer · Ft1 Mast-headed ───────────────────────────── */
.site-footer{ background:var(--color-surface); border-top:var(--rule-hair) solid var(--color-rule); }
.site-footer__inner{
  display:grid;
  grid-template-columns:auto minmax(0,1fr) auto;
  grid-template-areas:
    "mark tag nav"
    "legal legal legal";
  align-items:center;
  gap:var(--space-md) var(--space-xl);
  padding-block:var(--space-xl) var(--space-lg);
}
.site-footer__mark{
  grid-area:mark;
  display:flex; align-items:center; gap:var(--space-xs);
}
.site-footer__tag{
  grid-area:tag;
  font-size:var(--text-base);
  color:var(--color-ink-3);
}
.site-footer__nav{ grid-area:nav; display:flex; flex-wrap:wrap; gap:var(--space-lg); }
.site-footer__nav a{ font-size:var(--text-base); line-height:1; white-space:nowrap; color:var(--color-ink-2); }
.site-footer__nav a:hover{ color:var(--color-ink); }
.site-footer__nav a:active{ color:var(--color-accent); }
/* 备案号是外链(分别指向工信部与公安部的官方查询页),给它一个可点的提示态。 */
.site-footer__icp{ transition:color var(--dur-short) var(--ease-out); }
/* 公网安备的警徽。用 em 让它跟着 mono 小字缩放:
   12px 字号 → 15px 图标,而源图是 20px,retina 下仍有 1.33x 余量。 */
.site-footer__icp--gongan{ display:inline-flex; align-items:center; gap:var(--space-2xs); }
.site-footer__icp--gongan img{ width:1.25em; height:1.25em; flex:none; }
.site-footer__icp:hover{ color:var(--color-ink); }
.site-footer__icp:active{ color:var(--color-accent); }

.site-footer__legal{
  grid-area:legal;
  display:flex; flex-wrap:wrap; gap:var(--space-md);
  padding-top:var(--space-md);
  border-top:var(--rule-hair) solid var(--color-rule);
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  color:var(--color-ink-3);
}

/* ══ 动效层 ═════════════════════════════════════════════
   用户明确要求「看得出来」的动效强度。这里有意偏离了两条通行的克制守则:
     · 滚动视差
     · 循环动效(引擎呼吸、状态点脉冲)
   两条都是用户在知情下选定的。作为交换,纪律一条没松:
     · 只动 transform 与 opacity,绝不动 width/height/top/left
     · 只用三个命名 easing
     · 视差走 CSS scroll-driven animation(合成层),不挂 scroll 监听
     · prefers-reduced-motion 下全部退回静态终态
   ═════════════════════════════════════════════════════ */

/* ── 阅读进度 ─────────────────────────────────────────────
   纯 CSS scroll-driven animation:不挂 scroll 监听,走合成线程。
   默认 display:none —— 不支持 animation-timeline 的浏览器如果显示了它,
   那就是一条永远停在 0% 的死线,比没有更糟。 */
.progress{
  position:fixed; top:0; left:0; right:0;
  height:2px;
  z-index:var(--z-toast);
  pointer-events:none;
  display:none;
}
.progress i{
  display:block; height:100%;
  background:linear-gradient(90deg, var(--color-accent) 0%, var(--color-glow) 100%);
  transform:scaleX(0); transform-origin:left;
}
@supports (animation-timeline: scroll()){
  @media (prefers-reduced-motion: no-preference){
    .progress{ display:block; }
    .progress i{
      animation:progressGrow linear both;
      animation-timeline:scroll(root block);
    }
    @keyframes progressGrow{
      from{ transform:scaleX(0); }
      to  { transform:scaleX(1); }
    }
  }
}

/* ── 首屏入场:一次性,逐个错开 ───────────────────────── */
@keyframes heroIn{
  from{ opacity:0; transform:translateY(16px); }
  to  { opacity:1; transform:none; }
}
/* 图框比文字重得多,给它更长的行程和一点点缩放 ——
   同样 16px 的位移,大面板看着几乎没动。 */
@keyframes panelIn{
  from{ opacity:0; transform:translateY(30px) scale(.985); }
  to  { opacity:1; transform:none; }
}
.hero__title,
.hero__sub,
.hero__actions,
.hero__meta{
  animation:heroIn var(--dur-long) var(--ease-out) backwards;
}
.hero__title  { animation-delay:.06s; }
.hero__sub    { animation-delay:.16s; }
.hero__actions{ animation-delay:.24s; }
.hero__meta   { animation-delay:.32s; }
.figure--lead { animation:panelIn 900ms var(--ease-out) .42s backwards; }

/* 舞台背景比内容先到位:极光与网格淡进来,内容再落上去。
   注意不能复用 .pipe 那个 fadeIn —— 它只写了 to{opacity:1},
   隐式的 from 会取元素当前值(也是 1),等于什么都不做。 */
@keyframes stageIn{ from{ opacity:0; } to{ opacity:1; } }
.stage__bg{ animation:stageIn 1.4s var(--ease-out) backwards; }

/* ── 滚动揭示 ─────────────────────────────────────────── */
/* 默认可见;仅当 JS 确认可用时才启用隐藏,JS 失败不会白屏。 */
.reveal-ready .reveal{
  opacity:0;
  transform:translateY(20px);
  transition:opacity var(--dur-long) var(--ease-out),
             transform var(--dur-long) var(--ease-out);
}
.reveal-ready .reveal.is-in{ opacity:1; transform:none; }
/* .pipe 内部自带描线编排,外层不再做位移,避免两段动效互相打架。 */
.reveal-ready .pipe.reveal{ transform:none; }

/* section 头改成三拍编排:紫线先描过去 → 标题 → 引言。
   头块本身不再整体位移 —— 一条线一边移动一边被「画出来」,
   读起来像滑进来的,不像画上去的,那正好毁掉这个动作。 */
.reveal-ready .head.reveal{ opacity:1; transform:none; }
.reveal-ready .head.reveal::before{ transform:scaleX(0); }
.reveal-ready .head.reveal.is-in::before{ animation:ruleIn 720ms var(--ease-out) forwards; }
@keyframes ruleIn{ from{ transform:scaleX(0); } to{ transform:scaleX(1); } }

.reveal-ready .head.reveal .head__title,
.reveal-ready .head.reveal .head__intro{
  opacity:0; transform:translateY(14px);
  transition:opacity var(--dur-long) var(--ease-out),
             transform var(--dur-long) var(--ease-out);
}
.reveal-ready .head.reveal.is-in .head__title,
.reveal-ready .head.reveal.is-in .head__intro{ opacity:1; transform:none; }
.reveal-ready .head.reveal.is-in .head__title{ transition-delay:.14s; }
.reveal-ready .head.reveal.is-in .head__intro{ transition-delay:.24s; }

/* 列表逐条落位。--i 由 JS 按 DOM 序写入,总时长封顶 ~500ms。
   带一点缩放:大卡片只做 14px 位移几乎看不出来在动。 */
.reveal-ready .family.reveal .family__card,
.reveal-ready .soon.reveal .soon__row,
.reveal-ready .flow.reveal .flow__node,
.reveal-ready .who.reveal tbody tr{
  opacity:0; transform:translateY(22px) scale(.99);
  transition:opacity var(--dur-long) var(--ease-out),
             transform var(--dur-long) var(--ease-out);
  transition-delay:calc(var(--i, 0) * 70ms);
}
.reveal-ready .family.reveal.is-in .family__card,
.reveal-ready .soon.reveal.is-in .soon__row,
.reveal-ready .flow.reveal.is-in .flow__node,
.reveal-ready .who.reveal.is-in tbody tr{ opacity:1; transform:none; }

/* ── 引擎方块呼吸(循环)──────────────────────────────── */
/* SVG 的 <g> 默认以视口原点为变换基准,必须给 fill-box 才会绕自身中心缩放。 */
.pipe__engine{
  transform-box:fill-box;
  transform-origin:center;
  animation:breathe var(--dur-breath) var(--ease-in-out) infinite alternate;
}
@keyframes breathe{
  from{ transform:scale(1); }
  to  { transform:scale(1.045); }
}

/* ── 滚动视差 ─────────────────────────────────────────── */
/* CSS scroll-driven animation:走合成线程,不挂 scroll 事件,
   不支持的浏览器整段跳过(@supports),静态呈现,不降级成抖动。 */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .feature__visual,
    .pipe__svg{
      /* 这里用 linear 是对的,不是漏了缓动:动画由滚动位置驱动而非时间,
         加缓动会让图形相对滚动忽快忽慢,像在橡皮筋上。 */
      animation:drift linear both;
      animation-timeline:view();
      animation-range:entry 0% exit 100%;
    }
    @keyframes drift{
      from{ transform:translateY(20px); }
      to  { transform:translateY(-20px); }
    }
  }
}

/* ══ 临时 · 业务员快捷入口 ═══════════════════════════════
   整段可直接删除。用深藏青 + 深色专用紫,和产品家族卡同一族,
   不另起一套视觉。形状走胶囊 —— 「全站唯一圆角是按钮与状态标签」,
   这东西本质就是个常驻按钮,不破规矩。 */
.quickentry{
  position:fixed;
  right:var(--space-lg);
  bottom:var(--space-lg);
  z-index:var(--z-sticky);
  display:flex; align-items:stretch;
  max-width:calc(100vw - var(--space-lg) * 2);
  background:var(--color-slate);
  border-radius:var(--radius-pill);
  box-shadow:0 16px 40px -20px var(--color-shadow);
  /* 延迟到首屏入场跑完再滑进来,不跟 hero 抢注意力 */
  animation:quickIn var(--dur-long) var(--ease-out) 1.4s backwards;
}
@keyframes quickIn{
  from{ opacity:0; transform:translateY(12px); }
  to  { opacity:1; transform:none; }
}
.quickentry__link{
  display:grid;
  grid-template-columns:auto auto;
  grid-template-areas:"tag  tag" "name hint";
  align-items:baseline;
  gap:var(--space-3xs) var(--space-sm);
  padding:var(--space-sm) var(--space-md) var(--space-sm) var(--space-lg);
  min-width:0;
}
.quickentry__tag{
  grid-area:tag;
  font-family:var(--font-mono);
  font-size:var(--text-xs); font-weight:700; letter-spacing:.1em;
  color:var(--color-accent-on-dark);
}
.quickentry__name{
  grid-area:name;
  font-size:var(--text-base); font-weight:600;
  white-space:nowrap;
  color:var(--color-ink-invert);
}
.quickentry__hint{
  grid-area:hint;
  font-family:var(--font-mono);
  font-size:var(--text-xs);
  white-space:nowrap;
  color:var(--color-on-slate);
}
.quickentry__arrow{ display:inline-block; transition:transform var(--dur-short) var(--ease-out); }
.quickentry__link:hover .quickentry__arrow{ transform:translateX(4px); }
.quickentry__link:hover .quickentry__name{ color:var(--color-accent-on-dark); }

.quickentry__close{
  flex:none;
  width:2.75rem;                       /* 44px 触控下限 */
  border-left:var(--rule-hair) solid var(--color-slate-2);
  border-radius:0 var(--radius-pill) var(--radius-pill) 0;
  font-size:var(--text-md); line-height:1;
  color:var(--color-on-slate);
  transition:color var(--dur-short) var(--ease-out),
             background-color var(--dur-short) var(--ease-out);
}
.quickentry__close:hover{
  color:var(--color-ink-invert);
  background:var(--color-slate-2);
}
.quickentry__close:active{ color:var(--color-accent-on-dark); }

/* 悬浮窗是 fixed 的,会压住页脚最后一行。用 :has() 让页脚在它存在时
   自动让出高度 —— 业务员点了「收起」,元素被移除,padding 自动消失,
   不需要 JS 再去同步一次。不支持 :has() 的浏览器只是略有重叠,不会坏。 */
body:has(.quickentry) .site-footer{ padding-bottom:var(--space-3xl); }

@media (max-width:560px){
  /* 窄屏改成贴底居中,别顶着右下角挡住页脚链接 */
  .quickentry{
    right:var(--space-sm); left:var(--space-sm);
    bottom:var(--space-sm);
    max-width:none;
  }
  .quickentry__link{ flex:1; padding-left:var(--space-md); }
}

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width:1000px){
  .feature{ grid-template-columns:minmax(0,1fr); }
  .feature--rev .feature__text{ order:0; }
  .flow{ grid-template-columns:minmax(0,1fr); gap:var(--space-lg); }
  .flow__node:first-child{ border-top-width:var(--rule-hair); border-top-color:var(--color-rule); }
  .flow{ border-top:var(--rule-strong) solid var(--color-ink); padding-top:var(--space-xs); }
}

@media (max-width:900px){
  .nav{ display:none; }
  .nav-toggle{ display:block; }
  .mobile-nav{ display:flex; }
  .mobile-nav[hidden]{ display:none; }

  /* 中间那栏藏起来之后必须真的退成两列——否则 auto 轨道虽然收成 0,
     两道 24px 的间隙还在,320px 下把 wordmark 挤到按钮底下。 */
  .site-header__inner{
    grid-template-columns:auto minmax(0,1fr);
    gap:var(--space-sm);
  }

  .hero__inner{ grid-template-columns:minmax(0,1fr); align-items:start; }
  .hero__aside{ padding-bottom:0; }

  /* 压暗层在窄屏要换方向。100deg 的渐变线长度由较长的那条边决定,
     在 414×1100 这种竖长的框里,「压左边」会退化成「压顶部」——
     标题反而落在最亮的地方。单栏之后标题本来就通栏,横向压暗没有意义,
     改成上下各收一点。 */
  .stage__scrim{
    background:linear-gradient(to bottom,
      color-mix(in oklch, var(--color-void) 58%, transparent) 0%,
      transparent 32%,
      transparent 62%,
      color-mix(in oklch, var(--color-void) 66%, transparent) 100%);
  }
  .figure__body--split{ grid-template-columns:minmax(0,1fr); }
  .pane--alt{ border-left:0; border-top:var(--rule-hair) solid var(--color-rule); }
  .split{ grid-template-columns:minmax(0,1fr); }
  /* 单栏之后水印要让位:再压在正文正中间就是块斑,推到右边出血,
     只露出标记的左半边。 */
  .split__watermark{ right:-30%; height:min(22rem, 92%); }
}

@media (max-width:768px){
  .head{ grid-template-columns:minmax(0,1fr); align-items:start; }
  .head__intro{ max-width:none; }

  .family{ grid-template-columns:minmax(0,1fr); }

  /* 表格塌成堆叠块:表头藏起来,列名用 data-label 就地补回。
     外框还在,所以行间不再靠 margin 拉开距离 —— 那会在框里割出几条白缝。
     改成发丝线分隔 + 单元格自带内边距,行与行之间是连续的一张面。
     分隔线也不能再用 2px 墨线:框顶已经有一条 2px 品牌紫,
     里面再来三条更重的黑线,主次就反了。 */
  .who thead{ display:none; }
  .who tbody tr{ display:block; border-top:var(--rule-hair) solid var(--color-rule); }
  .who tbody tr:first-child{ border-top:0; }
  .who tbody th,
  .who tbody td{
    display:block; width:auto; border-top:0;
    padding:0 var(--space-md) var(--space-sm);
  }
  .who tbody th{ padding-top:var(--space-md); }
  .who tbody td::before{
    content:attr(data-label);
    display:block;
    font-family:var(--font-mono);
    font-size:var(--text-xs);
    letter-spacing:.08em;
    color:var(--color-ink-3);
  }

  .site-footer__inner{
    grid-template-columns:minmax(0,1fr);
    grid-template-areas:"mark" "tag" "nav" "legal";
    align-items:start;
    gap:var(--space-sm);
  }
}

@media (max-width:400px){
  /* 再窄就放不下按钮了。顶栏只留 wordmark + 汉堡,
     「联系我们」改由展开菜单承接(见 .mobile-nav 里的同名按钮)。 */
  .site-header__right > .btn{ display:none; }
}

@media (max-width:560px){
  /* 三个大数字并排在 375px 下会被压碎——改竖排,数字保持放大。 */
  .field-row,
  .field-row--2{ grid-template-columns:minmax(0,1fr); }
  .field-row .field + .field{
    padding-left:0;
    border-left:0;
    border-top:var(--rule-hair) solid var(--color-rule);
  }
  .soon__row{ grid-template-columns:minmax(0,1fr); gap:var(--space-2xs); }
  .field--wide{ grid-template-columns:minmax(0,1fr); gap:var(--space-2xs); }
  .hero__meta{ gap:var(--space-md); }
  .pipe{ overflow-x:auto; }
  .pipe__svg{ min-width:34rem; }
}

/* ── Reduced motion ─────────────────────────────────────── */
@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{
    animation-duration:150ms !important;
    animation-iteration-count:1 !important;
    transition-duration:150ms !important;
  }
  .rotator__item{ transition:none; }
  .reveal-ready .reveal{ opacity:1; transform:none; }
  .hero-play #heroFigure .field,
  .hero-play #heroFigure .pane__btn,
  .hero-play #heroFigure .result{ opacity:1; }

  /* 首屏入场、揭示、循环、视差 —— 一律落到静态终态,不做空间位移。 */
  .hero__title, .hero__sub, .hero__actions, .hero__meta, .figure--lead{
    animation:none; opacity:1; transform:none;
  }
  .reveal-ready .head.reveal .head__title,
  .reveal-ready .head.reveal .head__intro,
  .reveal-ready .family.reveal .family__card,
  .reveal-ready .soon.reveal .soon__row,
  .reveal-ready .flow.reveal .flow__node,
  .reveal-ready .who.reveal tbody tr{
    opacity:1; transform:none; transition-delay:0s;
  }
  .family__dot::after{ animation:none; opacity:0; }
  .pipe__engine{ animation:none; transform:none; }
  .feature__visual, .pipe__svg{ animation:none; transform:none; }
  .family__card:hover{ transform:none; box-shadow:none; }
  .quickentry{ animation:none; opacity:1; transform:none; }

  /* ── 本轮新增的动效,一律落到静态终态 ────────────────── */
  /* 极光停在起始位;它本来就是一团柔光,不动也成立。 */
  .stage__aurora, .cta-band::before{ animation:none; }
  .stage__bg{ animation:none; opacity:1; }
  /* 轮播词不做模糊位移(JS 在此偏好下本来也不轮播,只是把样式一并收干净)。 */
  .rotator__item{ transition:none; filter:none; transform:none; }
  .rotator__item:not(.is-active){ opacity:0; }
  /* 静止的一排小点看着像渲染坏了,整组撤掉比停住好。 */
  .pipe__spark{ display:none !important; }
  /* 扫光是纯装饰,直接不出现。 */
  .btn--solid::after{ display:none; }
  .family__card::before, .soon__row::before{ transition:none; }
  .feature__visual{ transition:none; }
  .head::before{ transform:none !important; animation:none !important; }
  .figure--lead{ animation:none; opacity:1; transform:none; }
  /* 描线与淡入直接落到终态,不做空间位移。 */
  .reveal-ready .pipe .pipe__src path,
  .reveal-ready .pipe .pipe__out path{ stroke-dashoffset:0; }
  .reveal-ready .pipe .pipe__tick,
  .reveal-ready .pipe .pipe__engine,
  .reveal-ready .pipe .pipe__block{ opacity:1; }
}
