/* ============================================================
   animation.css — シャッフル演出アニメーション
   5 段階: stack → shuffle → cut → deal → flip
   アニメ対象は transform / opacity のみ（box-shadow・width 等は不使用）。
   状態クラス（.stage-*）は shuffle.js が #shuffle-stage に付与する。
   ============================================================ */

/* ---- ステージ共通レイアウト ---- */
#shuffle-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(var(--card-w) / (300 / 527) + 4rem);
  perspective: 1400px;
  /* 金粉パーティクルはステージ基準の absolute 配置。カード外周へ舞い散る
     burst / ambient が縁で切れないよう clip しない。 */
  overflow: visible;
}

/* ---- 金粉銀河 canvas（gold-galaxy.js が生成） ---- */
/* ステージ全面に絶対配置。カード（束/グリッド）より背面（低 z-index）に敷き、
   pointer-events を切って操作を透過させる。描画は canvas 側（rAF）が担うため
   CSS アニメーション（transform/opacity）は一切付与しない。 */
.gold-galaxy {
  position: absolute;
  inset: 0;
  z-index: 1; /* カード（.shuffle-stack / .shuffle-grid = z-index 2）より下 */
  pointer-events: none;
}

/* ---- ダミー束（stack） ---- */
.shuffle-stack {
  position: relative;
  /* 金粉銀河 canvas（z-index:1）より前面に置く。 */
  z-index: 2;
  width: var(--card-w);
  aspect-ratio: var(--card-aspect);
  transform-style: preserve-3d;
}

.shuffle-stack .stack-card {
  position: absolute;
  inset: 0;
  width: 100%;
  /* --i: 束内インデックス。微妙な位置ズレ・回転差で「束」を表現。 */
  transform:
    translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
    rotate(calc((var(--i) - 3) * 1.1deg));
  opacity: 0;
}

/* 段階 1: stack — フェードイン（束が現れる） */
.stage-stack .shuffle-stack .stack-card {
  animation: stack-fade-in var(--duration-slow) var(--ease-out-expo) forwards;
  animation-delay: calc(var(--i) * 45ms);
}

@keyframes stack-fade-in {
  from {
    opacity: 0;
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px + 24px))
      rotate(calc((var(--i) - 3) * 1.1deg))
      scale(0.94);
  }
  to {
    opacity: 1;
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg))
      scale(1);
  }
}

/* stack 完了後（shuffle 待機まで）は表示を維持 */
.stage-shuffle .shuffle-stack .stack-card,
.stage-cut .shuffle-stack .stack-card {
  opacity: 1;
}

/* 段階 2: shuffle — リフルシャッフル風。
   data-half=left/right で左右に分かれ、位相差 delay で交互に噛み合う。 */
/* 項目4: 有機的な動き。--jx/--jy/--jr（位置・回転の揺らぎ）と --jd（時間の揺らぎ）を
   各カードに shuffle.js が付与。keyframes に組み込み、左右対称・同時刻の機械感を崩す。
   2回目のリフルは 1 回目と位相をずらすため、2回目相当の delay に --jd を加算する。 */
.stage-shuffle .shuffle-stack .stack-card[data-half='left'] {
  animation: riffle-left 0.45s var(--ease-in-out-soft) 2;
  animation-delay: calc(var(--i) * 30ms + var(--jd, 0ms));
}
.stage-shuffle .shuffle-stack .stack-card[data-half='right'] {
  animation: riffle-right 0.45s var(--ease-in-out-soft) 2;
  animation-delay: calc(var(--i) * 30ms + var(--jd, 0ms));
}

@keyframes riffle-left {
  0% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
  35% {
    transform:
      translate(calc(-52px + var(--jx, 0px)), calc(var(--i) * -1.4px + var(--jy, 0px)))
      rotate(calc(-9deg + var(--jr, 0deg)));
  }
  70% {
    transform:
      translate(calc(-14px + var(--jx, 0px) * 0.5), calc(var(--i) * -0.6px + var(--jy, 0px) * 0.5))
      rotate(calc(-3deg + var(--jr, 0deg) * 0.5));
  }
  100% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
}

@keyframes riffle-right {
  0% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
  35% {
    transform:
      translate(calc(52px + var(--jx, 0px)), calc(var(--i) * -1.4px + var(--jy, 0px)))
      rotate(calc(9deg + var(--jr, 0deg)));
  }
  70% {
    transform:
      translate(calc(14px + var(--jx, 0px) * 0.5), calc(var(--i) * -0.6px + var(--jy, 0px) * 0.5))
      rotate(calc(3deg + var(--jr, 0deg) * 0.5));
  }
  100% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
}

