/* Heop — les trois registres de mouvement.
 *
 * Une seule doctrine : le mouvement sert le geste ou l'arrivee de l'information.
 * Il ne decore jamais. Trois registres, pas un de plus.
 *
 *   1. REPONSE     un geste appelle une reponse — le filet terracotta.
 *   2. ACCUEIL     les surfaces entrent en scene — fade + elevation, en cascade.
 *   3. SURFACES    ce qui vit : squelettes, badge qui change.
 *
 * Interdits, sans exception :
 *   - aucune boucle visible (un mouvement se termine) ;
 *   - rien au-dela de 400ms (au-dela ce n'est plus une reponse, c'est une attente) ;
 *   - transform et opacity UNIQUEMENT — aucun decalage de mise en page, CLS zero ;
 *   - aucun JavaScript maison : CSS pur, et le repli est l'absence d'animation.
 *
 * prefers-reduced-motion coupe TOUT, en fin de fichier.
 */

/* =====================================================================
 * Registre 1 — REPONSE
 * Le filet terracotta qui se deploie sous un element au survol ou au focus.
 * Son intensite dit l'importance : discret sur un lien, franc sur un CTA.
 * ===================================================================== */

/* Pas d'`overflow: hidden` ici. Le filet est deja borne par ses propres
   `left`/`right` et ne deborde jamais : la coupe etait defensive et sans objet.
   Elle avait en revanche une consequence reelle — elle rognait tout ce qui
   depasse volontairement de la boite, a commencer par la pastille du compteur
   de panier, posee a -4px du bord haut. Un gabarit commun qui ne peut pas
   porter un badge n'est pas reutilisable. */
.ui-sweep {
    position: relative;
}

