/* Card play animation */
.card.anim-play {
  animation: card-play 0.4s ease;
}

@keyframes card-play {
  0% { opacity: 0; transform: scale(0.5) translateY(20px); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

/* Card draw animation.
 *
 * Two non-obvious bits:
 *   (1) `transform: none` is explicit at 100%. Omitting it makes the
 *       100% target the *underlying* transform, which is dynamic — hover
 *       (`.card:hover`) and DragController's `.preview-focused` both
 *       contribute a transform. If those toggle mid-animation the 100%
 *       target shifts under our feet and a one-frame jump appears when
 *       `.anim-draw` is later removed. Pinning to `none` makes the
 *       end-state predictable and matches the no-hover underlying so
 *       class removal is a no-op for transform.
 *   (2) `transition: none` overrides `.card`'s `transition: all 0.2s`
 *       for the lifetime of the animation. While `.anim-draw` is on,
 *       any other class flip (Renderer's `.playable` re-evaluation on
 *       hover, `.support-source` highlights, etc.) can't kick off a
 *       parallel 200 ms transition that races the 300 ms keyframe.
 *       Once the class is removed at `animationend`, the underlying
 *       `.card` transition resumes — but at that point the computed
 *       style equals the underlying style (per (1)), so removing the
 *       class doesn't trigger a transition either. */
.card.anim-draw {
  animation: card-draw 0.3s ease forwards;
  transition: none;
}

@keyframes card-draw {
  0%   { opacity: 0; transform: translateX(30px); }
  100% { opacity: 1; transform: none; }
}

/* Hidden placeholder for ability-drawn cards while the banner plays. The
 * card is in hand DOM (so layout is already stable when it reveals) but
 * invisible until the post-banner stagger reveals it with .anim-draw.
 *
 * `transition: none` overrides .card's `transition: all 0.2s ease`. Without
 * this, adding the class fades the card over 200ms instead of hiding it
 * instantly — and any setTimeout that fires mid-fade then snaps the
 * opacity back to 0 via the .anim-draw keyframe, which reads as a flicker
 * (especially with longer stagger delays). Instant hide ensures the card
 * is fully invisible by the time the staggered animation kicks in. */
.card.deferred-draw {
  opacity: 0;
  pointer-events: none;
  transition: none;
}

/* Attack swipe */
/* Attack swipe — player leans up (forward = toward opponent), opponent
   leans down. Keyframes account for the -25%/+25% baseline set by
   `.attacker` so the card doesn't snap back to zero mid-animation. */
.ring-area[data-side="player"] .card.anim-attack {
  animation: attack-swipe-player 0.4s ease;
}

.ring-area[data-side="opponent"] .card.anim-attack {
  animation: attack-swipe-opponent 0.4s ease;
}

@keyframes attack-swipe-player {
  0%   { transform: translateY(-25%); }
  40%  { transform: translateY(calc(-25% - 20px)); }
  100% { transform: translateY(-25%); }
}

@keyframes attack-swipe-opponent {
  0%   { transform: translateY(25%); }
  40%  { transform: translateY(calc(25% + 20px)); }
  100% { transform: translateY(25%); }
}

/* KO card placeholder — re-inserted into its old slot during the freeze
 * window so the player can see what's about to die behind the banner.
 * Disables tooltips / drag while it's visibly doomed.  */
.card.pending-ko {
  pointer-events: none;
}

/* Card fade out (KO/discard) */
.card.anim-ko {
  animation: card-ko 0.5s ease forwards;
}

@keyframes card-ko {
  0% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(1.1); filter: brightness(2); }
  100% { opacity: 0; transform: scale(0.5); }
}

/* Spirit change pulse */
.spirit-display.anim-change {
  animation: spirit-pulse 0.3s ease;
}

@keyframes spirit-pulse {
  0% { transform: scale(1); }
  50% { transform: scale(1.3); }
  100% { transform: scale(1); }
}

/* HP change flash */
.hp-bar-container.anim-damage {
  animation: hp-flash 0.3s ease;
}

@keyframes hp-flash {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.5); }
}

/* Freeze effect */
.card.anim-exhaust {
  animation: exhaust-flash 0.4s ease;
}

@keyframes exhaust-flash {
  0% { filter: brightness(1); }
  50% { filter: brightness(1.5) hue-rotate(180deg); }
  100% { filter: brightness(1); }
}

/* Turn transition */
.turn-transition {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 24px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 2px;
  color: var(--text-primary);
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
  z-index: 500;
  animation: turn-fade 1.2s ease forwards;
  pointer-events: none;
}

@keyframes turn-fade {
  0% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }
  20% { opacity: 1; transform: translate(-50%, -50%) scale(1.1); }
  70% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1); }
}

/* ── Action-card play ceremony ─────────────────────────────────────
 * Center-screen reveal that fires when an action card resolves. The
 * card itself never lives on the board, so without this the only signal
 * a play happened is whatever flyaway effects follow — opponents are
 * left puzzled. The host wraps a real `.card` element built by
 * createCardElement, sized up via --card-w/--card-h overrides and
 * scaled-text rules that mirror the .card-detail-modal pattern. */
.card-ceremony-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 600;
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.15) 70%, rgba(0, 0, 0, 0) 100%);
  animation: ceremony-backdrop-in 0.22s ease forwards;
  cursor: pointer;
}