/* 段階 3: cut — 上半分（--i >= 4）が持ち上がって横へ→下半分の下へ戻る。 */
.stage-cut .shuffle-stack .stack-card {
  animation: cut-lift 0.72s var(--ease-out-expo) 1;
}
/* 下半分（--i < 4）は微かに沈むだけ */
.stage-cut .shuffle-stack .stack-card[data-half='left'] {
  animation-name: cut-settle;
}

@keyframes cut-lift {
  0% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
  40% {
    transform:
      translate(calc(46px + var(--jx, 0px)), calc(-30px + var(--jy, 0px)))
      rotate(calc(6deg + var(--jr, 0deg))) scale(1.02);
  }
  70% {
    transform:
      translate(calc(10px + var(--jx, 0px) * 0.4), calc(6px + var(--jy, 0px) * 0.4))
      rotate(calc(2deg + var(--jr, 0deg) * 0.4));
  }
  100% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px + 3px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
}

@keyframes cut-settle {
  0% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
  50% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px + 4px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
  100% {
    transform:
      translate(calc(var(--i) * 0.6px - 2px), calc(var(--i) * -0.8px))
      rotate(calc((var(--i) - 3) * 1.1deg));
  }
}

/* 段階 4: deal — 実カードのグリッド。束からグリッド位置へ順次スライド。 */
.shuffle-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-md) var(--space-sm);
  justify-items: center;
  width: 100%;
  max-width: var(--content-max-width);
  /* 金粉銀河 canvas（z-index:1）より前面に置く。 */
  position: relative;
  z-index: 2;
}

.shuffle-grid .deal-card {
  width: 100%;
  max-width: var(--card-w);
  opacity: 0;
  /* --n: 配布順。束の中央位置から現在のグリッド位置へ寄せる出発点。 */
  transform: translate(0, -40px) scale(0.9);
}

.stage-deal .shuffle-grid .deal-card {
  animation: deal-in 0.5s var(--ease-out-expo) forwards;
  animation-delay: calc(var(--n) * 90ms);
}

@keyframes deal-in {
  from {
    opacity: 0;
    transform: translate(0, -40px) scale(0.9);
  }
  60% {
    opacity: 1;
  }
  to {
    opacity: 1;
    transform: translate(0, 0) scale(1);
  }
}

/* deal 中はダミー束をフェードアウト */
.stage-deal .shuffle-stack {
  animation: stack-fade-out var(--duration-normal) var(--ease-out-expo) forwards;
}

@keyframes stack-fade-out {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(0.92);
  }
}

/* deal 完了後・flip 段階でもカードは表示を維持 */
.stage-flip .shuffle-grid .deal-card,
.shuffle-grid.is-final .deal-card,
.shuffle-grid.is-final .card {
  opacity: 1;
  transform: translate(0, 0) scale(1);
}

/* 段階 5: flip は shuffle.js が .is-flipped を順次付与。
   実際の回転は cards.css の .card.is-flipped .card-inner が担う。 */

/* 項目3: フリップの「タメ」と儀式性。
   フリップ直前に shuffle.js が .is-revealing を付与 → カードが浮き上がり
   （scale/opacity のみ）、cards.css の金色グロー疑似要素がフェードインする。
   その後 shuffle.js が .is-revealing を外し .is-flipped を付与してフリップする。 */
.shuffle-grid .card.is-revealing .card-inner {
  transform: translateY(-6px) scale(1.05);
  transition: transform var(--duration-normal) var(--ease-out-expo);
}

.shuffle-grid .card.is-revealing::after {
  opacity: 0.85;
}

/* 最後の1枚（結果のカード）はグローをやや強く。 */
.shuffle-grid .card.is-revealing-final.is-revealing::after {
  opacity: 1;
}
.shuffle-grid .card.is-revealing-final.is-revealing .card-inner {
  transform: translateY(-8px) scale(1.07);
}

/* 項目1: 待機演出の「気配」。
   演出完了後に API 未解決なら main.js が .shuffle-grid へ .is-listening を付与。
   めくられた6枚へ微かな金色グローの脈動（疑似要素の opacity アニメ）を与える。 */
.shuffle-grid.is-listening .card::after {
  opacity: 0.28;
  animation: card-listen-pulse 1.8s var(--ease-in-out-soft) infinite;
  animation-delay: calc(var(--n, 0) * 140ms);
}

@keyframes card-listen-pulse {
  0%, 100% { opacity: 0.16; }
  50% { opacity: 0.4; }
}

/* 待機演出テキスト（main.js が #shuffle-stage に動的生成）。
   ステージ下部に重ね、ゆっくり明滅する（opacity のみ, 無限ループ）。 */