.ui-sweep::after {
    content: "";
    position: absolute;
    right: var(--ui-sweep-inset, 16px);
    bottom: var(--ui-sweep-bottom, 8px);
    left: var(--ui-sweep-inset, 16px);
    height: 2px;
    border-radius: 2px;
    background: var(--ui-color-accent, #C86138);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--ui-motion-response, 220ms) var(--ui-motion-ease, ease);
}

.ui-sweep:hover::after,
.ui-sweep:focus-visible::after,
.ui-sweep:focus-within::after {
    transform: scaleX(1);
}

/* Sur fond fonce, le filet passe en creme pour rester visible. */
.ui-sweep--cream::after {
    background: var(--ui-color-cream, #FFF8EC);
}

/* --- LA REPONSE AU DOIGT, POSEE UNE SEULE FOIS -------------------------
 *
 * MESURE : 122 selecteurs du projet repondent au SURVOL, 5 seulement a
 * l'APPUI. Cent dix-sept elements repondent donc a la souris et pas au doigt.
 * Sur un telephone, ou le survol n'existe pas, ces elements ne repondent a
 * RIEN : on appuie, il ne se passe visiblement rien, l'ecran change une demie
 * seconde plus tard. C'est ce qui fait dire d'une interface tactile qu'elle est
 * « lente » alors qu'elle ne l'est pas — elle est muette.
 *
 * Les corriger un par un demanderait cent dix-sept regles de plus, qui
 * divergeraient. On pose la reponse UNE fois, ici, sur les elements qui sont
 * structurellement des cibles.
 *
 * POURQUOI UNE BAISSE D'OPACITE ET NON UN ENFONCEMENT. Un `scale` est juste sur
 * un bouton ou une carte, et faux sur un lien de texte au milieu d'un
 * paragraphe — le mot rapetisse et la ligne saute. L'opacite, elle, se lit
 * comme une reponse sur n'importe quelle cible sans jamais deplacer quoi que ce
 * soit. Les gabarits de la recette gardent en plus leur enfoncement propre, la
 * ou il a du sens.
 *
 * `any-pointer: coarse` et non `hover: none` : une tablette avec un clavier a
 * les DEUX pointeurs, et son utilisateur touche encore l'ecran. On ajoute une
 * reponse au doigt sans retirer celle de la souris.
 */

@media (any-pointer: coarse) and (prefers-reduced-motion: no-preference) {
    a:active,
    button:active,
    label:active,
    summary:active,
    [role="button"]:active {
        opacity: .72;
        /* Aucune transition a l'appui : la reponse doit etre INSTANTANEE. Une
           reponse qui met 200ms a venir n'est plus une reponse, c'est un
           retard. Le retour, lui, s'adoucit — d'ou la transition portee sur
           l'etat de repos juste en dessous. */
        transition: none;
    }

    a, button, label, summary, [role="button"] {
        transition: opacity 160ms var(--ui-motion-ease, ease);
    }
}

/* =====================================================================
 * Registre 2 — ACCUEIL
 * Les surfaces entrent : elles montent de 12px en s'opacifiant. Au chargement
 * via @starting-style, au defilement via animation-timeline. Les deux sont des
 * ameliorations progressives : un navigateur qui ne les connait pas affiche
 * simplement le contenu, en place, tout de suite. C'est le repli voulu.
 * ===================================================================== */

@supports (transition-behavior: allow-discrete) {
    .ui-enter {
        opacity: 1;
        transform: translateY(0);
        transition:
            opacity var(--ui-motion-entrance, 380ms) var(--ui-motion-ease, ease),
            transform var(--ui-motion-entrance, 380ms) var(--ui-motion-ease, ease);
    }

    @starting-style {
        .ui-enter {
            opacity: 0;
            transform: translateY(var(--ui-motion-lift, 12px));
        }
    }
}

/* La cascade : chaque element d'une liste part legerement apres le precedent.
   Six paliers suffisent — au-dela l'attente se voit, et la sixieme carte n'a
   pas a se faire desirer. */
.ui-cascade > * { --ui-enter-delay: 0ms; }
.ui-cascade > :nth-child(2) { --ui-enter-delay: calc(var(--ui-motion-stagger, 50ms) * 1); }
.ui-cascade > :nth-child(3) { --ui-enter-delay: calc(var(--ui-motion-stagger, 50ms) * 2); }
.ui-cascade > :nth-child(4) { --ui-enter-delay: calc(var(--ui-motion-stagger, 50ms) * 3); }
.ui-cascade > :nth-child(5) { --ui-enter-delay: calc(var(--ui-motion-stagger, 50ms) * 4); }
.ui-cascade > :nth-child(n + 6) { --ui-enter-delay: calc(var(--ui-motion-stagger, 50ms) * 5); }

/* Descendant, pas enfant direct. La version precedente exigeait `>`, et la
   seule cascade du projet ne se declenchait donc jamais : sur l'ecran
   d'inscription, les boutons .ui-enter sont dans un <form>, donc petits-enfants
   du .ui-cascade. Le delai, lui, arrivait bien jusqu'a eux — c'est une propriete
   personnalisee, elle herite. Seule la regle qui la CONSOMME ratait sa cible. */
@supports (transition-behavior: allow-discrete) {
    .ui-cascade .ui-enter {
        transition-delay: var(--ui-enter-delay, 0ms);
    }
}

/* Au defilement, sans le moindre JavaScript : la vue elle-meme pilote la
   progression. Non supporte = le contenu est simplement la, ce qui reste un
   comportement correct. */
@supports (animation-timeline: view()) {
    .ui-reveal {
        animation: ui-reveal-in linear both;
        animation-timeline: view();
        animation-range: entry 0% entry 42%;
    }

    @keyframes ui-reveal-in {
        from {
            opacity: 0;
            transform: translateY(var(--ui-motion-lift, 12px));
        }
        to {
            opacity: 1;
            transform: translateY(0);
        }
    }
}

/* =====================================================================
 * Registre 3 — SURFACES VIVANTES
 * Ce qui charge, et ce qui vient de changer.
 * ===================================================================== */

/* Le squelette : un reflet qui balaie une surface en attente. C'est la SEULE
   animation repetee du systeme, et elle est legitime — elle dit « ca arrive ».
   Elle disparait avec le contenu. */
.ui-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--ui-plan-sunk, #F4EBD9);
    border-radius: var(--ui-radius-card, 14px);
}

.ui-skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(
        90deg,
        transparent 0%,
        rgba(255, 253, 247, .68) 50%,
        transparent 100%
    );
    animation: ui-shimmer 1400ms var(--ui-motion-ease, ease) infinite;
}

@keyframes ui-shimmer {
    to { transform: translateX(100%); }
}

/* Le badge qui vient de changer bat UNE fois. Pas de boucle : un compteur qui
   clignote sans fin devient du bruit, et on cesse de le voir. */
.ui-pulse-once {
    animation: ui-pulse-once 320ms var(--ui-motion-ease, ease) 1;
}

@keyframes ui-pulse-once {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.18); }
    100% { transform: scale(1); }
}

/* =====================================================================
 * Le seul interrupteur qui compte
 * ===================================================================== */

@media (prefers-reduced-motion: reduce) {
    .ui-sweep::after,
    .ui-enter,
    .ui-cascade .ui-enter {
        transition: none;
    }

    .ui-sweep::after {
        transform: scaleX(1);
    }

    .ui-reveal,
    .ui-skeleton::after,
    .ui-pulse-once {
        animation: none;
    }

    .ui-skeleton::after {
        opacity: 0;
    }
}
