/* ============================================================
   灵视 P1 空间相机共创计划 · 活动落地页
   ------------------------------------------------------------
   ★ 自包含（不引官网 css/main.css）。理由：
     ① 官网 main.css 已 979KB —— 一个落地页不该为了取几十个 token 加载它；
     ② 它正被并行编辑线频繁改动，耦合进来等于把活动页绑在一个动靶上；
     ③ 本页要能单独部署到自己的端口/域名，不依赖官网目录结构。
   ★ 但下面的 Design Token 与组件量值全部【逐字照抄】官网 css/main.css，
     不凭记忆、不重新拍数。抄的来源与原注释一并保留，便于日后核对。
   ============================================================ */

/* ============================================================
   0) Design Token —— 逐字照抄官网 :root
   ============================================================ */
:root {
  /* Colors */
  --color-page-bg: #FAFAFA;
  --color-card-bg: #FFFFFF;
  --color-card-hover: #F6F7FA;
  --color-border: #EAECF2;
  --color-text-primary: #131419;
  --color-text-secondary: #545866;
  --color-text-tertiary: #9FA2B3;
  --color-brand: #3364FF;
  --color-brand-hover: #6B8EFF;
  --color-brand-bright: #8EAAFF;   /* 暗底用更亮品牌色 */
  --color-brand-press: #2952E0;    /* 主按钮 hover：往深走，厚实沉稳感 */

  /* 暗色场景 */
  --color-dark-bg: #0e0f14;
  --color-dark-surface: #131419;
  --color-text-on-dark: #f0f1f3;
  --color-text-on-dark-2: #a1a7b3;
  --color-text-on-dark-3: #6b7280;
  --color-white: #ffffff;
  --color-black: #000000;
  --color-danger: #ff4757;
  --color-success: #34c759;

  /* ★ AA 对比度补丁（2026-08-11 实测，见 _audit-contrast.txt / _apply-aa.mjs）
     不改上面照抄官网的 token 本身 —— --color-text-tertiary 还服务 placeholder、select 箭头、
     装饰性 badge 等「不受正文 AA 约束」的用途，改它会牵连一片。这里另立两个语义色： */
  --color-text-muted: #686C7A;      /* 亮底上的弱化正文（原 tertiary 实测仅 2.53:1）
                                       实测：纯白 4.87:1 ／ 浅蓝卡底 rgb(247,249,255) 4.63:1 */
  --color-legal-on-dark: #8A8F9C;   /* 暗底 legal 小字（原 on-dark-3 实测 3.96:1）
                                       实测对 #0e0f14 = 5.92:1，仍比 on-dark-2 暗一档以保留层级 */

  /* Font
     ★ 栈首的 'AlibabaPuHuiTiRegular' 是 2026-09-10 补的（与 footer.css / navbar.css 同一处理）。
       本页的 @font-face 已上提到 base.css，其中 'AlibabaPuHuiTiRegular' 带
       local('Alibaba PuHuiTi 3.0') 别名 + 子集 woff2 兜底 ⇒ 放首位后：装了普惠体的机器用
       本机字体（与原型完全一致），没装的用子集，都不会像以前那样静默落到系统字体。 */
  --font-family: 'AlibabaPuHuiTiRegular', "Alibaba PuHuiTi 3.0", "PingFang SC", -apple-system,
    BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* Border Radius —— ★ 圆角语义分层铁律（官网 2026-07-17 定稿）：
     有文字标签的按钮 = pill 100px（无例外）；
     表单/录入/承诺 = 6–8px（方正暗示「请认真填写」）；
     卡片/标签/chip = 6–8px；面板 = 16px。 */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 16px;

  /* Shadows */
  --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.04);
  --shadow-card-hover: 0 8px 24px rgba(0, 0, 0, 0.08);

  /* Container */
  --container-default: 1540px;
  --container-wide: 1600px;
  --container-padding-x: 64px;

  /* 导航栈几何契约（官网同源，禁止在别处硬写 8/56/64）。
     ★ 本页已并入官网 SPA、启用官网导航栏（HeaderNew，桌面 56px；≤1100px 走 MHeaderNew，48px，
       见下方 @media）。故 --nav-h = 56px：二级菜单栏吸顶位 = --nav-bottom = 68px，落在导航栏下方。
     ⚠️ 若某处又需要无导航栏的独立部署，把 --nav-h 改回 0px 即可，无需改别处硬编码。 */
  --nav-inset: 12px;
  --nav-h: 56px;
  --nav-gap: 8px;
  --nav-bottom: calc(var(--nav-inset) + var(--nav-h));

  /* Easing */
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-hover: cubic-bezier(0.33, 1, 0.68, 1);

  /* 标题层级（官网子页链：hero 38.4 → 板块 24 → 卡片 16） */
  --title-hero-size: 2.4rem;
  --title-hero-weight: 700;
  --title-hero-ls: 0.01em;
  --sub-hero-size: 1.1875rem;   /* 19px，对齐行业子页 .isub-hero-sub —— 必须明显大于 eyebrow(15px) */
  --title-subsection-size: 2.25rem;
  /* 板块标题 —— 照抄官网 main.css 的 --title-section-size (L2085)
     配 .block-head h3 使用。 */
  --title-section-size: 2.5rem;
  --sub-section-size: 1.125rem;

  /* 表单组件 token（官网 XGRIDS Form Component Library 原值） */
  --xg-input-height: 44px;
  --xg-input-radius: 8px;
  --xg-input-border: #E0E2E8;
  --xg-input-border-hover: #C0C4CC;
  --xg-input-border-focus: var(--color-brand);
  --xg-input-border-error: var(--color-danger);
  --xg-input-bg: var(--color-white);
  --xg-input-shadow-focus: 0 0 0 3px rgba(51, 100, 255, 0.1);
}

/* 标题字号响应式覆盖 —— 必须紧跟基准 :root（同特异性后写者胜；
   写到文件前面会被基准值静默盖掉，官网踩过两次） */
@media (max-width: 1024px) {
  :root { --title-subsection-size: 1.75rem; --title-section-size: 2.125rem; }
}
@media (max-width: 480px) {
  :root { --title-hero-size: 1.75rem; }
}

/* ============================================================
   1) 字体
   ============================================================ */
/* ⚠ 2026-09-10：本页原来在这里声明的两个 @font-face（'Alibaba PuHuiTi 3.0' 的 local()
   与 'XGRIDS'）已上提到 src/styles/base.css（全局，main.ts 引入），这里删除以免重复注册。
   · XGRIDS 的字形、版本戳（保持 ?v=2）、以及「只有拉丁、不能用于正文」的约束，
     都写在 base.css 那段注释里。
   · 'Alibaba PuHuiTi 3.0' 现在作为 local() 别名挂在 AlibabaPuHuiTiRegular 家族下 ——
     装了普惠体的机器零下载直接用本机字体，没装的走子集 woff2（1.94 MB，见
     docs/字体子集化说明.md）。原先这份 local() 只在本页注册，是导致
     `var(--font-family)` 首选在其它页面匹配不到、footer 与正文两种字体的原因之一。
   ⚠ 本页 XGRIDS 的用途（nav-binary 用 measureText 实测墨迹比例反算 0/1 字号、
     幕布字标几何）对 capHeight 敏感 —— 换字体文件前先在这页看效果。 */

/* ============================================================
   2) Base
   ============================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
/* ★ 视觉隐藏但保留无障碍（屏幕阅读器仍能读到） */
.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; }
body {
  font-family: var(--font-family);
  background: var(--color-card-bg);   /* ★ 统一白底：幕布升起后露出的就是这一层 */
  color: var(--color-text-primary);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img { pointer-events: none; user-select: none; -webkit-user-drag: none; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }

.container {
  max-width: var(--container-default);
  margin: 0 auto;
  padding: 0 var(--container-padding-x);
}
.section { padding: 96px 0; }
/* ★ 全页统一白底（2026-08-13 用户定调「不要灰色底，融合为统一白色底」）。
   原来是 #FFF(surface) 与 #FAFAFA(page-bg) 交替 —— 两色每通道只差 5，
   满宽时肉眼分不出、等于没起到分隔作用，却在明暗交界处露成一条边。
   ★ 前提已实测（_probe-card.mjs）：所有卡片都有 1px solid #EAECF2 描边，
     边界不依赖底色差，所以改白不会让卡片"消失"。
   板块分隔改由 padding(96px) 与标题层级承担。 */
.section--surface { background: transparent; }

/* 板块标题（官网层级：eyebrow → h2 → sub） */
.sec-head { max-width: 860px; margin: 0 auto 44px; text-align: center; }
.kicker {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--color-brand); margin-bottom: 12px;
}
.kicker::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor; flex: none;
}
.sec-title {
  font-size: var(--title-subsection-size); font-weight: 700;
  line-height: 1.3; color: var(--color-text-primary);
}
.sec-sub {
  margin-top: 12px; font-size: var(--sub-section-size);
  line-height: 1.75; color: var(--color-text-secondary);
}

/* ============================================================
   3) 进场揭示 —— 官网 2026-08-06 定案：整体淡入，不逐字
      （逐字 SplitText 已废弃，用户判定「全在跳舞」）
   ============================================================ */
[data-reveal] {
  opacity: 0; transform: translateY(18px);
  transition: opacity .62s cubic-bezier(.22, 1, .36, 1),
              transform .62s cubic-bezier(.22, 1, .36, 1);
  will-change: opacity, transform;
}
[data-reveal].in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none; }
}

