/* ==========================================================================
   animations.css — 开屏信封、滚动入场、装饰动效
   ========================================================================== */

/* ==========================================================================
   开屏前锁定滚动
   仅在 JS 生效时加锁（信封本身也是 JS 才显示），无 JS 时不影响阅读。
   ========================================================================== */
html.is-locked,
html.is-locked body {
  overflow: hidden;
  height: 100%;
}

/* ==========================================================================
   滚动入场
   初始隐藏状态挂在 .js 下：无 JS 时内容直接可见。
   ========================================================================== */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.8s ease,
    transform 0.8s cubic-bezier(0.22, 0.68, 0.36, 1);
}

/* ==========================================================================
   信封开启
   全部使用 2D 变换：SVG 元素上的 3D 变换在各内核表现不一致，
   这里用 scaleY 收拢封盖 + 内页上滑，效果清晰且兼容性最好。
   ========================================================================== */

/* 封盖：以顶边为轴向上收拢 */
.env-flap {
  transform-box: view-box;
  transform-origin: 160px 86px;
  transition:
    transform 0.7s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.45s ease;
}

.is-open .env-flap {
  transform: scaleY(0);
  opacity: 0;
}

/* 火漆：弹开并淡出 */
.env-seal {
  transform-box: view-box;
  transform-origin: 160px 160px;
  transition:
    transform 0.55s cubic-bezier(0.34, 1.56, 0.64, 1),
    opacity 0.45s ease 0.1s;
}

.is-open .env-seal {
  transform: scale(1.55) rotate(26deg);
  opacity: 0;
}

/* 内页：向上滑出信封 */
.env-letter {
  transform-box: view-box;
  transition: transform 0.8s cubic-bezier(0.22, 0.68, 0.36, 1) 0.28s;
}

.is-open .env-letter {
  /* -86px 正好等于信封体上沿到 viewBox 顶部的距离：
     卡片上半部完整推出，且卡片顶边不会移出画布。 */
  transform: translateY(-86px);
}

/* 信封整体轻微上浮 */
.envelope__art {
  transition: transform 0.8s cubic-bezier(0.22, 0.68, 0.36, 1);
}

.is-open .envelope__art {
  transform: translateY(-8px) scale(1.03);
}

/* 按钮与提示在开启时让位 */
.envelope__btn,
.envelope__sub {
  transition:
    opacity 0.3s ease,
    transform 0.3s ease;
}

.is-open .envelope__btn,
.is-open .envelope__sub {
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
}

/* 遮罩整体上移淡出，交给下方内容 */
.envelope {
  transition:
    opacity 0.7s ease 0.75s,
    transform 0.9s cubic-bezier(0.4, 0, 0.2, 1) 0.75s,
    visibility 0s linear 1.45s;
}

.envelope.is-done {
  opacity: 0;
  transform: translateY(-7%);
  visibility: hidden;
  pointer-events: none;
}

/* 拆开后首屏内容轻微浮现 */
.cover__content {
  transition: opacity 1s ease 0.5s;
}

html.is-locked .cover__content {
  opacity: 0;
}

html.is-unsealed .cover__content {
  opacity: 1;
}

/* ==========================================================================
   装饰动效
   ========================================================================== */

/* 向下滑动提示 */
.cover__scroll svg {
  animation: hint-bounce 2.4s ease-in-out infinite;
}

@keyframes hint-bounce {
  0%,
  100% {
    transform: translateY(0);
    opacity: 0.55;
  }

  50% {
    transform: translateY(5px);
    opacity: 1;
  }
}

/* 枝叶极轻微摇曳
   注意：动画会覆盖元素上已有的 transform，所以每种朝向都用各自的
   keyframes，把基础姿态（rotate 180 / scaleX -1）直接写进关键帧。
   若只用一条 keyframes，--br 和 --tr 的镜像与翻转会在动画开始瞬间丢失。 */
.section__sprig,
.footer__sprig {
  animation: sprig-sway-a 7s ease-in-out infinite;
  transform-origin: 50% 50%;
}

.section__sprig--br {
  animation-name: sprig-sway-b;
  animation-delay: -2.5s;
}

.section__sprig--tr {
  animation-name: sprig-sway-c;
  animation-delay: -4s;
}

.footer__sprig {
  animation-delay: -1.2s;
}

@keyframes sprig-sway-a {
  0%,
  100% {
    transform: rotate(-1.6deg);
  }

  50% {
    transform: rotate(1.6deg);
  }
}

/* 垂直翻转（右下角）：基础姿态 rotate(180deg) 必须保留在关键帧里 */
@keyframes sprig-sway-b {
  0%,
  100% {
    transform: rotate(178.4deg);
  }

  50% {
    transform: rotate(181.6deg);
  }
}

/* 水平镜像（右上角） */
@keyframes sprig-sway-c {
  0%,
  100% {
    transform: scaleX(-1) rotate(-1.6deg);
  }

  50% {
    transform: scaleX(-1) rotate(1.6deg);
  }
}

/* ==========================================================================
   封面入场：花环与姓名逐字浮现
   初始隐藏全部靠 animation-fill-mode: both 制造，而不是普通声明。
   这样即使 is-unsealed 永不出现（例如信封模块异常），
   内容也只是保持静态可见，不会永久消失。
   ========================================================================== */
html.is-unsealed .cover__wreath {
  animation: wreath-in 1.1s cubic-bezier(0.22, 0.68, 0.36, 1) both;
}

@keyframes wreath-in {
  from {
    opacity: 0;
    transform: scale(0.86) rotate(-8deg);
  }

  to {
    /* 与 layout.css 中的静态值一致，动画结束后不会跳变 */
    opacity: 0.5;
    transform: none;
  }
}

/* 拆字后需要 inline-block 才能做位移与旋转 */
.cover__names .name-char {
  display: inline-block;
}

html.is-unsealed .cover__names .name-char,
html.is-unsealed .cover__amp {
  animation: name-char-in 0.72s cubic-bezier(0.22, 0.68, 0.36, 1) both;
  animation-delay: calc(var(--i, 0) * 95ms + 260ms);
}

@keyframes name-char-in {
  from {
    opacity: 0;
    transform: translateY(0.42em) rotate(-4deg);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* ==========================================================================
   章节标题下划线：随章节入场生长
   ========================================================================== */
.js .reveal .section__title span::after {
  transform: translateX(-50%) scaleX(0);
  transition: transform 0.65s cubic-bezier(0.22, 0.68, 0.36, 1) 0.3s;
}

.js .reveal.is-visible .section__title span::after {
  transform: translateX(-50%) scaleX(1);
}

/* ==========================================================================
   相册瀑布流：逐张淡入上浮
   页面本身在自动滚动，所以这里不需要任何自己的计时器——
   元素进入视口就由 IntersectionObserver 加 is-in（延迟由 JS 写入）。
   初始隐藏靠 is-in 的反面，而元素本身就是 JS 创建的，
   所以即使此处失效也不会出现「内容永久不可见」。
   ========================================================================== */
.gallery__item {
  opacity: 0;
  transform: translateY(18px) scale(0.97);
  transition:
    opacity 0.7s ease,
    transform 0.7s cubic-bezier(0.22, 0.68, 0.36, 1);
}

.gallery__item.is-in {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   减弱动态效果：关闭所有持续动效
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .cover__scroll svg,
  .section__sprig,
  .footer__sprig,
  .music.is-playing svg {
    animation: none;
  }

  .envelope {
    transition:
      opacity 0.2s ease,
      visibility 0s linear 0.2s;
  }

  .envelope.is-done {
    transform: none;
  }
}
