/* ============================================================
   KDY 余庆里 · PC 版首页样式
   2026-09-23 第二版：按客户 Cartier 对齐反馈整体改居中排版
   （斜体小眉题 + 大标题居中 → 小卡片居中在标题下方 → 副标语 → 居中按钮；
     左右白色线框方块切屏；切换动效改「斜角擦入」，小卡片更短时长做视差）

   设计基调沿用手机版定稿语言（详见根目录 styles.css 多语言总控区）：
   墨底 #0d0e0d / 品牌绿 #173c2c / 米白 #f2f0e8
   拉丁标题 FancyCutPro 300 大写（em 斜体 0.82）
   中文标题 PingFang Ultralight 200 单行 0.04em
   tagline / 按钮中文 PingFang Light 0.3em
   导航中文 KDY Songti（子集只含菜单标题 30 字，别用在其他中文上）
   【路径约定】字体等共用资源走绝对路径 /fonts/...（线上 nginx alias 回 h5，
   本地预览仓库根即 h5 根，两边都成立）；PC 自有素材走相对路径 media/...
   ============================================================ */

:root {
  --ease-silk: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-heavy: cubic-bezier(0.7, 0, 0.2, 1);
  --ease-title: cubic-bezier(0.16, 1, 0.3, 1);
  /* 斜角擦入专用：直接取自参考站 365ayearof.cartier.com 的 --ease-circ-in-out */
  --ease-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);

  /* 大视频擦入时长（2026-09-23 客户定 2000ms。
     参考站原值 1400ms，此前放过一倍到 2800ms，现收回 2000ms）。
     ⚠️ pc.js 的 TEXT_SWAP_MS / SWITCH_LOCK_MS 是按这个值配出来的：
       TEXT_SWAP_MS  ≈ 0.43 × wipe-dur（文字在斜边扫到一半时换）
       SWITCH_LOCK_MS = wipe-dur + 400ms 余量
     改这一个值时，上面两个常量要一起重算（在 pc.js 顶部）。 */
  --wipe-dur: 2000ms;
  /* 大视频与小视频【完全同步】（客户 2026-09-23 最后定）：
     两者同时起手、同时结束，不再有滞后。
     此前用 --wipe-card-delay: 800ms 做过"后起后止"的纵深视差，现已取消 ——
     视差改由「两者画面尺寸不同、走同一套百分比 keyframes」自然产生。
     从此大视频和小视频共用同一组 wipeSlideIn / wipeSlideInRev，
     时长、缓动、起止时刻全部一致；不再有卡片专用的 keyframes。 */
  --ink: #0d0e0d;
  --green: #173c2c;
  --paper: #f2f0e8;
  --sans-cn: -apple-system, "PingFang SC", "Helvetica Neue", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --serif-en: "Cormorant Garamond", serif;

  /* 居中版式的纵向节奏（对齐参考站，1440×900 基准量出。
     改这里 = 整体上下挪，别去改各元素自己的 top） */
  --y-eyebrow: 12.5vh;
  --y-title: 16.5vh;
  --y-card: 35vh;
  --h-card: 24vh;
  --y-tagline: 64vh;
  --y-button: 72.5vh;
}

