:root{
  --bg:#FCFBFF; --surface:#FFFFFF;
  --lav:#F4F1FE; --lav-2:#E9E3FB;
  --accent:#7C3AED; --accent-deep:#5B21B6; --accent-soft:#EFE9FE;
  --ink:#1B1630; --muted:#6C6683; --line:#E9E4F7;
  --radius:16px; --radius-lg:24px;
  --shadow:0 1px 2px rgba(27,22,48,.04), 0 8px 24px rgba(91,33,182,.06);
  --shadow-lg:0 2px 4px rgba(27,22,48,.04), 0 18px 48px rgba(91,33,182,.10);
  --max:1120px;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  background:var(--bg); color:var(--ink);
  font:400 16px/1.7 -apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Hiragino Sans GB","Microsoft YaHei",system-ui,sans-serif;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
/* sticky nav 高 64px，锚点跳转要让出这段 */
[id]{scroll-margin-top:84px}

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

/* ---------- nav ---------- */
nav{
  position:sticky;top:0;z-index:50;
  background:rgba(252,251,255,.86);
  backdrop-filter:saturate(180%) blur(12px);
  -webkit-backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid var(--line);
}
.nav-in{display:flex;align-items:center;gap:28px;height:64px}
.brand{display:flex;align-items:center;gap:10px;font-weight:650;font-size:17px;letter-spacing:-.01em;text-decoration:none}
.brand img{display:block;width:28px;height:28px}  /* block：行内图会带基线间隙，导致和文字错位 */
.nav-links{display:flex;gap:24px;margin-left:auto;font-size:14.5px;color:var(--muted)}
/* padding:10px 0 把点击区从 24.65px（14.5 x 1.7）撑到 44.65px。**版面一个像素都
   没动**：.nav-in 的高度是写死的 64px，这几个链接是它的 flex item，长高只是把
   自己那一格填满。横向不加 padding，否则 gap:24px 会被撑成视觉上的 40px。 */
.nav-links a{padding:10px 0;text-decoration:none;transition:color .15s}
.nav-links a:hover{color:var(--accent)}
/* 语言切换。不能放进 .nav-links——那一组在窄屏是 display:none，
   而换语言恰恰是手机上最需要点得到的东西。 */
.lang{
  flex:none;position:relative;margin-left:20px;padding:6px 13px;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);
  font-size:13px;font-weight:600;color:var(--muted);text-decoration:none;
  transition:color .15s,border-color .15s;
}
/* 点击区靠一个伪元素往外扩，而不是加 padding。理由是这颗胶囊的**可见尺寸**
   就该是 34.1px（13px x 1.7 + 12），真做成 44px 高会在 64px 的导航条里显得很
   重；而窄屏那一档 .nav-links 是 display:none 的，**它是手机上唯一一个能点的
   导航控件**，所以它的命中区必须够。inset 上下 -5px → 44.1px。 */
.lang::after{content:"";position:absolute;inset:-5px -6px;border-radius:999px}
.lang:hover{color:var(--accent);border-color:var(--accent)}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;gap:8px;
  padding:13px 24px;border-radius:12px;
  /* font-family:inherit 是 <button class="btn"> 需要的：表单控件不继承
     font-family，少了它商店那枚按钮会退回系统默认字体，而 <a class="btn">
     一切正常——所以这是一个只在一处出现、看起来像"那个按钮有点怪"的失效。 */
  font-family:inherit;line-height:1.7;
  font-size:15px;font-weight:600;text-decoration:none;
  border:1px solid transparent;cursor:pointer;
  transition:transform .12s, box-shadow .18s, background .15s;
}
.btn-primary{background:var(--accent);color:#fff;box-shadow:0 2px 12px rgba(124,58,237,.28)}
.btn-primary:hover{background:var(--accent-deep);transform:translateY(-1px);box-shadow:0 6px 20px rgba(124,58,237,.34)}
.btn-ghost{background:var(--surface);border-color:var(--line);color:var(--ink)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent)}
/* 禁用态。**必须是 :disabled，也就是这枚按钮必须是 <button>**：aria-disabled
   只是一句给辅助技术的声明，挂在 <span> 上时点击、Tab 聚焦、回车全都照旧工作，
   于是控件看起来是灰的、实际点得动——而"商店链接待上架"那一枚恰恰不能被点动。
   aria-disabled 那一半留着是为了将来真需要"可聚焦但不可操作"时有个出口；
   新代码用 :disabled，别再往 <span> 上挂 aria-disabled。 */
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;pointer-events:none}

