/* ════════════════════════════════════════════════════════════════════════
   motion.css —— 全站动效层（2026-09-12）
   十五个页面全都引这一份，排在 styles-v4.css 之后（要盖住那边的 hover 写法）。
   配套 motion.js。这两个文件是动效的全部，别把动效散写进别的 CSS。

   五条规矩，改之前先读：

   1. 只动 transform / translate / scale / opacity / clip-path。
      唯一的例外是页头那 8px 收缩（padding-bottom），它用等量的 margin-bottom
      同步补回，页面里其它东西一个像素都不动，详见第 4 节。
   2. 位移一律用独立的 `translate` 属性，不用 `transform`。
      `.v4-figure-wide` 自己带 `transform: translateX(-50%)` 做居中，
      再往 transform 里写位移就会把图版甩到左边去。translate / scale / rotate
      是三个独立属性，跟 transform 叠加，互不覆盖。
   3. 藏起来的状态挂在 `html.mo-ready` 下面。这个类由 motion.js 加，
      JS 没跑、跑出错、或者用户要求减少动效时都不会加——那时页面原样全显示。
      （旧的 .ai-reveal 出过「淡入忘了触发、正文永远 opacity:0」的事故，
        styles-v4.css §16 里那条兜底就是那次留下的。这里从设计上不让它发生。）
   4. hover 一律关在 `@media (hover: hover) and (pointer: fine)` 里。
      触屏上点一下会触发 hover，不关的话手指一碰卡片就浮起来不落下。
   5. 不用 will-change。全站几百个元素挂 will-change 会把合成层撑爆，
      transform / opacity 的过渡本来就走合成，不需要提前声明。

   禁止项（老板 2026-09-12 定）：紫色与彩虹渐变、玻璃拟态、发光边框、粒子、
   鼠标跟随光斑、无限循环的装饰动画、弹跳缓动、emoji。本文件一条都没有。
   ════════════════════════════════════════════════════════════════════════ */

:root {
  /* 进入型动效的缓动。起手快、收尾慢，看着「响应快」。
     不用 ease-in（起手慢＝界面迟钝），不用回弹曲线（老板禁止）。 */
  --mo-ease: cubic-bezier(0.16, 1, 0.3, 1);
  /* hover / 按下这类每天见几十次的动作用更短更平的曲线 */
  --mo-ease-ui: cubic-bezier(0.3, 0, 0.2, 1);

  --mo-dur: 520ms;   /* 滚动显现：位移＋淡入 */
  --mo-rise: 14px;   /* 位移距离 */
  --mo-step: 72ms;   /* 同一批元素之间的错落间隔 */
}

/* ════════════════════════════════════════════════════════════════════════
   1. 微交互（不挂在 mo-ready 下面——JS 没跑也该有）
   ════════════════════════════════════════════════════════════════════════ */

/* ── 1.1 链接：下划线从左往右画出 ──
   原来的 .v4-link 是一条常驻发丝下划线，hover 时整条换成墨色（瞬间变色）。
   现在改成：发丝线留着不动，另一条同色实线从左端画到右端，200ms。
   用 background-size 画，不占布局、不加元素、不动 ::after（那里是箭头）。 */
@media (hover: hover) and (pointer: fine) {
  .v4-page .v4-link {
    background-image: linear-gradient(currentColor, currentColor);
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 0% 1px;
    transition: background-size 200ms var(--mo-ease), border-color 160ms ease;
  }
  .v4-page .v4-link:hover { background-size: 100% 1px; }
  /* 线已经自己画出来了，就别再让那条发丝底线跟着变色——两条一起动会糊成一团。 */
  .v4-page .v4-link:hover { border-bottom-color: var(--v4-hair-strong); }
  .v4-page :is(.v4-band-dark, .v4-band-gray) .v4-link:hover { border-bottom-color: var(--v4-hair-dark); }
}

/* ── 1.2 按钮：hover 反色 160ms、按下缩到 0.98 ──
   反色＝底色与字色对调。原来只是「黑再黑一点」，看不出被点到。
   缩放用 scale 属性（不是 transform），跟别处的位移互不干扰。 */