/* ============================================================
   4) 按钮 + 视觉锤
   ------------------------------------------------------------
   ★ 视觉锤铁律（官网反复踩坑后定稿）：唯一通用实现 =
     【静态完整三椭圆 glyph（实心可见）+ CSS clip-path 扫描揭示 + 45° 旋转】。
     默认 width:0 且 svg clip-path:inset(0 100% 0 0)；hover 展 13px + inset(0)。
     ⚠️ 严禁把首页 hero 那个「JS 三圆逐帧生长」当通用组件 —— 小圆在 13px 容器里
        约 1.9px，肉眼几乎看不见（官网被用户连报两次「没有视觉锤」）。
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0 28px;
  font-size: 0.9375rem; font-weight: 600;
  font-family: inherit;
  border: 1px solid transparent;
  border-radius: 100px;              /* ★ 有文字标签的按钮一律 pill */
  cursor: pointer; white-space: nowrap; position: relative;
  transition: transform .3s cubic-bezier(.22, 1, .36, 1),
              background .25s var(--ease-hover), color .25s var(--ease-hover),
              border-color .25s var(--ease-hover), box-shadow .25s var(--ease-hover);
}
.btn-lg { min-height: 52px; padding: 0 32px; font-size: 1rem; }

.btn-primary { background: var(--color-brand); color: var(--color-white); }
.btn-primary:hover {
  background: var(--color-brand-press); color: var(--color-white);
  transform: translateY(-1px);
}
.btn-secondary {
  background: transparent; color: var(--color-brand);
  border: 1px solid var(--color-brand);
}
.btn-secondary:hover {
  color: var(--color-brand-press); border-color: var(--color-brand-press);
  box-shadow: inset 0 0 0 1px var(--color-brand-press);   /* 描边加粗，不填充 */
  transform: translateY(-1px);
}
/* 暗底次要按钮：官网 .hsh-btn-dark —— 白字 + 半透明玻璃底。
   ⚠️ 暗底上别用裸 .btn-secondary（蓝字蓝描边，实测对比不足）。 */
.btn-on-dark {
  background: rgba(255, 255, 255, 0.14);
  color: var(--color-white);
  border: 1px solid rgba(255, 255, 255, 0.45);
  backdrop-filter: blur(10px);
}
.btn-on-dark:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.7);
  color: var(--color-white); transform: translateY(-1px);
}
@supports not (backdrop-filter: blur(2px)) {
  .btn-on-dark { background: rgba(255, 255, 255, 0.2); }
}

/* 视觉锤 glyph 容器（JS 注入 SVG）
   ★ 2026-08-17 对齐官网 main.css SSOT：逐面片 scale stagger 生长 + 原路倒退。
   官网不用 clip-path 扫描：那套在退场时三面片同时收缩会失去层次，
   且「半成品立即倒退」需要 per-element 控制（scale(0) 退的是各自当前值）。 */
.btn-vh-auto-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 0; margin-left: 0; overflow: visible; color: currentColor; opacity: 0;
  transition: width .44s cubic-bezier(.34, 1.8, .5, 1),
              margin-left .44s cubic-bezier(.34, 1.8, .5, 1),
              opacity .28s ease;
}
.btn-vh-auto-mark svg {
  width: 13px; height: 13px; flex-shrink: 0; display: block; color: currentColor;
  transform: rotate(45deg); transform-origin: center;
  opacity: 0;
  transition: opacity .2s ease;
}
/* 三面片从圆点依次 stagger 弹性生长（圆点→短拖尾→长拖尾） */
.btn-vh-auto-mark svg ellipse,
.btn-vh-auto-mark svg path {
  transform-origin: center;
  transition: transform .24s cubic-bezier(.34, 1.56, .64, 1);
}
/* 默认态：隐藏（scale 0）。--vhi 记 stagger 序号，退场延迟归零 = 原路同时收缩 */
.btn-vh-auto-mark svg ellipse            { transform: scale(0); --vhi: 0; transition-delay: 0s; }
.btn-vh-auto-mark svg path:first-of-type { transform: scale(0); --vhi: 1; transition-delay: 0s; }
.btn-vh-auto-mark svg path:last-of-type  { transform: scale(0); --vhi: 2; transition-delay: 0s; }

/* hover 展开 + 三椭圆视觉锤：扫描揭示后静止定格 */
.btn:hover .btn-vh-auto-mark { width: 13px; margin-left: 8px; opacity: 1; }
.btn:hover .btn-vh-auto-mark svg { opacity: 1; }
.btn:hover .btn-vh-auto-mark svg ellipse            { transform: scale(1); }
.btn:hover .btn-vh-auto-mark svg path:first-of-type { transform: scale(1); }
.btn:hover .btn-vh-auto-mark svg path:last-of-type  { transform: scale(1); }
/* 进场 stagger：延迟只在 hover 态生效，退场恒 0 → 已显示面片同时收缩（原路倒退） */
.btn:hover .btn-vh-auto-mark svg ellipse,
.btn:hover .btn-vh-auto-mark svg path { transition-delay: calc(var(--vhi, 0) * 48ms); }

@media (prefers-reduced-motion: reduce) {
  .btn-vh-auto-mark, .btn-vh-auto-mark svg,
  .btn-vh-auto-mark svg ellipse, .btn-vh-auto-mark svg path { transition: none; }
}

/* ============================================================
   5b) 卡片四角视觉锤 —— CSS 侧【只需要一条】定位上下文
   ★★★ 2026-08-17：本组件是【官网 createH4CornerMarks 原样搬过来的】
     （xgrids-agent/js/main.js:14232），见 js/campaign.js 同名函数。
     它是 rAF 状态机驱动 SVG 的 rx/ry 属性，容器与 svg 的样式全部由 JS
     写成 inline style —— 所以 CSS 这边一条动画规则都不该有。

   ⚠️ 这里曾自绘过一套 `.h4-corner-mark` + CSS transition scale 的"照猫画虎"版本
      （官网压根没有 .h4-corner-mark 这个类），已删。别再加回来：
      · 官网是 rx/ry 逐帧插值 + 三圆 stagger（第 3 圆 2 倍速）+ 离开原路倒退，
        CSS transition 表达不出来；
      · 加了 CSS 规则反而会与 JS 写的 inline style 打架。
   ============================================================ */
.card-image-wrapper { position: relative; overflow: hidden; }


/* ============================================================
   6) Hero —— 暗色 + 官网低饱和极光
      极光配方 / mask / inset / opacity 全部照抄官网 .scx-hero-aurora
      （2026-08-05 全站统一那版，opacity 0.45、紫只 .24/.22、青用 rgb(0,190,255)）
      ⚠️ 六色之间的相对关系不要动，那是「低饱和」的本体。
   ============================================================ */
/* ============================================================
   幕布升起 v2 —— 几何契约（改这里前先把这一整段读完）
   ------------------------------------------------------------
   ★★★ 范式与参数取自【官网首页 hero】，2026-08-13 读源码 + 渲染实测：
         xgrids-agent/css/main.css:18036
           .hsh-scroll { height:140vh }   .hsh-sticky { sticky; top:0; height:100vh }
           => 滚动行程 runway = 140vh - 100vh = 40vh
         xgrids-agent/js/spatial-hero.js:518
           p = seg(smoothSp, 0, 0.8);  rise = -p*p*p * vh * 1.2;  rad = smoothSp * 320
           => 位移峰值 1.2vh（完全出画），三次曲线：前段几乎不动、后段极快
         xgrids-agent/css/main.css:11359
           .hsh-whatwedo { position:absolute; inset:0; z-index:3 }
           => 后一层【完全静止，初始就铺满视口】，hero 在它上面（z-index:4）
   ------------------------------------------------------------
   ★ 幕布对不对，只看两个数（node _probe-curtain2.mjs 会直接打印）：
       (1) P0 = scrollY=0 时【第二板块顶缘】的视口位置
             0  => 内容已铺满视口、在 hero 后面等着          <= 要的
             vh => 在视口下方，只能"跟随上滑才出现"          <= v1 就是这样，已被否决
       (2) 滚 100px 时 hero 的上移量
             ~100 => 1:1，等于普通滚动，只多了个圆角        <= v1 的病
             <40  => 三次曲线前段，幕布还没拉开             <= 要的
   ------------------------------------------------------------
   ★ 为什么第二板块靠 JS 写 transform，而不是照抄官网的 absolute-in-sticky：
     · 官网那一层是【一屏过场文案】，脱流无所谓；这里是【正式板块】，高度任意，
       absolute 会被 .hero-sticky 的 100svh + overflow 裁掉。
     · 让它自己 sticky 需要一个"比它高 runway"的包含块；它的父级是 body，
       sticky 行程 = bodyH - 自身高 => 会吸住半页，不可控。要可控就得加包裹 DOM。
     · 故：幕布期间 JS 写 translateY(scrollY) 抵消滚动（视觉静止），
       结束后固定在 translateY(runway)。零 DOM 改动、曲线完全可控。
   ------------------------------------------------------------
   ★★★ 四条必须同时成立，漏一条就错位（都推导过，别拆开）：
     (1) margin-top = -(100svh + runway)   => 第二板块文档位置 0 => P0 = 0
     (2) margin-bottom = runway            => 补偿 transform 的下移量，
                                              否则第二板块会压住后续板块 runway 那么多
     (3) 幕布结束后外层 transform 停在 runway（不是 0），否则交界处会跳一下
     (4) ★★★【内层】content 另有一个 transform: translateY(max(0, vh - |rise|))
         —— 让内容【贴着幕布下沿】被揭示出来。这一条是靠截图目视才发现必须加的，
         48 项自动断言全绿时它是缺的：
           幕布向上拉开 ⇒ 揭示窗口是 [vh-|rise|, vh]，从视口【底部】向上扩大；
           而内容若固定在盒子里，最先露出的就是板块的【最后一行】——
           实测 hero 升 264px 时，露出的是 #who 末尾那句 note，不是 kicker+标题。
           网页是自上而下读的，"先露结尾"与阅读方向相反。
         让内容顶缘跟住窗口顶缘后，用户看到的永远是内容的开头从幕布下沿浮现。
         ⚠️ 这【不是】用户否决的"跟随上滑才出现"：位移由 |rise|（三次曲线）驱动，
            不是由 scrollY 驱动 —— 滚 100px 幕布只开 45px，内容也只露 45px。
            v1 的病是 rise=scrollY（1:1）导致内容位移也 1:1，等于普通滚动。
         ⚠️ 因此内容必须【顶部对齐】，不能 flex 居中：居中会让内容顶缘低于窗口顶缘，
            窗口上方留出一条空带（试过，观感是"内容没跟上幕布"）。
         ⚠️ 也因此【不需要】min-height 撑高盒子。原先加 min-height:100svh 是为了把
            (2) 的过渡间隙压到视口外，但内容跟随窗口后它自己就盖住了那段间隙；
            剩下露出来的部分是纯白留白（全页统一白底），与板块下边距无异。
            去掉 min-height 还白拿一条：稳定态 reveal 底缘 = H+runway-scrollY
            = 后续板块位置，逐像素无缝、也不会撑出多余留白（实测 slack=0）。
   ★ .hero-sticky 必须 pointer-events:none，只有 .hero 是 auto：
     hero 升出画后容器仍 pin 在视口顶 100svh，不设会吞掉背后板块的点击。
   ★ 降级：CSS 默认【不做幕布】（普通流）。只有 js/campaign.js 判定可用后
     加 html.curtain-on 才生效。负 margin 绝不能写进默认态 —— JS 一挂，
     hero 就会和第二板块完全重叠。
   ============================================================ */