/* === 字体（与 h5 同一批文件，绝对路径引用） === */
@font-face {
  font-family: "FancyCutPro";
  src: url("/fonts/FancyCutPro-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "FancyCutPro";
  src: url("/fonts/FancyCutPro-LightItalic.woff2") format("woff2");
  font-weight: 300;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "FancyCutCondPro";
  src: url("/fonts/FancyCutCondPro-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "BrilliantCutPro";
  src: url("/fonts/BrilliantCutPro-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/fonts/CormorantGaramond-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
/* 宋体子集：只含菜单标题用字（改 menu 中文标题必须重新生成，见根目录必读文档） */
@font-face {
  font-family: "KDY Songti";
  src: url("/fonts/NotoSerifSC-Light-menu.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

* {
  box-sizing: border-box;
}

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--ink);
  font-family: "FancyCutPro", Georgia, "Times New Roman", serif;
  font-weight: 300;
}

button {
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
}

.pc {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--ink);
  color: #fff;
}

/* 内页覆盖或页签隐藏时，把首页切屏动画也停在原帧；移除此类即可续播。 */
.pc.is-home-paused,
.pc.is-home-paused *,
.pc.is-home-paused *::before,
.pc.is-home-paused *::after {
  animation-play-state: paused !important;
}

/* ============ preloader（同 h5 语气：黑底 + 亮绿品牌字 + 页脚署名） ============ */

.preloader {
  position: absolute;
  z-index: 40;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #000;
  transition: opacity 800ms ease;
}

.preloader.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.preloader__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 34px;
}

.preloader__brand {
  margin: 0;
  font-family: var(--sans-cn);
  font-size: 26px;
  font-weight: 200;
  letter-spacing: 6px;
  text-indent: 6px;
  color: #1b6143; /* loading 页专用绿（比品牌暗绿亮一档，黑底可读，同 h5） */
}

.preloader__bar {
  width: 210px;
  height: 1px;
  background: rgba(255, 255, 255, 0.14);
  overflow: hidden;
}

.preloader__bar span {
  display: block;
  width: 100%;
  height: 100%;
  background: #1b6143;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 300ms linear;
}

.preloader__footer {
  position: absolute;
  bottom: 30px;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  font-family: var(--sans-cn);
  font-size: 13px;
  font-weight: 200;
  letter-spacing: 4px;
  color: rgba(255, 255, 255, 0.5);
  white-space: nowrap;
}

/* ============ 顶部横排导航（2026-09-21 客户定：不用汉堡菜单） ============ */

.topbar {
  /* 顶部操作栏直接以窗口为定位基准，不跟随首页舞台的内容宽度。 */
  position: fixed;
  z-index: 20;
  top: var(--pc-view-top, 0px);
  left: var(--pc-view-left, 0px);
  width: var(--pc-view-width, 100%);
  max-width: 100vw;
  max-width: 100dvw;
  display: flex;
  align-items: center;
  gap: clamp(24px, 3vw, 56px);
  padding: clamp(20px, 2.6vh, 30px) clamp(28px, 3.4vw, 56px);
  opacity: 0;
  transform: translateY(-14px);
  pointer-events: none;
}

/* is-ready = 首屏入场触发器（boot 后由 JS 加上，首次落定即摘掉）
   is-chromed = 常驻 UI 已入场（首屏入场完成后由 JS 打上，之后永不摘除）。
   ⚠️ 顶栏/导航【不要】挂 is-settled：切屏时 goTo() 会先摘掉 is-settled 再补回来，
   挂它的话每切一屏顶栏就淡出再淡入一次（客户 2026-09-23 要求一次淡入后固定不动）。 */
.pc.is-ready .topbar {
  animation: topbarIn 900ms var(--ease-silk) 400ms forwards;
}

.pc.is-chromed .topbar {
  opacity: 1;
  transform: none;
  animation: none;
  pointer-events: auto;
}

/* logo3.svg 横排 5.3:1，按宽度定尺寸（同 h5 的 .brand img 策略） */
.topbar__brand {
  flex: 0 0 auto;
  display: block;
  line-height: 0;
}

.topbar__brand img {
  width: clamp(150px, 13vw, 210px);
  height: auto;
  display: block;
}

.topbar__nav {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: clamp(18px, 2.4vw, 44px);
  min-width: 0;
}

.topbar__item {
  border: 0;
  background: transparent;
  padding: 6px 0;
  color: rgba(255, 255, 255, 0.78);
  font-family: "BrilliantCutPro", "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  white-space: nowrap;
  position: relative;
  transition: color 360ms ease;
}

/* 悬停下划细线：从中间向两侧拉开 */
.topbar__item::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(255, 255, 255, 0.75);
  transform: scaleX(0);
  transition: transform 460ms var(--ease-silk);
}

.topbar__item:hover {
  color: rgba(255, 255, 255, 0.98);
}

.topbar__item:hover::after {
  transform: scaleX(1);
}

/* 中文导航用宋体子集（子集恰好只含菜单标题字） */
html[lang="zh-CN"] .topbar__item {
  font-family: "KDY Songti", "Songti SC", "STSong", "Noto Serif CJK SC", "Source Han Serif SC", SimSun, serif;
  font-size: 15px;
  font-weight: 300;
  letter-spacing: 0.14em;
  text-transform: none;
}

/* 导航标签现在只渲染主词（见 pc.js 的 navLabelText），
   原先"窄屏隐藏副词"的 .topbar__item-sub 规则已随结构一起删除。 */

/* 语言组：中文 / EN / FR 横排在最后 */
.topbar__lang {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
}

.topbar__lang-item {
  border: 0;
  background: transparent;
  padding: 6px 8px;
  color: rgba(255, 255, 255, 0.45);
  font-family: "BrilliantCutPro", "Helvetica Neue", Arial, sans-serif;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.14em;
  transition: color 360ms ease;
}

/* 「中文」两个字别用 BrilliantCut 回退，锁定细黑体 */
.topbar__lang-item[data-lang="zh"] {
  font-family: var(--sans-cn);
  font-weight: 300;
  letter-spacing: 0.1em;
}

.topbar__lang-item:hover {
  color: rgba(255, 255, 255, 0.85);
}

.topbar__lang-item.is-active {
  color: rgba(255, 255, 255, 0.95);
}

.topbar__lang-sep {
  width: 1px;
  height: 10px;
  background: rgba(255, 255, 255, 0.28);
}

/* ============ 舞台与幻灯 ============ */

.stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  isolation: isolate;
}

.slides {
  position: absolute;
  inset: 0;
}

/* 切换不再用交叉淡入，改「斜角擦入」（见 .is-wiping-* 区块）：
   进来的新屏带一条斜线边界扫过，旧屏在下面继续播着垫底 */
.slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
}

.slide.is-active {
  opacity: 1;
}

/* 旧屏在切换期间保持可见（作为擦除的底），擦完由 JS 暂停 */
.slide.is-leaving {
  opacity: 1;
  z-index: 1;
}

.slide__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.02);
}

/* 居中版式的压暗：四周略深、中间留亮，保证白色文字在任何画面上可读 */
.slide__shade {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(6, 8, 7, 0.42) 0%, rgba(6, 8, 7, 0.06) 26%, rgba(6, 8, 7, 0.08) 62%, rgba(6, 8, 7, 0.46) 100%),
    radial-gradient(120% 90% at 50% 46%, rgba(6, 8, 7, 0) 40%, rgba(6, 8, 7, 0.34) 100%);
  pointer-events: none;
}

/* ============ 居中 4:5 前景卡片（1080×1350 素材零裁切） ============
   【结构要点，2026-09-23 客户定】卡片是【舞台层的固定元素】，不挂在各屏里 ——
   位置永远钉在画面正中，切屏时不跟着屏退场，也就不会"消失又出现"。
   卡片内部每屏一个视频层叠放（.card__video），切换时新的一层用斜切盖住旧的一层，
   旧的在下面继续播着垫底，直到被完全盖满。 */

.card {
  position: absolute;
  z-index: 5;
  margin: 0;
  top: var(--y-card);
  left: 0;
  right: 0;
  height: var(--h-card);
  aspect-ratio: 4 / 5;
  /* aspect-ratio + auto 左右外边距 = 水平居中（不用 translateX，避免跟入场动画的
     transform 打架 —— h5 2026-07-08 踩过同类坑） */
  margin-left: auto;
  margin-right: auto;
  overflow: hidden;
  background: rgba(20, 22, 20, 0.55); /* 深色占位底，同 h5：避免视频首帧未解出时白闪 */
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.34);
  /* 默认就是最终可见状态（不淡入、不位移）：客户 2026-09-23 要求
     Loading 结束后大视频与小视频【始终叠在一起同时显示】，
     没有"出现"的过程。入场动画（cardIn）已随之取消。 */
  opacity: 1;
  transform: none;
  pointer-events: none;
}

