/* Engagement layer: answer feedback, floating points and micro-motion.
   Only transform and opacity are animated; every duration stays <= 450 ms.
   Never sets outline; focus styling stays in ui-overhaul.css. */

@keyframes fxPop {
  0% { transform: scale(1); }
  40% { transform: scale(1.06); }
  100% { transform: scale(1); }
}

@keyframes fxShake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(3px); }
}

@keyframes fxFloatUp {
  0% { opacity: 0; transform: translate(-50%, 0) scale(.9); }
  15% { opacity: 1; transform: translate(-50%, -8px) scale(1.05); }
  100% { opacity: 0; transform: translate(-50%, -56px) scale(1); }
}

@keyframes fxFeedbackIn {
  0% { opacity: 0; transform: translateY(8px) scale(.98); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes fxCardIn {
  0% { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

@keyframes fxScoreBump {
  0% { transform: scale(1); }
  35% { transform: scale(1.18); }
  100% { transform: scale(1); }
}

@keyframes fxStreakPulse {
  0% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.12); opacity: .85; }
  100% { transform: scale(1); opacity: 1; }
}

@keyframes fxStarIn {
  0% { opacity: 0; transform: scale(.4) rotate(-25deg); }
  70% { opacity: 1; transform: scale(1.15) rotate(6deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}

@keyframes fxTimerUrgent {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.06); opacity: .8; }
}

.fx-correct { animation: fxPop 320ms cubic-bezier(.2, .9, .3, 1.3); }
.fx-wrong { animation: fxShake 360ms ease-in-out; }
.fx-shake { animation: fxShake 360ms ease-in-out; }
.fx-feedback-in { animation: fxFeedbackIn 260ms ease-out; }
.fx-card-in { animation: fxCardIn 300ms ease-out; }
.fx-score-bump { animation: fxScoreBump 300ms ease-out; }
.fx-streak-pulse { animation: fxStreakPulse 400ms ease-in-out; }
.fx-star-in { animation: fxStarIn 420ms cubic-bezier(.2, .9, .3, 1.2) both; }
.fx-timer-urgent { animation: fxTimerUrgent 450ms ease-in-out infinite; }
.status-chip--timer.is-urgent { animation: fxTimerUrgent 450ms ease-in-out infinite; }
/* The wrong-answer flash must visibly win over the urgent pulse when a timeout fires while the chip is still urgent. */
.status-chip--timer.is-urgent.fx-wrong { animation: fxShake 360ms ease-in-out; }

/* Streak escalation: subtle scale steps by level, transform/opacity only. */
.fx-streak { display: inline-block; transform: scale(1.08); }
.fx-streak[data-level="4"] { transform: scale(1.12); }
.fx-streak[data-level="5"] { transform: scale(1.16); }
.fx-streak.fx-streak-pulse { animation: fxStreakPulse 400ms ease-in-out; }

/* Results celebration: staggered stars and burst pop. */
.fx-results-in .result-stars span.is-earned { animation: fxStarIn 420ms cubic-bezier(.2, .9, .3, 1.2) both; }
.fx-results-in .result-stars span.is-earned:nth-child(1) { animation-delay: 0ms; }
.fx-results-in .result-stars span.is-earned:nth-child(2) { animation-delay: 150ms; }
.fx-results-in .result-stars span.is-earned:nth-child(3) { animation-delay: 300ms; }
.fx-results-in .results-burst { animation: fxPop 420ms cubic-bezier(.2, .9, .3, 1.3); }

#gameShell { position: relative; }

.fx-float {
  position: absolute;
  z-index: 30;
  pointer-events: none;
  color: var(--primary-dark, #3f3db4);
  font-family: var(--font-display, inherit);
  font-size: clamp(20px, 2.4vw, 30px);
  font-weight: 800;
  text-shadow: 0 2px 0 #fff, 0 0 12px rgba(255, 255, 255, .9);
  white-space: nowrap;
  animation: fxFloatUp 450ms ease-out forwards;
}

.round-button.is-off,
.icon-button.is-off {
  opacity: .55;
  filter: grayscale(1);
}

/* Keep the muted look, but the opacity/grayscale must not wash out the focus ring. */
.round-button.is-off:focus-visible,
.icon-button.is-off:focus-visible {
  opacity: 1;
  filter: none;
}

/* body.motion-on marks an explicit user opt-in to motion that must override the OS preference. */
@media (prefers-reduced-motion: reduce) {
  body:not(.motion-on) .fx-correct,
  body:not(.motion-on) .fx-wrong,
  body:not(.motion-on) .fx-shake,
  body:not(.motion-on) .fx-feedback-in,
  body:not(.motion-on) .fx-card-in,
  body:not(.motion-on) .fx-score-bump,
  body:not(.motion-on) .fx-streak-pulse,
  body:not(.motion-on) .fx-star-in,
  body:not(.motion-on) .fx-timer-urgent,
  body:not(.motion-on) .status-chip--timer.is-urgent,
  body:not(.motion-on) .fx-results-in .result-stars span.is-earned,
  body:not(.motion-on) .fx-results-in .results-burst,
  body:not(.motion-on) .fx-float { animation: none; }
  body:not(.motion-on) .fx-streak,
  body:not(.motion-on) .fx-streak[data-level="4"],
  body:not(.motion-on) .fx-streak[data-level="5"] { transform: none; }
  body:not(.motion-on) .fx-float { display: none; }
}

body.reduced-motion .fx-correct,
body.reduced-motion .fx-wrong,
body.reduced-motion .fx-shake,
body.reduced-motion .fx-feedback-in,
body.reduced-motion .fx-card-in,
body.reduced-motion .fx-score-bump,
body.reduced-motion .fx-streak-pulse,
body.reduced-motion .fx-star-in,
body.reduced-motion .fx-timer-urgent,
body.reduced-motion .status-chip--timer.is-urgent,
body.reduced-motion .fx-results-in .result-stars span.is-earned,
body.reduced-motion .fx-results-in .results-burst,
body.reduced-motion .fx-float { animation: none; }
body.reduced-motion .fx-streak,
body.reduced-motion .fx-streak[data-level="4"],
body.reduced-motion .fx-streak[data-level="5"] { transform: none; }
body.reduced-motion .fx-float { display: none; }
