@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.fade-in-up { animation: fadeInUp .5s cubic-bezier(.16,.8,.3,1) both; }

@keyframes messageIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.message-row { animation: messageIn .28s ease both; }

@keyframes pulseGlow {
  0%, 100% { box-shadow: 0 0 18px rgba(125,211,252,0.35); }
  50% { box-shadow: 0 0 34px rgba(167,139,250,0.5); }
}
.pulse-glow { animation: pulseGlow 3.2s ease-in-out infinite; }

@keyframes drift {
  0%, 100% { transform: translateY(0px); }
  50% { transform: translateY(-8px); }
}
.drift { animation: drift 4.5s ease-in-out infinite; }

@keyframes toastIn {
  from { opacity: 0; transform: translateX(24px); }
  to { opacity: 1; transform: translateX(0); }
}
.toast { animation: toastIn .3s cubic-bezier(.16,.8,.3,1) both; }

@keyframes modalIn {
  from { opacity: 0; transform: translateY(16px) scale(.98); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
.modal-card { animation: modalIn .25s cubic-bezier(.16,.8,.3,1) both; }

@keyframes bannerSlide {
  from { opacity: 0; transform: translateY(-100%); }
  to { opacity: 1; transform: translateY(0); }
}
.announcement-banner { animation: bannerSlide .4s ease both; }

@keyframes typingDot {
  0%, 60%, 100% { opacity: .3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-3px); }
}
.typing-dot { animation: typingDot 1.2s ease-in-out infinite; }
.typing-dot:nth-child(2) { animation-delay: .15s; }
.typing-dot:nth-child(3) { animation-delay: .3s; }

@keyframes speakingRing {
  0%, 100% { box-shadow: 0 0 0 0 rgba(125,211,252,0.4); }
  50% { box-shadow: 0 0 0 6px rgba(125,211,252,0); }
}
.voice-tile.speaking .voice-tile-avatar { animation: speakingRing 1.4s ease-out infinite; border-radius: 50%; }