.card__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* 非当前层：缩到画面外，任何时刻都不参与显示 */
  opacity: 0;
  clip-path: polygon(160% 0, 200% 0, 200% 100%, 160% 100%);
}

/* ⚠️ 尺寸必须【写在 video 上】，不能只写在外层 .card__video 上：
   外层 div 的 width/height 不会约束子元素，object-fit 在 div 上更是空属性
   —— 不写这条，<video> 会按素材原始尺寸（1080×1350）铺开，
   而卡片只有 ~173px 宽 → 只露出画面左上角一块（2026-09-23 踩过）。 */
.card__video > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ⚠️ z-index 必须显式指定，不能靠 DOM 顺序 —— 三个 .card__video 是依次排列的兄弟，
   后面的天然盖住前面的。于是只有"往后切"能看见擦入（新层在后面），
   一旦回环（第 3 屏 → 第 1 屏）新层排在最前，就被旧层盖住、动作发生但看不见
   （2026-09-23 客户反馈：3→1 时小视频没切换、大视频正常 —— 大视频有同款保护：
   .slide.is-active 是 z-index 2、.slide.is-leaving 是 1，所以不受 DOM 顺序影响）。
   这里与 .slide 一一对应：擦入中的新层 2、当前层 1。 */
.card__video.is-front {
  z-index: 1;
  opacity: 1;
  clip-path: none;
}

/* 正在斜切擦入的新层：旧层还在下面亮着，新层带斜边扫过去 */
.card__video.is-wiping {
  z-index: 2;
  opacity: 1;
}

/* is-loading 期间先把 UI 藏起来（视频保持可见让 autoplay 生效，同 h5） */
.stage.is-loading .copy,
.stage.is-loading .card,
.stage.is-loading .bottom-ui,
.stage.is-loading .switch-ui,
.stage.is-loading .edge-nav,
.stage.is-loading .scroll-cue {
  opacity: 0;
}

/* ============ 居中排版：文案块（单例，随屏重渲染，同 h5 思路） ============ */

.copy {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
}

.copy > * {
  position: absolute;
  left: 0;
  right: 0;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

.eyebrow {
  top: var(--y-eyebrow);
  margin-top: 0;
  margin-bottom: 0;
  font-family: "FancyCutPro", Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: clamp(14px, 1.15vw, 18px);
  font-weight: 300;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.62);
  opacity: 0;
  transform: translateY(24px);
}

/* ⚠️ 这两条是【首页轮播标题】的样式，必须限定在 .copy 里（2026-09-24）。
   原来写的是裸 h1 / h1 em —— 内页面板里也有 <h1>（.pg-opener__title），
   于是被这两条漏进去：字体被顶成 FancyCutPro、字号被顶成 48~88px、
   还被加上 text-transform: uppercase，中文字因此回退成 PingFang 黑体，
   跟手机版的衬线调性完全不是一回事。
   教训：跨组件的样式一律带容器前缀，裸标签选择器在同一个页面里迟早会撞。
   （pages.css 那边靠 pg- 前缀隔离，但标签名隔离不了，只能从这一侧收窄。） */
.copy h1 {
  top: var(--y-title);
  margin: 0;
  color: rgba(255, 255, 255, 0.93);
  font-family: "FancyCutPro", Georgia, "Times New Roman", serif;
  font-size: clamp(48px, 5vw, 88px);
  font-weight: 300;
  line-height: 0.98;
  letter-spacing: 0;
  text-transform: uppercase;
  text-shadow: 0 18px 44px rgba(0, 0, 0, 0.32);
}

.copy h1 em {
  font-size: 0.82em;
  font-weight: 300;
  font-style: italic;
  text-transform: none;
  display: inline-block;
  position: relative;
  top: -0.03em;
}

.line {
  display: block;
  opacity: 0;
  transform: translateY(30px);
  filter: blur(5px);
  will-change: transform, opacity, filter;
}

.tagline {
  top: var(--y-tagline);
  margin: 0;
  color: rgba(255, 255, 255, 0.85);
  font-family: "FancyCutCondPro", "FancyCutPro", Georgia, serif;
  font-size: clamp(15px, 1.25vw, 19px);
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: 0.02em;
  text-shadow: 0 12px 24px rgba(0, 0, 0, 0.32);
  opacity: 0;
  transform: translateY(24px);
}

.read-button {
  top: var(--y-button);
  width: max-content; /* 配合 .copy > * 的 auto 外边距水平居中 */
  min-width: 168px;
  height: 42px;
  padding: 0 30px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  background: rgba(0, 0, 0, 0.04);
  color: rgba(255, 255, 255, 0.92);
  font-family: "BrilliantCutPro", "Helvetica Neue", Arial, sans-serif;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  overflow: hidden;
  opacity: 0;
  transform: translateY(24px);
  pointer-events: auto;
}

/* 悬停白色填充自下而上（h5 的 :active 效果搬到 PC hover） */
.read-button::before {
  content: "";
  position: absolute;
  inset: -2px;
  background: rgba(255, 255, 255, 0.88);
  transform: translateY(102%);
  transition: transform 560ms var(--ease-silk);
}

.read-button span {
  position: relative;
  z-index: 1;
  transition: color 360ms ease;
}

.read-button:hover::before {
  transform: translateY(0);
}

.read-button:hover span {
  color: #171311;
}

/* 「不加链接」状态：content.json 里 ads[编号].noLink = true，文案变「敬请期待」 */
.read-button.is-disabled {
  opacity: 0.55;
}