.v4-page .v4-btn {
  transition:
    background-color 160ms var(--mo-ease-ui),
    border-color 160ms var(--mo-ease-ui),
    color 160ms var(--mo-ease-ui),
    scale 120ms var(--mo-ease-ui);
}
.v4-page .v4-btn:active { scale: 0.98; }

@media (hover: hover) and (pointer: fine) {
  /* 亮底 */
  .v4-page .v4-btn--solid:hover {
    border-color: var(--v4-ink);
    background: var(--v4-paper);
    color: var(--v4-ink);
  }
  .v4-page .v4-btn--line:hover {
    border-color: var(--v4-ink);
    background: var(--v4-ink);
    color: var(--v4-paper);
  }
  /* 暗底：同样是对调，只是两端的颜色换成暗底那一套 */
  .v4-page :is(.v4-band-dark, .v4-band-gray) .v4-btn--solid:hover {
    border-color: var(--v4-on-dark);
    background: transparent;
    color: var(--v4-on-dark);
  }
  .v4-page :is(.v4-band-dark, .v4-band-gray) .v4-btn--line:hover {
    border-color: var(--v4-on-dark);
    background: var(--v4-on-dark);
    color: var(--v4-ink-bg);
  }
}

/* ── 1.3 导航：当前项与 hover 的下划线过渡 ──
   site-chrome.css 里本来就是 scaleX(0)→scaleX(1)，这里只把曲线换成全站同一条，
   时长从 180ms 提到 220ms（下划线是「画出来」，比纯变色需要多一点时间看清）。 */
.hqx-nav a::after,
.hqx-nav-trigger::after { transition: transform 220ms var(--mo-ease); }

/* ── 1.4 卡片 hover：1px 边框提亮 ＋ 上移 2px ──
   只有这两样。不加阴影、不加缩放、不加发光。
   上移用 transform（位移用的是 translate，两个属性分开，互不覆盖）。 */
@media (hover: hover) and (pointer: fine) {
  .v4-page .v4-shot-card,
  .v4-page .v4-stage--card {
    transition: transform 200ms var(--mo-ease), border-color 200ms ease;
  }
  .v4-page .v4-shot-card img { transition: border-color 200ms ease; }

  .v4-page .v4-shot-card:hover,
  .v4-page .v4-stage--card:hover { transform: translateY(-2px); }

  .v4-page .v4-stage--card:hover { border-color: var(--v4-hair-strong); }
  .v4-page :is(.v4-band-dark, .v4-band-gray) .v4-stage--card:hover { border-color: rgba(255, 255, 255, 0.26); }
  .v4-page .v4-shot-card:hover img { border-color: var(--v4-hair-strong); }
  .v4-page :is(.v4-band-dark, .v4-band-gray) .v4-shot-card:hover img { border-color: rgba(255, 255, 255, 0.26); }
}

/* ════════════════════════════════════════════════════════════════════════
   2. 页头：滚过首屏之后收 8px、底部发丝线出现、底色变实
   ════════════════════════════════════════════════════════════════════════
   ⚠ 这一段是全站唯一动布局属性的地方，写法有讲究，别照直觉改：

   页头是 position: sticky，它自己的高度算在文档流里。直接把高度收 8px，
   后面所有内容会跟着上移 8px —— 那是一次真实的版面跳动（CLS 会记一笔）。

   所以这里把 8px 拆成两半：
     静止态  .hqx-head 高 = --hqx-height - 8，页头再补 8px 下内边距 → 合计不变
     滚动态  下内边距 8→0，同时下外边距 0→8（同一条曲线、同一个时长）
   两者之和恒等于 8px，所以文档流里页头占的高度从头到尾是一个常数，
   页头里的内容（LOGO、导航）也一格没动——padding-top 始终是 0。
   看得见的只有：底色那块矮了 8px，下面那条发丝线浮出来。CLS 实测 0。 */

html.mo-ready .hqx-head { min-height: calc(var(--hqx-height) - 8px); }

html.mo-ready .hqx-header {
  padding-bottom: 8px;
  border-bottom-color: transparent;
  transition:
    padding-bottom 240ms var(--mo-ease),
    margin-bottom 240ms var(--mo-ease),
    border-bottom-color 240ms ease,
    background-color 240ms ease;
}