:root { --hero-runway: 40svh; }   /* = 官网 (140vh-100vh)/100vh = 0.40 */

.hero-scroll { position: relative; }

/* ---- 幕布启用态（<html class="curtain-on">，由 JS 判定后加）---- */
/* ★★★ pointer-events 必须【父子两级都设 none】，只设 .hero-sticky 是不够的：
   .hero-scroll 是高 (100svh + runway) = 1260px 的容器、z-index:2，
   它自己 pointer-events 默认 auto → 即使内容透明，那个盒子照样吞掉背后
   reveal 层的点击。实测（_verify-curtain2.mjs 第 5 项）：hero 已出画、
   命中栈顶却仍是 DIV.hero-scroll，reveal 里的链接点不到。
   pointer-events:none 的祖先下，子元素可以用 auto 单独恢复 —— 所以 .hero 仍可点。 */
/* ★★★★★ 2026-08-15 幕布 v3：整套"揭示编排"已拆掉，回归官网首页那套简单交互。
   ------------------------------------------------------------
   【为什么拆】用户原话「首页 hero 上滑的时候，下面的内容不是直接在后面等着出现的吗？
     就像官网首页的那种交互啊，怎么变成现在这样了，然后再往下的交互也是一样顺着走啊」
   【实测根因】v2 的三层 transform（外层 hold 抵消滚动 + 内层 peek 贴幕布下沿）
     让内容顶【严格等于幕布底沿】—— 内容是跟着幕布跑，不是在后面等着：
        sy=0   幕布底沿 1080 / 内容顶 1080
        sy=200        839 /        839
        sy=300        255 /        255      Δ 一路加速 -3 → -350
        sy=350       -216 /         96      ★ 跳变（hold 触顶、peek 归零）
     观感就是「内容贴在幕布圆角下方、周围一大片白」，且交界处不连续。
   【为什么 v2 会走到那一步】官网首页揭示的是 hero sticky 内部的
     absolute 覆盖层（#hsh-whatwedo，占满视口、靠 opacity 淡入）；
     而这里第二板块是【正式板块】、高度任意、不能 absolute，
     于是用负 margin 把它拉到与 hero 重叠再靠 transform 摆位 —— 方向就错了。
     ★ 结论：正式板块不要"揭示"，让它【普通滚上来】。
   【v3 = 官网首页最终定案那套】hero 一屏 + 零 sticky 行程 + 下一板块紧接着滚上来：
     · hero-scroll 高 100svh（不再 + runway）=> sticky 无行程，hero 随页面自然滚走
     · 第二板块【零负 margin、零 transform】，就在 hero 之后 => 严格无缝、零空白
     · "幕布"感只由底部圆角随滚动增长承担（见 js 的 curtainPaint）
     实测无缝：任意 sy 下 `hero 底沿 === 第二板块顶`（都等于 1080 - sy）。
   ⚠️ --hero-runway 现在【只作为圆角增长所需的滚动距离】被 JS 读，
      不再参与任何布局计算。改它只影响圆角快慢。
   ⚠️ 要回退到 v2：见 _backups/20260815-curtain-simplify/（HTML+CSS+JS 全量）。
   ============================================================ */
/* ★★★★★ 幕布 v6（2026-08-17）—— 被揭示层用【原生 sticky】，零 JS。
   ════════════════════════════════════════════════════════════════
   【实测依据】node _probe-curtain-truth.mjs → _probe-curtain-truth.txt
     官网 1440×900 真实几何：
       .hsh-scroll    height 1260 = 900(vh) + 360(runway = 0.4vh)
       .hsh-sticky    position:sticky; top:0; height:900; overflow:hidden
       视频层(幕布)    absolute，在 sticky 内，z-index:4，靠 transform 升起
       #hsh-whatwedo  absolute; inset:0，【在 sticky 内】，z-index:3
     被揭示层视口 top 曲线（实测）：
       sy = 0 / 100 / 200 / 300 / 360  →  top 恒为 0        （像素级静止）
       sy = 400 / 500 / 700 / 1200     →  top -40/-140/-340/-840（-1:1 线性）
     即：前 runway(360px) 静止，之后 1:1 随滚动。全程【零 JS】。

   【为什么必须是原生 sticky，不能用 JS transform】
     v4 用 rAF 每帧写 translateY 去"抵消"滚动。JS 与合成器不同帧 ——
     滚动已被合成器上屏、transform 还是上一帧的值，永远差一帧。
     那一帧的差就是用户报的「弹」。官网没有这个问题，因为它的静止
     由 position:sticky 在合成器侧完成，与滚动严格同帧。
     ⚠️ 所以【禁止】再回到 JS hold（无论用 raw scrollTop 还是 smoothSp
        —— 用 smoothSp 更糟：它自带滞后，本该静止的层会飘）。

   【结构差异与折中】
     官网被揭示层是能塞进一屏的 absolute 覆盖层；本页第二板块是正式 <section>、
     高度自由，absolute inset:0 会被 overflow:hidden 裁掉（一屏装不下是硬约束）。
     故改为「sticky 包装层 + ::after 行程块」：
       .reveal-scroll（父盒）  文档位置拉到 0；内容 = 粘停层 + ::after(runway 高)
       .reveal-sticky（粘停层）sticky top:0，粘停行程 = 父盒内容盒高 − 自身高 = runway
     由此得到与官网【完全相同】的 top 曲线，且不受一屏高度限制。
     ⚠️ 行程块【必须】是内容盒内的元素，不能写成父盒的 padding-bottom
        —— 详见下方 .reveal-scroll::after 那段的实测记录。

   【几何自洽验算】（H = 内容高，R = runway）
     .reveal-scroll 文档位置 = (100svh + R) − (100svh + R) = 0            ← 正好在 hero 后面
     粘停区间 = sy ∈ [0, R]：top 恒 0
     释放后 sy > R：top = −(sy − R)
     后续板块文档位置 = H + R；其视口 top = H + R − sy
     揭示层视觉 bottom（释放后）= −(sy−R) + H = H + R − sy   ← 与后续板块严格齐平
     ⇒ 零重叠、零空隙，且【不需要】v4 那个 margin-bottom 补偿
       （v4 需要它是因为 transform 不占布局空间；sticky 占，所以不需要）。
   ════════════════════════════════════════════════════════════════ */
:root { --hero-runway: 40svh; }   /* = 官网 (140vh-100vh)/100vh = 0.40 */

.hero-scroll { position: relative; }

.curtain-on .hero-scroll { height: calc(100svh + var(--hero-runway)); z-index: 2; pointer-events: none; }
.curtain-on .hero-sticky {
  position: sticky; top: 0; height: 100svh;
  overflow: hidden;        /* 圆角裁视频 */
  pointer-events: none;    /* hero 升出画后不吞背后揭示层的点击；.hero 自己恢复 auto */
}
.curtain-on .hero { height: 100%; min-height: 0; pointer-events: auto; }

/* 揭示层父盒：文档位置拉到 0（= 在 hero 正后面）。 */
.curtain-on .reveal-scroll {
  margin-top: calc(-100svh - var(--hero-runway));
  position: relative;
  z-index: 1;             /* < .hero-scroll 的 2 ⇒ 被幕布盖住 */
}
/* ★★★ 粘停行程必须来自【内容盒】里的东西，不能用 padding-bottom ★★★
   ── 这是本轮实测出来的关键，写死在这里免得后人再踩 ──
   sticky 的粘停范围 = 包含块的【内容盒】高 − 粘停元素高。padding 在内容盒之外，
   所以 padding-bottom 一点行程都不给。
   实测（_probe-sticky-iso.mjs）：
     .hero-scroll   额外 360px 来自 height  → 行程 360 → .hero-sticky 粘停正常
     .reveal-scroll 额外 360px 来自 padding → 行程 0   → .reveal-sticky 恒 -1:1，压根不粘
   ⚠️ 也不能给 .reveal-scroll 写死 height：内容高度随文案/视口变化，写死必错位。
   ⇒ 用 ::after 生成一个 runway 高的占位块（内容盒内的兄弟），高度自适应、零 HTML 改动。
   ⚠️ 它是【行程】不是视觉留白：粘停期间被 sticky 子层盖住，释放后落在视口下方看不到。 */
