/* =====================================================================
   sgbl_task-progress — attente d'une tâche générative (appel IA).
   Barre segmentée dont chaque point est une étape RÉELLEMENT franchie
   par le serveur, décompte du temps restant, emplacement d'échec.

   TROIS BANDES, et c'est la structure de référence (SVG de maquette) :
     1. la barre et ses NOEUDS NUS — aucun contenu dans le noeud ; le
        tronçon entre deux noeuds se REMPLIT en continu (migration `028`),
        au rythme du sous-segment en cours ;
     2. une bande d'ICÔNES seules — coche, croix, ou le throbber animé
        pour l'étape en cours ; rien pour une étape à venir ;
     3. la bande des LIBELLÉS.
   Sous le seuil mobile, ces trois bandes deviennent trois COLONNES :
   barre à gauche, icônes au milieu, libellé à droite.

   Le composant ne DÉFINIT aucune variable et ne code aucune couleur en
   dur : il consomme les tokens de `gbl_styles.css` (--cl-data-attente,
   --cl-data-valide, --cl-data-invalide, --theme-*). L'apparence du
   BOUTON en attente, elle, appartient au global — cf. `.exv-btn-attente`
   dans `gbl_styles.css` (`edition-css.md` : un composant ne redéfinit
   jamais l'apparence d'un bouton).

   Quatre sections ordonnées : A petits éléments · B textes ·
   C secondaires · D structurels, animations en fin de D.
   ===================================================================== */

/* ============ A — PETITS ÉLÉMENTS FONCTIONNELS ============ */

/* Le NOEUD, sur la barre. Il est NU : son état se lit à sa couleur seule,
   jamais à un contenu. L'icône correspondante vit dans la bande d'en dessous. */
.exv-tp-point {
    position: relative; z-index: 1;
    flex: 0 0 auto;
    width: 13px; height: 13px;
    border-radius: 50%;
    background: var(--theme-bg-quaterniaire);
    /* ANNEAU de la couleur du fond, et non une bordure de contour : la barre passe
       DERRIÈRE les noeuds, l'anneau est ce qui les en détache. Une bordure sombre
       cernerait le noeud ; ici il faut au contraire un vide autour de lui. */
    box-shadow: 0 0 0 2px var(--theme-bg-secondaire);
}
.exv-tp-etape[data-etat="encours"] .exv-tp-point {
    background: var(--cl-data-attente);
    /* Le GLOW naît et meurt avec l'étape en cours, jamais posé sur un noeud figé. */
    animation: exv-tp-glow 1.6s infinite;
}
.exv-tp-etape[data-etat="valide"] .exv-tp-point { background: var(--cl-data-valide); }
.exv-tp-etape[data-etat="invalide"] .exv-tp-point { background: var(--cl-data-invalide); }

/* Le TRONÇON qui relie un noeud au précédent : le RAIL CREUX, c'est-à-dire le
   trajet possible. Il ne se colore jamais lui-même. */
.exv-tp-lien {
    position: absolute; top: 4.5px; right: 50%;
    width: 100%; height: 4px;
    background: var(--theme-bg-quaterniaire);
}
.exv-tp-etape:first-child .exv-tp-lien { display: none; }

/* Le TRAJET PARCOURU. Deux éléments et non un, parce que ce sont deux choses : le
   rail dit où l'on peut aller, le remplissage dit jusqu'où l'on est allé.

   `--exv-tp-remplissage` (0..1) est posé par le composant à chaque relevé, et
   `--exv-tp-duree` porte le temps qu'il reste au sous-segment en cours. C'est le
   NAVIGATEUR qui interpole, linéairement, et qui s'ARRÊTE à la butée : le gel du
   remplissage — la barre ne bouge plus quand une opération déborde sa moyenne —
   n'est pas surveillé par du JavaScript, il tombe de la fin de la transition.

   C'est aussi ce qui tient l'invariant du 2026-08-20 : la butée servie est la
   frontière du SOUS-SEGMENT, jamais le noeud suivant. Le remplissage ne peut donc
   structurellement pas atteindre un noeud que le serveur n'a pas franchi. */
.exv-tp-lien-plein {
    position: absolute; left: 0; top: 0;
    width: calc(var(--exv-tp-remplissage, 0) * 100%);
    height: 100%;
    background: var(--cl-data-attente);
    transition: width var(--exv-tp-duree, 0s) linear,
                height var(--exv-tp-duree, 0s) linear;
}
/* Franchi : le trajet passe au vert, comme le noeud dont il part. Même grammaire
   pour le segment et pour le point — violet pendant, vert une fois acquis. */
