/* ============================================================
   Animation layers: aurora background, radar, node entrances,
   beams, toasts. All decorative motion uses transform/opacity
   only and is disabled under prefers-reduced-motion.
   ============================================================ */

/* ---------- Aurora background ---------- */

.aurora {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -2;
  overflow: hidden;
  pointer-events: none;
}

.aurora i {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.16;
  animation: aurora-drift 26s ease-in-out infinite alternate;
}

.aurora i:nth-child(1) {
  width: 46vmax;
  height: 46vmax;
  top: -18vmax;
  left: -12vmax;
  background: var(--accent-b);
}

.aurora i:nth-child(2) {
  width: 38vmax;
  height: 38vmax;
  bottom: -16vmax;
  right: -10vmax;
  background: var(--accent-a);
  animation-delay: -8s;
}

.aurora i:nth-child(3) {
  width: 30vmax;
  height: 30vmax;
  top: 40%;
  left: 55%;
  background: var(--accent-c);
  animation-delay: -16s;
}

[data-theme='light'] .aurora i {
  opacity: 0.12;
}

@keyframes aurora-drift {
  0%   { transform: translate(0, 0) scale(1); }
  50%  { transform: translate(6vmax, -4vmax) scale(1.12); }
  100% { transform: translate(-5vmax, 5vmax) scale(0.94); }
}

/* Subtle film grain over the aurora */
.grain {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}

/* ---------- Radar ---------- */

/* Rotating sweep — transform only for GPU compositing. */
.radar-sweep {
  animation: radar-spin 4.2s linear infinite;
}

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

/* Expanding ping ring, timed with the sweep. */
.radar-ping {
  animation: radar-ping 4.2s var(--ease-out) infinite;
}

@keyframes radar-ping {
  0%   { transform: scale(0.5); opacity: 0; }
  10%  { opacity: 0.5; }
  100% { transform: scale(1.05); opacity: 0; }
}

/* Contact blips flash shortly after the sweep passes their angle. */
.radar-blips i {
  animation: radar-blip 4.2s ease-out infinite;
}

.radar-blips i:nth-child(1) { animation-delay: 0.4s; }
.radar-blips i:nth-child(2) { animation-delay: 1.8s; }
.radar-blips i:nth-child(3) { animation-delay: 3.0s; }

@keyframes radar-blip {
  0%   { opacity: 0; transform: scale(0.4); }
  8%   { opacity: 0.9; transform: scale(1); }
  30%  { opacity: 0; transform: scale(1.5); }
  100% { opacity: 0; transform: scale(0.4); }
}

/* Gentle breathing of the center core. */
.radar-core {
  animation: radar-core-pulse 2.4s ease-in-out infinite;
}

@keyframes radar-core-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(1.35); opacity: 0.75; }
}

/* ---------- Nodes ---------- */

/*
  Entrance animation. The translate offset MUST match the base offset in
  components.css (half the avatar height) so the node settles exactly on
  its orbit point instead of jumping when the animation ends.
*/
.node.peer {
  animation: node-in 480ms var(--ease-spring) backwards;
}

@keyframes node-in {
  from {
    opacity: 0;
    transform: translate(-50%, calc(var(--node-avatar) / -2)) scale(0.5);
  }
  to {
    opacity: 1;
    transform: translate(-50%, calc(var(--node-avatar) / -2)) scale(1);
  }
}

/*
  Idle float is applied to the avatar wrapper using transform (not the
  standalone translate property) so it works on older engines and does
  not clash with the hover scale applied to the avatar itself.
*/
.node-avatar-wrap {
  animation: avatar-float 4.5s ease-in-out infinite alternate;
}

.node:nth-child(2n) .node-avatar-wrap { animation-delay: -1.4s; }
.node:nth-child(3n) .node-avatar-wrap { animation-delay: -2.6s; }
.node:nth-child(5n) .node-avatar-wrap { animation-delay: -3.3s; }

@keyframes avatar-float {
  from { transform: translateY(0); }
  to   { transform: translateY(-4px); }
}

/* Waiting for the other side to accept */
.node.waiting .ring-track {
  animation: waiting-pulse 1.3s ease-in-out infinite;
}

@keyframes waiting-pulse {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 0.8; }
}

/* ---------- Beams ---------- */

.beams line.active {
  animation: beam-flow 0.8s linear infinite;
}

@keyframes beam-flow {
  from { stroke-dashoffset: 12; }
  to   { stroke-dashoffset: 0; }
}

/* ---------- Status dot ---------- */

.status-dot[data-state='boot'],
.status-dot[data-state='claiming'],
.status-dot[data-state='scanning'],
.status-dot[data-state='reconnecting'] {
  animation: dot-pulse 1.4s ease-in-out infinite;
}

@keyframes dot-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

/* ---------- Toasts ---------- */

.toast {
  animation: toast-in 320ms var(--ease-spring);
}

.toast.leaving {
  animation: toast-out 260ms ease forwards;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateX(48px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateX(48px);
  }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .aurora i,
  .radar-sweep,
  .radar-ping,
  .radar-blips i,
  .radar-core,
  .node-avatar-wrap,
  .beams line.active,
  .status-dot {
    animation: none !important;
  }

  .node.peer {
    animation-duration: 1ms;
  }

  * {
    transition-duration: 1ms !important;
  }
}