/* --- 文案入场 / 退场编排 ---
   ⚠️ 首屏入场一律挂在 .pc.is-ready 下，且 **首次落定后 JS 会摘掉 is-ready**。
   否则切屏时 renderCopy() 重建的 .line 会重新匹配到这条带 1650ms 延迟的规则，
   在新屏上重播一遍首屏入场（h5 2026-07-08 踩过同款坑）。 */

.pc.is-ready .eyebrow {
  animation: copyIn 900ms var(--ease-silk) 1550ms forwards;
}
.pc.is-ready .line:nth-child(1) {
  animation: titleIn 1000ms var(--ease-title) 1650ms forwards;
}
.pc.is-ready .line:nth-child(2) {
  animation: titleIn 1000ms var(--ease-title) 1820ms forwards;
}
.pc.is-ready .tagline {
  animation: copyIn 900ms var(--ease-silk) 1980ms forwards;
}
.pc.is-ready .read-button {
  animation: buttonIn 860ms var(--ease-silk) 2150ms forwards;
}
/* 卡片不再有入场动画：Loading 结束即已在位（与大视频同时、叠在一起显示）。
   客户 2026-09-23：「不要有淡入或出现的过程，两者始终叠在一起同时显示」。
   要恢复入场：把 .card 的 opacity/transform 改回 opacity:0 + translateY(26px) scale(0.97)，
   并在这里补回 animation: cardIn 1100ms var(--ease-silk) 600ms forwards; */

.pc.is-settled .eyebrow,
.pc.is-settled .line,
.pc.is-settled .tagline {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  animation: none;
}
.pc.is-settled .read-button {
  opacity: 1;
  transform: translateY(0);
  animation: none;
}
.pc.is-settled .read-button.is-disabled {
  opacity: 0.55;
}
.pc.is-settled .card {
  opacity: 1;
  transform: translateY(0) scale(1);
  animation: none;
}

/* 退场：原地模糊淡出（客户 2026-09-23 定，见下方 @keyframes copyOut 的注释）。
   ⚠️ 两组选择器必须都写 from 可见态，否则透明度 0→0、退场看不见（当天踩过）。 */
.stage.is-switching .eyebrow,
.stage.is-switching .line {
  animation: titleOut 520ms var(--ease-heavy) forwards;
}
.stage.is-switching .tagline,
.stage.is-switching .read-button {
  animation: copyOut 520ms var(--ease-heavy) forwards;
}
/* 「敬请期待」按钮：可见态是 0.55 而非 1，用上面那条会先闪亮到 1 再淡出。
   这条要放在通用规则之后 —— 同为 .read-button、特异性靠后取胜。 */
.stage.is-switching .read-button.is-disabled {
  animation: copyOutFaint 520ms var(--ease-heavy) forwards;
}

.stage.is-revealing .eyebrow {
  animation: copyIn 720ms var(--ease-silk) 180ms forwards;
}
.stage.is-revealing .line:nth-child(1) {
  animation: titleIn 800ms var(--ease-title) 240ms forwards;
}
.stage.is-revealing .line:nth-child(2) {
  animation: titleIn 800ms var(--ease-title) 360ms forwards;
}
.stage.is-revealing .tagline {
  animation: copyIn 720ms var(--ease-silk) 460ms forwards;
}
.stage.is-revealing .read-button {
  animation: buttonIn 720ms var(--ease-silk) 560ms forwards;
}

/* ============ 大视频斜角擦入：clip-path 斜边扫描（单层遮罩） ============
   2026-09-23 第三版，客户定两条：
   ① 遮罩只保留【一层】—— 不再用 .wipe / .wipe__cover 双层推导；
   ② 大视频本体不做任何位移 —— 画面静止，只有斜边在动。

   做法：新屏 .slide.is-active 在旧屏【之上】擦入（z-index:2），
   旧屏原位不动当底继续播，擦满后 JS 直接暂停它。
   斜边即 .slide 自身的 clip-path，四点多边形在画面外起止，
   所以任意屏幕宽度下斜边都是完整一条、不会在角落露缝。

   ⚠️ 两个数值的差 = tan(4°) 折算：容器宽 W、高 H 时，
      竖直方向 100% 对应 tan4° × H/W ≈ 0.07 × (H/W) 的横向偏移。
      这里按 1440×900（16:10）取 6%，改角度改这个差。
   ⚠️ 百分比相对各自盒子：大视频按全屏盒子算，卡片按卡片盒子算，
      两边斜角天然一致。小视频的遮罩是独立的一套，见下方 .card__video 规则。 */

.slide.is-active {
  z-index: 2;
}

.stage.is-wiping-fwd .slide.is-active {
  animation: wipeSlideIn var(--wipe-dur) var(--ease-circ) both;
}

.stage.is-wiping-rev .slide.is-active {
  animation: wipeSlideInRev var(--wipe-dur) var(--ease-circ) both;
}

/* 卡片（小视频）与大视频【完全同步】（客户 2026-09-23 最后定）：
   同一组 keyframes（wipeSlideIn / wipeSlideInRev）、同一时长、同一缓动、【零延迟】。
   —— 同时起手、同时结束；两者都从各自画面的右上角起斜切入。
   视差效果来自「画面尺寸不同 + 百分比相对各自盒子」：
   同样扫过 100% 宽度，大视频走过约 1440px，卡片只走过约 173px，
   于是肉眼看到两条斜边以不同速度推进 —— 这就是要的视差，不需要再加时间差。

   ⚠️ 视频本体不位移、不 skew、不缩放，只动 clip-path（与大视频同款），
      所以卡片里的画面始终静止、只有斜边在动。
   ⚠️ 卡片本身（.card）位置永远钉住不动，作用的是内部的新视频层。 */
.stage.is-wiping-fwd .card__video.is-wiping {
  animation: wipeSlideIn var(--wipe-dur) var(--ease-circ) both;
}

.stage.is-wiping-rev .card__video.is-wiping {
  animation: wipeSlideInRev var(--wipe-dur) var(--ease-circ) both;
}

