/* ============================================================================
   【动画效果分类】完成庆祝模板层（specs/022 R4）
   12 类母题的 @keyframes。节点由 ui/completion-fx.js 构建，这里只管动。
   契约：
     · 主 motif ≤1.2s 演完（拆盲盒新鲜感 ≠ 拖时长；票根卡 1.05s 浮现接手）
     · 颜色优先 token；少数深墨/夜色装饰色是母题本体，写死并在此注明
     · prefers-reduced-motion：整层不构建（js 侧）+ 这里 animation:none 双保险
     · 类名 cfx-<类>-*，守闸 anim-taxonomy.smoke 逐类数 keyframes 是否齐全
   ========================================================================== */

/* ---------- 公共舞台 ---------- */
.cfx {
  position: absolute;
  left: 50%;
  top: 46%;
  width: 260px;
  height: 260px;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 2; /* 票根卡是 z-3：motif 演完正好把画面交给卡 */
}
.cfx span, .cfx div { position: absolute; }

/* ---------- 1 盖印：印章砸落 + 墨边微溅 ---------- */
.cfx-stamp-seal {
  inset: 50% auto auto 50%;
  width: 104px; height: 104px;
  margin: -52px 0 0 -52px;
  border-radius: 50%;
  background: var(--vermillion);
  color: #fff;
  font-family: var(--font-display);
  font-size: 52px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 22px rgba(200, 84, 58, .4);
  transform: scale(2.4) rotate(calc(-14deg + var(--tilt, 0deg)));
  opacity: 0;
  animation: cfx-stamp-drop .58s cubic-bezier(.2, .9, .25, 1.15) forwards;
}
@keyframes cfx-stamp-drop {
  0%   { transform: scale(2.4) rotate(calc(-14deg + var(--tilt))); opacity: 0; }
  55%  { transform: scale(.9)  rotate(calc(2deg + var(--tilt)));    opacity: 1; }
  78%  { transform: scale(1.06) rotate(var(--tilt)); }
  100% { transform: scale(1)   rotate(var(--tilt)); opacity: 1; }
}
.cfx-stamp-dot {
  left: 50%; top: 50%;
  width: 7px; height: 7px; margin: -3.5px;
  border-radius: 50%;
  background: var(--vermillion);
  opacity: 0;
  animation: cfx-stamp-splat .5s ease-out var(--d) forwards;
}
@keyframes cfx-stamp-splat {
  0%   { transform: translate(0, 0) scale(.4); opacity: 0; }
  30%  { opacity: .85; }
  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: 0; }
}