.curtain-on .reveal-scroll::after {
  content: '';
  display: block;
  height: var(--hero-runway);
}
/* 粘停层：原生 sticky —— 这一行就是「呆在后面不动」的全部实现（合成器驱动、零 JS）。 */
.curtain-on .reveal-sticky { position: sticky; top: 0; }

/* ★ 内容必须至少铺满一屏：否则幕布拉开后、内容底沿以下露出的是 body 白底
   （v2 截图里那一大片白就是这个）。让它铺满，露出的始终是板块自己的空间。 */
.curtain-on .reveal-sticky > * {
  min-height: 100svh;
  display: flex; align-items: center;
}
.curtain-on .reveal-sticky > * > * { flex: 1 1 auto; min-width: 0; }

.hero {
  position: relative;
  min-height: 100svh;      /* 默认（无幕布）：hero 自己至少一屏 */
  display: flex; align-items: center;
  padding: 88px 0;
  background: var(--color-dark-bg);
  overflow: hidden;        /* 圆角要裁住视频，必须保留 */
  pointer-events: auto;
  will-change: transform, border-radius;
}
/* 文案层：位移与淡出由 JS 写入 --hero-copy-y / --hero-copy-o。
   ★ 官网 .hsh-copy 与视频层是【兄弟】、各写同一个 rise（「和视频完全同步」）；
     本页文案是幕布层 .hero 的【子元素】、已继承父级 transform ⇒
     --hero-copy-y 恒为 0（CURTAIN.copyRise = 0），这里保留变量只为将来可调。
     淡出走官网口径 seg(sp, 0.3, 0.62)：前 30% 完全不淡。 */
.scx-hero-inner {
  transform: translateY(var(--hero-copy-y, 0px));
  opacity: var(--hero-copy-o, 1);
}

/* ============================================================
   Hero 移动端适配（≤768px）—— 照抄官网范式：【解除 pin】
   ------------------------------------------------------------
   ★★★ 2026-08-17：逐条对齐 xgrids-agent/css/main.css 的
       「首页 Hero 移动端适配（2026-07-29 专项）」@media(max-width:768px)。

   ★ 官网为什么在移动端解除 pin（原注释结论，不是我的推测）：
     桌面那套是「一屏 sticky 揭示」—— 被揭示层 absolute inset:0 叠在视频上，
     而它的内容在 390×844 下实测高 1330px > 视口 844px，会被 overflow:hidden
     硬裁 486px（375×667 裁 574 / 412×915 裁 431，三档全中）。
     「压缩进一屏」是做不到的：一屏装不下是硬约束。
     且移动端本来就以纵向滚动为交互本能，pin 一屏没有收益。

   ⚠️ 这里曾反向操作过（把 --hero-runway 从 40svh 加到 65svh 想让幕布"慢一点"），
      方向是错的 —— 官网移动端压根不 pin。

   ★ 仍保留升起 + 圆角：JS 照常写 transform/borderRadius，而
     curtainProgress() 的 total = .hero-scroll 实际高 − 视口高。解除 pin 后
     .hero-scroll 高度是 auto（= 一屏视频 + 下方板块内容），total 很大 ⇒
     sp 涨得很慢 ⇒ 圆角与位移都很温和，这正是官网移动端的体感。

   ⚠️ 配套约束（漏一条就错位）：
     1) .hero 是 absolute 铺满型的父级，sticky 高度变 auto 后它会被拉长
        → 必须显式钉成一屏（height:100svh）。
     2) .hero-sticky 必须保留一个 BFC（display:flow-root）：桌面版的
        overflow:hidden 顺带建了 BFC，改成 visible 后 BFC 消失，
        下方板块若带负 margin 会【向上塌陷穿透】容器一路顶到 body。
        position:relative 不阻止塌陷；flow-root 只建 BFC、不产生裁切。
     3) 第二板块回普通文档流：负 margin / min-height / flex 居中 全部复位，
        JS 侧的 hold transform 也要停（isPinned() 读 computed position 判断）。
   ============================================================ */
@media (max-width: 768px) {
  /* 滚动预算交给内容自身，不再是「一屏 + runway」 */
  .curtain-on .hero-scroll { height: auto; }

  /* 解除 pin 与裁切，但保留 BFC（见上方约束 2） */
  .curtain-on .hero-sticky {
    position: relative;
    top: auto;
    height: auto;
    overflow: visible;
    display: flow-root;
    pointer-events: auto;
  }

  /* 幕布层钉成"第一屏"，否则会随 sticky 变高一起拉伸 */
  .curtain-on .hero {
    height: 100vh; height: 100svh;
    min-height: 0;
  }

  /* 揭示层：解除 sticky，回到普通文档流（五条必须一起复位，漏一条就错位）
     ⚠️ ::after 是粘停行程的来源，解除 pin 后必须一起去掉，否则页面凭空多出 runway 的空白 */
  .curtain-on .reveal-scroll { margin-top: 0; }
  .curtain-on .reveal-scroll::after { display: none; }
  .curtain-on .reveal-sticky { position: static; }
  .curtain-on .reveal-sticky > * {
    min-height: 0;
    display: block;
  }
  .curtain-on .reveal-sticky > * > * { flex: none; }
}
/* 降级：reduced-motion 下 JS 不会加 .curtain-on，这里再兜一层
   （万一 class 已经加上了才切到 reduced-motion）。
   ★ 四处必须一起复位，漏一处就错位。 */
@media (prefers-reduced-motion: reduce) {
  .curtain-on .hero-scroll { height: auto; }
  .curtain-on .hero-sticky { position: static; height: auto; pointer-events: auto; }
  .curtain-on .hero { height: auto; min-height: 100svh; }
  .curtain-on .reveal-scroll { margin-top: 0; }
  .curtain-on .reveal-scroll::after { display: none; }
  .curtain-on .reveal-sticky { position: static; }
  .curtain-on .reveal-sticky > * { min-height: 0; display: block; }
  .hero { transform: none !important; border-radius: 0 !important; }
  .scx-hero-inner { transform: none; opacity: 1; }
}
/* Hero 背景视频（LCC 动画素材）+ 遮罩 */
.hero-video {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none;
}
.hero-video video {
  width: 100%; height: 100%; object-fit: cover;
}
/* ★ 双层遮罩（官网 hero 视频同款范式：径向压文字区 + 线性压上下边）。
   中心那个 0.78 不是手感值，是实测定的：素材是室内长廊 3DGS，画面最亮处
   正好是灭点、也正好在副标题那一行。
   实测（隐藏文字→截同坐标→取真实像素，node _probe-hero-contrast.mjs）：
     中心 0.52 时副标题最差处只有 1.89:1（远低于 AA 4.5）；主标题 6.23:1 本来就够。
   ⚠️ 视频每帧都在变，单一静态遮罩不可能对所有帧都刚好 —— 所以按【最差帧】定，
      而不是按平均值。 */
.hero-video::after {
  content: ''; position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 76% 84% at 50% 50%, rgba(14, 15, 20, 0.78) 0%, rgba(14, 15, 20, 0.9) 100%),
    linear-gradient(180deg, rgba(14, 15, 20, 0.72) 0%, transparent 32%, transparent 68%, rgba(14, 15, 20, 0.82) 100%);
}
/* [hero 极光整段已移除 —— 用户定调「背景就是遮罩和动画即可」]
   视频铺满后极光只在右侧透出一条蓝紫竖带，与视频自身色彩叠加反而脏。
   原先那套配方（官网 .scx-hero-aurora 六色低饱和 + 本页标定的 opacity 0.58）
   的实测过程记在 README「为什么是这些设计选择」一节，需要时可回溯。 */
/* 官网资源页 hero 内层：max-width 900px 居中（main.css L14819 原值）。
   ★ width:100% + min-width:0：.hero 是 flex 容器，本层是 flex item。flex item 默认
   min-width:auto，不会收缩到内容 max-content 以下 —— 小屏(<390)时会被内部长英文句
   撑到超过视口宽（实测 394 > 350），标题/副标题/按钮被右侧裁切。这两条让它老实收缩到
   父级宽度内。大屏不受影响：width:100% 仍被 max-width:900 约束、margin auto 居中。 */
.scx-hero-inner { position: relative; z-index: 2; width: 100%; max-width: 900px; min-width: 0; text-align: center; margin: 0 auto; }
.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  /* ★ 2026-08-15 放大一档（用户：「这个活动名字可能最好再大一些」）：
       13px/500 -> 15px/600，padding 7/16 -> 9/20 保持 pill 比例。
     ⚠️ 上限是 hero 副标题的 16.8px —— eyebrow 是「活动名标签」，
        比副标题还大会让层级倒挂（h1 38.4 > sub 16.8 > eyebrow 15 才对）。
        要再大就得连 --sub-hero-size 一起重排层级，别只推这一个数。 */
  padding: 9px 20px; border-radius: 100px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  font-size: 0.8125rem; font-weight: 600;
  color: var(--color-text-on-dark);
  backdrop-filter: blur(10px);
  margin-bottom: 26px;
}
/* ★ 2026-08-13：蓝色圆点去掉（用户要求中英两版统一不显示）。恢复：把 display:none 删掉。 */
.hero-eyebrow .dot {
  display: none;
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--color-brand-bright);
  box-shadow: 0 0 0 4px rgba(142, 170, 255, 0.18);
}
/* ★ 2026-08-17：改为与官网行业子页 hero h1 同一字号（industry-subpage.js .isub-hero h1）：
   clamp(2rem, 4.6vw, 3.4rem) = 32px → 54.4px，比资源页的 2.4rem(38.4px) 大一档，
   视觉上与行业子页开头标题一致。 */
