/* ════════════════════════════════════════════════
   AstroE TrendBoard — Motion
   Rule: animate transform + opacity only. Glows that "pulse"
   are separate layers whose opacity/scale changes, never box-shadow.
   ════════════════════════════════════════════════ */

:root {
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --fast: 150ms;
  --base: 250ms;
  --slow: 450ms;
  --stagger: 50ms;
}

/* ── Keyframes ── */
@keyframes fade-in      { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise-in      { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes window-in    { from { opacity: 0; transform: scale(.98); } to { opacity: 1; transform: none; } }
@keyframes slide-in-r   { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
@keyframes breathe      { 0%, 100% { opacity: .75; transform: scale(1); } 50% { opacity: 1; transform: scale(1.08); } }
@keyframes twinkle      { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@keyframes float        { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
@keyframes pulse-ring   { 0% { opacity: .6; transform: scale(1); } 70%, 100% { opacity: 0; transform: scale(2.4); } }
@keyframes halo         { 0% { opacity: .55; transform: scale(1); } 100% { opacity: 0; transform: scale(1.9); } }
@keyframes spin         { to { transform: rotate(360deg); } }
@keyframes ticker       { to { transform: translateX(-50%); } }
@keyframes flash        { 0% { opacity: 1; } 100% { opacity: 0; } }
@keyframes draw         { to { stroke-dashoffset: 0; } }
@keyframes toast        { 0% { opacity: 0; transform: translate(-50%, 12px); }
                          12%, 80% { opacity: 1; transform: translate(-50%, 0); }
                          100% { opacity: 0; transform: translate(-50%, -6px); } }

/* ── Page-load entrance ──
   .enter on an element plays once; set --i for stagger order. */
.enter-window { animation: window-in var(--slow) var(--ease-out) both; }
.enter {
  animation: rise-in var(--slow) var(--ease-out) both;
  animation-delay: calc(var(--slow) * .6 + var(--i, 0) * var(--stagger));
}

/* ── Scroll reveal (js/animations.js adds .is-visible once).
   Scoped to html.js so content never stays hidden if scripts fail. ── */
.js .reveal { opacity: 0; transform: translateY(16px); }
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  transition: opacity var(--slow) var(--ease-out), transform var(--slow) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}

/* ── Ambient loops ── */
.breathe { animation: breathe 10s var(--ease-in-out) infinite; }
.twinkle { animation: twinkle 3s ease-in-out infinite; }
.float   { animation: float 6s var(--ease-in-out) infinite; }

/* LIVE dot: the ring is a pseudo-element that scales + fades */
.live-dot { position: relative; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--up); flex-shrink: 0; }
.live-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--up);
  animation: pulse-ring 1.8s var(--ease-out) infinite;
}

/* ── Row flash on data refresh: overlay fades, row background never animates ── */
.flash-up, .flash-down { position: relative; }
.flash-up::before, .flash-down::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  animation: flash 1s var(--ease-out) forwards;
}
.flash-up::before   { background: var(--up-soft); }
.flash-down::before { background: var(--down-soft); }

/* ── SVG self-drawing stroke (sparklines). JS sets --len. ── */
.draw { stroke-dasharray: var(--len, 200); stroke-dashoffset: var(--len, 200); animation: draw 700ms var(--ease-out) forwards; }

/* ── Theme switch: color transition only during the switch ── */
html.theme-switching,
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
  transition: background-color 300ms var(--ease-in-out), color 300ms var(--ease-in-out),
              border-color 300ms var(--ease-in-out), fill 300ms var(--ease-in-out),
              stroke 300ms var(--ease-in-out), opacity 300ms var(--ease-in-out) !important;
}

/* ── Pausing ──
   .is-paused: an off-screen section. html.tab-hidden: the whole tab is hidden. */
.is-paused, .is-paused *,
html.tab-hidden *, html.tab-hidden *::before, html.tab-hidden *::after {
  animation-play-state: paused !important;
}

/* ── Reduced motion: no loops, orbits or counters; fades only ── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .breathe, .twinkle, .float, .live-dot::after,
  .glow, .beacon, .bn-ring, .cs-dots i { animation: none !important; }
  .js .reveal { transform: none; }
  .js .reveal.is-visible { transition: opacity var(--base) linear; transition-delay: 0ms; }
  .draw { stroke-dashoffset: 0; animation: none; }
}