/* ---------- hero ----------
   第一屏右半边是**一个会翻面的盒子**：正面是一扇边界清晰的浏览器窗（里面是一页
   聊天），转过去背面是一部手机（屏幕里播那条成片）。

   **这一版换掉的是「遮罩渐隐」那一版**，那版把成片四条边都用 mask 化掉、让它像
   一团氛围而不是一个播放器。换掉的理由有两条，都是实测出来的：
   1) 那版的聊天页是**编进视频里**的（前 4.1 秒），于是它必须经过一次 h264 编码、
      再在 Retina 屏上被放大 1.2×——一页全是小字的界面，这两件事叠起来就是糊。
      现在它是一张静态图，按原生像素显示，**这是这一屏清晰度的主要来源**。
   2) 一段没有边界的画面说不清"这是个什么东西"。一扇浏览器窗翻成一部手机，
      本身就把产品那句话演了一遍，不需要旁边再摆箭头或者假气泡去解释。

   跟着 mask 一起删掉的还有 .hero-tint（那层给画面上紫的色调层，它存在的理由是
   "让一块没有边界的画面沾上页面的紫"，现在画面有边界了）与 mask-composite。
   **更早还删过三条 HTML 聊天气泡和一道指向画面的箭头**，那两样别再放回来：
   气泡是在用一个假的聊天界面解释一个真的，箭头被使用者明确否过。

   **翻转这件事有一个绕不开的代价，写在这儿免得下次有人想"修"它**：浏览器窗是横
   的（约 1.49:1），手机是竖的（9:16），而 rotateY 交换的是**同一个盒子**的两面。
   所以盒子必须在翻转过程中变形。处理办法是**不去动布局**：外面那个 .hero-stage
   取两个面的并集尺寸（宽取聊天窗、高取手机），两个面各自定尺寸、各自以中心
   对齐同一根转轴。于是尺寸的变化就是"它正在变形"这件事本身，而不是一次抖动。
   动 width/height 会每帧触发一次布局，读出来就是卡。 */
