/* Animations subtiles façon terminal Bloomberg.
 *
 * Règle d'or : chaque animation communique un changement d'état réel
 * (donnée qui bouge, poll qui rafraîchit, élément qui se charge). Pas de
 * mouvement gratuit qui dégraderait la confiance dans le tableau.
 *
 * Toutes les animations respectent `prefers-reduced-motion` — on désactive
 * complètement pour les utilisateurs sensibles au mouvement.
 *
 * Ajouté le 2026-05-13. Chargé après terminal.css dans base.html.
 */

@media (prefers-reduced-motion: reduce) {
  .flash-pos, .flash-neg,
  .live-tick, .rsb-pulse-poll,
  .sparkline-reveal path, .sparkline-reveal polyline,
  .matrix-dot-stagger circle,
  .skeleton {
    animation: none !important;
    transition: none !important;
  }
}

/* ── 1. Flash sur changement de valeur ─────────────────────────────────────
   Appliqué par anim.js sur les cellules `data-flash-key` quand leur
   valeur a changé entre 2 refreshes. Vert si la valeur monte, rouge si
   elle baisse. La couleur de fond fade en 800ms — assez long pour être vu,
   assez court pour ne pas distraire la lecture suivante. */

@keyframes flash-pos-bg {
  0%   { background-color: rgba(111, 220, 160, 0.55); }
  100% { background-color: transparent; }
}
@keyframes flash-neg-bg {
  0%   { background-color: rgba(224, 112, 112, 0.55); }
  100% { background-color: transparent; }
}
.flash-pos { animation: flash-pos-bg 800ms ease-out; }
.flash-neg { animation: flash-neg-bg 800ms ease-out; }

/* ── 2. Live tick — colon qui blink à 1Hz ──────────────────────────────────
   Wrappé dans <span class="live-tick">:</span> dans les libellés
   "HH:MM" qui doivent paraître "vivants". Cycle 1s, fonction step pour
   un on/off net (pas de transition smooth qui ferait "pulsation"). */

@keyframes live-tick-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0.25; }
}
.live-tick {
  animation: live-tick-blink 1.2s steps(1, end) infinite;
  display: inline-block;
}

/* ── 3. Pulse sidebar — panel mis à jour par HTMX poll ─────────────────────
   Quand HTMX swap le contenu, on ajoute brièvement la classe
   `rsb-pulse-poll` sur l'aside : un fin éclair teal traverse la bordure
   gauche des titres en 1.2s. Communique "données rafraîchies" sans
   distraire de la lecture. */

@keyframes rsb-pulse-border {
  0%   { border-left-color: var(--accent); box-shadow: -2px 0 6px -1px rgba(76, 201, 168, 0.7); }
  60%  { border-left-color: var(--accent); box-shadow: -2px 0 0 0 rgba(76, 201, 168, 0); }
  100% { border-left-color: var(--accent); box-shadow: none; }
}
.rsb-pulse-poll .rsb-h {
  animation: rsb-pulse-border 1.2s ease-out;
}

/* ── 4. Sparkline reveal — tracé SVG qui s'écrit ───────────────────────────
   Pour les chart paths : la longueur totale du path est utilisée comme
   dasharray, l'offset descend de cette longueur à 0 sur 700ms ease-out.
   Le script anim.js mesure getTotalLength() au load et applique les vars
   --pl (path length) inline.

   Marche pour <polyline> et <path>. Si la longueur n'est pas mesurée,
   fallback gracieux : le tracé apparaît instantanément (pas de bug). */

.sparkline-reveal path,
.sparkline-reveal polyline {
  stroke-dasharray: var(--pl, 1);
  stroke-dashoffset: var(--pl, 1);
  animation: sparkline-draw 700ms ease-out forwards;
}
@keyframes sparkline-draw {
  to { stroke-dashoffset: 0; }
}

