/* animations.css — every keyframe on the site.
 *
 *   1. Entrances      how the page and its pieces arrive
 *   2. Title          .page-title.ta-<id>  (picked in Animations tab)
 *   3. Overlays       .ov-anim-<id>        (picked per image)
 *   4. UI details     shine, spinner, live dot, page-leave
 *
 * Durations are divided by --anim-speed, the Animations tab's Speed slider.
 * Entrance animations use fill-mode `backwards`, never `both`: a transform-like
 * value left behind after the animation ends would make #player-root a
 * containing block for its fixed children.
 */

/* ── 1. entrances ─────────────────────────────────────────────────────────── */
.page-title { animation: titleEnter .95s var(--ease-spring) .1s backwards; }
.top-buttons, .acct { animation: dropIn .6s var(--ease-spring) .35s backwards; }
#player-root { animation: riseIn .75s var(--ease-out) .45s backwards; }
.song-request { animation: slideInLeft .75s var(--ease-out) .55s backwards; }
.overlay-layer { animation: fadeIn 1.2s ease .3s backwards; }

@keyframes titleEnter {
  from { opacity: 0; translate: 0 -36px; scale: .86; filter: blur(8px); }
  60%  { filter: blur(0); }
  to   { opacity: 1; translate: 0 0; scale: 1; }
}
@keyframes dropIn {
  from { opacity: 0; translate: 0 -18px; scale: .8; }
  to   { opacity: 1; translate: 0 0; scale: 1; }
}
@keyframes riseIn {
  from { opacity: 0; translate: 0 40px; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes slideInLeft {
  from { opacity: 0; translate: -40px 0; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes popIn {
  from { opacity: 0; transform: scale(.85) translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* A request just went out: a quick confirming pulse around the box. */
.song-request.sent { animation: sentPulse .7s var(--ease-out); }
@keyframes sentPulse {
  0%   { box-shadow: var(--shadow), 0 0 0 0 var(--theme-glow); }
  100% { box-shadow: var(--shadow), 0 0 0 16px transparent; }
}

/* ── 2. title animations ──────────────────────────────────────────────────── */
/* Each runs on .title-text (or its letters), never on the h1: the h1's own
   transform is what centres it. The settings panel's sample words use the
   very same rules, through .anim-sample. */

.ta-glow .title-text { animation: taGlow calc(2.4s / var(--anim-speed)) ease-in-out infinite; }
@keyframes taGlow {
  0%, 100% { text-shadow: var(--title-shadow), 0 0 6px var(--theme-glow); }
  50%      { text-shadow: var(--title-shadow), 0 0 16px var(--theme-color), 0 0 44px var(--theme-glow), 0 0 80px var(--theme-glow); }
}

.ta-pulse .title-text { animation: taPulse calc(1.8s / var(--anim-speed)) ease-in-out infinite; }
@keyframes taPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.07); }
}

.ta-float .title-text { animation: taFloat calc(3.6s / var(--anim-speed)) ease-in-out infinite; }
@keyframes taFloat {
  0%, 100% { transform: translateY(0) rotate(-.6deg); }
  50%      { transform: translateY(-12px) rotate(.6deg); }
}

.ta-swing .title-text {
  transform-origin: 50% -40%;
  animation: taSwing calc(2.8s / var(--anim-speed)) ease-in-out infinite;
}
@keyframes taSwing {
  0%, 100% { transform: rotate(-3.5deg); }
  50%      { transform: rotate(3.5deg); }
}

/* Per-letter. --i is each letter's index, set by animations.js. */
.ta-wave .tl {
  animation: taWave calc(1.8s / var(--anim-speed)) ease-in-out infinite;
  animation-delay: calc(var(--i) * -0.11s / var(--anim-speed));
}
@keyframes taWave {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-0.22em); }
}

.ta-bounce .tl {
  animation: taBounce calc(2.4s / var(--anim-speed)) var(--ease-out) infinite;
  animation-delay: calc(var(--i) * 0.07s / var(--anim-speed));
}
@keyframes taBounce {
  0%, 30%, 100% { transform: translateY(0) scale(1, 1); }
  10%           { transform: translateY(-0.38em) scale(.94, 1.08); }
  20%           { transform: translateY(0) scale(1.08, .92); }
}

.ta-rainbow .tl {
  animation: taRainbow calc(3.2s / var(--anim-speed)) linear infinite;
  animation-delay: calc(var(--i) * -0.14s / var(--anim-speed));
}
@keyframes taRainbow {
  0%, 100% { color: #ff4d6d; }
  16%      { color: #ffa94d; }
  33%      { color: #ffe14d; }
  50%      { color: #4dff88; }
  66%      { color: #4dd2ff; }
  83%      { color: #b44dff; }
}

/* Glitch: two colored copies of the text, sliced into bands that jump about. */
.ta-glitch .title-text { animation: taGlitchShake calc(3.2s / var(--anim-speed)) steps(1) infinite; }
.ta-glitch .title-text::before,
.ta-glitch .title-text::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  text-shadow: none;
  pointer-events: none;
}
.ta-glitch .title-text::before {
  color: #ff2d95;
  animation: taGlitchA calc(2.1s / var(--anim-speed)) linear infinite alternate-reverse;
}
.ta-glitch .title-text::after {
  color: var(--theme-color);
  animation: taGlitchB calc(1.7s / var(--anim-speed)) linear infinite alternate-reverse;
}
@keyframes taGlitchShake {
  0%, 86%, 100% { transform: none; }
  88% { transform: translate(-3px, 1px) skewX(-6deg); }
  90% { transform: translate(3px, -1px) skewX(4deg); }
  92% { transform: translate(-1px, 0); }
}
@keyframes taGlitchA {
  0%   { clip-path: inset(12% 0 78% 0); transform: translate(-4px, 0); }
  20%  { clip-path: inset(62% 0 12% 0); transform: translate(3px, 0); }
  40%  { clip-path: inset(32% 0 52% 0); transform: translate(-2px, 1px); }
  60%  { clip-path: inset(82% 0 4% 0);  transform: translate(4px, -1px); }
  80%  { clip-path: inset(4% 0 88% 0);  transform: translate(-3px, 0); }
  100% { clip-path: inset(46% 0 38% 0); transform: translate(2px, 0); }
}
@keyframes taGlitchB {
  0%   { clip-path: inset(72% 0 10% 0); transform: translate(4px, 0); }
  25%  { clip-path: inset(8% 0 76% 0);  transform: translate(-3px, 0); }
  50%  { clip-path: inset(40% 0 44% 0); transform: translate(2px, -1px); }
  75%  { clip-path: inset(88% 0 2% 0);  transform: translate(-4px, 1px); }
  100% { clip-path: inset(22% 0 62% 0); transform: translate(3px, 0); }
}

.ta-flicker .title-text { animation: taFlicker calc(4s / var(--anim-speed)) linear infinite; }
@keyframes taFlicker {
  0%, 17%, 21%, 23%, 55%, 57%, 100% {
    opacity: 1;
    text-shadow: var(--title-shadow), 0 0 8px var(--theme-color), 0 0 28px var(--theme-glow), 0 0 60px var(--theme-glow);
  }
  18%, 22%, 56% { opacity: .35; text-shadow: var(--title-shadow); }
}

.ta-typewriter .tl {
  animation: taType .01s steps(1) backwards;
  animation-delay: calc(var(--i) * 0.075s / var(--anim-speed) + .5s);
}
.ta-typewriter .title-text::after {
  content: '';
  display: inline-block;
  width: .07em;
  height: .95em;
  margin-left: .08em;
  vertical-align: -.08em;
  background: var(--theme-color);
  box-shadow: 0 0 10px var(--theme-glow);
  animation: caretBlink 1s steps(1) infinite;
}
@keyframes taType { from { opacity: 0; } }
@keyframes caretBlink { 50% { opacity: 0; } }

/* ── 3. overlay animations ────────────────────────────────────────────────── */
.overlay-media { animation-delay: var(--ov-delay, 0s); }

.ov-anim-float  { animation: ovFloat  calc(3.4s / var(--anim-speed)) ease-in-out infinite; }
.ov-anim-spin   { animation: ovSpin   calc(7s   / var(--anim-speed)) linear infinite; }
.ov-anim-pulse  { animation: ovPulse  calc(1.8s / var(--anim-speed)) ease-in-out infinite; }
.ov-anim-bounce { animation: ovBounce calc(1.6s / var(--anim-speed)) var(--ease-out) infinite; }
.ov-anim-swing  { animation: ovSwing  calc(2.6s / var(--anim-speed)) ease-in-out infinite; transform-origin: 50% 0; }
.ov-anim-wobble { animation: ovWobble calc(2.2s / var(--anim-speed)) ease-in-out infinite; }
.ov-anim-glow   { animation: ovGlow   calc(2.4s / var(--anim-speed)) ease-in-out infinite; }
/* The animation-delay above is shorthand-reset by `animation`, so restate it. */
.overlay-media[class*="ov-anim-"] { animation-delay: var(--ov-delay, 0s); }

@keyframes ovFloat  { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }
@keyframes ovSpin   { to { transform: rotate(360deg); } }
@keyframes ovPulse  { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.09); } }
@keyframes ovBounce {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  30%      { transform: translateY(-22px) scale(.96, 1.04); }
  55%      { transform: translateY(0) scale(1.05, .95); }
  70%      { transform: translateY(-6px); }
}
@keyframes ovSwing  { 0%, 100% { transform: rotate(-8deg); } 50% { transform: rotate(8deg); } }
@keyframes ovWobble {
  0%, 100% { transform: rotate(0) skewX(0); }
  25%      { transform: rotate(-4deg) skewX(4deg); }
  75%      { transform: rotate(4deg) skewX(-4deg); }
}
@keyframes ovGlow {
  0%, 100% { filter: drop-shadow(0 0 4px var(--theme-glow)); }
  50%      { filter: drop-shadow(0 0 22px var(--theme-color)) brightness(1.1); }
}

/* ── 4. UI details ────────────────────────────────────────────────────────── */
@keyframes gearSpin { to { transform: rotate(360deg); } }
@keyframes dropPulse { 50% { border-color: transparent; } }

/* A light sweep across important buttons, every few seconds. */
.shine { position: relative; overflow: hidden; }
.shine::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: -60%;
  width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .55), transparent);
  transform: skewX(-20deg);
  animation: shineSweep 3.6s ease-in-out 1s infinite;
  pointer-events: none;
}
@keyframes shineSweep {
  0%   { left: -60%; }
  35%, 100% { left: 130%; }
}

.spinner {
  display: inline-block;
  width: 13px;
  height: 13px;
  flex: none;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  vertical-align: -2px;
  animation: gearSpin .7s linear infinite;
}

@keyframes livePulse {
  0%   { box-shadow: 0 0 0 0 rgba(255, 49, 49, .7); }
  70%  { box-shadow: 0 0 0 8px rgba(255, 49, 49, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 49, 49, 0); }
}

/* Auto-switch is taking the listener elsewhere: dim the page behind the
   "switching…" card. */
body.page-leaving::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 14000;
  background: rgba(3, 7, 18, .72);
  backdrop-filter: blur(3px);
  animation: fadeIn .9s ease both;
}

