/* images.css — états de chargement/erreur d'image et fondus d'apparition.
   Chargé en dernier : ces règles (`img`, `.fade-img`) doivent passer devant celles
   des fichiers précédents. Les media queries suivent chaque composant, dans son
   propre fichier. */

/* Image manquante (fichier supprimé/déplacé après coup, entrée du CMS
   pointant vers un chemin qui n'existe plus...) : la source est remplacée
   par un SVG vide/transparent (voir onImageError, js/images.js) — ce fond
   gris est donc ce qui reste visible, même teinte que le reste du site
   pendant un chargement (--surface), pas un gris distinct dédié à
   l'erreur. */
.img-missing {
    background: var(--surface);
}

/* Image pas encore chargée (requête /.netlify/images en cours, surtout avec
   loading="lazy") : tant qu'elle n'a pas de pixels à afficher, le navigateur
   affiche par défaut le texte alt — qui hérite du style de lien (bleu
   souligné) quand l'image est dans un <a> (grilles, panier, produits
   associés). On le rend invisible ; l'espace est déjà réservé ailleurs
   (aspect-ratio ou width/height fixes), donc aucun saut de mise en page. */
img {
    color: transparent;
    text-decoration: none;
}

/* Fondu d'apparition des images : invisibles tant que la classe "loaded"
   n'est pas posée — par revealImagesOnScroll pour les galeries (quand
   l'image est visible, voir js/images.js), par revealInOrder pour
   les vignettes et le slider, par js/lightbox.js pour la lightbox. Espace déjà réservé
   (aspect-ratio), donc aucun saut de mise en page pendant le fondu. */
.fade-img {
    opacity: 0;
    transition: opacity .35s ease;
}

.fade-img.loaded {
    opacity: 1;
}

/* Fondu + légère montée : image principale d'une fiche produit (rejoué à
   chaque changement de vignette, voir swapImage, js/product-shared.js),
   images de galerie, slider.
   Double classe pour passer devant `.fade-img` (transition d'opacité seule)
   quelle que soit la place dans le fichier. */
.fade-img.fade-rise {
    transition: opacity .6s ease, transform .6s cubic-bezier(.2, .7, .2, 1);
}

/* Seulement tant que non chargée : une fois `.loaded`, aucun transform ne
   s'applique, ce qui laisse le zoom au survol (.zoom-frame, components.css)
   s'appliquer sur les mêmes images. */
.fade-img.fade-rise:not(.loaded) {
    transform: translateY(18px);
}

/* Textes d'une carte (titre, prix, pastille) : apparaissent en même temps que leur
   image, avec le même mouvement (fondu + montée). Pas de JS : la carte est « en
   attente » tant qu'une de ses images n'a pas `.loaded`. */
:is(.original-card, .print-card, .similar-card) :is(.card-meta, .card-badge) {
    transition: opacity .6s ease, transform .6s cubic-bezier(.2, .7, .2, 1);
}

:is(.original-card, .print-card, .similar-card):has(img:not(.loaded)) :is(.card-meta, .card-badge) {
    opacity: 0;
    transform: translateY(18px);
}

/* .img-disabled (print épuisé, grisé à 50%) doit garder son opacité réduite
   une fois chargée — sans ce override, .fade-img.loaded (plus spécifique
   que .img-disabled seul) la repasserait à 100%. */
.fade-img.loaded.img-disabled {
    opacity: 0.5;
}