/* ---------- 2 星尘：缓落星点 + 呼吸月晕 ---------- */
.cfx-stardust-grain {
  top: -6%;
  left: var(--x);
  width: var(--sz); height: var(--sz);
  border-radius: 50%;
  background: var(--gold, #c49a4f); /* 星点是烫金本体色 */
  box-shadow: 0 0 6px rgba(196, 154, 79, .8);
  opacity: 0;
  animation:
    cfx-stardust-fall var(--fall) linear var(--d) forwards,
    cfx-stardust-breath var(--breath) ease-in-out var(--d) infinite alternate;
}
@keyframes cfx-stardust-fall {
  0%   { transform: translateY(0); opacity: 0; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { transform: translateY(300px); opacity: 0; }
}
@keyframes cfx-stardust-breath { from { scale: .7; } to { scale: 1.25; } }
.cfx-stardust-moon {
  inset: 18% auto auto 50%;
  width: 64px; height: 64px; margin-left: -32px;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 34%, #efe8d6, var(--gold, #c49a4f) 62%, transparent 72%);
  filter: blur(.4px);
  opacity: 0;
  animation: cfx-moon-rise 1.1s ease-out .1s forwards;
}
@keyframes cfx-moon-rise {
  0% { transform: translateY(26px) scale(.8); opacity: 0; }
  60% { opacity: .95; }
  100% { transform: translateY(0) scale(1); opacity: .9; }
}

/* ---------- 3 冲刺：速度线掠过 + 印章前冲回弹 ---------- */
.cfx-dash-line {
  right: -20%;
  top: var(--top);
  width: var(--w); height: 4px;
  border-radius: 4px;
  background: linear-gradient(90deg, transparent, var(--vermillion));
  opacity: 0;
  animation: cfx-dash-swipe .42s cubic-bezier(.1, .8, .2, 1) var(--d) forwards;
}
@keyframes cfx-dash-swipe {
  0%   { transform: translateX(60px); opacity: 0; }
  25%  { opacity: .95; }
  100% { transform: translateX(-320px); opacity: 0; }
}
.cfx-dash-seal {
  inset: 50% auto auto -30%;
  width: 84px; height: 84px; margin-top: -42px;
  border-radius: 50%;
  background: var(--vermillion); color: #fff;
  font-size: 40px;
  display: flex; align-items: center; justify-content: center;
  transform: translateX(0) rotate(var(--tilt, 0deg));
  animation: cfx-dash-slide .74s cubic-bezier(.2, .9, .2, 1.2) var(--d) forwards;
}
@keyframes cfx-dash-slide {
  0%   { transform: translateX(0) scale(.7); opacity: 0; }
  40%  { opacity: 1; }
  72%  { transform: translateX(140%) scale(1.08); }
  100% { transform: translateX(118%) scale(1); opacity: 1; }
}

/* ---------- 4 生长：藤蔓逐节描边 + 叶片错时舒展 ---------- */
.cfx-grow-svg { inset: 0; width: 100%; height: 100%; }
.cfx-grow-vine {
  fill: none;
  stroke: #6b7b5e; /* 叶绿是母题本体 */
  stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 110; stroke-dashoffset: 110;
  animation: cfx-grow-draw 1s ease-out forwards;
}
@keyframes cfx-grow-draw { to { stroke-dashoffset: 0; } }
.cfx-grow-leaf {
  fill: rgba(143, 163, 122, .9);
  transform: scale(0); transform-origin: center;
  animation: cfx-leaf-pop .38s cubic-bezier(.3, 1.4, .5, 1) var(--d) forwards;
}
@keyframes cfx-leaf-pop { to { transform: scale(1); } }

/* ---------- 5 翻页：3D 折页 + 落款 ---------- */
.cfx-page-book {
  inset: 50% auto auto 50%;
  width: 120px; height: 140px;
  margin: -70px 0 0 -60px;
  perspective: 420px;
}
.cfx-page-back {
  inset: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px 10px 10px 4px;
  color: var(--vermillion);
  font-family: var(--font-display);
  font-size: 44px;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 10px 26px rgba(60, 50, 30, .18);
}
.cfx-page-sheet {
  inset: 0;
  transform-origin: left center;
  background: linear-gradient(105deg, #efe8d6, var(--paper) 55%);
  border: 1px solid var(--line-soft);
  border-radius: 4px 10px 10px 4px;
  animation: cfx-page-turn .9s cubic-bezier(.4, .1, .3, 1) var(--d) forwards;
}
@keyframes cfx-page-turn {
  0%   { transform: rotateY(0deg); }
  100% { transform: rotateY(-172deg); }
}

/* ---------- 6 显影：糊→清核心 + 扩散显影环 ---------- */
.cfx-develop-core {
  inset: 50% auto auto 50%;
  width: 118px; height: 118px; margin: -59px 0 0 -59px;
  border-radius: 14px;
  background: #4e6a6e; /* 相纸青灰是母题本体 */
  display: flex; align-items: center; justify-content: center;
  filter: blur(14px); opacity: 0;
  clip-path: circle(20% at 50% 50%);
  animation: cfx-develop-in .95s ease-out var(--d) forwards;
}
@keyframes cfx-develop-in {
  0%   { filter: blur(14px); opacity: 0; clip-path: circle(18% at 50% 50%); }
  60%  { opacity: 1; }
  100% { filter: blur(0); opacity: 1; clip-path: circle(75% at 50% 50%); }
}
.cfx-develop-mark { color: #fff; font-size: 46px; }
.cfx-develop-ring {
  inset: 50% auto auto 50%;
  width: 40px; height: 40px; margin: -20px;
  border-radius: 50%;
  border: 1.5px solid rgba(196, 154, 79, .8);
  opacity: 0;
  animation: cfx-develop-ring .8s ease-out var(--d) forwards;
}
@keyframes cfx-develop-ring {
  0%   { transform: scale(.5); opacity: 0; }
  30%  { opacity: .9; }
  100% { transform: scale(3.4); opacity: 0; }
}

/* ---------- 7 晕染：不规则墨团渗开 ---------- */
.cfx-bleed-blob {
  inset: 50% auto auto 50%;
  width: var(--sz); height: var(--sz);
  background: radial-gradient(circle, rgba(58, 66, 56, .92), rgba(107, 123, 94, .45) 68%, transparent 74%);
  transform: translate(var(--tx), var(--ty)) scale(0);
  opacity: 0;
  animation: cfx-bleed-in .95s cubic-bezier(.2, .7, .3, 1) var(--d) forwards;
}
@keyframes cfx-bleed-in {
  0%   { transform: translate(var(--tx), var(--ty)) scale(0); opacity: 0; }
  35%  { opacity: .95; }
  100% { transform: translate(var(--tx), var(--ty)) scale(1); opacity: .82; }
}

/* ---------- 8 撕票：锯齿两片分离 ---------- */
.cfx-tear-strip {
  inset: 50% auto auto 50%;
  width: 150px; height: 88px;
  margin: -44px 0 0 -75px;
}
.cfx-tear-half {
  left: 0; width: 150px; height: 88px;
  background: var(--paper);
  border: 1px solid var(--line);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 26px; line-height: 88px; text-align: center;
}
.cfx-tear-top {
  top: 0;
  color: var(--vermillion);
  animation: cfx-tear-up .62s cubic-bezier(.3, .8, .4, 1) var(--d) forwards;
}
.cfx-tear-bottom {
  top: 0;
  animation: cfx-tear-down .62s cubic-bezier(.3, .8, .4, 1) var(--d) forwards;
}
@keyframes cfx-tear-up {
  0%   { transform: translateY(0) rotate(0); opacity: 1; }
  100% { transform: translateY(-58px) rotate(calc(-9deg + var(--tilt, 0deg))); opacity: .94; }
}
@keyframes cfx-tear-down {
  0%   { transform: translateY(0) rotate(0); opacity: 1; }
  100% { transform: translateY(58px) rotate(7deg); opacity: .94; }
}

/* ---------- 9 刮开：银漆刮除见奖 ---------- */
.cfx-scratch-card {
  inset: 50% auto auto 50%;
  width: 132px; height: 92px;
  margin: -46px 0 0 -66px;
  border-radius: 10px;
  background: var(--paper);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: 0 10px 24px rgba(60, 50, 30, .16);
}
.cfx-scratch-prize { color: var(--vermillion); font-size: 42px; font-weight: 600; }
.cfx-scratch-cover {
  left: -10%; top: -10%;
  width: 120%; height: 120%;
  background: repeating-linear-gradient(115deg, #d9d2bd, #cdbfa0 8px, #d9d2bd 16px);
  transform: rotate(var(--tilt, 0deg));
  clip-path: inset(0 0 0 0);
  animation: cfx-scratch-off .78s cubic-bezier(.3, .7, .3, 1) var(--d) forwards;
}
@keyframes cfx-scratch-off { to { clip-path: inset(0 0 0 104%); } }
.cfx-scratch-spark {
  inset: 50% auto auto 50%;
  width: 4px; height: 14px; margin: -7px;
  border-radius: 3px;
  background: var(--gold, #c49a4f);
  opacity: 0;
  transform: rotate(var(--a)) translateY(0);
  animation: cfx-scratch-spark .5s ease-out var(--d) forwards;
}
@keyframes cfx-scratch-spark {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(10px); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(-64px); }
}

/* ---------- 10 翻转：卡牌定稿 ---------- */
.cfx-flip-card {
  inset: 50% auto auto 50%;
  width: 96px; height: 128px;
  margin: -64px 0 0 -48px;
  transform-style: preserve-3d;
  animation: cfx-flip-turn .82s cubic-bezier(.3, .9, .3, 1.05) var(--d) forwards;
  transform: perspective(500px) rotateY(0deg) rotate(var(--tilt, 0deg));
}
@keyframes cfx-flip-turn {
  0%   { transform: perspective(500px) rotateY(0deg); }
  100% { transform: perspective(500px) rotateY(calc(180deg * var(--spin, 1))) rotate(var(--tilt, 0deg)); }
}
.cfx-flip-face {
  inset: 0;
  backface-visibility: hidden;
  border-radius: 8px;
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
}
.cfx-flip-back {
  background:
    repeating-linear-gradient(45deg, rgba(78, 106, 110, .22) 0 6px, transparent 6px 12px),
    #4e6a6e;
}
.cfx-flip-front {
  transform: rotateY(180deg);
  background: var(--paper);
  color: var(--vermillion);
  font-family: var(--font-display);
  font-size: 48px;
}

/* ---------- 11 绽放：径向花瓣 + 余晖环 ---------- */
.cfx-bloom-petal {
  inset: 50% auto auto 50%;
  width: 12px; height: var(--h);
  margin: calc(var(--h) / -2) 0 0 -6px;
  border-radius: 60% 60% 40% 40%;
  background: linear-gradient(var(--vermillion), rgba(244, 90, 42, .35));
  transform-origin: 50% 100%;
  transform: rotate(var(--a)) translateY(-6px) scale(.2);
  opacity: 0;
  animation: cfx-bloom-open .66s cubic-bezier(.3, 1.3, .5, 1) var(--d) forwards;
}
@keyframes cfx-bloom-open {
  0%   { transform: rotate(var(--a)) translateY(-6px) scale(.2); opacity: 0; }
  40%  { opacity: 1; }
  100% { transform: rotate(var(--a)) translateY(-52px) scale(1); opacity: .95; }
}
.cfx-bloom-heart {
  inset: 50% auto auto 50%;
  width: 60px; height: 60px; margin: -30px;
  border-radius: 50%;
  background: var(--gold, #c49a4f);
  color: #fff; font-size: 30px;
  display: flex; align-items: center; justify-content: center;
  animation: cfx-bloom-heart .5s cubic-bezier(.3, 1.5, .5, 1) .18s backwards;
}
@keyframes cfx-bloom-heart { 0% { transform: scale(0); } 100% { transform: scale(1); } }
.cfx-bloom-ring {
  inset: 50% auto auto 50%;
  width: 70px; height: 70px; margin: -35px;
  border-radius: 50%;
  border: 2px solid rgba(196, 154, 79, .7);
  opacity: 0;
  animation: cfx-bloom-ring 1s ease-out var(--d) forwards;
}
@keyframes cfx-bloom-ring {
  0% { transform: scale(.6); opacity: 0; }
  35% { opacity: .9; }
  100% { transform: scale(3); opacity: 0; }
}

/* ---------- 12 扫光：烫金光带掠过票面 ---------- */
.cfx-sweep-ticket {
  inset: 50% auto auto 50%;
  width: 168px; height: 96px;
  margin: -48px 0 0 -84px;
  border-radius: 8px;
  background: var(--paper);
  border: 1.5px solid var(--vermillion);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  box-shadow: 0 12px 30px rgba(60, 50, 30, .2);
  animation: cfx-sweep-lift .6s cubic-bezier(.3, 1.2, .4, 1) var(--d, 0s) backwards;
}
@keyframes cfx-sweep-lift {
  0% { transform: translateY(30px) scale(.92); opacity: 0; }
  100% { transform: translateY(0) scale(1); opacity: 1; }
}
.cfx-sweep-label {
  font-family: var(--font-display);
  color: var(--vermillion);
  font-size: 20px; letter-spacing: .2em;
}
.cfx-sweep-band {
  top: -20%; left: -40%;
  width: 34%; height: 140%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .8) 45%, rgba(212, 168, 83, .65) 55%, transparent);
  transform: skewX(-18deg) translateX(-40px);
  animation: cfx-sweep-pass .8s ease-in-out calc(var(--d) + .12s) forwards;
}
@keyframes cfx-sweep-pass {
  0%   { transform: skewX(-18deg) translateX(-40px); opacity: 0; }
  20%  { opacity: 1; }
  100% { transform: skewX(-18deg) translateX(560px); opacity: 0; }
}

/* ---------- 稀有金光前奏（副类叠加，≤200ms） ---------- */
.cfx-gold { width: 100%; height: 100%; left: 0; top: 0; }
.cfx-gold-band {
  top: 30%; left: -50%;
  width: 60%; height: 40%;
  background: linear-gradient(95deg, transparent, rgba(212, 168, 83, .55), transparent);
  transform: skewX(-20deg);
  animation: cfx-gold-pass .2s ease-in-out forwards;
}
@keyframes cfx-gold-pass {
  0%   { transform: skewX(-20deg) translateX(0); }
  100% { transform: skewX(-20deg) translateX(420%); }
}

/* ---------- reduced-motion 双保险 ---------- */
@media (prefers-reduced-motion: reduce) {
  .cfx, .cfx *, .cfx-gold, .cfx-gold-band {
    animation: none !important;
    opacity: 0 !important;
  }
}