.hero h1 {
  font-size: clamp(2rem, 4.6vw, 3.4rem);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.14;
  margin-bottom: 12px;
  color: var(--color-white);
}
/* ★ 2026-08-13：用户要求标题统一白色，去掉品牌渐变高亮。
   恢复渐变：把下面那条改回上面注释掉的那三行。 */
/* .hero h1 .accent { background: linear-gradient(120deg, var(--color-brand-bright) 0%, #5ED0FF 100%); -webkit-background-clip: text; background-clip: text; color: transparent; } */
.hero h1 .accent { color: inherit; }
/* 副标题：官网 .scx-hero p 同款字号（--sub-hero-size 1.05rem）。
   ★ 颜色【不用】--color-text-on-dark-2(#a1a7b3)：那是给纯色暗底定的，
     压在会动的视频上实测最差只有 1.89:1。压到视频画面上必须更亮。 */
.hero-sub {
  margin: 0 auto; max-width: 720px;
  font-size: var(--sub-hero-size); line-height: 1.7;
  color: rgba(255, 255, 255, 0.86);
}
.hero-sub b { color: var(--color-white); font-weight: 600; }

/* ★ hero 双 CTA —— 2026-08-13 起【两页都用】。
   （上一版这里写的是「国内版不用，已合并进 sticky 分段控件」，那个前提已不成立：
     用户定调身份选择要在首屏给出，国内版 hero 也加了同一组两个按钮。）
   两页 hero 都是 text-align:center，所以 justify-content:center 是共用基线；
   海外版在 world-backup.css 里只覆写了 margin-top（30px），没有改对齐。
   ⚠️ 这里曾有一条 .hero-meta（hero 底部的活动信息条）。2026-08-13 英文 hero 精简后
      两个页面都不再有那个元素，故连规则一起删除，避免留下死 CSS。
      要恢复活动信息条：把规则加回来 + 在 hero 里补那个 <p>。 */
.hero-cta {
  display: flex; flex-wrap: wrap; gap: 14px;
  justify-content: center; margin-top: 34px;
}

/* ============================================================
   7) 认识空间相机
   ============================================================ */
.whatis-grid {
  display: grid; grid-template-columns: 1.15fr 0.85fr;
  gap: 56px; align-items: start;
}
.whatis-lead {
  font-size: 1.0625rem; line-height: 1.8; color: var(--color-text-primary);
  margin: 18px 0 16px;
}
.whatis-text > p {
  font-size: 0.9375rem; line-height: 1.85;
  color: var(--color-text-secondary); margin-bottom: 14px;
}
.whatis-text b { color: var(--color-text-primary); font-weight: 600; }
.whatis-quote {
  margin: 24px 0 28px; padding: 16px 20px;
  border-left: 3px solid var(--color-brand);
  background: rgba(51, 100, 255, 0.05);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: 0.9375rem; line-height: 1.75; color: var(--color-text-primary);
}
/* 规格卡（旧）—— 已被 2026-08-17 改版替换为 .whatis-img + .whatis-features，
   规则保留备恢复，当前无消费者。 */
.whatis-card {
  position: relative; overflow: hidden;
  padding: 34px 30px;
  background: var(--color-card-bg);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}
.wc-specs li:first-child { border-top: none; padding-top: 0; }
.wc-specs li {
  padding: 15px 0; border-top: 1px solid var(--color-border);
  font-size: 0.875rem; line-height: 1.7; color: var(--color-text-secondary);
}
.wc-specs li:last-child { border-bottom: 1px solid var(--color-border); }
.wc-specs li b {
  display: block; font-size: 0.9375rem; font-weight: 600;
  color: var(--color-text-primary); margin-bottom: 3px;
}

/* ★ 2026-08-17：右侧改产品图 + 下方四特性横排 icon ★ */
.whatis-img {
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  border-radius: var(--radius-lg);
  padding: 24px;
  min-height: 280px;
}
.whatis-img img {
  max-width: 100%; max-height: 320px;
  object-fit: contain;
  border-radius: var(--radius-lg);
}
.whatis-features {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px;
  margin-top: 40px;
}
.wf-item {
  text-align: center; padding: 20px 12px;
  background: var(--color-card-bg);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
}
.wf-ic {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: 0 auto 12px;
  border-radius: 50%;
  background: rgba(51, 100, 255, 0.06);
  color: var(--color-brand);
}
.wf-ic svg { width: 22px; height: 22px; }
.wf-item b {
  display: block; font-size: 0.9375rem; font-weight: 600;
  color: var(--color-text-primary); margin-bottom: 4px;
}
.wf-item span {
  font-size: 0.8125rem; line-height: 1.6; color: var(--color-text-secondary);
}
.whatis-cta {
  text-align: center; margin-top: 36px;
}

/* ============================================================
   8) 身份切换 —— 官网灵动岛 tab 范式（滚动吸顶收成 pill）
   ============================================================ */
.program { padding: 0 0 110px; background: transparent; }  /* 灰底去掉，见上方统一白底说明 */

/* ============================================================
   二级菜单栏 —— 逐条照抄官网 css/main.css 的 .scx-tabs-wrap 组件
   （L15073~15214，含 condensed 灵动岛态与 ink bar）。
   ★ wrap 本身透明、sticky 在 --nav-bottom；呼吸间隙由 condensed 态的
     padding-top = --nav-gap 承担（官网原注释）。
   ★ 融合态上移【必须挂在 .scx-tabs-row 上，不能挂 wrap】：
     JS 拿 wrap 的 getBoundingClientRect() 做 condensed 判定，
     而 rect 会计入 transform —— 挂 wrap 会让判定自己把自己带偏（官网原注释）。
   ============================================================ */
/* 分段控件的小标题 —— 有了它，两个选项的文案才能缩短（原来「我是高校实验室 / 培训机构」
   12 个字，移动端会折两行）。实测收益：横版临界宽度 478px → 356px，
   于是 375 / 390 / 414 这些主流手机都能一行放下（见 _probe-seg.mjs）。

   ★ 用 padding-top 而不是 margin-top 承担上方留白：margin 会与父级 .container / .program
     折叠并溢出到 section 之外，间距表现还要看上一个 section 有没有 margin-bottom；
     padding 不折叠，位置确定。
   ★ 实测间距（别写成「与改动前逐像素一致」—— 那不准确）：
       到【小标题】顶缘 = 桌面 64px / 移动端 136px（.program 自带 72 + 这里 64）
         —— 这个数与改动前「到控件顶缘」的距离相同，即留白载体是等价搬移的；
       到【控件】顶缘 = 桌面 96px / 移动端 168px，各多 32px（标题文字 20 + margin-bottom 12）。
     多出来的 32px 是加了一行标题的必然结果，且与用户上一轮「红框上面多一些留白」同向。 */
.seg-label {
  margin: 0 0 12px; padding-top: 64px;
  text-align: center;
  font-size: 0.875rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-text-secondary);
}
/* ★ 仅海外版（world-backup.html）：那一页的 seg-label 与 tabs 是 .program 的【直接子元素】，
   不在 .container 里（因为它两个 tab 装的是顶层满宽 section，进 container 会让白底不满宽）。
   于是 .container 原本提供的左右留白要在这里补回来。
   ⚠ 这个选择器【只匹配海外版结构】—— 中文版的 wrap 在 .container 内，是
   .program > .container > .scx-tabs-wrap，命不中，故零副作用。改结构前先看这句。 */
.program > .seg-label,
.program > .scx-tabs-wrap { padding-left: var(--container-padding-x, 24px); padding-right: var(--container-padding-x, 24px); }
/* 收起态是 fixed 居中 pill，不需要左右留白（且 .is-condensed 的 padding 简写会覆盖上面这条）*/

.scx-tabs-wrap {
  position: sticky; top: var(--nav-bottom); z-index: 30;
  background: transparent;
  border-bottom: none;
  /* ★ 留白已交给上方 .seg-label 的 padding-top，这里必须是 0 ——
     condensed 判定读的是 wrap 的 getBoundingClientRect().top，
     wrap 自己带 margin/padding 都会让判定与视觉顶缘错开。 */
  margin-top: 0;
  transition: padding 0.3s var(--ease-reveal);
}
/* ★ 切换组件：正常显示，吸顶后收成 condensed pill。
   与英文版逻辑一致。
   ★ 中文版（非 .wb-page）：hero 可视时 pill 必须隐藏（否则跟 hero CTA 重复）。
   由 JS 在滚过 hero 后加 body.hero-passed 激活。 */
/* ★ 2026-08-15：pill 的"首屏隐藏"规则已删除（连同 js 的 body.hero-passed）。
   幕布 v3 让 hero 回到一屏、第二板块不再被负 margin 拉到与 hero 重叠，
   于是 pill 的文档位置退回到 hero(1080) + 第二板块之后 => 首屏天然在视口外，
   跟英文版一样【靠距离】而不是靠隐藏。少一套状态就少一处能错的地方。
   ⚠️ 若哪天又把第二板块拉到与 hero 重叠，这个问题会回来 —— 那时应优先改布局，
      而不是重新加隐藏规则。 */
.scx-tabs-wrap.is-condensed {
  background: transparent;
  border-bottom-color: transparent;
  padding: var(--nav-gap) 0;
}
/* ★★★ 正常态就带底框 = 【分段控件】（用户定调：「之前是一个有底框的组件，
   把这两个按钮组合在一起的感觉；现在的 button 看起来像是要点击跳转到新页面」）。
   两个独立的描边按钮读作「两个各自跳走的 CTA」；装进一个底框里才读作
   「同一个维度的二选一」—— 这正是身份切换要表达的语义。
   于是正常态与收起态是【同一个组件的大小两档】，只有尺寸差别，形态一致。 */