html.mo-ready.mo-scrolled .hqx-header {
  margin-bottom: 8px;
  border-bottom-color: var(--hqx-line);
  padding-bottom: 0;
  background: var(--hqx-surface-solid);   /* 0.94 → 不透明，滚动时正文不透出来 */
}

/* ════════════════════════════════════════════════════════════════════════
   3. 滚动显现：淡入 ＋ 上移
   ════════════════════════════════════════════════════════════════════════
   哪些元素参与由 motion.js 挑（挑法见那边的 PICK 常量），这里只管长相。
   `.mo-r` = 参与显现；`.mo-in` = 已经进过视口，加上就不再摘掉——
   语言切换、窗口缩放都不会让它重新播一遍。 */

/* ── 3.0 开场前的一层遮挡（防「先出现再消失再淡入」的闪烁）──
   mo-ready 是在 <head> 里同步加上的，第一帧就有；但「哪些元素参与显现」
   要等 DOM 解析完（DOMContentLoaded）由 JS 一个个挑，中间隔着一两帧。
   不挡的话首屏会先原样画出来一次，再被藏起来淡入，眼睛看得出那一下抖。
   所以正文整块先按住，JS 把 .mo-r 派发完立刻在 <html> 上加 mo-set 放行。
   实测这一挡只有 25ms 上下，跟首次绘制是同一时刻，看不出来。

   ⚠ 那条 mo-failsafe 是保险丝：万一 mo-set 两秒还没来（JS 被拦、被改坏），
     动画把正文强行显出来。0s 时长＋2s 延迟，不占 CPU，不是装饰动画。 */
html.mo-ready:not(.mo-set) .v4-main {
  opacity: 0;
  animation: mo-failsafe 0s linear 2s forwards;
}
@keyframes mo-failsafe { to { opacity: 1; } }

/* ⚠ 过渡写在 .mo-in 这一边，不能写在 .mo-r 上——这是 2026-09-12 实测踩到的坑。
   .mo-r 是 JS 在 DOMContentLoaded 时才加上的。要是过渡跟 opacity:0 写在同一条规则里，
   加类的那一刻元素就开始「用 520ms 慢慢淡出」，紧接着 mo-in 又把它拉回来，
   两下抵消，画面上一点动静都没有（当时量到的就是：加了 mo-r 之后 opacity 仍是 1）。
   过渡只写在终点状态上，加 .mo-r 时没有过渡可用 → 瞬间藏好；
   加 .mo-in 时过渡与终点值一起进来 → 才开始播。 */
html.mo-ready .mo-r {
  opacity: 0;
  translate: 0 var(--mo-rise);
}

html.mo-ready .mo-r.mo-in {
  opacity: 1;
  translate: 0 0;
  transition:
    opacity var(--mo-dur) var(--mo-ease) var(--mo-delay, 0ms),
    translate var(--mo-dur) var(--mo-ease) var(--mo-delay, 0ms),
    /* 下面两条是给卡片 hover 留的。transition 是一条声明，写在这里
       就不能在别处再写一遍（后写的会把整条覆盖掉）。 */
    transform 200ms var(--mo-ease),
    border-color 200ms ease;
}

/* 错落。同一批进视口的元素按从上到下的顺序领号，最多领到 7 号
   （7 × 72ms ＝ 504ms，再长就变成「等它慢慢排队」了）。 */
.mo-d0 { --mo-delay: 0ms; }
.mo-d1 { --mo-delay: 72ms; }
.mo-d2 { --mo-delay: 144ms; }
.mo-d3 { --mo-delay: 216ms; }
.mo-d4 { --mo-delay: 288ms; }
.mo-d5 { --mo-delay: 360ms; }
.mo-d6 { --mo-delay: 432ms; }
.mo-d7 { --mo-delay: 504ms; }

/* 描边章号：金色空心数字，位移小一点、时间长一点，让它「浮上来」而不是「弹上来」。
   不做数字滚动（老板禁止无谓的装饰动画）。 */
html.mo-ready .v4-num.mo-r { --mo-rise: 10px; }
html.mo-ready .v4-num.mo-r.mo-in { transition-duration: 620ms, 620ms, 200ms, 200ms; }

/* ════════════════════════════════════════════════════════════════════════
   4. 首屏入场
   ════════════════════════════════════════════════════════════════════════
   五段错落：眉标 → 大标题 → 说明 → 按钮 → 图版。
   首屏不等滚动，页面一画出来就开始。 */

