/**
 * Complex Chem Quest - Animations & Transitions
 * Smooth animations for local co-op gameplay
 */

/* ============================================
   DICE ANIMATIONS
   ============================================ */

@keyframes dice-roll {
  0% {
    transform: rotate(0deg) scale(1);
  }
  25% {
    transform: rotate(90deg) scale(1.1);
  }
  50% {
    transform: rotate(180deg) scale(1.2);
  }
  75% {
    transform: rotate(270deg) scale(1.1);
  }
  100% {
    transform: rotate(360deg) scale(1);
  }
}

@keyframes dice-shake {
  0%, 100% {
    transform: translateX(0) rotate(0deg);
  }
  10%, 30%, 50%, 70%, 90% {
    transform: translateX(-5px) rotate(-5deg);
  }
  20%, 40%, 60%, 80% {
    transform: translateX(5px) rotate(5deg);
  }
}

.dice-rolling {
  animation: dice-roll 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.dice-shake {
  animation: dice-shake 0.5s ease-in-out;
}

/* ============================================
   PIECE MOVEMENT
   ============================================ */

@keyframes piece-hop {
  0% {
    transform: translateY(0) scale(1);
  }
  30% {
    transform: translateY(-20px) scale(1.15);
  }
  50% {
    transform: translateY(-15px) scale(1.1);
  }
  70% {
    transform: translateY(-20px) scale(1.15);
  }
  100% {
    transform: translateY(0) scale(1);
  }
}

@keyframes piece-pulse {
  0%, 100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
  }
  50% {
    transform: scale(1.05);
    box-shadow: 0 0 0 10px rgba(255, 255, 255, 0);
  }
}

.piece-moving {
  animation: piece-hop 0.4s ease-in-out;
  z-index: 100 !important;
}

.piece-selectable {
  animation: piece-pulse 1.5s infinite;
  cursor: pointer;
  filter: brightness(1.2);
}

.piece-selectable:hover {
  transform: scale(1.1);
  filter: brightness(1.4);
}

/* ============================================
   MODAL ANIMATIONS
   ============================================ */

@keyframes modal-fade-in {
  from {
    opacity: 0;
    backdrop-filter: blur(0px);
  }
  to {
    opacity: 1;
    backdrop-filter: blur(4px);
  }
}