.scx-tabs-row {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  max-width: max-content; margin: 0 auto; padding: 6px;
  position: relative;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: 100px;
  box-shadow: var(--shadow-card);
  /* ★根治白底闪现：background/max-width/border-radius/box-shadow/border 全部瞬间切换
     （无过渡），两个方向都不会出现「拉伸」的中间帧。官网原设计，勿改。 */
  transition: none;
}
/* row 是 inline-flex，需要外层把它居中 */
.scx-tabs-wrap { text-align: center; }
.scx-tabs-wrap.is-condensed .scx-tabs-row {
  background: rgba(255, 255, 255, 0.82);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border-radius: 100px;
  padding: 5px 8px;
  box-shadow: 0 8px 28px rgba(20, 30, 80, 0.10);
  gap: 12px;
}
.scx-tabs { position: relative; display: flex; justify-content: center; gap: 14px; }
.scx-tabs-wrap.is-condensed .scx-tabs { gap: 4px; }

/* ★ 正常态 = 两个【大按钮】（用户定调：「变成两个大 button 的形式，一开始的风格也挺好的，
   这样比较醒目」）。所以这里不是官网那种纯文字 tab + 2px ink，而是按钮形态：
     · 未选中 = 描边（品牌蓝字 + 品牌蓝描边）
     · 选中   = 品牌蓝实底 + 白字
   选中态由「填充」表达，故【不需要 ink 下划线】—— 原 .scx-tab-ink 已连同 JS 一起移除。
   ★ 圆角仍是 pill 100px：这是「探索 / 可逆切换」语义（官网圆角语义分层铁律），
     与表单提交按钮的 8px 方角刻意区分开。
   ★ 吸顶收起后变成 pill 里的【分段控件】：未选中项脱掉描边变纯文字，选中项保持实底。 */
/* 分段控件里的两个选项：未选中 = 纯文字（不描边，否则底框里两个框很吵），
   选中 = 品牌蓝实底。「二选一」的语义靠底框 + 填充表达，不靠各自描边。 */
.scx-tab {
  position: relative; display: flex; align-items: center; justify-content: center;
  min-height: 48px; padding: 0 28px;
  font-size: 1rem; font-weight: 600; cursor: pointer;
  font-family: var(--font-family);
  color: var(--color-text-secondary);
  background: transparent;
  border: none;
  border-radius: 100px;
  white-space: nowrap;
  transition: color 0.25s var(--ease-hover), background 0.25s var(--ease-hover),
              min-height 0.3s var(--ease-reveal), padding 0.3s var(--ease-reveal),
              font-size 0.3s var(--ease-reveal);
}
.scx-tab:hover { color: var(--color-text-primary); background: rgba(19, 20, 25, 0.04); }
.scx-tab.active { background: var(--color-brand); color: var(--color-white); }
.scx-tab.active:hover { background: var(--color-brand-press); color: var(--color-white); }
/* 收起态：同一个组件收小一档 */
.scx-tabs-wrap.is-condensed .scx-tab { min-height: 34px; padding: 0 16px; font-size: 0.8125rem; }
.scx-tabs-wrap.is-condensed .scx-tab:hover { background: rgba(19, 20, 25, 0.04); }
.scx-tabs-wrap.is-condensed .scx-tab.active { background: var(--color-brand); color: var(--color-white); }
/* ⚠️ [hidden] 藏不住东西：UA 的 [hidden]{display:none} 优先级低于 .scx-tab{display:flex}，
   必须 !important（官网踩过两次）。 */
.scx-tab[hidden] { display: none !important; }

.program-block { display: none; }
.program-block.active { display: block; animation: blockIn .5s cubic-bezier(.22, 1, .36, 1) both; }
/* ★ 无身份切换的页面（英文版）：两个 block 都是顺序叙事的一部分，必须都显示。
   上面那条 .program-block{display:none} 的前提是「有 tab 在切换、JS 给当前面板加 .active」；
   英文版已移除身份切换（world-backup.html 的 #identitySwitch 是 inline display:none），
   于是没有 .active 的那一块被永久藏死 —— 实测 #block-explore h=0，
   连带 #further / #faq / #rules 三个板块访客完全看不到（规则涉及法务，FAQ 是必备内容）。
   ⚠️ 这条必须写在 .program-block.active 之后：两者特异性同为 (0,2,0)，靠源码顺序决胜。
   ⚠️ 中文版【不能】加这个类 —— 它的身份切换是有意的产品行为（用户明确要求）。 */
.program--no-tabs .program-block { display: block; }
@keyframes blockIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .program-block.active { animation: none; } }

/* max-width 880 → 1080：880px 下高校块那段说明的末尾「3 折。」会被挤到单独一行
   （用户：「可以宽一点，避免换行」）。1080 是实测能让两条通道的说明都排满不落单的宽度。 */
.block-head { max-width: 1080px; margin: 40px auto 44px; text-align: center; }
.block-head h3 {
  font-size: var(--title-section-size); font-weight: 700;
  line-height: 1.25; color: var(--color-text-primary); margin-bottom: 14px;
}
.block-head p {
  font-size: var(--sub-section-size); line-height: 1.8;
  color: var(--color-text-secondary);
}
.block-head b, .block-sub b { color: var(--color-brand); font-weight: 600; }
.block-section { margin-top: 64px; }
.block-sub {
  font-size: 1.0625rem; font-weight: 700; color: var(--color-text-primary);
  margin-bottom: 22px; padding-left: 12px;
  border-left: 3px solid var(--color-brand);
  line-height: 1.4;
}

/* 折扣盒 —— 改亮色（用户判定暗底板块「割裂」） */
.discount-box {
  display: grid; grid-template-columns: auto 1fr; gap: 34px; align-items: center;
  margin-top: 40px; padding: 30px 34px;
  background: var(--color-card-bg);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}
.discount-num {
  font-weight: 700; line-height: 1; color: var(--color-brand);
  font-size: clamp(3rem, 7vw, 4.5rem); white-space: nowrap;
}
.discount-num span {
  display: block; font-size: 0.8125rem; font-weight: 500;
  letter-spacing: 0.08em; color: var(--color-brand); margin-bottom: 8px;
}
.discount-num em { font-style: normal; font-size: 0.42em; margin-left: 4px; }
.discount-desc h4 {
  font-size: 1.0625rem; font-weight: 600;
  color: var(--color-text-primary); margin-bottom: 10px;
}
.discount-desc p {
  font-size: 0.9375rem; line-height: 1.8; color: var(--color-text-secondary);
}
.discount-desc b { color: var(--color-brand); font-weight: 600; }

/* 五步流程（官网行业子页工作流范式） */
.flow-steps {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px;
}
.flow-step {
  position: relative; padding: 24px 20px;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  transition: border-color .3s var(--ease-hover), box-shadow .3s var(--ease-hover);
}
/* .flow-step:hover 已移除（R8：纯展示卡不做 hover 反馈） */
.step-n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: rgba(51, 100, 255, 0.1); color: var(--color-brand);
  font-size: 0.875rem; font-weight: 700; margin-bottom: 14px;
}
.flow-step h4 {
  font-size: 0.9375rem; font-weight: 600;
  color: var(--color-text-primary); margin-bottom: 8px;
}
.flow-step p { font-size: 0.8125rem; line-height: 1.7; color: var(--color-text-secondary); }

/* 交换：你付出 / 你得到 */
.exch-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.exch-col {
  padding: 28px 26px; border-radius: var(--radius-md);
  background: var(--color-card-bg); border: 1px solid var(--color-border);
}
/* ⚠️ 「你能得到」原来带 rgba(51,100,255,0.03) 蓝底，用户判定「不干净」→ 去掉。
   正 / 负两列的区分交给品牌色描边（更克制，且卡片底色与全站其它卡一致）。 */
.exch-col.get { border-color: rgba(51, 100, 255, 0.28); }
.exch-col h4 {
  display: flex; align-items: center; gap: 10px;
  font-size: 1rem; font-weight: 700; color: var(--color-text-primary);
  margin-bottom: 18px;
}
.pill {
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.04em;
  padding: 4px 10px; border-radius: var(--radius-sm);   /* ★ 信息标注 = 小圆角 */
  background: rgba(19, 20, 25, 0.06); color: var(--color-text-secondary);
}
.exch-col.get .pill { background: rgba(51, 100, 255, 0.12); color: var(--color-brand-press); }  /* AA：品牌蓝在此 3.69:1 → press 4.82:1 */
.exch-col li {
  display: flex; gap: 12px; padding: 13px 0;
  border-top: 1px solid var(--color-border);
  font-size: 0.875rem; line-height: 1.7; color: var(--color-text-secondary);
}
/* ★ 清单项前缀 = 圆点，不是视觉锤 glyph（用户：「每一个标题左侧不用视觉锤，太碎了，
   可以是圆点都好」）。视觉锤留给按钮 hover 那一处 —— 它在那里是「一个」品牌签名动作；
   在 7 个并列清单项前重复出现就退化成噪点。
   ⚠️ HTML 上的 data-glyph 属性必须一起删掉，否则 js/campaign.js 的
      querySelectorAll('[data-glyph]') 仍会往 span 里塞 svg。
   margin-top 9px = (行高 14px×1.7 = 23.8 − 圆点 6) / 2，对齐第一行文字的光学中线。 */
.exch-col li .mk {
  flex: none; width: 6px; height: 6px; margin-top: 9px;
  border-radius: 50%; background: var(--color-brand);
}
.exch-col li b {
  display: block; font-weight: 600; color: var(--color-text-primary); margin-bottom: 3px;
}