.exv-tp-etape[data-amont="valide"] .exv-tp-lien-plein { background: var(--cl-data-valide); }
/* Aucune règle pour un amont INVALIDE, et c'est délibéré : le tronçon qui descend
   d'une étape échouée mène à une étape jamais atteinte. Le peindre en rouge
   annoncerait un trajet parcouru jusqu'à la suivante, alors que le pipeline s'est
   arrêté net. Il garde donc son violet, arrêté là où le travail s'est arrêté — la
   croix porte l'échec, à elle seule.
   (Défaut vu à la RASTÉRISATION, pas au test : aucune assertion ne regardait la
   couleur d'un tronçon aval.) */

/* La BANDE D'ICÔNES. Sa hauteur est réservée même vide, sans quoi la barre
   sauterait à l'instant où une icône paraît — c'est-à-dire à chaque étape. */
.exv-tp-icone {
    flex: 0 0 auto;
    height: 20px;
    display: flex; align-items: center; justify-content: center;
    color: var(--theme-texte-tertiaire);
}
.exv-tp-icone svg { width: 20px; height: 20px; display: block; overflow: visible; }
.exv-tp-etape[data-etat="valide"] .exv-tp-icone { color: var(--cl-data-valide); }
.exv-tp-etape[data-etat="invalide"] .exv-tp-icone { color: var(--cl-data-invalide); }

/* THROBBER — trois pastilles en fondu alterné. Il sert à DEUX endroits : dans le
   bouton (où `gbl_styles.css` le repointe vers le blanc via `--exv-tp-dot`), et
   ici, comme icône de l'étape en cours. Repris du modèle fourni à deux écarts
   près : la couleur vient d'un token (le throbber suit donc le thème) et l'état
   bas s'en DÉRIVE, au lieu d'être une seconde valeur à tenir d'accord. */
.dot-attente,
.dot-attente::before,
.dot-attente::after {
    width: 7px; height: 7px; border-radius: 50%;
    background-color: var(--exv-tp-dot, var(--cl-data-attente));
    animation: exv-tp-battement 0.5s infinite alternate;
}
.dot-attente { position: relative; animation-delay: 0.25s; }
.dot-attente::before,
.dot-attente::after { content: ""; display: inline-block; position: absolute; top: 0; }
.dot-attente::before { left: -11px; animation-delay: 0s; }
.dot-attente::after  { left: 11px;  animation-delay: 0.5s; }

/* ============ B — TEXTES ============ */

.exv-tp-libelle {
    font-size: var(--ht-texte-07);
    color: var(--theme-texte-tertiaire);
    text-align: center;
    line-height: 1.25;
}
.exv-tp-etape[data-etat="encours"] .exv-tp-libelle { color: var(--cl-data-attente); font-weight: var(--wg-texte-medium); }
.exv-tp-etape[data-etat="valide"] .exv-tp-libelle { color: var(--theme-texte-secondaire); }
.exv-tp-etape[data-etat="invalide"] .exv-tp-libelle { color: var(--cl-data-invalide); font-weight: var(--wg-texte-medium); }

.exv-tp-temps {
    margin: 0;
    font-size: var(--ht-texte-08);
    color: var(--theme-texte-tertiaire);
    font-variant-numeric: tabular-nums;   /* le décompte ne doit pas trembler à chaque seconde */
    text-align: center;
}

/* L'emplacement DÉDIÉ de l'erreur : elle ne se substitue pas au décompte,
   elle s'ajoute — l'étape qui a échoué reste lisible à côté de son motif. */
.exv-tp-echec {
    margin: 0;
    font-size: var(--ht-texte-08);
    color: var(--cl-data-invalide);
    text-align: center;
}

/* ============ C — SECONDAIRES ============ */

.exv-tp {
    display: flex; flex-direction: column; gap: 10px;
    margin-top: 14px;
    padding: 14px 4px 12px;
    border-top: 1px solid var(--theme-border-color);
}

/* ============ D — STRUCTURELS ============ */

.exv-tp-rail {
    display: flex;
    margin: 0; padding: 0;
    list-style: none;
}

/* Les trois bandes : noeud, icône, libellé — empilés, alignés d'une colonne
   à l'autre parce que chacune a une hauteur propre et fixe. */