/* ============ 左右线框（2026-09-23 客户按草图改定） ============
   左右【各固定一个】与卡片同比例 4:5 的线框，常驻不消失；
   左 = 上一屏、右 = 下一屏，三屏循环。切屏时框的位置不动，框里做擦换（见下方「线框擦换」）。
   悬停显示该屏缩略图，点左框上一屏、点右框下一屏。 */

/* .edge-nav 本身就是那块【隐形遮罩窗口】（客户草图里的虚线框）：
   比线框每边大 --edge-pad，overflow:hidden，无边框无底色 —— 线框平移出了窗口就看不见。
   --edge-pad 同时是：擦换时两个框之间的间隙；悬停 scale(1.12) 放大的余量（52px 的框放大后每边多 ~3px）。 */
.edge-nav {
  /* 客户 2026-09-23：线框整体等比放大 20%，再放大 10%（1.2 × 1.1 = 1.32；原 clamp(38px, 3vw, 52px)、间隙 6px） */
  --edge-w: calc(clamp(38px, 3vw, 52px) * 1.32);
  --edge-pad: 7.92px;
  --edge-dur: calc(var(--wipe-dur) - 500ms); /* 线框擦换时长，比大视频快 0.5s */
  position: absolute;
  z-index: 12;
  top: 50%;
  transform: translateY(-50%);
  width: calc(var(--edge-w) + var(--edge-pad) * 2);
  height: calc(var(--edge-w) * 1.25 + var(--edge-pad) * 2); /* 线框 4:5 */
  overflow: hidden;
}

/* 位置减掉 --edge-pad，线框本身仍落在原来 clamp(28px, 3.4vw, 56px) 的位置 */
.edge-nav--prev {
  left: calc(clamp(28px, 3.4vw, 56px) - var(--edge-pad));
}

.edge-nav--next {
  right: calc(clamp(28px, 3.4vw, 56px) - var(--edge-pad));
}

.edge-nav__item {
  position: absolute;
  top: var(--edge-pad);
  left: var(--edge-pad);
  width: var(--edge-w);
  aspect-ratio: 4 / 5; /* 与卡片、与素材一致的长方形 */
  display: block;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.6); /* 客户 2026-09-23：不透明度 0.5 → 0.6 */
  /* 客户 2026-09-23 试：硬边毛玻璃 + 很淡的白底，让线框区别于普通描边。
     回滚：background 改回 transparent，删掉两行 backdrop-filter。 */
  background: rgba(255, 255, 255, 0.07);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  overflow: hidden;
  opacity: 0;
  transition: border-color 420ms var(--ease-silk);
}

/* 线框随 UI 一起入场 */
.pc.is-ready .edge-nav__item {
  animation: fadeUp 700ms var(--ease-silk) 2300ms forwards;
}
/* ⚠️ 常驻：挂 is-chromed 而不是 is-settled —— is-settled 每次切屏开始都会被摘掉，
   挂在它上面的话切屏期间线框整个消失（改版前就是这样）。 */
.pc.is-settled .edge-nav__item,
.pc.is-chromed .edge-nav__item {
  opacity: 1;
  animation: none;
}

.pc.is-loading .edge-nav__item {
  opacity: 0;
}

.edge-nav__item img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 420ms var(--ease-silk);
}

.edge-nav__item:hover {
  border-color: rgba(255, 255, 255, 0.95);
  /* 客户 2026-09-23：悬停不再 scale(1.12) 放大，只亮边 + 显示缩略图 */
}

/* 悬停才显示该屏缩略图 */
.edge-nav__item:hover img {
  opacity: 1;
}

/* ---- 线框擦换：与大视频【同时起手】（同一 --ease-circ、零延迟），但时长单独用 --edge-dur ----
   --edge-dur = --wipe-dur − 500ms（客户 2026-09-23 定：线框比大视频快 0.5 秒，现 1.5s）；
   线框先到位，大视频随后擦满。 */
   按客户第二张草图「隐形遮罩 + 一个出画、一个入画」：
   线框大小不变、整体平移；旧框滑出遮罩窗口，新框从窗口另一侧外面滑进来，
   两框之间始终隔一个 --edge-pad，像一条横向走片。
     前进（is-wiping-fwd，从右往左）：旧框往左出画，新框从右边入画
     后退（is-wiping-rev，从左往右）：镜像（--edge-dir 取 -1）
   平移量 = --edge-w + --edge-pad：刚好让框从「窗口内原位」走到「完全出窗口」。
   ⚠️ keyframes 两端都显式写全（第四轮踩过：只写 to 会取到退回后的底层值）。 */
.stage.is-wiping-fwd .edge-nav { --edge-dir: 1; }
.stage.is-wiping-rev .edge-nav { --edge-dir: -1; }

.stage.is-wiping-fwd .edge-nav__item.is-out,
.stage.is-wiping-rev .edge-nav__item.is-out {
  animation: edgeFrameOut var(--edge-dur) var(--ease-circ) both;
}
.stage.is-wiping-fwd .edge-nav__item.is-in,
.stage.is-wiping-rev .edge-nav__item.is-in {
  animation: edgeFrameIn var(--edge-dur) var(--ease-circ) both;
}

@keyframes edgeFrameOut {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--edge-dir, 1) * -1 * (var(--edge-w) + var(--edge-pad)))); }
}

@keyframes edgeFrameIn {
  from { transform: translateX(calc(var(--edge-dir, 1) * (var(--edge-w) + var(--edge-pad)))); }
  to   { transform: translateX(0); }
}

/* ============ 底部进度线（居中收窄） ============ */

/* ============ 底部两行（2026-09-23 客户定） ============
   上：◀ ｜ ▶ 切换组（.switch-ui）—— 单独一行提上去，垂直落在
       「探索更多」按钮与进度线的【中点】。
   下：三条进度线（.bottom-ui）。
   ⚠️ 两者是【分开的两个定位元素】，别并回同一个 flex 行 ——
      并在一起时整组会被 bottom 顶到画面最底部，箭头贴边、
      和右下角 SCROLL 提示挤在一起（本轮踩过）。 */