/* 生态三段 */
.eco-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.eco-stats {
  display: block; margin-top: 14px;
  font-size: 0.8125rem; color: var(--color-brand);
  font-weight: 600;
}
.eco-card {
  padding: 30px 26px; border-radius: var(--radius-md);
  background: var(--color-card-bg); border: 1px solid var(--color-border);
  transition: border-color .3s var(--ease-hover), box-shadow .3s var(--ease-hover);
}
/* .eco-card:hover 已移除（R8） */
.eco-n {
  font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.08em;
  color: var(--color-brand); margin-bottom: 14px;
}
.eco-card h4 {
  font-size: 1.0625rem; font-weight: 700;
  color: var(--color-text-primary); margin-bottom: 12px;
}
.eco-card p { font-size: 0.875rem; line-height: 1.8; color: var(--color-text-secondary); }
.eco-card b { color: var(--color-text-primary); font-weight: 600; }

/* ★ 2026-08-18：左大右上下布局（.eco-grid--featured） */
.eco-grid--featured {
  grid-template-columns: 1.2fr 1fr;
  grid-template-rows: 1fr 1fr;
}
.eco-grid--featured .eco-card--main {
  grid-row: 1 / -1; /* 左侧占满两行 */
}

/* 扎克力卡底部三张横图 */
.eco-gallery {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  margin-top: 16px;
}
.eco-thumb {
  width: 100%; aspect-ratio: 4/3; object-fit: cover;
  border-radius: 8px; cursor: pointer;
  transition: transform .22s var(--ease-hover), opacity .22s;
}
.eco-thumb:hover { transform: scale(1.04); opacity: .88; }

/* Lightbox */
.lightbox {
  position: fixed; inset: 0; z-index: 10000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.82); backdrop-filter: blur(8px);
  opacity: 0; pointer-events: none;
  transition: opacity .28s ease;
  cursor: zoom-out;
}
.lightbox.is-open { opacity: 1; pointer-events: auto; }
.lightbox img {
  max-width: min(640px, 90vw); max-height: 86vh;
  border-radius: 12px; box-shadow: 0 12px 48px rgba(0,0,0,.5);
  transform: scale(.92); transition: transform .32s cubic-bezier(.34,1.56,.64,1);
}
.lightbox.is-open img { transform: scale(1); }

/* 移动端回退 */
@media (max-width: 768px) {
  .eco-grid--featured {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
  }
  .eco-grid--featured .eco-card--main {
    grid-row: auto;
  }
}
.tbd {
  display: inline-block; margin-top: 10px; padding: 4px 10px;
  border-radius: var(--radius-sm); font-size: 0.75rem;
  background: rgba(51, 100, 255, 0.08); color: rgba(51, 100, 255, 0.72);
  border: 1px solid rgba(51, 100, 255, 0.15);
}

/* 变现路径 */
.earn-lead {
  font-size: 0.9375rem; line-height: 1.8;
  color: var(--color-text-secondary); margin-bottom: 24px; max-width: 760px;
}
.earn-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-bottom: 24px;
}
.earn-card {
  padding: 22px 18px; border-radius: var(--radius-md);
  background: var(--color-card-bg); border: 1px solid var(--color-border);
  transition: border-color .3s var(--ease-hover), box-shadow .3s var(--ease-hover);
}
/* .earn-card:hover 已移除（R8） */
.earn-ic {
  width: 34px; height: 34px; margin-bottom: 14px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-sm);
  background: rgba(51, 100, 255, 0.09); color: var(--color-brand);
}
.earn-ic svg { width: 19px; height: 19px; }
.earn-card h5 {
  font-size: 0.9375rem; font-weight: 600;
  color: var(--color-text-primary); margin-bottom: 7px;
}
.earn-card p { font-size: 0.8125rem; line-height: 1.7; color: var(--color-text-secondary); }
.earn-ways { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.earn-ways span {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.875rem; color: var(--color-text-primary);
}
.earn-ways svg { width: 16px; height: 16px; flex: none; color: var(--color-success); }
.earn-note { font-size: 0.8125rem; line-height: 1.7; color: var(--color-text-tertiary); }

/* ============================================================
   9) 报名表 —— 官网 XGRIDS Form 组件规格
      ★ 圆角语义分层：这里是【录入 / 承诺】态 → 8px 方正，不用 pill。
   ============================================================ */
.signup-wrap {
  margin-top: 64px; padding: 40px;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.field { margin-bottom: 20px; }
.field > label {
  display: block; font-size: 0.875rem; font-weight: 500;
  color: var(--color-text-primary); margin-bottom: 8px;
}
.field .req { color: var(--color-danger); margin-left: 2px; }
.field input, .field select, .field textarea {
  width: 100%; font-family: inherit; font-size: 14px;
  border: 1px solid var(--xg-input-border);
  border-radius: var(--xg-input-radius);
  background: var(--xg-input-bg);
  color: var(--color-text-primary);
  transition: border-color .2s ease, box-shadow .2s ease;
  outline: none; box-sizing: border-box;
}
.field input, .field select { height: var(--xg-input-height); padding: 0 16px; }
.field textarea { min-height: 120px; padding: 12px 16px; resize: vertical; line-height: 1.7; }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--xg-input-border-hover); }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--xg-input-border-focus);
  box-shadow: var(--xg-input-shadow-focus);
}
.field input::placeholder, .field textarea::placeholder { color: var(--color-text-tertiary); }
.field select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239FA2B3' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 16px;
  padding-right: 40px; cursor: pointer;
}
.err-msg {
  display: none; margin-top: 6px; font-size: 0.8125rem; color: var(--color-danger);
}
.field.has-error input, .field.has-error select, .field.has-error textarea {
  border-color: var(--xg-input-border-error);
}
.field.has-error .err-msg { display: block; }
.field-hint {
  margin-top: 8px; padding: 11px 14px;
  background: rgba(51, 100, 255, 0.04);
  border-radius: var(--radius-sm);
  font-size: 0.8125rem; line-height: 1.7; color: var(--color-text-secondary);
}
.field-hint b { color: var(--color-brand); font-weight: 600; }
.form-consent {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 0.875rem; line-height: 1.6;
  color: var(--color-text-secondary); cursor: pointer; margin: 6px 0 24px;
}
.form-consent input { width: 17px; height: 17px; margin-top: 2px; flex: none; accent-color: var(--color-brand); cursor: pointer; }
.form-consent a { color: var(--color-brand); }
.form-consent a:hover { color: var(--color-brand-hover); text-decoration: underline; }
.form-consent.has-error { color: var(--color-danger); }
/* ★ 表单提交按钮：圆角语义分层 —— 这里是【提交/承诺】态 → 小圆角 8px。
   且按官网意向咨询页规范：左右拉满宽度（铁律 2026-08-11 用户明确要求对齐）。 */
.form-submit { display: flex; justify-content: stretch; }
.form-submit .btn {
  width: 100%;
  border-radius: var(--xg-input-radius);   /* 8px，与输入框等高、同圆角 —— 暗示「是表单的一部分」 */
}

/* ============================================================
   10) FAQ —— 官网 .home-faq 范式（细分隔线 + 品牌椭圆 ✚ 转 ✕）
   ============================================================ */
.faq-list { max-width: 1000px; margin: 0 auto; }
.faq-item { border-bottom: 1px solid var(--color-border); }
.faq-q {
  appearance: none; -webkit-appearance: none;
  width: 100%; min-height: 68px; padding: 20px 0;
  border: 0; background: transparent; font: inherit; text-align: left;
  font-size: 1.05rem; font-weight: 500; letter-spacing: -0.01em;
  color: var(--color-text-primary); cursor: pointer;
  display: flex; align-items: center; gap: 16px;
  transition: color .22s ease;
}
.faq-q:hover { color: var(--color-brand); }
.faq-label { display: block; flex: 1; }
.faq-toggle {
  width: 22px; height: 22px; flex: 0 0 22px; margin-left: auto;
  display: flex; align-items: center; justify-content: center;
  color: var(--color-text-tertiary); transform-origin: center;
  transition: transform .35s cubic-bezier(.22, 1, .36, 1), color .22s ease;
}
.faq-toggle svg { width: 100%; height: 100%; }
.faq-toggle ellipse { fill: currentColor; }
.faq-q:hover .faq-toggle { color: var(--color-brand); }
.faq-item.is-open .faq-toggle { transform: rotate(45deg); color: var(--color-brand); }
.faq-a {
  display: grid; grid-template-rows: 0fr; overflow: hidden;
  transition: grid-template-rows .42s cubic-bezier(.22, 1, .36, 1);
}
.faq-item.is-open .faq-a { grid-template-rows: 1fr; }
.faq-a-inner { min-height: 0; overflow: hidden; }
.faq-a p {
  padding: 0 40px 24px 0; 
  font-size: 0.9375rem;
  line-height: 1.85; color: var(--color-text-secondary);
}
/* 以清单结尾的 FAQ 项（如"How are winners chosen"的评审维度）：wb-list 无底部内边距，
   会让内容紧贴折叠容器底边/分隔线；补 24px 与 <p> 结尾的项对齐。 */
.faq-a-inner > .wb-list:last-child { padding-bottom: 24px; }
/* 答案含多个块级子元素（如"How are winners chosen"是引导 <p> + 评审清单）：非末尾的 <p>
   去掉下边距，靠 line-height 分隔即可；末尾元素（单段落答案的 <p> 或清单）仍保留 24px 底距。 */
.faq-a-inner > p:not(:last-child) { padding-bottom: 0; }

/* ============================================================
   12) Toast
   ============================================================ */