.exv-tp-etape {
    position: relative;
    flex: 1 1 0;
    min-width: 0;
    display: flex; flex-direction: column; align-items: center; gap: 6px;
}

/* ---- MOBILE : les trois bandes deviennent trois COLONNES ----
   Au seuil mobile de l'app (640 px, celui d'`analyses.css`), cinq libellés côte
   à côte se chevauchent. La lecture pivote alors d'un quart de tour : la barre
   et ses noeuds à gauche, les icônes au milieu, le libellé à droite. Mêmes
   éléments, mêmes rôles, DOM identique — seules les positions changent. */
@media (max-width: 640px) {
    .exv-tp-rail { flex-direction: column; }

    .exv-tp-etape {
        flex: 0 0 auto;
        display: grid;
        /* 30 px pour la colonne d'icônes, et non 20 : le throbber mesure 29 px
           d'envergure (trois pastilles de 7 px espacées de 11). Rétrécir le
           throbber en aurait fait un second objet ; c'est la gouttière qui cède. */
        grid-template-columns: 13px 30px 1fr;
        column-gap: 12px;
        align-items: center;
        /* 34 px : au-delà du diamètre d'un noeud, pour que le tronçon respire. */
        min-height: 34px;
    }

    /* COLONNE 1 — la barre et ses noeuds. Le tronçon relie toujours au noeud du
       DESSUS ; empilés, ils dessinent le même trajet qu'à l'horizontale. */
    .exv-tp-lien {
        top: auto; right: auto;
        left: 4.5px;
        bottom: 50%;
        width: 4px; height: 100%;
    }
    /* Le trajet parcouru descend du noeud du dessus : c'est la HAUTEUR qui porte
       désormais le remplissage. Mêmes variables, même transition — seul l'axe
       change, comme pour tout le reste de ce pivot. */
    .exv-tp-lien-plein {
        width: 100%;
        height: calc(var(--exv-tp-remplissage, 0) * 100%);
    }
    .exv-tp-point { grid-column: 1; grid-row: 1; justify-self: center; }

    /* COLONNE 2 — les icônes seules. */
    .exv-tp-icone { grid-column: 2; grid-row: 1; }

    /* COLONNE 3 — le libellé, aligné à gauche : centré, en colonne, il donnerait
       une pile en dents de scie. */
    .exv-tp-libelle { grid-column: 3; grid-row: 1; text-align: left; }
}

/* ---- ANIMATIONS (fin de section D) ---- */

/* Fondu du throbber — l'état bas est DÉRIVÉ du token, jamais une seconde
   couleur à maintenir. */
@keyframes exv-tp-battement {
    0%        { background-color: var(--exv-tp-dot, var(--cl-data-attente)); }
    50%, 100% { background-color: color-mix(in srgb, var(--exv-tp-dot, var(--cl-data-attente)) 20%, transparent); }
}

/* Glow du noeud en cours : halo qui s'écarte puis s'éteint, à BORDS FRANCS.
   Même construction que `exv-pulse` de `gbl_styles.css` (pastille « marché
   ouvert ») — un seul vocabulaire de halo dans l'app. Les ombres portées
   diffuses des maquettes sont la manière dont l'outil de dessin rend un halo
   statique, pas une consigne de style. */
@keyframes exv-tp-glow {
    0%   { box-shadow: 0 0 0 2px var(--theme-bg-secondaire), 0 0 0 2px color-mix(in srgb, var(--cl-data-attente) 60%, transparent); }
    70%  { box-shadow: 0 0 0 2px var(--theme-bg-secondaire), 0 0 0 9px transparent; }
    100% { box-shadow: 0 0 0 2px var(--theme-bg-secondaire), 0 0 0 2px transparent; }
}

/* Une attente qui dure plusieurs minutes signifie une animation qui tourne
   plusieurs minutes : c'est exactement le cas où un utilisateur sensible au
   mouvement en souffre. Les COULEURS d'état restent — elles portent
   l'information ; seul le mouvement s'arrête. */
@media (prefers-reduced-motion: reduce) {
    .exv-tp-etape[data-etat="encours"] .exv-tp-point,
    .dot-attente, .dot-attente::before, .dot-attente::after { animation: none; }
    /* La barre avance alors par paliers, d'un relevé à l'autre. Le MOUVEMENT
       s'arrête ; l'information, elle, reste entièrement lisible — c'est la
       position qui la porte, jamais le fait de la voir glisser. */
    .exv-tp-lien-plein { transition: none; }
}