@keyframes modal-slide-up {
  from {
    transform: translateY(100px) scale(0.9);
    opacity: 0;
  }
  to {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

@keyframes modal-bounce-in {
  0% {
    transform: scale(0.3);
    opacity: 0;
  }
  50% {
    transform: scale(1.05);
  }
  70% {
    transform: scale(0.9);
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

.modal-backdrop {
  animation: modal-fade-in 0.3s ease-out;
}

.modal-content {
  animation: modal-bounce-in 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.modal-slide {
  animation: modal-slide-up 0.4s ease-out;
}

/* ============================================
   CARD FLIP ANIMATIONS
   ============================================ */

@keyframes card-flip {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(180deg);
  }
}

@keyframes card-flip-back {
  0% {
    transform: rotateY(180deg);
  }
  100% {
    transform: rotateY(0deg);
  }
}

.card-flipping {
  animation: card-flip 0.6s ease-in-out;
  transform-style: preserve-3d;
}

.card-flip-back {
  animation: card-flip-back 0.6s ease-in-out;
  transform-style: preserve-3d;
}

/* ============================================
   TURN INDICATOR ANIMATIONS
   ============================================ */

@keyframes turn-indicator-enter {
  0% {
    transform: translateX(-50%) translateY(-100px) scale(0.8);
    opacity: 0;
  }
  60% {
    transform: translateX(-50%) translateY(10px) scale(1.05);
  }
  100% {
    transform: translateX(-50%) translateY(0) scale(1);
    opacity: 1;
  }
}

@keyframes turn-glow {
  0%, 100% {
    box-shadow: 0 0 20px rgba(255, 255, 255, 0.5);
  }
  50% {
    box-shadow: 0 0 30px rgba(255, 255, 255, 0.8);
  }
}

.turn-indicator-show {
  animation: turn-indicator-enter 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.turn-glow {
  animation: turn-glow 2s ease-in-out infinite;
}

/* ============================================
   NOTIFICATION ANIMATIONS
   ============================================ */

@keyframes notification-slide-in {
  from {
    transform: translateX(400px);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes notification-slide-out {
  from {
    transform: translateX(0);
    opacity: 1;
  }
  to {
    transform: translateX(400px);
    opacity: 0;
  }
}

.notification-enter {
  animation: notification-slide-in 0.4s ease-out;
}

.notification-exit {
  animation: notification-slide-out 0.3s ease-in;
}

/* ============================================
   SUCCESS / ERROR FEEDBACK
   ============================================ */

@keyframes success-pop {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes error-shake {
  0%, 100% {
    transform: translateX(0);
  }
  10%, 30%, 50%, 70%, 90% {
    transform: translateX(-10px);
  }
  20%, 40%, 60%, 80% {
    transform: translateX(10px);
  }
}

@keyframes checkmark-draw {
  0% {
    stroke-dashoffset: 100;
  }
  100% {
    stroke-dashoffset: 0;
  }
}

.success-pop {
  animation: success-pop 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.error-shake {
  animation: error-shake 0.5s ease-in-out;
}

/* ============================================
   LOADING STATES
   ============================================ */

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes pulse {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
}

@keyframes dots {
  0%, 20% {
    content: '.';
  }
  40% {
    content: '..';
  }
  60%, 100% {
    content: '...';
  }
}

.loading-spin {
  animation: spin 1s linear infinite;
}

.loading-pulse {
  animation: pulse 1.5s ease-in-out infinite;
}

/* ============================================
   LIGAND COLLECTION CELEBRATION
   ============================================ */

@keyframes confetti-fall {
  0% {
    transform: translateY(-100vh) rotate(0deg);
    opacity: 1;
  }
  100% {
    transform: translateY(100vh) rotate(720deg);
    opacity: 0;
  }
}

@keyframes celebrate {
  0%, 100% {
    transform: scale(1);
  }
  25% {
    transform: scale(1.1) rotate(-5deg);
  }
  50% {
    transform: scale(1.2) rotate(5deg);
  }
  75% {
    transform: scale(1.1) rotate(-5deg);
  }
}

.confetti {
  animation: confetti-fall 3s ease-in forwards;
}

.celebrate {
  animation: celebrate 0.6s ease-in-out;
}

/* ============================================
   BUTTON INTERACTIONS
   ============================================ */

@keyframes button-press {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(0.95);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes button-glow {
  0%, 100% {
    box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
  }
  50% {
    box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
  }
}

.button-press {
  animation: button-press 0.2s ease-in-out;
}

.button-glow {
  animation: button-glow 1.5s ease-in-out infinite;
}

/* ============================================
   SCORE COUNTER ANIMATIONS
   ============================================ */

@keyframes count-up {
  0% {
    transform: translateY(20px) scale(0.8);
    opacity: 0;
  }
  50% {
    transform: translateY(-5px) scale(1.2);
  }
  100% {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
}

@keyframes points-float {
  0% {
    transform: translateY(0);
    opacity: 1;
  }
  100% {
    transform: translateY(-50px);
    opacity: 0;
  }
}

.count-up {
  animation: count-up 0.5s ease-out;
}

.points-float {
  animation: points-float 1s ease-out forwards;
}

/* ============================================
   PLAYER HIGHLIGHT (CURRENT TURN)
   ============================================ */

@keyframes player-highlight {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7);
    transform: scale(1);
  }
  50% {
    box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
    transform: scale(1.02);
  }
}

.player-active {
  animation: player-highlight 2s ease-in-out infinite;
  border: 3px solid #3b82f6;
}

/* ============================================
   SMOOTH TRANSITIONS (UTILITY)
   ============================================ */

.transition-smooth {
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.transition-bounce {
  transition: all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.transition-spring {
  transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* ============================================
   FADE UTILITIES
   ============================================ */

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

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

.fade-in {
  animation: fade-in 0.3s ease-in;
}

.fade-out {
  animation: fade-out 0.3s ease-out;
}

/* ============================================
   SCALE UTILITIES
   ============================================ */

.scale-in {
  animation: scale-in 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

@keyframes scale-in {
  from {
    transform: scale(0);
    opacity: 0;
  }
  to {
    transform: scale(1);
    opacity: 1;
  }
}

/* ============================================
   RESPONSIVE ADJUSTMENTS
   ============================================ */

@media (max-width: 640px) {
  @keyframes piece-hop {
    0% {
      transform: translateY(0) scale(1);
    }
    30% {
      transform: translateY(-10px) scale(1.1);
    }
    100% {
      transform: translateY(0) scale(1);
    }
  }

  .turn-glow {
    animation: turn-glow 2s ease-in-out infinite;
    box-shadow: 0 0 15px rgba(255, 255, 255, 0.5);
  }
}

/* ============================================
   QUESTION MODAL ANIMATIONS
   ============================================ */

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: scale(0.95);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes bounceIn {
  0% {
    opacity: 0;
    transform: scale(0.3) translateY(-50px);
  }
  50% {
    opacity: 1;
    transform: scale(1.05);
  }
  70% {
    transform: scale(0.9);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes shakeX {
  0%, 100% {
    transform: translateX(0);
  }
  10%, 30%, 50%, 70%, 90% {
    transform: translateX(-10px);
  }
  20%, 40%, 60%, 80% {
    transform: translateX(10px);
  }
}

@keyframes slideInLeft {
  from {
    opacity: 0;
    transform: translateX(-100px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

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

@keyframes progress {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}

/* Animation utility classes */
.animate-fadeIn {
  animation: fadeIn 0.3s ease-out;
}

.animate-bounceIn {
  animation: bounceIn 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.animate-shakeX {
  animation: shakeX 0.6s ease-in-out;
}

.animate-slideInLeft {
  animation: slideInLeft 0.4s ease-out;
}

.animate-slideIn {
  animation: slideIn 0.4s ease-out;
}

.animate-pulse-once {
  animation: pulse-once 0.4s ease-in-out;
}

.animate-progress {
  animation: progress 4s linear forwards;
}
/* ============================================
   BOARD MIDDLE PATH RESPONSIVE ALIGNMENT
   ============================================ */

/* Path tables themselves must sit flush — no border-spacing, no margin,
   top-aligned. Default <table> has 2px user-agent border-spacing and a
   baseline gap that show up as a horizontal seam between board rows. */
.path {
  margin: 0;
  border-spacing: 0;
  vertical-align: top;
}

/* Force the ludo-board flex rows to top-align children and not wrap so a
   2-px height mismatch between the home corner and the adjacent path
   never opens a vertical gap below the row. */
#top-game-row,
#middle-game-row,
#bottom-game-row {
  align-items: flex-start;
  flex-wrap: nowrap;
}

/* Ensure ALL path cells are square and consistent */
.path td {
  aspect-ratio: 1 / 1;
  box-sizing: border-box;
}

/* Mobile Landscape (up to 640px) */
@media (max-width: 640px) and (orientation: landscape) {
  .path td {
    min-width: 36px !important;
    min-height: 36px !important;
  }
}

/* Tablet (641px - 1024px) */
@media (min-width: 641px) and (max-width: 1024px) {
  .path td {
    min-width: 38px !important;
    min-height: 38px !important;
  }
}

/* Desktop (1025px and up) */
@media (min-width: 1025px) {
  .path td {
    min-width: 40px !important;
    min-height: 40px !important;
  }
}

/* Extra large desktop (1440px and up) */
@media (min-width: 1440px) {
  .path td {
    min-width: 42px !important;
    min-height: 42px !important;
  }
}

/* Ensure middle row extends properly on all devices */
.middle-row-red,
.middle-row-green,
.middle-row-blue,
.middle-row-yellow {
  white-space: nowrap;
}


/* ============================================
   PLAYER HOME & WINNING CELLS RESPONSIVE
   ============================================ */

/* Winning cells (center finish area) */
.winning-cell {
  aspect-ratio: 1 / 1;
  box-sizing: border-box;
}

.winning-cells-table {
  border-collapse: collapse;
}

/* Player home areas (starting positions) */
.player-home-area {
  transition: all 0.3s ease;
}

/* Mobile Landscape */
@media (max-width: 640px) and (orientation: landscape) {
  .winning-cell {
    min-width: 36px !important;
    min-height: 36px !important;
  }
}

/* Tablet */
@media (min-width: 641px) and (max-width: 1024px) {
  .winning-cell {
    min-width: 38px !important;
    min-height: 38px !important;
  }
}

/* Desktop */
@media (min-width: 1025px) {
  .winning-cell {
    min-width: 40px !important;
    min-height: 40px !important;
  }
}

/* Large Desktop */
@media (min-width: 1440px) {
  .winning-cell {
    min-width: 42px !important;
    min-height: 42px !important;
  }
}


/* ============================================
   START TILES — #8
   ============================================ */

/* Each player's starting tile gets a vivid colour + subtle pulsing ring
   so newcomers can spot where their piece will enter the board. */
.start-tile.start-tile-red {
  background-color: #fc0404 !important;
  box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px #fc0404;
}
.start-tile.start-tile-blue {
  background-color: #046cfc !important;
  box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px #046cfc;
}
.start-tile.start-tile-yellow {
  background-color: #fcbc04 !important;
  box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px #fcbc04;
}
.start-tile.start-tile-green {
  background-color: #0cc704 !important;
  box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px #0cc704;
}

@keyframes start-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
.start-tile .start-label {
  animation: start-pulse 2s ease-in-out infinite;
}

/* ============================================
   PIECE VISIBILITY GUARANTEE
   ============================================
   Project-wide: horse pieces must never be hidden by a coloured tile
   background (ligand recolour, START tile, home stretch, fate tile,
   etc). Forces the piece img above any sibling span and high above
   the td's own background layers. Also keeps labels/text spans below
   the piece no matter what.
*/

.path td img,
.path td span > img {
  position: relative !important;
  z-index: 40 !important;
  pointer-events: auto !important;
}

/* Piece sizing — uniform square box for every horse/blook image
   regardless of source aspect ratio. The legacy CSS only set width,
   so character-swap.js's tall dogs (128x161) and wide pandas
   (178x128) rendered at very different heights, with the tall ones
   overflowing into the next cell ("bertindih"). Force a square box
   and let object-fit:contain scale the source artwork inside. */
img.gh1, img.gh2, img.gh3, img.gh4,
img.yh1, img.yh2, img.yh3, img.yh4,
img.rh1, img.rh2, img.rh3, img.rh4,
img.bh1, img.bh2, img.bh3, img.bh4 {
  aspect-ratio: 1 / 1 !important;
  height: auto !important;
  object-fit: contain !important;
}

/* Multi-piece cell normalization. When two players share a tile,
   mergeHorses() wraps them in a <span> sized 21.5px with inline
   `width: 50%` on each img — but a piece appended AFTER the merge
   (e.g. fate Second Chance, kill respawn) stays at the solo 28px,
   producing the "panda small + dog big" mismatch Hazim flagged
   2026-05-13. These rules override the legacy inline styles so
   every piece in a path cell renders at the same size regardless
   of whether it's wrapped in a merge span or pinned directly to
   the td. */
.path td > span:first-child:has(img),
.path td > span > span:has(img) {
  width: auto !important;
  height: auto !important;
  max-width: 100% !important;
  max-height: 100% !important;
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 1px !important;
  padding: 1px !important;
}
.path td img.gh1, .path td img.gh2, .path td img.gh3, .path td img.gh4,
.path td img.yh1, .path td img.yh2, .path td img.yh3, .path td img.yh4,
.path td img.rh1, .path td img.rh2, .path td img.rh3, .path td img.rh4,
.path td img.bh1, .path td img.bh2, .path td img.bh3, .path td img.bh4 {
  width: 18px !important;
  height: 18px !important;
}
@media (min-width: 641px) {
  .path td img.gh1, .path td img.gh2, .path td img.gh3, .path td img.gh4,
  .path td img.yh1, .path td img.yh2, .path td img.yh3, .path td img.yh4,
  .path td img.rh1, .path td img.rh2, .path td img.rh3, .path td img.rh4,
  .path td img.bh1, .path td img.bh2, .path td img.bh3, .path td img.bh4 {
    width: 20px !important;
    height: 20px !important;
  }
}
@media (min-width: 1025px) {
  .path td img.gh1, .path td img.gh2, .path td img.gh3, .path td img.gh4,
  .path td img.yh1, .path td img.yh2, .path td img.yh3, .path td img.yh4,
  .path td img.rh1, .path td img.rh2, .path td img.rh3, .path td img.rh4,
  .path td img.bh1, .path td img.bh2, .path td img.bh3, .path td img.bh4 {
    width: 22px !important;
    height: 22px !important;
  }
}
@media (min-width: 1440px) {
  .path td img.gh1, .path td img.gh2, .path td img.gh3, .path td img.gh4,
  .path td img.yh1, .path td img.yh2, .path td img.yh3, .path td img.yh4,
  .path td img.rh1, .path td img.rh2, .path td img.rh3, .path td img.rh4,
  .path td img.bh1, .path td img.bh2, .path td img.bh3, .path td img.bh4 {
    width: 24px !important;
    height: 24px !important;
  }
}

/* Any decorative text inside a tile (ligand names, START label)
   must stay below the piece. */
.path td > span:not(:has(img)) {
  z-index: 5 !important;
}

/* Ensure the merge wrapper (span that groups multiple pieces on the
   same cell) doesn't clip children. */
.path td > span:first-child:has(img) {
  z-index: 40 !important;
  overflow: visible !important;
}

/* START-tile text stays behind the piece too. */
.start-tile .start-label {
  z-index: 4 !important;
}

/* Ligand-tile text label (applied by ligand-tile-colors.js) — also
   below the piece. */
.ligand-tile > span {
  z-index: 5 !important;
}

/* ============================================
   DICE ARROW STABILITY
   ============================================
   The legacy one-vs-*.js code clears the arrow img's src via
   $.attr('src', '') and re-sets it to 'gifs/arrow1.gif' between turns.
   Without a reserved size, the img collapses to 0×h while empty, which
   reflows the player-card row and makes adjacent path pieces appear to
   jiggle — especially noticeable near end-game when multiple turn-rotation
   handlers race the src attribute. Lock the box and hide visually when
   empty so the layout never moves. */
img[id$="-dice-arrow"] {
  display: inline-block;
  vertical-align: middle;
}
/* !important is required: turn-manager.js sets inline
   `style.visibility = 'visible'` on the current player's arrow, which
   would otherwise override this rule during the brief window when a
   transferDiceCode has cleared src="" but the next-player visibility
   sync hasn't run yet — that window flashes the browser's broken-image
   placeholder. Same hide rule applied to the player dice img below,
   which renders without `src` until a game script assigns one. */
img[id$="-dice-arrow"]:not([src]),
img[id$="-dice-arrow"][src=""] {
  visibility: hidden !important;
}
img.player-dice:not([src]),
img.player-dice[src=""] {
  visibility: hidden !important;
}