/* ⚠️ 两者都是绝对定位，各自贴底。改其中一条时【别把 position/left/right/bottom
      一起删掉】—— 2026-09-23 重构时漏了 .bottom-ui 的定位，
      它退回 static、跑到画面顶上被内容盖住，看着就像"进度线没了"。 */
.switch-ui {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  bottom: 15vh;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 34px; /* 竖线到两侧箭头的间距 */
  opacity: 0;
}

/* 中间那条竖线：与手机版同款做法，用伪元素画，不占 DOM 节点。
   手机版是「◀ ｜ 进度线 ｜ ▶」同一行里的 42px 竖线
   （根目录 styles.css 的 .bottom-ui::after）；PC 版换到切换组里。 */
.switch-ui::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1px;
  height: 42px;
  background: rgba(255, 255, 255, 0.4);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* 左右切换箭头：三角形用 border 画（同手机版 .arrow::before），
   按钮本体透明、只做热区。点一下切一屏。 */
.arrow {
  position: relative;
  z-index: 1; /* 压在竖线之上，避免竖线盖住三角形的尖 */
  flex: 0 0 auto;
  width: 28px;
  height: 42px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.arrow::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transform: translate(-50%, -50%);
  opacity: 0.78;
  transition: opacity 320ms var(--ease-silk);
}

.arrow--prev::before {
  border-right: 6px solid rgba(255, 255, 255, 0.9);
}

.arrow--next::before {
  border-left: 6px solid rgba(255, 255, 255, 0.9);
}

.arrow:hover::before {
  opacity: 1;
}

/* 进度线：留在底部原处（与重构前一致）。
   ⚠️ 重构底部两行时最容易漏掉的就是这一块 —— 漏了它 .bottom-ui 会退回 static、
      跑到画面顶部被内容盖住，现象看起来就像"三条进度线不见了"（2026-09-23 踩过）。 */
.bottom-ui {
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  bottom: 5vh;
  display: flex;
  justify-content: center;
  opacity: 0;
}

.pc.is-ready .bottom-ui {
  animation: fadeUp 780ms var(--ease-silk) 2350ms forwards;
}
.pc.is-ready .switch-ui {
  animation: fadeUp 780ms var(--ease-silk) 2350ms forwards;
}
/* 进度线 + 切换组都是常驻 UI：挂 is-chromed 而不是 is-settled —— 后者在每次切屏开始时
   会被 goTo() 摘掉再补回来，挂它的话它们会跟着一闪一灭（客户 2026-09-23 要求常驻）。
   pointer-events 明确写 auto，别依赖继承：.stage 上有 pointer-events 相关规则，
   一旦哪天被改成 none，进度线和箭头就都点不动了。 */
.pc.is-chromed .bottom-ui,
.pc.is-chromed .switch-ui {
  opacity: 1;
  animation: none;
  pointer-events: auto;
}

/* 可点击切屏：1px 的线，鼠标落在线附近很难对准。
   用透明 padding 把热区撑到约 56px 高（上下各 28px），负外边距抵消，
   视觉位置与粗细完全不变 —— 鼠标只要进这个范围就是手型、就能点。
   客户 2026-09-23 要求热区"不要集中在这根线上，上面可以覆盖一个透明的热区"。 */
.progress {
  display: flex;
  gap: 10px;
  width: min(24vw, 240px);
  /* ⚠️ 这里是 height: auto + 子元素定高，【不要】写成 height: 1px。
     全局是 box-sizing: border-box（见文件顶部），固定高会把 padding 算进总高，
     上下各 28px 的 padding 会把这 1px 吃干净 → 三根 span 被压成 0 高、整条线消失。
     现在：矮的 span 自己撑出 1px 的线，padding 往外扩出热区。 */
  height: auto;
  padding: 28px 0;
  margin: -28px 0;
}

/* 每一段：定高的 span 当粗细尺，::before 在内部按 --p 填充。
   热区由容器（.progress）的 padding 提供，这里只负责那 1px 的线。 */
.progress span {
  position: relative;
  flex: 1 1 auto;
  height: 1px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.22);
  /* 可点击：给手型光标 + 悬停提亮，否则 1px 的线看不出能点（客户 2026-09-23 反馈） */
  cursor: pointer;
  transition: background-color 320ms var(--ease-silk);
}

.progress span:hover {
  background: rgba(255, 255, 255, 0.42);
}

/* 当前屏那一段：悬停时再亮一点，暗示"已在这屏，点了不会切" */
.progress span:hover::before {
  filter: brightness(1.15);
}

.progress span::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.95);
  transform: scaleX(var(--p, 0));
  transform-origin: left center;
}

/* ============ 右下 scroll 提示 ============ */

.scroll-cue {
  position: absolute;
  z-index: 10;
  right: clamp(28px, 3.4vw, 56px);
  bottom: 5vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  opacity: 0;
}

.pc.is-ready .scroll-cue {
  animation: fadeUp 780ms var(--ease-silk) 2500ms forwards;
}
.pc.is-settled .scroll-cue {
  opacity: 1;
  animation: none;
}

.scroll-cue__label {
  font-family: "BrilliantCutPro", "Helvetica Neue", Arial, sans-serif;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  color: rgba(255, 255, 255, 0.55);
  writing-mode: vertical-rl;
}

.scroll-cue__line {
  width: 1px;
  height: 44px;
  background: rgba(255, 255, 255, 0.3);
  overflow: hidden;
  position: relative;
}

.scroll-cue__line::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 40%;
  background: rgba(255, 255, 255, 0.9);
  animation: cueDrop 2200ms var(--ease-silk) infinite;
}

/* ============ 「敬请期待」浅色浮层（版式同 h5 定稿） ============ */