.hero{
  /* 聊天窗的宽度。**上限 564 = 上一版 470 的 1.2 倍**，2026-09-09 按"整个区域
     调大 20%"改的，吃掉的是正文右缘与卡片之间那段留白（见文件末尾那张实测间距表）。
     **它现在略微越过了源图的 1:2 线，这是一次有据的让步，不是疏漏**：hero-chat.webp
     只有 1100 像素宽，Retina 上 564 CSS px 要 1128 源像素才够 1:2，实际是 1.95×
     ——短 2.5%，肉眼分不出来。真要拿回那 2.5%，办法是把 hero-chat 按源图 1264 宽
     重出一次（约 +25K，assets 预算够），而不是把这个数改回去。
     下限 370 **刻意没跟着 ×1.2**：901 那一档正文与卡片之间实测只剩 87px，
     再宽 74px 就会压到正文上。0.408vw 在 vw≈907 与 370 相交，而 900 以下整张卡
     是 display:none 的，所以这个下限实际只管那 6px 宽的一条缝。 */
  --stage-w:clamp(370px,40.8vw,564px);
  /* 手机的高度。**上限 636 = 上一版 530 的 1.2 倍**，与 --stage-w 同一次改动。
     **这一条真的越过了源片的 1:2 线，而且越得不少，是量过之后接受的**：
     屏幕高 = H - 24×(H/530)（上下边框跟着一起缩放），屏幕宽 = 屏幕高 × 9/16，
     H=636 时屏幕宽 341.6 CSS px，Retina 上 683 物理像素，而源片只有 576 ——
     也就是 1.19 倍上采样。接受它的依据是实测 SSIM：把现役这份 576/crf23 放到
     683 宽显示，得 0.96796；而按 704 宽重新编一份（+41K）是 0.96782 ——**几乎
     完全相同**，与仓库里那条"不要为了更清楚先上采样再编码"是同一件事的两面。
     所以这次刻意不动 hero-clip.mp4：多花 41K 换不到任何东西。
     下限 430 没跟着 ×1.2，理由同 --stage-w。
     min(...,80vh) 是纵向那道闸：.hero 的 min-height 是 min(88vh,780px) 且
     overflow:hidden，短屏（例如 1440x700）上 636 的手机会被裁掉一截，而裁掉
     不报错。80vh 在 vh≈795 以下才咬得住，宽屏高屏一个像素都没变。 */
  --phone-h:clamp(430px,min(48vw,80vh),636px);
  /* 卡片单位：--stage-w 等于 470px 时它正好是 1px（1/470 = 0.00212766）。
     正面那些**量出来**的像素值全部写成 calc(var(--u) * N)，于是整张卡换尺寸时
     它们自己跟着走。**这一层不是装饰**：.hero-fab 的 bottom 就是量出来的——
     62px 时它刚好落在截图里那条输入框上方，写死之后卡片一放大它就掉回输入框
     上，这一屏又开始说"那个平台自己就能把聊天变成视频"。 */
  --u:calc(var(--stage-w) * 0.00212766);
  /* 手机单位：--phone-h 等于 530px 时正好 1px（1/530 = 0.00188679）。 */
  --pu:calc(var(--phone-h) * 0.00188679);
  --phone-w:calc((var(--phone-h) - var(--pu) * 24) * 0.5625 + var(--pu) * 24);
  /* 整组从屏幕右缘往中间收这么多。**它不是 5vw 也不是一个定值，这是量出来的**：
     内收是宽屏才有的排场，而在 1080 那一档 5vw 要走 54px，代价是正文被压到只剩
     420px、h1 断成四行、CTA 掉出首屏。所以它写成「一个斜率减一个常数」——常数
     决定它从哪一档开始收，斜率决定宽屏收多快。
     **30vw-320px 是第三版**，前两版是 12vw-100px（上限 80）与 20vw-200px（上限
     180）。每一版都只改斜率与常数、不改形状，理由都一样：宽屏那头的横向余量本来
     就富裕，把余量摊在两边不如把卡片挪进版心。
     原则：横向预算紧的时候先花在内容上，宽松了才花在留白上——所以收得多的那一头
     必须是宽屏，而不是靠抬高一个定值把每一档一起往里推。
     **第三版是使用者说「往左移动下」之后量出来的，量的是两条边而不是一条：**
     卡片右缘离视口右边 r，而导航那条右边线（.wrap 的右缘）在 0.5W-536 处——卡片
     一直越过它（1512 上越 118px），这就是「被推到右边」的来源。**但对齐是几何上
     做不到的**：正文 512 + 卡片 564 = 1076，比版心内宽 1072 还多 4px，真对齐就是
     叠上。所以能做的只有「在正文与卡片之间那道间距的下限之内尽量往左」，实测下限
     落在 vw≈1440–1512 那个局部最小上：新参数在 1512 上把间距从 120 压到 82，
     那是全曲线最紧的一格（窄端 901 仍是 87，没动）。
     **两条不变量**：r 在 W≤1000 时归零（窄屏卡片仍然齐着视口右缘），且 r 永远
     小于 0.5W-536（卡片只会越过版心右边线，不会被拉到它里面去）。check-site.cjs
     把这两条连着两个间距下限一起算了一遍。 */
  --stage-r:clamp(0px,30vw - 320px,320px);
  position:relative;overflow:hidden;min-height:min(88vh,780px);display:flex;align-items:center;
  background:radial-gradient(115% 90% at 74% 42%, #F0EAFE 0%, #F7F4FE 46%, var(--bg) 78%)}
/* .hero-in 同时挂着 .wrap，而 .hero 是 flex 容器——于是这里有两个都不报错的坑：
   1) flex item 会收缩到内容宽，再被 .wrap 的 margin:0 auto 在「视口」里居中，
      而不是待在那个 1120 容器里。结果第一屏比页面上其他每一节都往里缩，
      且这个偏移与视口宽无关。width:100% 先把 flex item 撑满，max-width 与
      padding 才轮得到 .wrap 说话。
   2) padding 的横向那一位必须是 24px：写成 88px 0 会把 .wrap 的 padding:0 24px
      一起抹掉，窄屏下第一屏文字直接贴屏幕边。纵向那一位按 vh 收，是因为
      min-height 是 88vh 而内容不跟着矮：1080x800 那一档实测 CTA 会掉出首屏。
   z-index:2 是因为翻转卡在 .hero-stage 里、和它是兄弟——两个都参与层叠的盒子只按
   DOM 顺序绘制，漏写就等于文字压在卡片下面。 */
.hero-in{position:relative;z-index:2;width:100%;padding:clamp(56px,8vh,88px) 24px;
  display:flex;align-items:center}
/* 限宽的是文字，不是容器：这一屏横向要同时塞下正文、翻转卡，外加 .wrap 那条左边线，
   所以正文不能是定值——34vw 让它跟着一起收。见文件末尾那张实测间距表。 */
.hero-copy{flex:1 1 auto;min-width:0;max-width:clamp(420px,34vw,512px)}
.hero-img-mobile{display:none}

/* 转轴与景深都在这一层。perspective 必须挂在**父元素**上而不是挂在会转的那个盒子
   上：挂在自己身上时每个面各有各的灭点，翻转看起来像在缩放而不是在转。
   1600px 是试出来的——再小（比如 800）近大远小太重，聊天窗会像被掰过去。
   高度取 --phone-h（两个面里高的那个），宽度取 --stage-w（宽的那个），
   于是两个面都装得下，谁也不会在翻到一半时被裁掉。 */
.hero-stage{
  position:absolute;z-index:0;right:var(--stage-r);
  top:50%;margin-top:calc(var(--phone-h) / -2);
  width:var(--stage-w);height:var(--phone-h);
  perspective:1600px;
}
/* preserve-3d 要挂在**装两个面的那一层**，不是挂在面上。少了它，两个面会被压平到
   同一个平面，backface-visibility 随之失效，于是翻转过程中两张图叠在一起。 */
.hero-flip{position:absolute;inset:0;transform-style:preserve-3d}
/* 动画是 JS 加 .run 才开始的（site.js 里那三道闸之后），不是写死在 .hero-flip 上。
   这样省流量、减弱动态效果、窄屏三种情况下它压根不转，而且 JS 挂掉时停在正面
   ——正面是那页聊天，是这一屏最该被看到的东西，所以这个降级方向是对的。
   **一个循环 10.2 秒，每个数都不是随手取的**。9.6 秒那一版翻得太快、而且是凭空翻的，
   所以加了一次点击并把翻转放慢到 1.5s；12.2 秒那一版补的是另一个毛病——它让手机
   那一面比片子长出 1.2 秒，于是**片尾那 0.35 秒的淡出走完之后，还要再举着一整帧
   纯黑 1.2 秒，接着带着这帧黑转回去**，合起来约 2.1 秒黑屏。现在把手机正面那一段
   收短，让片尾的淡出正好落在翻回去的过程里：
     0    → 2.20s  浏览器窗正面停住（够读完那三条消息）
     2.20 → 3.05   光标滑到右下角那枚按钮上
     3.15 → 3.30   按下（缩到 .94、颜色压深），随后弹回并扩一圈涟漪
     3.40 → 4.90   翻过去，90° 交叉在 4.15s —— site.js 就是在这一刻重启视频
     4.90 → 8.70   手机正面，视频 5.33s 从 4.15 播到 9.48
     8.70 → 10.20  翻回来（转到 360° 而不是退回 0°，一直朝同一个方向转）。
                   90° 交叉在 9.45s，比片子结束的 9.48s 早 0.03s——所以那 0.35 秒的
                   淡出是在卡片转开的过程中走完的，读起来是"画面随着转身暗下去"，
                   **不再有任何一帧静止的黑**。往回加时长就等于把黑屏加回来。
   改 keyframes 的百分比就必须同步改 site.js 里的 PLAY_AT，两边对不上的表现是
   "视频从中间开始播"或者"翻过去还黑着"，而且不会有任何东西报错。
   **点击那三个元素的动画挂在 .hero-flip.run 的后代选择器上**，而不是各写各的
   animation-delay：同一个 .run 同时启动、同一个周期，它们和翻转天生对齐，
   中间不会漂。四个 keyframes 的百分比因此共用同一个分母 10200ms。 */
.hero-flip.run{animation:heroFlip 10.2s cubic-bezier(.65,0,.35,1) infinite}
@keyframes heroFlip{
  0%,33.333%      {transform:rotateY(0deg)}
  48.039%,85.294% {transform:rotateY(180deg)}
  100%            {transform:rotateY(360deg)}
}
/* 两个面都以自己的中心对齐同一根转轴：top/left 50% 把它们的左上角放到中心，
   再各自 translate(-50%,-50%) 拉回来。**backface-visibility:hidden 是承重的**，
   没有它，背对观众的那一面会镜像着透出来压在正面上。 */
.face{position:absolute;top:50%;left:50%;
  -webkit-backface-visibility:hidden;backface-visibility:hidden}

/* ---- 正面：浏览器窗 ---- */
.face-chat{
  width:var(--stage-w);
  /* 34px 是下面那条窗口栏的高度；1.4905 是 hero-chat 那张图自己的宽高比
     （1100x738）。分母写死在这里，换图就要改这个数，否则图会被 cover 裁掉一条。 */
  height:calc(var(--stage-w) / 1.4905 + var(--u) * 34);
  transform:translate(-50%,-50%);
  background:var(--surface);border:1px solid var(--line);border-radius:calc(var(--u) * 14);
  overflow:hidden;box-shadow:0 2px 6px rgba(27,22,48,.05), 0 24px 60px rgba(91,33,182,.16);
}
/* 窗口栏里**一个字都没有**，这是刻意的：三个圆点加一条空的地址栏。
   写上任何域名、标签页标题或者产品名，这一屏就变成"在冒充某个网站"——而页脚
   第一句正是「与任何 AI 聊天平台均无隶属」。见 CLAUDE.md「官网文案纪律」。 */
.chat-bar{height:calc(var(--u) * 34);display:flex;align-items:center;
  gap:calc(var(--u) * 6);padding:0 calc(var(--u) * 13);
  background:var(--lav);border-bottom:1px solid var(--line)}
.chat-bar span{flex:none;width:calc(var(--u) * 9);height:calc(var(--u) * 9);
  border-radius:50%;background:var(--lav-2)}
.chat-bar span:last-child{flex:1;width:auto;height:calc(var(--u) * 16);
  margin-left:calc(var(--u) * 9);border-radius:calc(var(--u) * 8);
  background:var(--surface);border:1px solid var(--line)}
/* 聊天页走 CSS 背景图而不是 <img>，**这是为了窄屏那 54K**：display:none 的元素
   不会去下载 background-image，而 <img> 即使被 display:none 也照样下载。
   image-set 那一行让支持的浏览器拿 webp，不支持的落回上一行的 jpg。
   注意 check-site 现在会把 site.css 里的 url() 一起当资源查（存在 + 没被
   .gitignore 吞掉），所以这两个文件名改了那边会报。 */
.chat-shot{height:calc(100% - var(--u) * 34);background:#fff no-repeat 50% 0/cover;
  background-image:url(./hero-chat.jpg);
  background-image:image-set(url(./hero-chat.webp) type("image/webp"), url(./hero-chat.jpg) type("image/jpeg"));}

/* ---- 正面上那次点击 ----
   翻转前的过场：光标滑到右下角那枚按钮上、按下、扩一圈涟漪，然后卡片才开始翻。
   它解决的是上一版一个说不出口的问题——**一扇浏览器窗自己转成手机，说不出"谁
   让它变的"**。加一次点击，翻转就有了动因，而不只是一个循环动画。

   **这三个元素里一个字都不许有，这是合规不是审美。**按钮上那两道白条是伪元素
   画的形状，不是文字；往里写「生成短片」这类字样，这一屏就从"示意"变成在冒充
   某个真实界面，而页脚第一句正是「与任何 AI 聊天平台均无隶属」。check-site.cjs
   第 13 项按这个断言（剥完标签不剩非空白字符，标签只允许 div/figure/span/
   video/source）。

   动画一律挂在 .hero-flip.run 的后代上：跟着翻转同时启动、同周期，不用 delay
   也不用 JS 对时。停在 .run 之外时按钮是静止的常态——它本来就该看起来能点。 */
/* **bottom 是 62px 而不是 16px，这一条是量出来才对的。**16px 时它正好落在截图里
   那条输入框上，读起来就是那个平台自己的发送按钮——于是这一屏在说"这个网站能
   把聊天变成视频"，而那不是真的，扩展才是。抬到输入框上方之后它是一枚浮在页面
   之上的按钮，也就是扩展本来的样子。白色那圈 box-shadow 是同一件事：让它看起来
   叠在界面上，而不是嵌在界面里。换聊天底图要重新量这个数。
   **62 现在是 62 个 --u 而不是 62 个 px**，也就是"卡片宽 470 时的 62px"。
   2026-09-09 卡片放大 20% 时改的：写死 px 的话它会原地不动、而输入框跟着底图
   一起往上长，于是按钮又落回那条线上——一个不报错、只是重新开始说谎的失效。 */
.hero-fab{
  position:absolute;right:calc(var(--u) * 18);bottom:calc(var(--u) * 62);
  width:calc(var(--u) * 78);height:calc(var(--u) * 30);border-radius:999px;
  background:#7C3AED;                       /* = --accent；keyframes 里要插值，写死值 */
  box-shadow:0 3px 10px rgba(27,22,48,.30),0 0 0 3px rgba(255,255,255,.16);
}
/* 按钮上的"内容"是两个形状：一个方块图标加一道横条。**不是字。** */
.hero-fab::before{content:"";position:absolute;left:calc(var(--u) * 12);top:50%;
  margin-top:calc(var(--u) * -4.5);
  width:calc(var(--u) * 9);height:calc(var(--u) * 9);
  border-radius:calc(var(--u) * 2);background:rgba(255,255,255,.92)}
.hero-fab::after{content:"";position:absolute;left:calc(var(--u) * 28);
  right:calc(var(--u) * 12);top:50%;margin-top:calc(var(--u) * -3);
  height:calc(var(--u) * 6);border-radius:calc(var(--u) * 3);background:rgba(255,255,255,.58)}
/* 涟漪。它是"这一下真的按下去了"的唯一视觉证据——只有 scale 的话，30px 高的
   按钮缩 6% 在 470px 宽的卡片里几乎看不见。 */
.fab-ring{position:absolute;inset:0;border-radius:999px;
  border:calc(var(--u) * 2) solid #7C3AED;opacity:0}
/* 光标。clip-path 画的标准箭头，drop-shadow 当描边——在浅色聊天页上没有描边
   的白箭头会直接消失。宽 17px 是按 .face-chat 的实际显示尺寸估的：再大就比
   截图里的界面元素还大，读起来像贴纸。**箭头的尖是元素的左上角**，所以静止
   位置要按"尖落在按钮上半部"来定，不是按元素中心。动 .hero-fab 的位置就必须
   跟着动这里，两处对不上的表现是光标停在按钮旁边的空处按了一下。 */
.hero-cursor{
  position:absolute;right:calc(var(--u) * 38);bottom:calc(var(--u) * 62);
  width:calc(var(--u) * 17);height:calc(var(--u) * 24);
  background:#fff;opacity:0;
  filter:drop-shadow(0 1px 2px rgba(27,22,48,.45));
  clip-path:polygon(0 0,0 76%,21% 58%,38% 96%,56% 88%,39% 51%,68% 47%);
}
.hero-flip.run .hero-fab   {animation:fabPress  10.2s cubic-bezier(.34,1.4,.64,1) infinite}
.hero-flip.run .fab-ring   {animation:fabRing   10.2s ease-out infinite}
.hero-flip.run .hero-cursor{animation:cursorTap 10.2s cubic-bezier(.4,0,.2,1) infinite}
/* 30.88% = 3150ms 按下，32.35% = 3300ms 到底，33.85% = 3453ms 弹回。
   弹回用的缓动带一点回弹（1.4），所以它会略微过冲——按钮该有的手感。
   **这几个百分比在 12.2s 那一版分别是 25.82 / 27.05 / 28.3**：一圈缩到 10.2s
   之后分母变了，绝对时刻一个都没动（这次改的只是手机那一面停多久）。 */
@keyframes fabPress{
  0%,30.88%   {transform:scale(1);  background:#7C3AED}
  32.35%      {transform:scale(.94);background:#5B21B6}   /* = --accent-deep */
  33.85%,100% {transform:scale(1);  background:#7C3AED}
}
@keyframes fabRing{
  0%,30.88%   {transform:scale(1);  opacity:0}
  31.82%      {opacity:.55}
  37.68%,100% {transform:scale(2.1);opacity:0}
}
/* 19.14% = 1952ms 起步，29.9% = 3050ms 到位，36.48% = 3721ms 已淡出——翻转 3400ms
   就开始了，所以光标是在卡片转起来的过程中消失的，不会跟着转到一半还杵在那。
   同上：12.2s 那一版是 16 / 25 / 30.5，绝对时刻没变，变的是分母。 */
@keyframes cursorTap{
  0%,19.14%   {opacity:0;transform:translate(calc(var(--u) * 24),calc(var(--u) * 20))}
  23.92%      {opacity:1}
  29.9%,31.82%{opacity:1;transform:translate(0,0)}
  32.35%      {transform:translate(0,calc(var(--u) * 2))}
  33.85%      {transform:translate(0,0)}
  36.48%,100% {opacity:0;transform:translate(0,0)}
}

/* ---- 背面：手机 ---- */
.face-phone{
  width:var(--phone-w);height:var(--phone-h);
  transform:translate(-50%,-50%) rotateY(180deg);
  padding:calc(var(--pu) * 12);                   /* 上下各 12 —— --phone-w 的算式依赖这个数 */
  background:linear-gradient(160deg,#241C46 0%,#15112B 55%,#241C46 100%);
  border-radius:calc(var(--pu) * 38);
  box-shadow:0 2px 6px rgba(27,22,48,.06), 0 28px 70px rgba(91,33,182,.24),
             inset 0 0 0 1px rgba(255,255,255,.10);
}
.phone-screen{position:relative;width:100%;height:100%;
  border-radius:calc(var(--pu) * 27);overflow:hidden;background:#0B0918}
.phone-screen video{width:100%;height:100%;object-fit:cover;display:block}
/* 刘海：一条窄横杠。它是这个盒子里唯一让人一眼认出"这是手机"的东西，
   而手机这个形状正是"竖屏短片"那句话的图解。 */
.phone-notch{position:absolute;top:calc(var(--pu) * 8);left:50%;transform:translateX(-50%);
  width:calc(var(--pu) * 62);height:calc(var(--pu) * 5);
  border-radius:calc(var(--pu) * 3);background:rgba(255,255,255,.20)}

.eyebrow{
  display:inline-flex;align-items:center;gap:8px;
  padding:6px 14px;margin-bottom:26px;
  background:var(--surface);border:1px solid var(--line);border-radius:999px;
  font-size:13px;font-weight:600;color:var(--accent-deep);box-shadow:var(--shadow);
}
.dot{width:6px;height:6px;border-radius:50%;background:var(--accent)}
/* 系数从 5.4vw 收到 4.4vw：这行字排在 .hero-copy 那一栏里，而那一栏在中间档
   只有 420px——按视口算出来的 54px 会把它断成四行，连带把 CTA 顶出首屏。
   上限 54 没动，所以宽屏一个像素都没变。 */
h1{font-size:clamp(34px,4.4vw,54px);line-height:1.14;letter-spacing:-.025em;font-weight:700}
h1 em{font-style:normal;color:var(--accent)}
.lede{margin-top:22px;font-size:clamp(16px,1.9vw,18.5px);color:var(--muted);max-width:540px}
.hero-cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:34px}
.hero-note{margin-top:18px;font-size:13.5px;color:var(--muted)}
/* a{color:inherit}，所以正文里的链接默认**看不出来是链接**。这两处（上架通知
   邮箱）是全页仅有的两条正文链接，必须自己带颜色与下划线，否则它读起来就是
   一段普通文字，而它是这一页上唯一能收到回音的地方。 */
.hero-note a,.cta-note a{color:var(--accent-deep);text-decoration:underline;text-underline-offset:3px}
.hero-note a:hover,.cta-note a:hover{color:var(--accent)}
.cta-note{margin:22px auto 0;max-width:560px;font-size:13.5px;color:var(--muted)}

/* ---------- sections ---------- */
section{padding:92px 0}
.sec-head{max-width:660px;margin-bottom:52px}
h2{font-size:clamp(26px,3.4vw,36px);line-height:1.2;letter-spacing:-.02em;font-weight:700}
.sec-head p{margin-top:16px;font-size:17px;color:var(--muted)}
.alt{background:var(--surface);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}

.split{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:center}
.split figure{border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-lg)}

.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;margin-top:44px}
.step{position:relative;padding:28px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.step-n{
  display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;margin-bottom:16px;border-radius:9px;
  background:var(--accent-soft);color:var(--accent-deep);font-size:14px;font-weight:700;
}
.step h3{font-size:17px;font-weight:650;margin-bottom:8px}
.step p{font-size:14.5px;color:var(--muted)}
/* 曾经是 .step time。<time> 的语义是"一个机器可解析的日期或时间"，而这两处
   写的是「桌面版 Chrome」与「等待约 3 分钟」——后者是时长、却不是 ISO 8601 的
   duration，前者根本不是时间。不带 datetime 的 <time> 不报错，只是把一句普通
   说明伪装成结构化数据。 */
.step .step-note{display:block;margin-top:12px;font-size:12.5px;color:var(--accent-deep);font-weight:600}

.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:22px}
.card{padding:28px;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}
.card h3{font-size:17px;font-weight:650;margin-bottom:10px}
.card p{font-size:14.5px;color:var(--muted)}
.icon{
  display:flex;align-items:center;justify-content:center;
  width:38px;height:38px;margin-bottom:16px;border-radius:11px;
  background:var(--accent-soft);font-size:18px;
}

/* ---------- 能力边界（FAQ 的开场）----------
   这几条原本是单独一节 #limits 里的一个淡紫大方框，那一节按要求删掉了，
   它们本身挪到常见问题的开头。位置可以变，**一条都不能少**（2026-09-10 起是五条，
   新增的第五条是「让 AI 挑」那个按钮确实会把勾选的消息发出去）——见
   CLAUDE.md「能力边界只增不减」，校验脚本第 11 项对两版都按原文查。
   这里刻意不套面板：套上去就又变回被删掉的那一节，只是换了个位置。
   它是 FAQ 的开场白，所以靠留白跟下面的问答隔开，而不是用一个框把自己围起来。
   这里刻意不给它 border-bottom：details:first-of-type 已经有一条 border-top，
   两条会叠成相距 20px 的平行横线。 */
.upfront{
  list-style:none;max-width:800px;
  display:grid;gap:16px;
  margin-bottom:46px;
}
.upfront li{display:flex;gap:12px;font-size:14.5px;line-height:1.6}
.upfront li b{display:block;font-size:15.5px;font-weight:650;color:var(--ink);margin-bottom:3px}
/* :last-child 而不是 span：前面那个 span 是 .mark，写成 .upfront li span
   会以更高的特异性盖掉 .mark 自己的 color，那个「!」就跟着变灰。 */
.upfront li > span:last-child{color:var(--muted)}
/* 底色从 --surface 换成 --lav：它原来待在淡紫方框里，白底才有对比；现在待在
   .alt 那一节里，而 .alt 的背景就是 --surface，白底白块只剩一圈边。 */
.mark{flex:none;width:20px;height:20px;margin-top:2px;border-radius:6px;background:var(--lav);border:1px solid var(--lav-2);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--accent-deep);font-weight:700}

/* ---------- privacy ---------- */
.privacy{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:22px;margin-top:40px}
.pv{padding:26px;border-radius:var(--radius);background:var(--surface);border:1px solid var(--line)}
.pv b{display:block;font-size:15.5px;font-weight:650;margin-bottom:8px}
.pv p{font-size:14px;color:var(--muted)}
.pv code{font:500 12.5px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;background:var(--accent-soft);padding:1px 5px;border-radius:4px;color:var(--accent-deep)}

/* ---------- faq ---------- */
.faq{max-width:800px}
details{border-bottom:1px solid var(--line);padding:22px 0}
details:first-of-type{border-top:1px solid var(--line)}
summary{
  display:flex;justify-content:space-between;gap:20px;align-items:flex-start;
  font-size:16.5px;font-weight:600;cursor:pointer;list-style:none;
}
summary::-webkit-details-marker{display:none}
summary::after{content:"+";flex:none;font-size:20px;font-weight:400;color:var(--accent);line-height:1}
details[open] summary::after{content:"–"}
details p{margin-top:14px;font-size:15px;color:var(--muted);max-width:700px}
details p + p{margin-top:10px}

/* ---------- cta ---------- */
.cta-box{
  padding:64px 40px;text-align:center;border-radius:var(--radius-lg);
  background:linear-gradient(140deg,var(--lav) 0%,#F7F2FE 50%,var(--accent-soft) 100%);
  border:1px solid var(--lav-2);
}
.cta-box h2{max-width:660px;margin:0 auto}
.cta-box p{margin:16px auto 32px;max-width:520px;font-size:16.5px;color:var(--muted)}

/* ---------- footer ---------- */
footer{border-top:1px solid var(--line);padding:52px 0 60px;font-size:14px;color:var(--muted)}
.foot-top{display:flex;flex-wrap:wrap;gap:28px;align-items:center;margin-bottom:28px}
.foot-links{display:flex;flex-wrap:wrap;gap:22px;margin-left:auto}
.foot-links a{text-decoration:none}
.foot-links a:hover{color:var(--accent)}
.disclaimer{padding-top:24px;border-top:1px solid var(--line);font-size:13px;line-height:1.75}

/* ---------- reveal ---------- */
.reveal{opacity:0;transform:translateY(16px);transition:opacity .6s ease,transform .6s ease}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .reveal{opacity:1;transform:none;transition:none}
  /* **这一页又有一个 CSS 无限动画了**（第一屏那张卡的翻转），所以这里必须给它一个
     出口。它看起来是冗余的——site.js 里那三道闸的第二道本来就不会给
     .hero-flip 加上 .run——但那道闸在 JS 挂掉时不生效，而这一句在。
     一页永远在自转的东西对勾了这个偏好的人是实实在在的不适。
     曾经这里只剩 .reveal 一件事要管（气泡入场与箭头上那颗点随气泡、箭头删掉了），
     所以那时候的注释写着"这一页一个 CSS 动画都没有"，别照那句话理解现在。 */
  .hero-flip.run{animation:none}
  /* 点击过场那三个也要单独停：它们是各自独立的 animation，上面那句
     .hero-flip.run{animation:none} 管不到后代。少这一句，勾了这个偏好的人会看到
     一枚永远在自己按自己的按钮——而卡片本身已经不转了，那就更莫名其妙。 */
  .hero-flip.run .hero-fab,.hero-flip.run .fab-ring,.hero-flip.run .hero-cursor{animation:none}
  /* 光标停在"还没出现"那一帧，而不是停在按钮上杵着 */
  .hero-cursor{opacity:0}
}

/* ---------- responsive ---------- */
/* 这一屏横向现在只要容下两样东西：正文，和右边那张翻转卡。气泡与箭头删掉之后，
   原来那一整档 @media (max-width:1060px){.hero-bubbles,.hero-flow{display:none}}
   跟着没了——**这不是把断点放宽了，是那两个元素不存在了**。

   会碰的那对线是「正文右缘」与「卡片左缘」。这两个东西不在同一个布局流里
   ——.hero-stage 绝对定位、.hero-copy 被自己的 max-width 钳住——所以谁也挤不动谁，
   这个间距只能量，不能靠「谁把谁挤走」推。用 CDP 读 getBoundingClientRect
   扫 20 档（下面这组是 --stage-r 第三版、卡片放大 20% 之后重扫的，10.2s 一圈，翻转本身仍是 1.5s、仍从 3400ms 起步，所以这张表没有失效，
   rest = 静止态，min = 整个翻转周期里最紧的那一帧）：

     vw    rest  min          vw    rest  min
     1920   164  160         1200   186  184
     1728   126  121         1160   195  192
     1600   100   96         1121   203  200
     1512    82   78         1119   203  200
     1470    86   82         1080   191  189
     1440    90   86         1040   172  170
     1400    96   92         1000   148  146
     1360   111  106          960   124  123
     1280   155  151          940   112  111
     1200   186  184          920   101   99
                              901    87   86

   **量的必须是 .hero-flip，不是 .hero-stage**：后者是定尺寸的容器、根本不转，
   量它会在每一档都得到 min == rest @0ms —— 一个看起来完全正常、其实什么都没量
   的结果。

   **min 比 rest 只紧 1~4px，而这一条是量出来的、不是推出来的**：perspective 会把
   靠近观众那半边放大，所以翻到一半时卡片的投影包围盒可能比静止时更宽。实测最紧
   的一帧落在 T≈3600–3750ms（翻转 3.40s 起步之后那一两百毫秒）。**这个绝对时刻
   跟着一圈的时长走**（9.6s 那一版是 T≈2650ms），景深一改（现在 1600px）数值也会
   变，所以别把「量静止态就够了」当成一条性质。

   **曲线不是单调的，中间有一个局部最小：vw≈1440–1512 那一带 82–90px**，比 1920
   的 164 和 1121 的 203 都紧，而这两档恰好是最常见的 MacBook 宽度，所以这是全
   曲线最该盯的地方。折点各有出处：--stage-w 在 vw≈1382（564÷0.408）顶到上限、
   在 vw≈907 落到 370 下限；--stage-r 在 vw≈2133 顶到 320px、在 vw≈1067 归零；
   .hero-copy 在 vw≈1506 顶到 512 上限、在 vw≈1235 落到 420 下限。
   最紧的一档已经不在最窄那头了（901→87px）——第三版把中间那一带压到了 82，**而
   中间那个局部最小才是危险的**：更早的一版曾在 vw≈1512 那一带实测到 -5px（真的
   叠上了），而最宽和最窄两头看起来都完全正常，截图根本发现不了。**所以这一屏只截
   两三张图是核不完的，必须扫一遍宽度**——参数是扫出来的，不是算一档定的。
   check-site.cjs 现在把这条曲线用三个 clamp 算了一遍，下限 80px（静止态）。

   **第三版与第二版在 vw=1200 交叉**（0.2W-200 = 0.3W-320），所以 1200 以下卡片
   其实比上一版**往右**了最多 12px（1080 那一档 r 从 16 掉到 4）。那一带间距正处
   在全曲线最松的几档（155–203px），且卡片本来就快贴着视口右缘了，所以这是刻意
   接受的代价，不是漏算。 */
@media (max-width:900px){
  .hero{min-height:0;display:block}
  /* 窄屏不放翻转卡：手机上第一屏已经有主视觉（下面那张 .hero-img-mobile），
     再多一张就是白付 630K 流量（成片 577K + 聊天页 53K）。
     **这一句要配合另外两处才真的省下来，缺一处就变成"下载完不显示"**：
       <video preload="none">      —— 视频那 577K
       .chat-shot 用 background-image 而不是 <img> —— display:none 的元素不会去
                                     下载背景图，<img> 则照下不误
     这是 site.js 里那三道闸之外的第四道，而且是唯一一道纯 CSS 的。 */
  .hero-in{display:block;padding:60px 24px 40px}   /* 24px：同上，别退回简写抹掉横向边距 */
  .hero-stage{display:none}
  /* 正文在这一档要居中，因为下面那张图居中。**注意这只是 margin，
     max-width 一个像素都没动**：那个值守的是宽屏上「正文右缘 ↔ 翻转卡
     左缘」那道间距，而翻转卡在这一档是 display:none 的，那条约束在这里不适用。
     ~470px 以下它是一条空语句（父容器已经比 clamp 的 420px 下限还窄），
     所以它只改 500–900 那一段——而那正是正文靠左、图居中、右边空一大块的那一档。 */
  .hero-copy{margin:0 auto}
  /* 加 max-width 是因为这一档最宽能到 900px：不钳住的话在 900 上要占 852 CSS px
     = 1704 设备像素，而这张图只有 1200 宽，等于 1.42 倍上采样。560 时最多 1120，
     源像素还够。width 那一位替代原来的左右 margin，好让它同时居中。 */
  .hero-img-mobile{display:block;width:calc(100% - 48px);max-width:560px;margin:0 auto 60px;
    border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-lg)}
  .split{grid-template-columns:1fr;gap:36px}
  .steps{grid-template-columns:1fr}
  section{padding:64px 0}
  .sec-head{margin-bottom:36px}
  .nav-links{display:none}
  .lang{margin-left:auto}
  .cta-box{padding:48px 24px}
}
@media (max-width:520px){
  .cta-box{padding:28px 20px}
}