.mo-h0 { --mo-delay: 0ms; }
.mo-h1 { --mo-delay: 90ms; }
.mo-h2 { --mo-delay: 180ms; }
.mo-h3 { --mo-delay: 260ms; }
.mo-h4 { --mo-delay: 60ms; } /* 首屏图版是 LCP 元素，延迟从 340ms 收到 60ms */

/* 首屏图版：淡入的同时从 1.02 收到 1，外框的落影跟着渗出来。
   scale 是独立属性，不碰 .v4-figure-wide 自己的 translateX(-50%) 居中。
   1.02 在 375px 上是 335px→341.7px，两边各多出 3.3px，够不着视口边，不会横向溢出。 */
html.mo-ready .mo-fig-hero {
  --mo-rise: 10px;
  scale: 1.02;
}
html.mo-ready .mo-fig-hero.mo-in {
  scale: 1;
  transition:
    opacity 620ms var(--mo-ease) var(--mo-delay, 0ms),
    translate 620ms var(--mo-ease) var(--mo-delay, 0ms),
    scale 620ms var(--mo-ease) var(--mo-delay, 0ms);
}

/* 落影：起手是一层几乎贴地的浅影，落位时长到 styles-v4.css §18.6 的那一层。
   两层影的第一层（0 1px 2px）两端一样，不动；动的只有第二层。 */
html.mo-ready .mo-fig-hero .v4-frame {
  box-shadow:
    0 1px 2px rgba(16, 19, 24, 0.06),
    0 6px 14px -12px rgba(16, 19, 24, 0.22);
}
html.mo-ready .mo-fig-hero.mo-in .v4-frame {
  box-shadow:
    0 1px 2px rgba(16, 19, 24, 0.06),
    0 30px 60px -34px rgba(16, 19, 24, 0.40);
  transition: box-shadow 760ms var(--mo-ease) var(--mo-delay, 0ms);
}

/* ════════════════════════════════════════════════════════════════════════
   5. 通栏图版揭示（首屏那张除外，它走上面第 4 节）
   ════════════════════════════════════════════════════════════════════════
   clip-path 从底部往上揭开 600ms。
   ⚠ clip 挂在 <img> 上，不挂 .v4-frame——挂外框的话那层落影会被一起裁掉，
     揭到最后影子「啪」地出现，很难看。挂在图上，外框的边、圆角、影从头就在，
     画面从框里长出来。 */
html.mo-ready .mo-clip { clip-path: inset(0 0 100% 0); }
html.mo-ready .mo-r.mo-in .mo-clip {
  clip-path: inset(0 0 0 0);
  transition: clip-path 600ms var(--mo-ease) 80ms;
}

/* ════════════════════════════════════════════════════════════════════════
   6. 页面切换：淡入淡出 200ms
   ════════════════════════════════════════════════════════════════════════
   浏览器原生的 View Transitions（跨文档）。支持的浏览器（Chrome 126＋）
   在两页之间做一次 200ms 交叉淡入；不支持的浏览器什么都不做，照旧硬切。
   不引任何库，不拦截点击，不劫持导航——JS 关了照样能翻页。 */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 200ms;
  animation-timing-function: var(--mo-ease);
}

/* ════════════════════════════════════════════════════════════════════════
   7. 减少动效 ＋ 打印
   ════════════════════════════════════════════════════════════════════════
   系统里开了「减少动态效果」时，motion.js 压根不加 mo-ready，页面原样全显示。
   下面这段是第二道闸：万一 mo-ready 已经加上了用户才去改系统设置，
   这里也立刻把所有藏起来的东西放出来（JS 那边同时也监听了这个变化）。
   打印同理——纸上不存在「滚动进视口」。 */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }

  html.mo-ready .mo-r,
  html.mo-ready .mo-fig-hero {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    transition: none !important;
  }
  html.mo-ready .mo-clip { clip-path: none !important; transition: none !important; }
  html.mo-ready .hqx-header { transition: none !important; }
}

@media print {
  html.mo-ready .mo-r,
  html.mo-ready .mo-fig-hero {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
  }
  html.mo-ready .mo-clip { clip-path: none !important; }
}