.toast {
  /* ★ 屏幕正中，不是页面正中（2026-08-31 用户反馈 + 实测）：
     原型把 toast 贴在视口顶部（top:24px），页面并入官网 SPA 后那个位置正好压在
     fixed 导航（top 8 ~ 56，z-index:10000 > toast 的 9999）底下，只露出下半条；
     而表单在页面很靠下，提交时视线在屏幕中部，顶部提示既被挡又容易看漏。
     故改为 fixed 垂直居中：无论滚到哪、移动端地址栏怎么伸缩，都稳定落在视口正中。
     ⚠️ 保持 position:fixed，且祖先不得出现 transform/filter/will-change/contain
       （会把 fixed 的包含块从视口换成该祖先，toast 就变成「跟着页面走」了）。 */
  position: fixed; left: 50%; top: 50%;
  /* 进场：从略微上移 + 缩小回弹到正中（位移量小于原型的 16px，避免居中态视觉抖动） */
  transform: translate(-50%, calc(-50% - 10px)) scale(0.96);
  z-index: 9999;
  /* ★ width:max-content 不能省（2026-08-31 移动端实测）：
     fixed + left:50% 且 width:auto 时，浏览器的 shrink-to-fit 可用宽度只算
     「left 到包含块右边缘」= 半个视口。窄屏下长文案（如「报名已提交，我们会尽快与你联系」）
     被压到 182px、折成两行，看着像一块偏左的小方块，而不是居中的一条 toast。
     改成按内容取宽 + max-width 兜住不出屏，配合 translateX(-50%) 才真正居中。 */
  width: max-content; max-width: calc(100vw - 32px);
  display: flex; align-items: center; gap: 10px;
  padding: 13px 20px; border-radius: 100px;
  background: var(--color-dark-surface); color: var(--color-white);
  font-size: 0.9375rem; font-weight: 500;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.24);
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease, transform .42s cubic-bezier(.34, 1.56, .64, 1);
}
.toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.toast svg { width: 18px; height: 18px; flex: none; color: var(--color-success); }

/* ============================================================
   13) 响应式
   ------------------------------------------------------------
   ★ 1100px 是唯一的「进入移动端」断点（官网铁律 2026-08-04）：
     导航切汉堡的断点在 1100，任何「桌面多列 → 移动端版式」的切换必须也写 1100，
     否则会出现「汉堡导航 + 桌面多列」的割裂区间。
   ★ 列数切换与配套的边框/间距修正必须写在同一个 @media 里。
   ============================================================ */
@media (max-width: 1400px) {
  .flow-steps { grid-template-columns: repeat(3, 1fr); }
  .earn-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 1100px) {
  :root { --container-padding-x: 32px; --nav-h: 48px; }
  .whatis-grid { grid-template-columns: 1fr; gap: 36px; }
  .whatis-features { grid-template-columns: repeat(2, 1fr); gap: 14px; margin-top: 28px; }
  .eco-grid { grid-template-columns: 1fr; }
  .eco-grid--featured { grid-template-columns: 1.2fr 1fr; grid-template-rows: 1fr 1fr; }
  .eco-grid--featured .eco-card--main { grid-row: 1 / -1; }
  .exch-cols { grid-template-columns: 1fr; }
  .flow-steps { grid-template-columns: repeat(2, 1fr); }
  .earn-grid { grid-template-columns: repeat(2, 1fr); }
  .form-row { grid-template-columns: 1fr; gap: 0; }
  .section { padding: 48px 0; }    /* ★ R8：用户反馈「移动端间隙太大」—— 96→72 还不够，再收到 48 */
  .program { padding: 24px 0 56px; }  /* ★ R9：program 顶部再收（与上方 section 底 48px 合计仅 72px，不再是 96） */

  /* ★ 平板区间（769~1100px）仍是 pin 模式，行程略加长（40svh 在这档偏快）。
     ≤768px 走下方「Hero 移动端适配」解除 pin —— 那里不再需要 runway。 */
  :root { --hero-runway: 65svh; }
  .signup-wrap { padding: 28px 22px; }
  .discount-box { grid-template-columns: 1fr; gap: 18px; padding: 26px 22px; }
  .form-submit { justify-content: stretch; }  /* 已在基础规则拉满，这里保持一致 */
}
/* ★★★ 竖版堆叠的断点是【实测出来的，不是拍的】：
   选项文案缩短后（「我是高校实验室 / 培训机构」→「高校 / 培训机构」、去掉「我是」前缀），
   两个选项横排的临界宽度从 478px 降到 【356px】（二分扫描 320~620，步长 2px）。
   于是 390px / 375px 这类主流手机现在一行就放得下 ——
   这正是加小标题「我是谁？」换来的收益（用户：「这样移动端也看着比较精简，不会变成两行」）。
   断点取 370px（临界 356 + 14px 余量，防字体回退时文字变宽）：
     · ≥371px  横版（含 375 / 390 / 414 全部主流手机）
     · ≤370px  竖版堆叠（320 / 360 这类窄屏）
   ⚠️ 改文案就要重跑 node _probe-seg.mjs 复核这个数 —— 文案一长回去，390px 会重新溢出。
   ⚠️ 必须 :not(.is-condensed)：收起态在 pill 里是横向分段控件
      （13px 字号下 390px 放得下，实测 315 < 350），堆叠会把 pill 撑成两层。 */
@media (max-width: 370px) {
  /* 底框仍在，只是里面两个选项上下排（分段控件竖版）。
     ★★★ 同心圆角公式：【外圆角 = 内圆角 + 间距】。
       内部 .scx-tab 是 pill（min-height 48 → 实际圆角被夹到 24），底框 padding 6，
       所以外框必须是 24 + 6 = 30px。
       ⚠️ 之前写 --radius-lg(16px) 是错的：内 24 > 外 16，同心关系反了，
          选中项的蓝色 pill 会在四个角上顶出底框轮廓，用户实测截图指出「圆角没拟合好」。
       ⚠️ 也不能继续用 pill 100px：竖版底框高约 112px，100px 会被夹成 56px（半高），
          整块变成药丸形。
       ↔ 横版时底框高 60px、border-radius:100px 被浏览器夹到 30px —— 与这里数值恰好相同，
         所以两个形态的圆角是视觉连续的（这不是巧合，是同一个公式的两个解）。 */
  .scx-tabs-wrap:not(.is-condensed) .scx-tabs-row {
    display: block; max-width: none; border-radius: 30px;
  }
  .scx-tabs-wrap:not(.is-condensed) .scx-tabs { flex-direction: column; width: 100%; gap: 4px; }
  .scx-tabs-wrap:not(.is-condensed) .scx-tab { width: 100%; }
}
@media (max-width: 640px) {
  :root { --container-padding-x: 20px; }
  .flow-steps { grid-template-columns: 1fr; }
  .earn-grid { grid-template-columns: 1fr; }
  .faq-a p { padding-right: 0; }
  /* ★ R9：hero-cta 移动端保持一行横排（按钮文案已缩短，放得下）。
     删掉原来的 flex-direction:column + width:100%。 */
}

/* ---------- 自定义 Select 组件（替代原生下拉，对齐官网 .xg-select）---------- */
.cs-wrap { position: relative; }
.cs-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; height: var(--xg-input-height);
  padding: 0 14px 0 16px;
  background: var(--color-white, #fff);
  border: 1px solid var(--xg-input-border, #EAECF2);
  border-radius: var(--xg-input-radius, 8px);
  font-size: 0.9375rem; color: var(--color-text-primary);
  cursor: pointer; transition: border-color .2s, box-shadow .2s;
  text-align: left;
}
.cs-trigger:hover { border-color: var(--xg-input-border-hover, #C8CADC); }
.cs-wrap.is-open .cs-trigger,
.cs-trigger:focus-visible {
  border-color: var(--color-brand); outline: none;
  box-shadow: 0 0 0 3px rgba(51, 100, 255, 0.10);
}
.cs-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-wrap:not(.has-value) .cs-value { color: var(--color-text-muted, #9FA2B3); }
.cs-chevron {
  flex: none; width: 16px; height: 16px; color: var(--color-text-muted, #9FA2B3);
  transition: transform .25s cubic-bezier(.22,1,.36,1);
}
.cs-wrap.is-open .cs-chevron { transform: rotate(180deg); }
/* 下拉浮层 */
.cs-list {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--color-white, #fff);
  border: 1px solid var(--xg-input-border, #EAECF2);
  border-radius: 12px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.10), 0 2px 8px rgba(0, 0, 0, 0.04);
  padding: 6px; z-index: 100;
  max-height: 260px; overflow-y: auto;
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity .2s, visibility .2s, transform .2s cubic-bezier(.22,1,.36,1);
}
.cs-wrap.is-open .cs-list { opacity: 1; visibility: visible; transform: translateY(0); }
/* 滚动条：全局 base.css 的无前缀 ::-webkit-scrollbar-thumb 是品牌蓝（#2B4EBF），
   套到这层浮层上过重、抢走选项文字的注意力。此处按原型还原为中性灰细滑块。 */
.cs-list { scrollbar-width: thin; scrollbar-color: #C9CBD6 transparent; }
.cs-list::-webkit-scrollbar { width: 6px; height: 6px; }
.cs-list::-webkit-scrollbar-track { background: transparent; }
.cs-list::-webkit-scrollbar-thumb { background: #C9CBD6; border-radius: 6px; }
.cs-list::-webkit-scrollbar-thumb:hover { background: #A8ABBA; }
.cs-item {
  padding: 10px 14px; border-radius: 8px; cursor: pointer;
  font-size: 0.875rem; line-height: 1.5; color: var(--color-text-primary);
  transition: background .15s, color .15s;
}
.cs-item:hover { background: rgba(51, 100, 255, 0.06); color: var(--color-brand-press); }
.cs-item[aria-selected="true"] { color: var(--color-brand); font-weight: 600; }
/* has-error 状态传递 */
.field.has-error .cs-trigger { border-color: var(--xg-input-border-error, #FF3B30); }

/* ★ R13 已移除：切换器不需要单独的显隐逻辑。它是页面内容的一部分，
   跟着幕布揭示自然出现即可（与英文版行为一致）。 */