.shuffle-waiting {
  position: absolute;
  left: 50%;
  bottom: -0.5rem;
  transform: translateX(-50%);
  margin: 0;
  z-index: var(--z-overlay);
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-heading-ja);
  font-size: var(--text-sm);
  letter-spacing: var(--letter-spacing-wide);
  color: var(--accent-gold-soft);
  pointer-events: none;
  animation: waiting-pulse 1.8s var(--ease-in-out-soft) infinite;
}

@keyframes waiting-pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 0.9; }
}

/* ネタバレ防止: シャッフルステージ内（.shuffle-grid スコープ限定）では、
   フリップ前はカード名・正逆ラベルを非表示にし、.is-flipped が付いた
   タイミングでふわっと表示する（opacity のみを transition）。
   結果画面のグリッドは .cards-grid（別クラス）なので影響しない。 */
.shuffle-grid .card .card-label {
  opacity: 0;
  transition: opacity var(--duration-slow) var(--ease-out-expo);
  /* フリップ回転が半分進み、表面が見え始めるタイミングに同期させる */
  transition-delay: 200ms;
}

.shuffle-grid .card.is-flipped .card-label {
  opacity: 1;
}

/* 最終グリッド（reducedMotion / skip 時の即時表示含む）は静的表示 */
.shuffle-grid.is-final .card {
  opacity: 1;
}

/* skip / reducedMotion の最終状態では遅延なく即ラベル表示 */
.shuffle-grid.is-final .card.is-flipped .card-label {
  transition-delay: 0ms;
}

/* 項目6: 鑑定文の段階フェードイン。
   render.js が各ブロックへ .reveal-block と --k（stagger index）を付与する。
   opacity のみのフェードイン。animation-fill-mode: forwards で最終状態を
   opacity:1 に確定させる（html2canvas の PNG 保存はアニメ完了後の姿を撮る前提）。 */
.reveal-block {
  opacity: 0;
  animation: reveal-fade-in 0.6s var(--ease-out-expo) forwards;
  animation-delay: calc(var(--k, 0) * 180ms);
}

@keyframes reveal-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ============================================================
   金粉パーティクル（gold-dust）の動き
   1 粒の色・形は cards.css。ここでは transform / opacity のみで
   burst（放射→減速→フェード）と ambient（ゆらゆら漂う）を定義する。
   ============================================================ */

/* ---- burst: 起点(--gox/--goy)から放射(--gx/--gy)して減速フェード ---- */
.gold-dust--burst {
  /* 起点へ移動してから飛散する。keyframes 内で translate を上書きする。 */
  animation:
    gold-burst var(--gdur, 1.4s) var(--ease-out-expo) var(--gdelay, 0s) forwards;
}

@keyframes gold-burst {
  0% {
    opacity: 0;
    transform:
      translate(var(--gox, 0px), var(--goy, 0px))
      rotate(0deg)
      scale(0.4);
  }
  18% {
    opacity: 1;
    transform:
      translate(
        calc(var(--gox, 0px) + var(--gx, 0px) * 0.55),
        calc(var(--goy, 0px) + var(--gy, 0px) * 0.55)
      )
      rotate(calc(var(--grot, 0deg) * 0.5))
      scale(1);
  }
  55% {
    opacity: 0.95;
    transform:
      translate(
        calc(var(--gox, 0px) + var(--gx, 0px) * 0.85),
        calc(var(--goy, 0px) + var(--gy, 0px) * 0.85)
      )
      rotate(calc(var(--grot, 0deg) * 0.75))
      scale(0.95);
  }
  100% {
    opacity: 0;
    transform:
      translate(
        calc(var(--gox, 0px) + var(--gx, 0px)),
        /* 終端はやや落下させ「舞い落ちる」余韻を残す */
        calc(var(--goy, 0px) + var(--gy, 0px) + 26px)
      )
      rotate(var(--grot, 0deg))
      scale(0.85);
  }
}

/* ---- trail mote: 魔法の杖の軌跡を構成する微細粒 ----
   起点 (--tx, --ty) にその場でふわっと現れ、drift ベクトル (--tdx, --tdy) 方向へ
   ゆっくり漂いながら 2 回ほど明滅して消える。gold-dust.js が付与する gdelay で
   粒ごとに出現時刻をずらし、帯（トレイル）が path に沿って流れて見える。
   transform / opacity のみ（compositor フレンドリー）。 */
.gold-dust--mote {
  animation:
    gold-trail-mote var(--gdur, 0.9s) var(--ease-out-expo) var(--gdelay, 0s) both;
}