.card-ceremony-overlay.dismissing {
  animation: ceremony-backdrop-out 0.3s ease forwards;
}

.card-ceremony-host {
  /* Sized up from the in-grid 130×195 baseline. Sits comfortably on a
     phone in landscape and on a desktop board without crowding either. */
  --card-w: min(260px, 60vw);
  --card-h: calc(var(--card-w) * 1.5);
  --modal-scale: calc(var(--card-w) / 110);
  display: inline-flex;
  animation: ceremony-card-in 0.45s cubic-bezier(0.2, 1.3, 0.4, 1) forwards;
  filter: drop-shadow(0 18px 40px rgba(0, 0, 0, 0.65));
}

.card-ceremony-overlay.dismissing .card-ceremony-host {
  animation: ceremony-card-out 0.3s ease forwards;
}

/* Disable the in-game hover-lift transform — the ceremony card should
   sit still while the player reads it. */
.card-ceremony-host .card { cursor: default; }
.card-ceremony-host .card:hover { transform: none; box-shadow: none; }

/* Scale text + badges proportional to --modal-scale, matching the
   .card-detail-modal rules. Same baseline of 110px / scale=1. */
.card-ceremony-host .card                    { font-size: calc(9px  * var(--modal-scale)); }
.card-ceremony-host .card .card-name         { font-size: calc(10px * var(--modal-scale)); }
.card-ceremony-host .card .card-power        { font-size: calc(15px * var(--modal-scale)); }
.card-ceremony-host .card .card-armor        { font-size: calc(10px * var(--modal-scale)); }
.card-ceremony-host .card .card-spirit       { font-size: calc(12px * var(--modal-scale)); }
.card-ceremony-host .card .card-clock        { font-size: calc(12px * var(--modal-scale)); }
.card-ceremony-host .card .card-ability-icon { font-size: calc(12px * var(--modal-scale)); }
.card-ceremony-host .card .card-ability-text { font-size: calc(8px  * var(--modal-scale)); }

@keyframes ceremony-backdrop-in  { from { opacity: 0; } to   { opacity: 1; } }
@keyframes ceremony-backdrop-out { from { opacity: 1; } to   { opacity: 0; } }

@keyframes ceremony-card-in {
  0%   { opacity: 0; transform: scale(0.4) rotate(-6deg); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: scale(1)   rotate(0); }
}

@keyframes ceremony-card-out {
  0%   { opacity: 1; transform: scale(1)    translateY(0); }
  100% { opacity: 0; transform: scale(0.85) translateY(-24px); }
}

/* ── Character on-play ability banner ──────────────────────────────
 * A horizontal callout that fires when a character's on-play ability
 * triggers. The character itself is already on the board, so the
 * banner is the lighter-weight cousin of the full action-card
 * ceremony — no backdrop dim, no full-card render — just an art
 * thumbnail anchoring the connection plus name + effect text. */
.ability-banner-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 22dvh;
  z-index: 600;
  pointer-events: auto;
  cursor: pointer;
}

/* No backdrop dim by default — the banner annotates, doesn't take over.
   The empty area above/below the banner stays click-through to skip. */
.ability-banner-overlay { background: transparent; }

/* Click-through mode: applied for player-initiated banners (their own
   PLAY_CHARACTER / ACTIVATE_ABILITY). The full-screen overlay no longer
   intercepts pointer events, so follow-up actions (playing an action
   card, staging an attack, ending the turn) register immediately while
   the banner is still on screen. The banner box itself keeps
   pointer-events: auto so a tap on the banner still dismisses early. */
.ability-banner-overlay.click-through { pointer-events: none; }
.ability-banner-overlay.click-through .ability-banner { pointer-events: auto; }

.ability-banner {
  display: flex;
  align-items: center;
  gap: 14px;
  width: min(480px, 92vw);
  padding: 12px 18px 12px 12px;
  background: linear-gradient(180deg, rgba(22, 32, 48, 0.96) 0%, rgba(14, 22, 36, 0.96) 100%);
  border: 1px solid rgba(120, 170, 220, 0.55);
  border-radius: 10px;
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4) inset;
  animation: ability-banner-in 0.32s cubic-bezier(0.2, 1.2, 0.4, 1) forwards;
}

.ability-banner-overlay.dismissing .ability-banner {
  animation: ability-banner-out 0.25s ease forwards;
}

.ability-banner-art {
  flex: 0 0 auto;
  width: 72px;
  height: 72px;
  border-radius: 6px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}

.ability-banner-art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ability-banner-art-fallback {
  font-size: 22px;
  font-weight: 700;
  color: rgba(220, 235, 255, 0.85);
  letter-spacing: 1px;
}

.ability-banner-text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ability-banner-name {
  font-size: 16px;
  font-weight: 700;
  color: #f4f8ff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.ability-banner-desc {
  font-size: 13px;
  line-height: 1.35;
  color: rgba(220, 232, 246, 0.95);
}

@keyframes ability-banner-in {
  0%   { opacity: 0; transform: translateY(-30px) scale(0.94); }
  100% { opacity: 1; transform: translateY(0)     scale(1); }
}

@keyframes ability-banner-out {
  0%   { opacity: 1; transform: translateY(0)     scale(1); }
  100% { opacity: 0; transform: translateY(-20px) scale(0.96); }
}