.soon {
  position: absolute;
  z-index: 30;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--paper);
  opacity: 0;
  visibility: hidden;
  transition: opacity 640ms var(--ease-silk), visibility 0s linear 640ms;
  cursor: pointer;
}

.soon.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 640ms var(--ease-silk);
}

.soon__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 0 6vw;
}

.soon__eyebrow {
  margin: 0;
  font-size: 12px;
  font-weight: 300;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-transform: uppercase;
  color: rgba(23, 60, 44, 0.5);
}

.soon__rule {
  width: 44px;
  height: 1px;
  margin: clamp(20px, 3.4vh, 30px) 0;
  background: rgba(23, 60, 44, 0.28);
  transform: scaleX(0);
  transition: transform 900ms var(--ease-title) 460ms;
}

.soon.is-open .soon__rule {
  transform: scaleX(1);
}

.soon__title {
  margin: 0;
  font-size: clamp(40px, 4.6vw, 72px);
  font-weight: 300;
  line-height: 1.25;
  letter-spacing: 0.18em;
  text-indent: 0.18em;
  color: var(--green);
}

.soon__deco {
  margin: 16px 0 0;
  font-family: var(--serif-en);
  font-size: clamp(15px, 1.4vw, 22px);
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: var(--green);
  opacity: 0.66;
}

.soon__deco:empty {
  display: none;
}

.soon__hint {
  margin: clamp(36px, 7vh, 64px) 0 0;
  font-size: 10px;
  font-weight: 300;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
  text-transform: uppercase;
  color: rgba(23, 60, 44, 0.42);
}

.soon__eyebrow,
.soon__title,
.soon__deco,
.soon__hint {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 800ms var(--ease-title), transform 800ms var(--ease-title);
}

.soon.is-open .soon__eyebrow { transition-delay: 340ms; }
.soon.is-open .soon__title   { transition-delay: 460ms; }
.soon.is-open .soon__deco    { transition-delay: 560ms; }
.soon.is-open .soon__hint    { transition-delay: 700ms; }

.soon.is-open .soon__eyebrow,
.soon.is-open .soon__title,
.soon.is-open .soon__deco,
.soon.is-open .soon__hint {
  opacity: 1;
  transform: translateY(0);
}

/* ╔════════════════════════════════════════════════════╗
   ║  多语言字体总控（同 h5 思路：基础规则=英文默认版）  ║
   ╚════════════════════════════════════════════════════╝ */

/* 中文：主标题细黑体单行（h5 定稿四件套：细黑/同字号/单行/无斜体）
   ⚠️ 限定在 .copy 里（2026-09-24）：这条原来也是裸 h1，
      会漏进内页面板（面板首屏标题也是 <h1>），把中文主标顶成 PingFang 黑体。
      而手机版内页的中文标题是宋体衬线（--serif-cn），必须保持一致。
      选择器权重：html[lang] .copy h1 = (0,2,2)，压得过上面的 .copy h1 (0,1,1)。 */
html[lang="zh-CN"] .copy h1 {
  font-family: var(--sans-cn);
  font-weight: 200;
  font-size: clamp(40px, 4vw, 68px);
  line-height: 1.2;
  letter-spacing: 0.04em;
}

html[lang="zh-CN"] .copy h1 .line {
  display: inline-block;
}

html[lang="zh-CN"] .copy h1 em {
  font-size: 1em;
  font-style: normal;
  top: 0;
}

html[lang="zh-CN"] .eyebrow {
  font-family: var(--sans-cn);
  font-style: normal;
  font-weight: 300;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
}

html[lang="zh-CN"] .tagline {
  font-family: var(--sans-cn);
  font-weight: 300;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
}

html[lang="zh-CN"] .read-button {
  font-family: var(--sans-cn);
  font-weight: 300;
  letter-spacing: 0.3em;
  text-indent: 0.3em;
}

html[lang="zh-CN"] .soon__eyebrow,
html[lang="zh-CN"] .soon__title,
html[lang="zh-CN"] .soon__hint {
  /* 故意不用 KDY Songti：子集不含这些字，会宋黑混排（同 h5 注释） */
  font-family: var(--sans-cn);
  font-weight: 300;
}

/* 法文：大写重音字母行高放宽，按钮字距略收（同 h5） */
html[lang="fr"] h1 {
  line-height: 1.02;
}

html[lang="fr"] .read-button {
  letter-spacing: 0.14em;
}

/* ============ 响应式：iPad（横 / 竖）与窄桌面 ============ */

/* 窄横屏：纵向节奏整体压紧（导航标签此前靠隐藏副词来缩短，
   现在只渲染主词，已无需处理） */
@media (max-width: 1180px) {
  :root {
    --y-eyebrow: 10.5vh;
    --y-title: 14vh;
    --y-card: 32vh;
    --h-card: 22vh;
    --y-tagline: 60vh;
    --y-button: 69vh;
  }
}

/* iPad 竖屏 & 窄竖窗口：文案块整体上移，卡片跟着缩小 */
@media (max-width: 900px), (orientation: portrait) and (max-width: 1024px) {
  :root {
    --y-eyebrow: 11vh;
    --y-title: 14.5vh;
    --y-card: 30vh;
    --h-card: 20vh;
    --y-tagline: 56vh;
    --y-button: 65vh;
  }

  h1 {
    font-size: clamp(38px, 6.6vw, 62px);
  }

  html[lang="zh-CN"] h1 {
    font-size: clamp(32px, 5.6vw, 52px);
  }

  .edge-nav__sq {
    width: 30px;
  }

  .scroll-cue {
    display: none;
  }

  .progress {
    width: min(44vw, 320px);
  }
}

/* ============ keyframes ============ */