/* ── 5. moving with the music — the rest of the screen ───────────────────── */
/* By default only the player follows the music (players.css). These rules add
   the title, images and corner buttons, and apply only when the station owner
   switched on "Move the whole screen" (html.audio-screen) AND this listener's
   player is playing (html.audio-live, set by audio-react.js). The --audio-*
   values update every frame and are already scaled by the listener's volume.
   Individual `scale` / `filter` properties are used so this stacks on top of
   whatever keyframe animation is already running instead of replacing it. */
html.audio-live.audio-screen .page-title {
  scale: calc(1 + var(--audio-beat, 0) * 0.07 + var(--audio-bass, 0) * 0.03);
  filter: drop-shadow(0 0 calc(var(--audio-level, 0) * 22px) var(--theme-glow));
}
html.audio-live.audio-screen .overlay-media {
  scale: calc(1 + var(--audio-beat, 0) * 0.1 + var(--audio-bass, 0) * 0.04);
}
html.audio-live.audio-screen .ov-anim-glow {
  filter: drop-shadow(0 0 calc(4px + var(--audio-level, 0) * 26px) var(--theme-color));
  animation-name: none;
}
/* The corner buttons catch a little of the light, too. */
html.audio-live.audio-screen .top-btn,
html.audio-live.audio-screen .acct-chip {
  box-shadow: var(--shadow), 0 0 calc(var(--audio-beat, 0) * 18px) var(--theme-glow);
}

/* ── reduced motion ───────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001s !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-duration: .001s !important;
  }
  .offline-text { opacity: 1; text-shadow: 0 0 22px rgba(255, 40, 40, .8); }
  .ta-glitch .title-text::before, .ta-glitch .title-text::after { display: none; }
}
