/* formations.css — ambient letter-cluster shape motions.
   Each formation gets its own clearly-marked block so any one can be tweaked
   (speed, amplitude, easing) without disturbing the others.

   The position of each tile (--tx / --ty) is set by formations.js. The CSS
   transition on `translate` already in tiles.css (2.0s cubic-bezier) handles
   the smooth glide between shapes — nothing here overrides it.

   Ambient per-formation motion is layered as:
     - cluster animation: whole-cluster scaling (heart pulse)
     - per-tile animation: relative offset on top of --tx/--ty using
       transform: translate(...) on the tile (separate from `translate:`)
   `transform` and `translate` are different properties, so the two compose
   without fighting each other. */


/* ============================================================
   FORMATION: HEART — heartbeat pulse on the whole cluster.
   Two-beat lub-dub at ~60bpm. Whole cluster scales together so the heart
   shape stays intact while it breathes.
   Tweak: amplitude in the scale() values, rhythm in the % stops.
   ============================================================ */
.tile-cluster.formation-heart {
  animation: lineSway 16s ease-in-out infinite alternate,
             heartbeat 1.0s ease-in-out infinite;
  transform-origin: 50% 50%;
}
/* Use the standalone `scale` property (independent of transform) so the
   heartbeat composes with lineSway's translateX instead of overriding it. */
@keyframes heartbeat {
  0%   { scale: 1.00; }
  12%  { scale: 1.07; }   /* lub */
  24%  { scale: 1.01; }
  36%  { scale: 1.04; }   /* dub */
  48%  { scale: 1.00; }
  100% { scale: 1.00; }   /* rest fills the second half */
}


/* ============================================================
   FORMATION: SMILEY — slow blink on eye letters only.
   Mouth letters stay still. Each eye blinks once every ~12s; the blink
   itself stays quick (~0.4s) so it reads as a blink, not a slow squint.
   Tweak: blink interval in animation-duration; squash depth in scaleY.
   ============================================================ */
.tile-cluster.formation-smiley .tile.eye-letter {
  animation: blinkEye 12s ease-in-out infinite;
  /* Stagger eyes by half a cycle so only one closes at a time → reads as a
     wink, not a double-blink. --formation-i is 0 for the left eye, 1 for the
     right (set by the layout loop). */
  animation-delay: calc(var(--formation-i, 0) * -6s);
}
@keyframes blinkEye {
  0%, 96.5%, 100% { transform: scaleY(1); }
  98%             { transform: scaleY(0.08); }   /* eyelid down */
  99.5%           { transform: scaleY(1); }       /* eyelid back up */
}


/* ============================================================
   FORMATION: WAVE — small vertical bob, staggered left-to-right.
   Each tile uses --formation-i (its index) as a phase offset, so the bob
   travels through the row like a swell rolling through.
   Tweak: amplitude in the translateY values; speed in animation-duration.
   ============================================================ */
.tile-cluster.formation-wave .tile {
  animation: waveBob 2.4s ease-in-out infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.15s);
}
@keyframes waveBob {
  0%, 100% { transform: translateY(0); }
  25%      { transform: translateY(-9px); }
  50%      { transform: translateY(0); }
  75%      { transform: translateY(9px); }
}


/* ============================================================
   FORMATION: RAIN — dampened. Each letter drifts a few pixels down,
   fades slightly, and resets. Reads as "rain falling on the row" rather
   than "letters falling like rain" — gameplay-friendly motion.
   Tweak: drop distance in translateY; cycle speed in animation-duration.
   ============================================================ */
.tile-cluster.formation-rain .tile {
  animation: rainDrop 2.6s ease-in infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.27s);
}
@keyframes rainDrop {
  0%   { transform: translateY(-4px); opacity: 1; }
  60%  { transform: translateY(7px);  opacity: 0.85; }
  85%  { transform: translateY(8px);  opacity: 0.4; }
  86%  { transform: translateY(-4px); opacity: 0; }
  100% { transform: translateY(-4px); opacity: 1; }
}