@keyframes topbarIn {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes copyIn {
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

/* 退场一律【原地模糊淡出】—— 客户 2026-09-23 定：
   「主标语、副标语以及探索更多的框不要突然消失，做个原地的模糊淡出」。

   ⚠️⚠️ 这组 keyframes 必须【显式写 from】，不能只写 to（这是当天踩的大坑）：
   .line / .eyebrow / .tagline / .read-button 的基础态就是 opacity: 0
   （那是给入场用的起手态），可见的 opacity: 1 挂在 .pc.is-settled 上。
   切屏时 JS 先摘 is-settled 再挂 is-switching，只写 to 的话起始值会取"底层值"
   = 已经退回 opacity: 0 → 0 动到 0，透明度全程是 0，
   只有 blur/位移在空跑 —— 现象就是【文字瞬间消失，退场根本看不见】。
   实测过：点击后 32ms，opacity 已经是 0。

   ⚠️ transform 必须在 from 和 to 里【都写住同一个值】：
   两者都不写 → 不参与动画，直接取底层值（按钮会当场下跳 24px）；
   只写一边 → 另一边取底层值，同样会漂。要"原地"就把两边都钉成 translateY(0)。

   （入场 titleIn / copyIn / buttonIn 仍带位移，那是「浮上来」的手感，
     客户说入场 OK，别跟着一起改。） */
@keyframes copyOut {
  from {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
  to {
    opacity: 0;
    transform: translateY(0);
    filter: blur(8px);
  }
}

/* is-disabled（「敬请期待」）按钮的退场：它的可见态是 0.55，不是 1，
   用上面那条会先闪亮到 1 再淡出。 */
@keyframes copyOutFaint {
  from {
    opacity: 0.55;
    transform: translateY(0);
    filter: blur(0);
  }
  to {
    opacity: 0;
    transform: translateY(0);
    filter: blur(8px);
  }
}

@keyframes titleIn {
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

@keyframes titleOut {
  from {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
  to {
    opacity: 0;
    transform: translateY(0);
    filter: blur(18px);
  }
}

@keyframes buttonIn {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes fadeUp {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes cardIn {
  from {
    opacity: 0;
    transform: translateY(26px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* ---- 大视频：clip-path 斜边扫描（只服务 .slide，单层遮罩）
   视频本体【不位移、不 skew、不缩放】—— 只动 clip-path，画面必然静止。
   斜边走向：两个方向擦出的可见区都是【上宽下窄】（对齐参考站，客户 2026-09-23 确认：
   参考站两个方向只是镜像，倾斜是对称的，两个方向的可见区一律上宽下窄）。

   ⚠️ 判定「上宽下窄」的唯一标准：可见区的宽度随 y 增大而变窄。
      前进（边界从右往左扫）→ 可见区 = 边界【右侧】，宽度 = 160 - 边界x，
        宽度变小 ⇔ 边界x 变大 → 上端x【小于】下端x；
      反向（边界从左往右扫）→ 可见区 = 边界【左侧】，宽度 = 边界x，
        宽度变小 ⇔ 边界x 变小 → 上端x【大于】下端x。
      ⚠️ 两个方向的要求【相反】—— 不要把一个方向的数直接抄给另一个方向，
         那样必然有一个方向是反的（这条踩过一次）。
   ⚠️ 起点与终点的斜率必须一致（上端x - 下端x 恒为同一个值），
      否则斜边会在扫动过程中自己转向。
   ⚠️ 卡片那套（wipeIn / wipeInRev）是独立实现，改这边别顺手改那边。

   斜率取 11%：在 1440×900 基准上约 10°（实测观感值）。
      真按参考站的 skewX(4deg) 应为 4.4%（= 900 × tan4° ÷ 1440）
      —— 两者只差这一处，想换成参考站角度就改这一组数。 ---- */

@keyframes wipeSlideIn {
  from { clip-path: polygon(101% 0, 160% 0, 160% 100%, 112% 100%); }
  to   { clip-path: polygon(-71% 0, 160% 0, 160% 100%, -60% 100%); }
}

/* 反向：边界从左往右扫，可见区在边界【左侧】。
   四角 = 左上(0,0)、右上(x上,0)、右下(x下,100%)、左下(0,100%)；
   可见宽度 = x上 / x下，所以【上宽下窄 ⇒ x上 > x下】，差值恒为 11（与前进同斜率）。

   ⚠️ 与前进是【镜像】关系：前进是上端小、下端大（可见区在右侧，宽度 = 160 − x）；
      反向正好相反，是上端大、下端小。两个方向的要求【相反】，
      把一个方向的数抄给另一个方向必然有一个方向是反的（这条踩过两次）。

   ⚠️【两个方向扫过的距离必须相同】172%：时长一样、位移不同 → 看起来一快一慢。
      2026-09-23 客户报「点左箭头明显更快」，就是当时反向写了 231%。
      现在：上端 1% → 172%（跨度 172），下端 -10% → 161%（跨度 171）。
      起点整体还差一点才露头（斜边在画面左外侧，几乎看不见），
      终点超出右边缘（整屏盖满）。 */
@keyframes wipeSlideInRev {
  from { clip-path: polygon(0% 0, 1% 0, -10% 100%, 0% 100%); }
  to   { clip-path: polygon(0% 0, 172% 0, 161% 100%, 0% 100%); }
}

/* ---- 小视频（卡片）：与大视频【共用同一组 keyframes】----
   这里不再有卡片专用的 keyframes。
   卡片规则见上方 .stage.is-wiping-* .card__video.is-wiping —— 同样是
   wipeSlideIn / wipeSlideInRev、同样的 --wipe-dur、同样的缓动、零延迟。
   历史：曾用过一套「双层反向 skew 平移」（wipeIn / wipeInRev / wipeOut /
   wipeCover* 系列），配合 --wipe-card-delay 做滞后视差；
   2026-09-23 客户定为「两者同时起止」后全部废弃并删除。 */

@keyframes cueDrop {
  0% {
    transform: translateY(-100%);
  }
  60%,
  100% {
    transform: translateY(260%);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    transition-duration: 1ms !important;
  }
}