/* ── 5. Matrix dots stagger — apparition échelonnée ───────────────────────
   Sur les SVG de matrice (forex, commodities, onchain), chaque dot reçoit
   `style="--i: N"` dans le template et la classe `.matrix-dot-stagger` sur
   le <g> ou <svg> parent. Délai = i × 15ms, fade-in + scale 0.6→1 sur 350ms.
   Effet "données qui chargent" naturel. */

.matrix-dot-stagger circle {
  opacity: 0;
  transform-origin: center;
  transform: scale(0.6);
  animation: matrix-dot-in 350ms cubic-bezier(0.3, 1.4, 0.5, 1) forwards;
  animation-delay: calc(var(--i, 0) * 15ms);
}
@keyframes matrix-dot-in {
  to { opacity: 1; transform: scale(1); }
}

/* ── 6. Skeleton shimmer — placeholder pour les loads longs ───────────────
   Appliquer sur un élément vide en attendant que la donnée arrive
   (backtest, calendar refresh). Gradient qui défile horizontalement. */

@keyframes skeleton-shimmer {
  0%   { background-position: -200px 0; }
  100% { background-position: calc(200px + 100%) 0; }
}
.skeleton {
  display: inline-block;
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0.04) 0%,
    rgba(255,255,255,0.12) 50%,
    rgba(255,255,255,0.04) 100%
  );
  background-size: 200px 100%;
  background-repeat: no-repeat;
  animation: skeleton-shimmer 1.4s linear infinite;
  border-radius: 3px;
  min-height: 12px;
  color: transparent;
  user-select: none;
}

/* ── 8. Regime tint — teinte de fond très subtile selon le régime FI
   Le body reçoit data-regime="offensif|neutre|defensif|none". Un overlay
   fixe en haut de la page (gradient vertical 0.04 → 0) communique le
   sentiment global sans distraire. Désactivable via prefers-reduced-motion. */

body[data-regime="offensif"]::before {
  content: ''; position: fixed; inset: 0 0 auto 0; height: 220px;
  pointer-events: none; z-index: -1;
  background: linear-gradient(180deg, rgba(111, 220, 160, 0.06) 0%, rgba(111, 220, 160, 0) 100%);
}
body[data-regime="defensif"]::before {
  content: ''; position: fixed; inset: 0 0 auto 0; height: 220px;
  pointer-events: none; z-index: -1;
  background: linear-gradient(180deg, rgba(224, 112, 112, 0.06) 0%, rgba(224, 112, 112, 0) 100%);
}
@media (prefers-reduced-motion: reduce) {
  body[data-regime]::before { display: none !important; }
}

/* ── 7. Pos52w mini-bar — composant visuel pour la position dans la range 52w
   Pas une animation à proprement parler, mais lié au même besoin "rendre la
   donnée instantanément lisible". Voir aussi templates onchain/sidebar. */

.pos52w-bar {
  display: inline-block;
  vertical-align: middle;
  width: 44px; height: 7px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 2px;
  position: relative;
  margin-right: 4px;
  overflow: hidden;
}
.pos52w-bar::before {
  /* ligne de milieu pour repérer le 50% */
  content: ''; position: absolute;
  top: 0; bottom: 0; left: 50%; width: 1px;
  background: rgba(255, 255, 255, 0.18);
}
.pos52w-fill {
  display: block; height: 100%;
  background: var(--pos52w-color, #888);
}
/* Couleur du fill selon la zone : near low = teal (opportunité côté
   BOTTOM ACCUM), mid = amber, near high = rouge (TOP DISTRI / surchauffe). */
.pos52w-bar[data-zone="low"]  { --pos52w-color: #6fdca0; }
.pos52w-bar[data-zone="mid"]  { --pos52w-color: #e0c060; }
.pos52w-bar[data-zone="high"] { --pos52w-color: #e07070; }

.pos52w-val {
  font-variant-numeric: tabular-nums;
  color: var(--fg-mut);
  font-size: inherit;
}