/* ============================================================
   FORMATION: EVAPORATION — dampened. Letters drift slowly upward with
   a tiny horizontal waver, opacity dipping near the top before resetting.
   Slower than rain so it reads as steam rising rather than raindrops.
   Tweak: rise distance in translateY/translateX; cycle in animation-duration.
   ============================================================ */
.tile-cluster.formation-evaporation .tile {
  animation: evapRise 5.2s ease-in-out infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.5s);
}
@keyframes evapRise {
  0%   { transform: translate(0, 4px);   opacity: 1; }
  35%  { transform: translate(2px, -3px); opacity: 0.95; }
  65%  { transform: translate(-2px, -7px); opacity: 0.7; }
  85%  { transform: translate(1px, -8px); opacity: 0.35; }
  86%  { transform: translate(0, 4px);   opacity: 0; }
  100% { transform: translate(0, 4px);   opacity: 1; }
}


/* ============================================================
   FORMATION: LINE — explicit "no extra motion" rest state.
   Cluster keeps its existing lineSway from tiles.css; tiles get nothing
   added on top. Rule kept here so the formation set is symmetric and any
   previously-applied per-tile transform animation is cleared.
   ============================================================ */
.tile-cluster.formation-line .tile {
  animation: none;
}


/* The selected tile (currently in the input word) is locked deep blue and
   "frozen" — kill any formation animation on it so it doesn't bob away
   from the player's tap. Matches existing .tile.selected behavior. */
.tile-cluster .tile.selected {
  animation: none !important;
}
/* The tap-bounce + tilt is the player feedback for "you just typed me" — it
   has to win over the .selected animation kill above so the tilt actually
   plays, since appendLetter immediately marks the tile as selected. */
.tile-cluster .tile.selected.pop {
  animation: tilePop 0.25s ease-out !important;
}


/* ============================================================
   FORMATION: CIRCLE — letters sit on a full ring, the cluster slowly swirls
   so each letter orbits the center. Tiles counter-rotate around their own
   axis so the glyph stays upright as it travels.
   Tweak: clusterSwirl duration → faster/slower orbit. Counter MUST match.
   ============================================================ */
.tile-cluster.formation-circle {
  animation: lineSway 16s ease-in-out infinite alternate,
             clusterSwirl 32s linear infinite;
}
@keyframes clusterSwirl {
  from { rotate: 0deg; }
  to   { rotate: 360deg; }
}
.tile-cluster.formation-circle .tile {
  animation: tileCounterSwirl 32s linear infinite;
}
@keyframes tileCounterSwirl {
  from { rotate: 0deg; }
  to   { rotate: -360deg; }
}


/* ============================================================
   FORMATION: EQUALS — two horizontal rows. Subtle crossfade-style
   shimmer so it's not entirely static.
   ============================================================ */
.tile-cluster.formation-equals .tile {
  animation: equalsShimmer 4.4s ease-in-out infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.18s);
}
@keyframes equalsShimmer {
  0%, 100% { transform: translateY(0);    opacity: 1;    }
  50%      { transform: translateY(-2px); opacity: 0.92; }
}


/* ============================================================
   FORMATION: HORSESHOE-UP / HORSESHOE-DOWN — letters arc along a
   semicircle. Gentle bob in the open direction so the shape feels alive.
   ============================================================ */
.tile-cluster.formation-horseshoe-up .tile {
  animation: horseshoeBobUp 3.2s ease-in-out infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.20s);
}
@keyframes horseshoeBobUp {
  0%, 100% { transform: translateY(0);   }
  50%      { transform: translateY(-5px); }
}
.tile-cluster.formation-horseshoe-down .tile {
  animation: horseshoeBobDown 3.2s ease-in-out infinite;
  animation-delay: calc(var(--formation-i, 0) * -0.20s);
}
@keyframes horseshoeBobDown {
  0%, 100% { transform: translateY(0);   }
  50%      { transform: translateY(5px);  }
}
