/* ==========================================================================
   beebso-fx.css
   Reusable motion for the Beebso kids app.

   Nothing in this file references an image file, a word, a category or a
   character. Every effect is applied to whatever element is on screen at the
   time, so regenerating artwork can never break it.

   House rules kept: no drop shadows anywhere, 3px borders, no emojis.
   Motion marks events (tap, right, wrong, unlock). Only the companion
   character idles, and only gently.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Tunables. Change these, not the keyframes.
   -------------------------------------------------------------------------- */
:root {
  --fx-pop-time:       320ms;
  --fx-tap-time:       200ms;
  --fx-shake-time:     420ms;
  --fx-celebrate-time: 760ms;
  --fx-float-time:     3.2s;
  --fx-unlock-time:    620ms;

  /* Ring colour for the unlock burst. Override per category if you like. */
  --fx-ring-color: #2FB4A6;
}


/* --------------------------------------------------------------------------
   1. POP IN — a card or picture arriving on screen.
   -------------------------------------------------------------------------- */
@keyframes fx-pop-in {
  0%   { opacity: 0; transform: scale(0.62); }
  60%  { opacity: 1; transform: scale(1.08); }
  80%  {             transform: scale(0.97); }
  100% { opacity: 1; transform: scale(1); }
}

.fx-pop-in {
  animation: fx-pop-in var(--fx-pop-time) cubic-bezier(0.34, 1.56, 0.64, 1) both;
}


/* --------------------------------------------------------------------------
   2. TAP SQUISH — the child presses the picture. Squash, then settle.
   -------------------------------------------------------------------------- */
@keyframes fx-tap-squish {
  0%   { transform: scale(1, 1); }
  35%  { transform: scale(0.90, 0.86); }
  70%  { transform: scale(1.05, 1.03); }
  100% { transform: scale(1, 1); }
}

.fx-tap-squish {
  animation: fx-tap-squish var(--fx-tap-time) ease-out both;
}


/* --------------------------------------------------------------------------
   3. SHAKE — the attempt did not land. Short and soft on purpose.
      A three-year-old should read this as "again", not as "no".
   -------------------------------------------------------------------------- */
@keyframes fx-shake {
  0%   { transform: translateX(0); }
  15%  { transform: translateX(-7px) rotate(-1.5deg); }
  30%  { transform: translateX(7px)  rotate(1.5deg); }
  45%  { transform: translateX(-5px) rotate(-1deg); }
  60%  { transform: translateX(5px)  rotate(1deg); }
  80%  { transform: translateX(-2px); }
  100% { transform: translateX(0); }
}

.fx-shake {
  animation: fx-shake var(--fx-shake-time) ease-in-out both;
}


/* --------------------------------------------------------------------------
   4. CELEBRATE — the attempt landed. Two hops with a wiggle.
      Transform origin sits at the feet so cut-outs hop off the ground
      rather than drifting from their middle.
   -------------------------------------------------------------------------- */
@keyframes fx-celebrate {
  0%   { transform: translateY(0)     scale(1, 1)       rotate(0deg); }
  15%  { transform: translateY(0)     scale(1.06, 0.92) rotate(0deg); }
  35%  { transform: translateY(-26px) scale(0.96, 1.08) rotate(-5deg); }
  50%  { transform: translateY(0)     scale(1.06, 0.94) rotate(0deg); }
  68%  { transform: translateY(-13px) scale(0.98, 1.04) rotate(4deg); }
  85%  { transform: translateY(0)     scale(1.03, 0.97) rotate(0deg); }
  100% { transform: translateY(0)     scale(1, 1)       rotate(0deg); }
}

.fx-celebrate {
  transform-origin: 50% 100%;
  animation: fx-celebrate var(--fx-celebrate-time) cubic-bezier(0.28, 0.84, 0.42, 1) both;
}


/* --------------------------------------------------------------------------
   5. FLOAT — slow idle breathing. Companion character only.
      Do not put this on word pictures; constant motion competes with
      listening and speaking, which is the thing you actually want happening.
   -------------------------------------------------------------------------- */
@keyframes fx-float {
  0%   { transform: translateY(0)    rotate(0deg); }
  50%  { transform: translateY(-8px) rotate(1.2deg); }
  100% { transform: translateY(0)    rotate(0deg); }
}

