/* motion.css — 全站动效公共层样式。
   契约：docs/motion/motion-contract.md v1（2026-10-27 冻结）。
   所有规则包在 html.motion-on 门控下：类不存在 = 全套静止、内容完整可见
   （无 JS / RM / JS 失败的安全态）。reduced-motion 由 tokens.css 全局硬停
   兜底，这里不重复处理。只动 transform / opacity / filter。
   时长/缓动照契约 §5：揭示 0.7s、img 落位 0.85s、字符 0.5s/45ms、
   roll 0.32s/18ms、编排落位 0.6s/90ms、灰度 0.4s，统一 cubic-bezier(.7,0,.2,1)。
   配合 site/js/motion.js（拆字符 / 加 .is-in / .stage-go / 写 .px 位移）。 */

/* ---- §4 grain 纸纹（全站，静态颗粒；低于光标 9995，高于幕布 200 / 内容）-- */
html.motion-on body::after {
  content: ""; position: fixed; inset: 0; z-index: 999; pointer-events: none;
  opacity: 0.05; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---- §2 .rv / .rv-img 遮罩揭示（.is-in 由 motion.js IO 加，once）----------
   墨色盖板 scaleY(1)→0（origin top）揭开 0.7s；同时内层 img 1.08→1 落位 0.85s。
   盖板常驻 = 揭开前内容被盖住；z-index 2 压在 img 上，低于 grain(999)。 */
html.motion-on .rv { position: relative; overflow: hidden; }
html.motion-on .rv::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  background: #141414; pointer-events: none;
  transform: scaleY(1); transform-origin: top;
  transition: transform 0.7s cubic-bezier(.7, 0, .2, 1);
}
html.motion-on .rv.is-in::after { transform: scaleY(0); }
html.motion-on .rv .rv-img {
  transform: scale(1.08);
  transition: transform 0.85s cubic-bezier(.7, 0, .2, 1);
}
html.motion-on .rv.is-in .rv-img { transform: scale(1); }

/* ---- §2 .gs hover 灰度→彩色 ----------------------------------------------
   只动滤镜，不碰父容器自身的缩放；img 自身 hover 或可点父级
   （a/button/figure）hover、键盘 focus-visible 时恢复彩色，0.4s ease。 */
html.motion-on .gs {
  filter: grayscale(1) contrast(.98);
  transition: filter 0.4s ease;
}
html.motion-on .gs:hover,
html.motion-on a:hover .gs,
html.motion-on a:focus-visible .gs,
html.motion-on button:hover .gs,
html.motion-on button:focus-visible .gs,
html.motion-on figure:hover .gs { filter: grayscale(0) contrast(1); }

/* 触屏没有 hover：灰度常态会让预览栈永远灰色，直接彩色；
   按压瞬间也出彩（触屏的 hover 等价物）；
   手机端 grain 加浓一档（小屏 + 高 DPR 下颗粒感知更弱）；
   视差幅度由 JS 对触屏放大 1.6x，放大余量同步加大。 */
@media (hover: none) {
  html.motion-on .gs { filter: none; }
  html.motion-on body::after { opacity: 0.08; }
  html.motion-on .px { scale: 1.14; }
}
html.motion-on .gs:active { filter: grayscale(0) contrast(1); }

/* ---- §2 .roll hover 字符上下滚动（双份字符结构由 motion.js 拆出）-----------
   .roll-c：1em 高的裁字窗；.roll-i 两份同字符上下叠，第二份初始
   translateY(100%)；父级 hover / focus-visible 时两份同上移 -100%。
   每字符 delay 递增 18ms（--ri 由 JS 按字符序号写入）。
   vertical-align:-0.12em 是裁字窗底边对基线的光学补偿，视觉验收可微调。 */
html.motion-on .roll .roll-c {
  display: inline-block; height: 1em; overflow: hidden;
  vertical-align: -0.12em;
}
html.motion-on .roll .roll-i {
  display: block; line-height: 1;
  transform: translateY(0);
  transition: transform 0.32s cubic-bezier(.7, 0, .2, 1);
  transition-delay: calc(var(--ri, 0) * 18ms);
}
html.motion-on .roll .roll-i + .roll-i { transform: translateY(100%); }
html.motion-on .roll:hover .roll-i,
html.motion-on .roll:focus-visible .roll-i,
html.motion-on a:focus-visible .roll .roll-i,
html.motion-on button:focus-visible .roll .roll-i { transform: translateY(-100%); }

/* ---- §2 data-chars 字符入场（单份字符结构由 motion.js 拆出，父级 once）-----
   fadeUp：translateY 0.6em→0 + opacity 0→1，0.5s，每字符 stagger 45ms
   （--ch-i 由 JS 写入）。transition 只挂在 .is-in 态：藏是瞬时的，
   只有入场动画。 */
html.motion-on [data-chars] .ch-c {
  display: inline-block;
  opacity: 0; transform: translateY(0.6em);
}
html.motion-on [data-chars].is-in .ch-c {
  opacity: 1; transform: translateY(0);
  transition: opacity 0.5s cubic-bezier(.7, 0, .2, 1),
              transform 0.5s cubic-bezier(.7, 0, .2, 1);
  transition-delay: calc(var(--ch-i, 0) * 45ms);
}

/* ---- §2 data-seq 首屏编排（html.stage-go 由 motion.js 设置）----------------
   待登场态 opacity 0 + translateY(14px)；stage-go 后按
   calc(var(--seq) * 90ms + 60ms) 阶梯落位 0.6s。--seq 取内联 style 路线
   （契约 §2 二选一取简单者；JS 会给只写了 data-seq 属性的元素回填变量，
   两种标记都成立）。 */
html.motion-on [data-seq] { opacity: 0; transform: translateY(14px); }
html.motion-on.stage-go [data-seq] {
  opacity: 1; transform: translateY(0);
  transition: opacity 0.6s cubic-bezier(.7, 0, .2, 1),
              transform 0.6s cubic-bezier(.7, 0, .2, 1);
  transition-delay: calc(var(--seq, 0) * 90ms + 60ms);
}

/* ---- §2 .px 轻视差：位移全部由 motion.js 每帧写 translate3d ----------------
   CSS 只做 GPU 合成层提示 + 基础放大（scale 独立属性与 JS 写的 transform
   复合，端点位移不露 overflow 容器的边）；无 motion-on 时规则不存在。 */
html.motion-on .px { will-change: transform; scale: 1.09; }