@keyframes gold-trail-mote {
  0% {
    opacity: 0;
    transform:
      translate(var(--tx, 0px), var(--ty, 0px))
      rotate(0deg)
      scale(0.4);
  }
  /* 出現は早めに完了（ふわっと点灯） */
  16% {
    opacity: 1;
    transform:
      translate(
        calc(var(--tx, 0px) + var(--tdx, 0px) * 0.18),
        calc(var(--ty, 0px) + var(--tdy, 0px) * 0.18)
      )
      rotate(calc(var(--grot, 0deg) * 0.2))
      scale(1);
  }
  /* 明滅 1 回目の谷 */
  40% {
    opacity: 0.55;
    transform:
      translate(
        calc(var(--tx, 0px) + var(--tdx, 0px) * 0.45),
        calc(var(--ty, 0px) + var(--tdy, 0px) * 0.45)
      )
      rotate(calc(var(--grot, 0deg) * 0.5))
      scale(0.95);
  }
  /* 明滅 2 回目の山（漂いながら再び瞬く） */
  62% {
    opacity: 0.9;
    transform:
      translate(
        calc(var(--tx, 0px) + var(--tdx, 0px) * 0.68),
        calc(var(--ty, 0px) + var(--tdy, 0px) * 0.68)
      )
      rotate(calc(var(--grot, 0deg) * 0.72))
      scale(0.9);
  }
  100% {
    opacity: 0;
    transform:
      translate(
        calc(var(--tx, 0px) + var(--tdx, 0px)),
        calc(var(--ty, 0px) + var(--tdy, 0px))
      )
      rotate(var(--grot, 0deg))
      scale(0.7);
  }
}

/* ---- ambient: 起点の横位置(--gxr)から、ゆらゆら上昇/落下しながら明滅 ---- */
.gold-dust--ambient {
  left: var(--gxr, 50%);
}

/* 下から上へゆっくり上昇（translateY のマイナス方向） */
.gold-dust--rise {
  bottom: 0;
  top: auto;
  animation:
    gold-rise var(--gdur, 6s) var(--ease-in-out-soft) var(--gdelay, 0s) forwards;
}

@keyframes gold-rise {
  0% {
    opacity: 0;
    transform: translate(0, 0) rotate(0deg) scale(0.7);
  }
  15% {
    opacity: 0.9;
  }
  50% {
    /* 左右にゆっくり揺れる（sway 振幅）+ 中間で少し大きく */
    opacity: 0.7;
    transform:
      translate(var(--gsway, 8px), -46%) rotate(calc(var(--grot, 0deg) * 0.5)) scale(1);
  }
  85% {
    opacity: 0.4;
  }
  100% {
    opacity: 0;
    transform:
      translate(calc(var(--gsway, 8px) * -0.6), -100%) rotate(var(--grot, 0deg)) scale(0.8);
  }
}

/* 上から下へ舞い落ちる（translateY のプラス方向） */
.gold-dust--fall {
  top: 0;
  animation:
    gold-fall var(--gdur, 6s) var(--ease-in-out-soft) var(--gdelay, 0s) forwards;
}

@keyframes gold-fall {
  0% {
    opacity: 0;
    transform: translate(0, 0) rotate(0deg) scale(0.7);
  }
  15% {
    opacity: 0.85;
  }
  50% {
    opacity: 0.65;
    transform:
      translate(var(--gsway, 8px), 46%) rotate(calc(var(--grot, 0deg) * 0.5)) scale(1);
  }
  85% {
    opacity: 0.35;
  }
  100% {
    opacity: 0;
    transform:
      translate(calc(var(--gsway, 8px) * -0.6), 100%) rotate(var(--grot, 0deg)) scale(0.8);
  }
}

/* prefers-reduced-motion: reduce ----
   モーション低減設定のユーザーには、全アニメーション・トランジションを
   実質的に無効化する（即時状態へスキップ）。
   shuffle.js 側は animationend/transitionend の待機を伴うため、
   duration を極小化することで「即結果表示」に相当する挙動にする。 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  /* 静的表示の保証: 演出用カードが opacity:0 / ずれた transform のまま
     取り残されないよう、reduce 時は常に整列・可視の最終状態にする。 */
  .shuffle-stack {
    display: none !important;
  }

  .shuffle-grid .deal-card,
  .shuffle-grid .card {
    opacity: 1 !important;
    transform: none !important;
  }

  /* 項目6: 段階フェードインは reduce 時、即時に最終状態（可視）へ。
     PNG 保存の前提（opacity:1 確定）も同時に満たす。 */
  .reveal-block {
    opacity: 1 !important;
    animation: none !important;
  }

  /* 金粉パーティクルは JS 側で spawn しないが、二重で安全に無効化する。
     万一 span が残っても不可視にし、レイアウトへ影響させない。 */
  .gold-dust {
    display: none !important;
    animation: none !important;
    opacity: 0 !important;
  }

  /* 渦 canvas は reduced-motion では start() が何もせず生成されないが、
     万一残っても不可視にして安全側へ倒す。 */
}