.fx-float {
  animation: fx-float var(--fx-float-time) ease-in-out infinite;
}


/* --------------------------------------------------------------------------
   6. UNLOCK — a new Dinaurpion is earned. The character scales up out of
      nothing while a ring expands past it. The ring is injected by the JS.
   -------------------------------------------------------------------------- */
@keyframes fx-unlock-pop {
  0%   { opacity: 0; transform: scale(0.2) rotate(-14deg); }
  55%  { opacity: 1; transform: scale(1.14) rotate(5deg); }
  75%  {             transform: scale(0.96) rotate(-2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

.fx-unlock {
  animation: fx-unlock-pop var(--fx-unlock-time) cubic-bezier(0.3, 1.5, 0.5, 1) both;
}

@keyframes fx-ring-out {
  0%   { opacity: 0.95; transform: translate(-50%, -50%) scale(0.35); }
  100% { opacity: 0;    transform: translate(-50%, -50%) scale(1.9); }
}

/* Injected by BeebsoFX.unlock(). Sits behind the character. */
.fx-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  padding-bottom: 100%;
  height: 0;
  border: 3px solid var(--fx-ring-color);
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
  animation: fx-ring-out 620ms ease-out forwards;
}

.fx-ring--late {
  animation-delay: 130ms;
  opacity: 0;
}


/* --------------------------------------------------------------------------
   7. STAR POP — the star that lands after the child speaks.
   -------------------------------------------------------------------------- */
@keyframes fx-star-pop {
  0%   { opacity: 0; transform: scale(0.1) rotate(-40deg); }
  55%  { opacity: 1; transform: scale(1.35) rotate(8deg); }
  75%  {             transform: scale(0.92) rotate(-3deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}

.fx-star-pop {
  animation: fx-star-pop 460ms cubic-bezier(0.3, 1.6, 0.5, 1) both;
}


/* --------------------------------------------------------------------------
   8. LISTENING — a calm pulse while the microphone is open, so the child
      can see the app is waiting for them. Stops the moment recording ends.
   -------------------------------------------------------------------------- */
@keyframes fx-listening {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.07); }
  100% { transform: scale(1); }
}

.fx-listening {
  animation: fx-listening 1s ease-in-out infinite;
}


/* --------------------------------------------------------------------------
   Accessibility. Some children are motion sensitive, and iPads get set to
   Reduce Motion in classrooms. Effects still fire, they just resolve
   instantly, so nothing in the game logic has to change.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .fx-pop-in,
  .fx-tap-squish,
  .fx-shake,
  .fx-celebrate,
  .fx-float,
  .fx-unlock,
  .fx-ring,
  .fx-star-pop,
  .fx-listening {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* --------------------------------------------------------------------------
   Word-card safe variants. Transform only — these never touch opacity, so
   they cannot fight .word-icon.swap or the content-dim fade.
   -------------------------------------------------------------------------- */
@keyframes fx-pop-scale {
  0%   { transform: scale(0.72); }
  60%  { transform: scale(1.07); }
  80%  { transform: scale(0.98); }
  100% { transform: scale(1); }
}

.fx-pop-scale {
  animation: fx-pop-scale 340ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .fx-pop-scale { animation-duration: 1ms !important; }
}

/* --------------------------------------------------------------------------
   Tap squish, second pass. Three bounces instead of one, deeper squash, and
   long enough that a four-year-old actually registers it. Origin at the base
   so the picture presses down rather than shrinking in place.
   -------------------------------------------------------------------------- */
@keyframes fx-tap-squish {
  0%   { transform: scale(1, 1); }
  12%  { transform: scale(1.14, 0.80); }
  24%  { transform: scale(0.92, 1.12); }
  40%  { transform: scale(1.10, 0.86); }
  54%  { transform: scale(0.95, 1.08); }
  70%  { transform: scale(1.06, 0.92); }
  84%  { transform: scale(0.98, 1.03); }
  100% { transform: scale(1, 1); }
}

.fx-tap-squish {
  transform-origin: 50% 100%;
  animation: fx-tap-squish 620ms cubic-bezier(0.4, 0.1, 0.3, 1) both;
}
