/* =====================================================================
   STELVOREN — FICHE PRODUIT, SQUELETTE (tâche 70)

   Mesures relevées sur les frames Figma 118:5827 (desktop, 1840) et
   118:5671 (mobile, 393) : contenu 1680, colonne galerie ≈ 960, colonne
   résumé 672, gouttière 48. La somme tombe juste — 960 + 48 + 672 =
   1680 — et ce n'est pas un hasard : la barre d'achat collante de la
   tâche 76 fait elle aussi 672, parce qu'elle s'aligne sur cette
   colonne.

   Ce fichier ne porte QUE la mise en page du squelette. L'habillage des
   blocs arrive avec eux : galerie (71), résumé (72), achat (73), puces
   (75), barre (76), onglets (77), collection (78).

   Les jetons font foi (assets/css/jetons.css). Aucune valeur en dur
   qui existe déjà comme variable.
   ================================================================== */

/* ---------------------------------------------------------------------
   La marge latérale, en une variable

   Échelle décidée pour l'accueil et reprise ici : 80 / 48 / 32 / 20.
   Une variable plutôt qu'une règle par bloc : les quatre sections de la
   fiche doivent s'aligner au pixel, et trois déclarations séparées
   finissent toujours par diverger.
   ------------------------------------------------------------------ */

/*
   LE PLAFOND DE HELLO ELEMENTOR.

   `main.site-main` reçoit une largeur maximale des réglages de mise en
   page du thème parent (Hello Elementor → Réglages → Mise en page,
   largeur de contenu, 1140 par défaut). Notre grille a beau viser 1680,
   elle est enfermée bien avant — constaté le 15/09 sur la fiche, contenu
   rendu à ~1110 px au lieu de 1680.

   On lève le plafond LÀ OÙ IL EST, sur la seule page produit, plutôt que
   de changer un réglage global qui toucherait toutes les pages du site
   et qu'il faudrait re-capturer. Même raisonnement que pour le hero,
   dont le plafond venait d'Elementor.

   Classe répétée : le sélecteur du parent n'est pas connu avec certitude
   et peut changer d'une version à l'autre. Deux fois la même classe pèse
   0,2,0 et passe devant tout ce que Hello écrit avec une classe et un
   élément — c'est l'idiome de `piege-specificite-elementor.md`.
*/
main.stv-fiche-page.stv-fiche-page {
  max-width: none;
  width: 100%;
  padding-inline: 0;

  /*
     LE FOND DE LA SECTION — relevé au screenshot Figma du 15/09.

     Gris clair du bandeau du haut jusqu'au bas des onglets. « Dans la
     même collection » repasse en blanc plus bas ; la réassurance vit
     hors de `main` et garde son propre fond bleu pétrole.

     Posé ICI plutôt que sur une enveloppe dédiée : le fil d'Ariane est
     rendu par `single-product.php` et les onglets par
     `content-single-product.php`, donc aucun élément commun ne les
     contient tous les deux sans toucher aux deux gabarits. `main` les
     contient déjà.
  */
  background: var(--stv-gris-clair);

  /*
     LES 48 DU HAUT SONT UN PADDING, PAS UNE MARGE SUR LE FIL.

     Elles l'étaient, et ça se voyait : une marge haute portée par le
     PREMIER enfant d'un conteneur sans bordure ni marge intérieure
     FUSIONNE avec celle du conteneur et ressort de lui. Les 48 px
     poussaient donc `main` vers le bas au lieu de s'y ajouter, et la
     bande ainsi libérée montrait le fond de page — un ruban blanc au
     ras du menu, là où la maquette veut du gris.

     Un padding ne fusionne jamais. C'est aussi ce qui referme la boîte
     de `main` : tant qu'elle n'a ni bordure ni padding en haut, toute
     marge de premier enfant s'échappera de la même façon.
  */
  padding-block-start: var(--stv-espace-48);
}

/*
   … et la collection reprend le blanc : pleine largeur dans `main`, son
   propre fond suffit à couper le gris. Ses règles vivent avec elle, dans
   la section « Dans la même collection » en fin de fichier (78).
*/
.stv-fiche-page {
  --stv-fiche-marge: var(--stv-espace-80);

  /* Vignettes de la galerie — relevé `118:5841` et `118:5848`. Déclarées
     ici et non sur `.stv-galerie` : la grille les lit aussi. */
  --stv-vignette: 64px;
  --stv-vignette-gouttiere: var(--stv-espace-16);
}

@media (max-width: 1439px) {
  .stv-fiche-page { --stv-fiche-marge: var(--stv-espace-48); }
}

@media (max-width: 1024px) {
  .stv-fiche-page { --stv-fiche-marge: var(--stv-espace-32); }
}

@media (max-width: 767px) {
  .stv-fiche-page { --stv-fiche-marge: 20px; }
}

/* ---------------------------------------------------------------------
   Le bandeau de contenu

   Marge et plafond sur le MÊME élément, plutôt qu'une enveloppe et un
   intérieur comme le pied : le plafond intègre les deux marges, donc la
   zone de contenu fait bien 1680 et non 1680 moins les marges. C'est la
   seule subtilité de ce fichier, et elle vaut mieux qu'un div de plus
   dans chaque section.
   ------------------------------------------------------------------ */

.stv-fil,
.stv-fiche__grille {
  box-sizing: border-box;
  max-width: calc(var(--stv-largeur-contenu) + 2 * var(--stv-fiche-marge));
  margin-inline: auto;
  padding-inline: var(--stv-fiche-marge);
}

/*
   LES ONGLETS ET LA COLLECTION SONT SORTIS DE CE BANDEAU le 15/09, et
   pas pour la même raison.

   Les onglets sont une CARTE BLANCHE sur le gris (`118:5911`, 1680 de
   large, 48 de marge intérieure) : leur fond s'arrête à la largeur de
   contenu, donc leur boîte fait 1680 et non 1680 + les gouttières.

   La collection est une SECTION BLANCHE pleine largeur (`118:5929`) :
   son fond va d'un bord à l'autre de l'écran, et c'est son contenu qui
   est capé. Avec l'ancien bandeau, un écran de 2560 laissait 360 px de
   gris de chaque côté.

   `width: calc(100% - 2 * marge)` plutôt que `padding-inline` : la
   gouttière reste à l'EXTÉRIEUR du fond blanc, ce que la maquette
   demande, alors qu'un padding l'aurait peinte en blanc.
*/
.stv-fiche__onglets {
  box-sizing: border-box;
  width: calc(100% - 2 * var(--stv-fiche-marge));
  max-width: var(--stv-largeur-contenu);
  margin-inline: auto;
  background: var(--stv-blanc);
}

/* ---------------------------------------------------------------------
   Le fil d'Ariane — relevé `118:5831`

   Inter Light 14 en noir plein pour les maillons cliquables, Inter
   Regular 14 à 50 % pour le dernier. Le contraste s'inverse donc par
   rapport à ce qui était posé : les liens étaient à 80 % et le dernier
   à 70 %, presque indistincts.

   Marges : 8 px au-dessus et en dessous du texte (`py-8` de la
   maquette), et 48 px entre le haut de la section et le fil — la
   frame `118:5830` commence à y = 48 dans `118:5829`.

   La maquette ne montre pas le nom du produit dans le fil ; ce point
   attend la réponse du designer (tâche 67), donc rien n'est retiré.
   ------------------------------------------------------------------ */

/*
   LE DERNIER MAILLON N'EST PAS UN ÉLÉMENT — c'est un nœud de texte nu.

   `woocommerce/breadcrumb.php` n'enveloppe pas le dernier maillon : il
   l'écrit en texte, sans balise. Aucun sélecteur CSS ne peut l'atteindre,
   et les deux sélecteurs posés à la 70 pour le griser
   (`.breadcrumb_last`, `> span > span:last-child`) ne touchaient rien —
   vérifié en façade le 15/09 : zéro correspondance.

   On prend donc le problème dans l'autre sens. Le NAV porte la couleur
   du dernier maillon, et ce sont les LIENS qui remontent en noir plein.
   Le texte nu hérite, sans qu'on ait à le nommer. Effet de bord
   bienvenu : les chevrons séparateurs prennent eux aussi le ton pâle,
   ce que fait la maquette.

   `line-height: normal` et non l'interligne de texte : la maquette donne
   un fil de 33 px de haut pour 8 px de marge au-dessus et en dessous,
   soit 17 px de texte — du 14 px à 1,2, pas à 1,5.
*/
.stv-fil {
  padding-block: var(--stv-espace-8);
  font-size: var(--stv-taille-petit);
  font-weight: 400;
  line-height: normal;
  color: var(--stv-texte-tertiaire);
}

/*
   CLASSE RÉPÉTÉE SUR LE NAV — huitième occurrence du piège.

   `.stv-fil a` pèse 0,1,1, exactement comme `.elementor-kit-44301 a`.
   À égalité, c'est l'ordre de chargement qui tranche, et le kit passe
   après le thème : les maillons sortaient en Regular avec l'interligne
   du kit, d'où un fil de 40 px là où la maquette en donne 33.

   Le symptôme est sournois parce que la COULEUR passait — le kit n'en
   pose pas sur `a` — et qu'une couleur juste avec une graisse fausse ne
   se lit pas sur une capture. `.stv-fil.stv-fil a` pèse 0,2,1 et
   reprend la main sur les trois propriétés.
*/
.stv-fil.stv-fil a {
  color: var(--stv-texte);
  /*
     `font-size` REDÉCLARÉE, alors que le nav la porte déjà.

     L'héritage est la plus faible des sources : n'importe quelle
     déclaration visant `a` directement l'emporte, quelle que soit sa
     spécificité. Le kit en pose une — les maillons sortaient à 16 px
     dans un fil réglé à 14. C'est la variante silencieuse du piège de
     spécificité : ici ce n'est pas un sélecteur plus lourd qui gagne,
     c'est le simple fait qu'une valeur héritée n'est pas une valeur
     déclarée.

     Corollaire pour la suite : dans un bloc où le kit peut écrire sur
     `a`, `button` ou un titre, il ne suffit pas de régler le parent.
  */
  font-size: var(--stv-taille-petit);
  font-weight: 300;
  line-height: normal;
  text-decoration: none;
}

.stv-fil.stv-fil a:hover,
.stv-fil.stv-fil a:focus-visible {
  text-decoration: underline;
}

/* Yoast, lui, enveloppe le dernier maillon. Il hérite déjà du bon ton ;
   la règle reste pour le cas où un lien s'y glisserait. */
.stv-fil .breadcrumb_last,
.stv-fil a.breadcrumb_last {
  color: var(--stv-texte-tertiaire);
  font-weight: 400;
}

/*
   MOBILE : UNE SEULE LIGNE, QUI GLISSE SOUS LE DOIGT — relevé `118:5675`.

   La maquette mobile ne replie pas le fil : 12 px, écart 8, et un fil
   plus large que l'écran (444 px de texte pour 353 de large) dont la
   fin disparaît sous un FONDU vers le gris de la section — le nœud
   `118:5683`, un dégradé de transparent à `#f2f6f8` sur les 18 derniers
   pour cent.

   Le glissement est natif : `overflow-x: auto` et rien d'autre, pas une
   ligne de script. Le fondu est un MASQUE (`mask-image`) et non un
   calque posé par-dessus : un masque s'applique à la boîte visible de
   l'élément et reste donc à sa place quand le contenu défile, là où un
   `::after` en position absolue défilerait avec le texte. Le fondu est
   permanent, comme dans la maquette — y compris une fois arrivé au
   bout. Le faire disparaître à la fin du défilement demanderait un
   écouteur `scroll` ; on ne l'ajoute pas pour 18 % d'une ligne de 12 px.

   Le dégradé part de la couleur de section (`--stv-gris-clair`) : si
   le fond de la fiche change, c'est le jeton qui suit, pas ce bloc.

   `padding-inline` conservé : la marge latérale de la page reste la
   même, et le début du fil s'aligne sur la galerie en dessous.
*/
@media (max-width: 767px) {
  .stv-fil {
    /* 15 px de haut dans la maquette (`118:5675`) : le texte seul, sans
       les 8 px de marge du desktop. Le rythme vertical mobile est posé
       plus bas, avec la grille. */
    padding-block: 0;
    font-size: var(--stv-taille-copyright);
    white-space: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, #000 81.6%, transparent);
    mask-image: linear-gradient(to right, #000 81.6%, transparent);
  }

  .stv-fil::-webkit-scrollbar {
    display: none;
  }

  /*
     L'espace de fin. Un conteneur qui défile en ligne ignore sa marge
     intérieure droite dans plusieurs navigateurs : sans cet élément
     vide, le dernier maillon vient buter contre le bord de l'écran une
     fois le fil tiré au bout. Il mesure la marge de page, donc la fin
     du fil s'aligne aussi.
  */
  .stv-fil::after {
    content: "";
    display: inline-block;
    width: var(--stv-fiche-marge);
    height: 1px;
  }

  /* Regular partout en mobile (`118:5676`), et non Light comme en
     desktop : à 12 px, le Light ne tient plus le contraste. */
  .stv-fil.stv-fil a {
    font-size: var(--stv-taille-copyright);
    font-weight: 400;
  }
}

/* ---------------------------------------------------------------------
   La grille galerie | résumé

   Colonnes proportionnelles et non en pixels : entre 980 et 1680 elles
   se resserrent ensemble au lieu de déborder. Même raisonnement que la
   grille du pied.

   L'IMAGE TIENT DANS LA FENÊTRE — demande de Logan du 15/09.

   La maquette est dessinée à 1840 × 4578 : une frame sans hauteur de
   fenêtre. Sur un écran de 1920 × 937, la grande image de 880 dépasse
   le bas de l'écran et se retrouve coupée ; c'est le cas courant d'un
   portable. La règle : la colonne galerie prend la plus PETITE de deux
   largeurs — sa part proportionnelle (960 sur 1632 du contenu hors
   gouttière, la proportion de la maquette) ou ce que la hauteur
   disponible autorise pour une image carrée. La colonne de droite
   prend tout le reste (`1fr`), et non plus 672 fixes.

   La hauteur disponible se calcule depuis les jetons : barre d'annonce,
   en-tête, les 48 du haut de section, le fil (33, sa hauteur relevée),
   les 24 au-dessus de la grille, et 48 de respiration sous l'image. La
   barre d'administration WordPress s'ajoute quand elle est là — c'est
   la variable que WordPress pose lui-même sur `html`. La colonne étant
   plus large que l'image de la colonne de vignettes et de sa gouttière
   (80), ces 80 sont rendus au calcul ; sur une galerie à une seule image
   il n'y a pas de colonne de vignettes, et `.stv-fiche--galerie-unique`
   les retire.

   `100vh` et non `100dvh` : la règle ne s'applique qu'à partir de 980,
   là où la barre d'adresse mobile n'existe pas. Sur un grand écran,
   `min()` garde la proportion de la maquette : la hauteur ne contraint
   que lorsqu'elle manque.

   CONSÉQUENCE POUR LA 76, plus vraie encore : la colonne de droite ne
   fait 672 px que sur un écran assez haut ET assez large. La barre
   d'achat collante devra lire la largeur réelle de `.stv-fiche__resume`.
   ------------------------------------------------------------------ */

.stv-fiche__grille {
  --stv-fiche-haut: calc(
    var(--wp-admin--admin-bar--height, 0px)
    + var(--stv-annonce-hauteur)
    + var(--stv-entete-hauteur)
    + var(--stv-espace-48)
    + 33px
    + var(--stv-espace-24)
  );
  --stv-galerie-decalage: calc(var(--stv-vignette) + var(--stv-vignette-gouttiere));
  --stv-galerie-plafond: calc(100vh - var(--stv-fiche-haut) - var(--stv-espace-48) + var(--stv-galerie-decalage));
  --stv-galerie-part: calc((100% - var(--stv-gouttiere)) * 960 / 1632);

  display: grid;
  grid-template-columns: minmax(0, min(var(--stv-galerie-part), var(--stv-galerie-plafond))) minmax(0, 1fr);
  gap: var(--stv-gouttiere);
  align-items: start;
  /*
     24 au-dessus, et non 48 : les 48 du haut de section sont passés au
     fil d'Ariane, qui est désormais le premier élément de la page.
     Relevé `118:5839`, à y = 57 dans une frame dont le fil occupe les
     33 premiers pixels.
  */
  padding-block: var(--stv-espace-24) var(--stv-section-y);
}

.stv-fiche--galerie-unique .stv-fiche__grille {
  --stv-galerie-decalage: 0px;
}

/* Empilement sous 980 : la règle de la tâche 8, reprise telle quelle. */
@media (max-width: 979px) {
  .stv-fiche__grille {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--stv-espace-32);
  }
}

/*
   LE RYTHME VERTICAL MOBILE — relevé `118:5671`, signalé par Logan le
   15/09. Les valeurs desktop (48 / 8 / 24 / 80 / 48) ne descendent pas
   telles quelles sur 393 px : la maquette mobile resserre tout.

     y = 86    fin de l'en-tête (30 + 56, les jetons mobiles)
     +12       marge haute de la section          → `main`, 12 et non 48
     15        le fil, texte seul                  → `.stv-fil`, plus haut
     +12       avant la galerie                    → grille, 12 et non 24
     580       la galerie
     +32       avant le résumé                     → `gap` de la grille, déjà 32
     …
     +32       sous la fiche, avant la carte       → grille, 32 et non 80
     16        marge intérieure de la carte        → onglets, 16 et non 48
     +32       sous la carte                       → onglets, 32 et non 80

   Point de rupture à 767 comme pour les jetons : entre 768 et 979 la
   grille est déjà empilée mais garde les espacements desktop — la
   maquette n'a pas de frame tablette, et c'est la lecture la plus
   proche des deux qu'elle a.
*/
@media (max-width: 767px) {
  main.stv-fiche-page.stv-fiche-page {
    padding-block-start: var(--stv-espace-12);
  }

  .stv-fiche__grille {
    padding-block: var(--stv-espace-12) var(--stv-espace-32);
  }

  /* La carte des onglets (16 / 32) est réglée à côté de sa règle
     desktop, plus bas : à poids égal, c'est l'ordre qui tranche. */
}

.stv-fiche__galerie,
.stv-fiche__resume {
  min-width: 0; /* sans quoi une image large fait déborder la colonne */
}

/*
   LA COLONNE DE DROITE EST UNE SEULE COLONNE À 24 — relevé 72.

   La maquette ne sépare pas « le résumé » et « l'achat » : `118:5861`
   est un unique empilement à écart 24, du nom de marque jusqu'à la
   liste « Choisir une option ». Nos blocs sont découpés par tâche — 72
   le résumé, 73 le formulaire, 74 Alma, 75 les puces — mais le rythme,
   lui, appartient à la colonne, pas aux blocs. Il est donc posé ICI, une
   fois, et aucun des quatre n'aura à porter de marge basse.
*/
.stv-fiche__resume {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-24);
}

/* ---------------------------------------------------------------------
   Le rythme vertical des sections basses
   ------------------------------------------------------------------ */

/*
   Les onglets : 48 de marge intérieure sur les quatre côtés (relevé
   `118:5912`, premiers enfants à x = 48 et y = 48), et 80 sous la carte
   avant la section suivante — les 80 du dessus viennent déjà du bas de
   la grille.
*/
.stv-fiche__onglets {
  padding: var(--stv-espace-48);
  margin-block-end: var(--stv-section-y);
}

/* Mobile — relevé `118:5749` : 16 de marge intérieure, 32 dessous.
   ICI et non dans le bloc mobile de la grille : même poids que la règle
   du dessus, donc elle doit venir après. */
@media (max-width: 767px) {
  .stv-fiche__onglets {
    padding: var(--stv-espace-16);
    margin-block-end: var(--stv-espace-32);
  }
}

/*
   `:empty` A ÉTÉ RETIRÉ LE 15/09 — il ne marchait pas.

   Un gabarit PHP laisse des sauts de ligne et des tabulations entre ses
   balises, et en local `stelvoren_fiche_partie()` dépose en plus un
   commentaire HTML. Chrome ne considère pas un élément à espaces
   blancs comme vide : la règle n'a jamais rien masqué depuis la 70.
   Invisible tant que ces conteneurs n'avaient ni fond ni marge — la
   collection est devenue une bande blanche de 160 px le jour où elle a
   reçu les deux.

   C'est `stelvoren_fiche_section()` qui décide maintenant, en PHP :
   pas de contenu, pas de conteneur. Une règle CSS ne peut pas juger du
   vide, le PHP si.
*/

/* ---------------------------------------------------------------------
   Les zones d'injection

   Volontairement SANS plafond ni marge latérale : la section Elementor
   que le client y dépose porte ses propres réglages de largeur, et la
   contraindre ici lui interdirait le pleine-largeur. On ne règle que le
   rythme vertical.
   ------------------------------------------------------------------ */

/*
   LA ZONE N'ÉCARTE PLUS RIEN — 18/09.

   Elle posait 80 de marge haute et basse. Or un bloc injecté est un
   conteneur Elementor, et le kit donne aux conteneurs une marge
   INTÉRIEURE de 80 en desktop, 32 en tablette, 20 en mobile. Les deux
   s'additionnaient : 160 px au-dessus des cartes services, pour 80
   voulus par la maquette.

   Et depuis que ces blocs portent un fond — les cartes services sont
   sur du blanc alors que la fiche est sur du gris —, une marge posée
   DEHORS laisse voir la couleur de la page entre la section précédente
   et le fond du bloc. Le bloc doit donc aller bord à bord, et porter
   lui-même sa respiration.

   La règle est ainsi : l'espacement d'un bloc injecté appartient au
   bloc, jamais à la zone. C'est aussi ce qui permet au client de le
   régler dans Elementor sans qu'on ait à toucher au thème.
*/

/* Dans la colonne résumé, la zone reste dans le flux de la colonne et
   n'a pas à s'écarter autant. */
/*
   Dans la colonne de droite, l'écart de 24 vient maintenant de la
   colonne elle-même (voir plus haut) : ce complément de 8 porte le
   total à 32 et garde le bloc client visiblement détaché du produit,
   ce qui était l'intention de la 70.
*/
.stv-fiche__resume .stv-fiche__zone {
  margin-block: var(--stv-espace-8) 0;
}

/* ---------------------------------------------------------------------
   Réassurance — PLUS RIEN ICI, le 17/09.

   La fiche en rendait une copie ; elle vient du pied de page, pour
   toutes les pages du site (`stelvoren_pied_reassurance()`), et c'est
   `section.stv-reassurance` qui porte son habillage. Le gabarit et la
   règle `.stv-fiche__reassurance > *` sont partis ensemble.
   ------------------------------------------------------------------ */

/* =====================================================================
   LA GALERIE — tâche 71

   Frames 118:5827 (desktop) et 118:5671 (mobile). Colonne de vignettes
   à gauche, grande image carrée sur `--stv-gris-clair` (#f2f6f8), badge
   en haut à gauche, flèches en bas au centre, plein écran en bas à
   droite. Mobile : une image en glissement, compteur.

   MESURES CORRIGÉES LE 15/09, AU RELEVÉ DEV MODE. La première version
   posait des vignettes de 80 px et une gouttière de 24 : c'était un
   relevé à l'œil dans Chrome, noté « ≈ 80 px » dans le plan du 14/09.
   Dev Mode donne `118:5840` = 960 de large, `118:5841` = **64**, et la
   grande image `118:5848` à **x = 80** — soit 64 + 16 + 880 = 960, une
   somme qui tombe juste, ce que 80 + 24 + 856 ne faisait pas.

   L'écart vertical entre vignettes est relevé à 10,667 px — la trace
   d'un composant dessiné à 96 px puis mis à l'échelle ⅔. On garde le
   jeton 12 : l'écart au dessin est de 1,3 px par vignette, invisible,
   et une valeur hors échelle dans la feuille coûterait plus cher à
   maintenir qu'elle ne rapporte.

   LE CAS COURANT N'EST PAS CELUI QUI EST DESSINÉ. Relevé 68 : 133
   produits sur 476 n'ont qu'UNE image (28 %), 287 en ont deux à quatre,
   56 seulement en ont cinq ou plus. Les états dégradés ne sont donc pas
   une finition, c'est le régime normal — d'où le modificateur
   `--unique`, et une colonne de vignettes qui se contente de ce qu'elle
   a au lieu de réserver cinq emplacements.
   ================================================================== */

/*
   PAS UNE GRILLE, ET C'EST VOULU.

   La colonne de vignettes doit faire exactement la hauteur de la grande
   image. En grille, c'est impossible à écrire proprement : la hauteur de
   la rangée est le maximum des deux éléments, donc douze vignettes
   étireraient la rangée et l'image carrée avec elle. On tourne en rond.

   On sort donc la colonne du flux. La grande image devient le seul
   enfant en flux : la hauteur de la galerie est la sienne, et la colonne
   s'y ajuste au pixel par `top: 0; bottom: 0`. Un décalage à gauche
   réserve sa place.

   Effet de bord heureux : à pleine largeur l'image fait 880 px de haut,
   soit **onze** vignettes de 64 px au lieu des cinq que tenait la
   hauteur fixe. Le défilement ne sert donc plus que dans les cas
   extrêmes — 56 produits sur 476 passent cinq images, aucun ne passe
   onze — ce qui était la demande.
*/
.stv-galerie {
  /*
     Les mesures des vignettes (`--stv-vignette`, `--stv-vignette-gouttiere`)
     sont déclarées sur `.stv-fiche-page`, en tête de fichier : la grille
     en a besoin pour calculer la largeur de colonne qui fait tenir
     l'image dans la fenêtre. Ici on ne garde que ce qui n'a pas à
     remonter.
  */
  /* `rgba(0,29,61,0.25)` — c'est le jeton `--stv-pastille` à 0,25 au
     lieu de 0,1. Deux opacités du même bleu, pas deux couleurs. */
  --stv-vignette-filet: rgba(0, 29, 61, 0.25);

  position: relative;
}

.stv-galerie--multiple {
  padding-left: calc(var(--stv-vignette) + var(--stv-vignette-gouttiere));
}

/* --- La colonne de vignettes ---------------------------------------- */

.stv-galerie__vignettes {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--stv-vignette);
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-12);
  overflow-y: auto;
  /*
     `clip` EXPLICITE sur l'axe horizontal. La règle CSS est contre-
     intuitive : dès qu'un axe n'est plus `visible`, l'autre ne peut plus
     l'être non plus et bascule en `auto`. Déclarer seulement
     `overflow-y: auto` créait donc une barre horizontale en prime — c'est
     ce qu'on voyait le 15/09, deux barres pour un débordement de 2 px.
  */
  overflow-x: clip;
  /*
     Barre masquée, comme sur la piste principale : la maquette n'en
     dessine pas, et une barre de 8 px dans une colonne de 80 se voit
     plus qu'elle ne sert.

     CE QUE ÇA COÛTE, et pourquoi ce n'est pas gratuit : plus rien
     n'annonce visuellement qu'il y a une sixième vignette. La
     contrepartie est dans galerie.js, qui fait suivre la colonne à
     l'image affichée — au clic sur une flèche, au glissement, au
     clavier. La vignette active est donc TOUJOURS visible, et c'est ce
     mouvement qui dit qu'il y a autre chose en dessous.

     La colonne étant en `position: absolute`, elle est d'office le
     repère de `offsetTop` de ses vignettes — c'est ce dont ce calcul a
     besoin.
  */
  scrollbar-width: none;
  overscroll-behavior: contain;
}

.stv-galerie__vignettes::-webkit-scrollbar {
  display: none;
}

/*
   CLASSE RÉPÉTÉE, ET CE N'EST PAS UNE COQUETTERIE.

   Le kit Elementor écrit `.elementor-kit-44301 button` — une classe et
   un élément, poids 0,1,1. Une règle à classe unique perd, et les
   vignettes sortaient avec le fond sombre du kit et son survol rose.
   Septième occurrence du piège décrit dans
   `piege-specificite-elementor.md` ; la barre de flèches du thème s'en
   protège déjà par `.stv-fleches .stv-fleches__bouton`.

   Classe répétée plutôt qu'ancêtre de portée : la galerie ne vit pas
   forcément dans un seul conteneur — le plein écran, demain, pourra la
   rendre ailleurs.
*/
.stv-galerie__vignette.stv-galerie__vignette {
  /*
     `border-box` EXPLICITE, et c'est la cause du défaut du 15/09 : sans
     lui, la bordure d'un pixel s'ajoutait aux 80 px, chaque vignette
     faisait 82, et cinq d'entre elles dépassaient de 10 px la hauteur
     calculée pour en contenir exactement cinq. D'où la barre verticale
     là où il ne devait pas y en avoir — et, par la règle rappelée
     au-dessus, la barre horizontale avec.

     On ne se repose pas sur une règle globale : le thème enfant rend des
     composants qui vivent aussi hors des pages Elementor.
  */
  box-sizing: border-box;
  flex: 0 0 var(--stv-vignette);
  width: var(--stv-vignette);
  height: var(--stv-vignette);
  padding: 0;
  /*
     Bordure au repos : `rgba(0,29,61,0.25)`, relevée sur `118:5844`.
     Elle est POSÉE, pas transparente — une vignette sans contour se
     confond avec le fond gris de la section.
  */
  border: 1px solid var(--stv-vignette-filet);
  border-radius: var(--stv-rayon);
  background: var(--stv-blanc);
  color: inherit;
  cursor: pointer;
  transition: border-color 0.15s ease;
}

/* Le survol du kit est rose : on pose explicitement le nôtre. */
.stv-galerie__vignette.stv-galerie__vignette:hover,
.stv-galerie__vignette.stv-galerie__vignette:focus {
  background: var(--stv-blanc);
  border-color: var(--stv-texte-secondaire);
}

/*
   L'ACTIVE : 2 px d'anthracite. La maquette dessine 1,33 px — c'est un
   composant à l'échelle ⅔, donc 2 px à la source, et 1,33 ne se rend
   proprement sur aucun écran non-Retina. On garde l'entier, et c'est la
   COULEUR qui porte l'état de toute façon : anthracite plein contre un
   bleu à 25 %.

   `box-sizing: border-box` est déjà posé plus haut : la vignette active
   ne grandit donc pas d'un pixel en devenant active — c'est l'image
   intérieure qui perd 2 px, ce que la maquette fait aussi.
*/
.stv-galerie__vignette.stv-galerie__vignette[aria-current="true"] {
  border-width: 2px;
  border-color: var(--stv-anthracite);
}

.stv-galerie__vignette.stv-galerie__vignette:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

.stv-galerie__vignette-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* --- La scène -------------------------------------------------------- */

/*
   LA SCÈNE EST BLANCHE, LA SECTION EST GRISE — relevé du 15/09.

   C'est l'inverse de ce qui était posé, et l'inverse de ce que disait le
   plan du 14/09 (« grande image carrée sur #f2f6f8 »), qui lisait un
   aplat gris sur une maquette zoomée sans voir qu'il appartenait à la
   section et non à l'image. Le contraste de la maquette vient de là :
   une carte blanche posée sur un fond gris.

   `mix-blend-mode` retiré en même temps. Il servait à noyer le fond
   blanc des photos dans le gris — sur un fond blanc il n'a plus d'objet,
   et il ne coûtait pas rien : il assombrit toute photo prise sur fond
   sombre, et il crée un contexte d'empilement qui complique le plein
   écran.
*/
.stv-galerie__scene {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--stv-blanc);
}

.stv-galerie__image {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
}

/*
   La piste. Zone défilante à accroche : elle fonctionne au doigt, à la
   molette et au clavier SANS JavaScript. Les flèches et le compteur sont
   un supplément — c'est ce qui permet à l'image principale, qui est le
   LCP, de ne dépendre d'aucun script.
*/
.stv-galerie__piste {
  display: flex;
  height: 100%;
  overflow-x: auto;
  /*
     `hidden` EXPLICITE sur l'axe vertical (recette d'Estevan R32, 28/10) :
     un axe `auto` fait passer l'autre en `auto`, et une image plus haute que
     la scène (photo en portrait, ou image qui ne se charge pas) rendait la
     piste défilante EN HAUTEUR — la molette, sur l'image, faisait monter et
     descendre l'image dans son cadre au lieu de faire défiler la page.
     Relevé : 3 000 de haut pour 578 visibles. La piste ne défile plus qu'en
     largeur ; la molette verticale revient à la page.
  */
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}

.stv-galerie__piste::-webkit-scrollbar {
  display: none;
}

.stv-galerie__piste:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: -2px;
}

/*
   À LA SOURIS, LA PISTE NE DÉFILE PLUS À LA MAIN (recette d'Estevan R32,
   28/10, tâche 206 : « il est possible de scroller dans le visualiseur
   d'images »). Un geste latéral du pavé tactile — ou une diagonale en
   faisant défiler la page — faisait glisser les images librement. Une fois
   la galerie équipée par galerie.js (`data-stv-equipee`), la piste passe
   en `overflow-x: hidden` : elle reste une zone défilante pour le script
   (`scrollTo`, accroche comprise), qui la mène par les flèches, les
   vignettes et les flèches du clavier.

   Seulement à partir de 980 (là où flèches et vignettes existent) et à la
   souris : au doigt (téléphone, tablette même large), le glissement reste
   le geste principal. Sans script, rien ne change : la piste défile, c'est
   ce qui garde la galerie utilisable.
*/
@media (min-width: 980px) and (hover: hover) and (pointer: fine) {
  .stv-galerie[data-stv-equipee] .stv-galerie__piste {
    overflow-x: hidden;
  }
}

/*
   `min-width: 0` (28/10, vu en reproduisant R32) : une image qui ne se
   charge pas s'affiche à la largeur de son attribut `width` avec son texte
   alternatif, et le minimum automatique d'un élément flexible élargissait
   alors sa vue (3 000 au lieu de 578) — les flèches et les vignettes
   visaient des positions fausses. La vue garde sa largeur quoi qu'il
   arrive ; avec de vraies images, rien ne change (même cause que les
   cartes, composants.css « HAUTEUR DES IMAGES », 26/10).
*/
.stv-galerie__vue {
  flex: 0 0 100%;
  min-width: 0;
  scroll-snap-align: center;
  display: grid;
  /*
     Une case de la taille de la vue (28/10, R32) : sans pistes définies, la
     rangée prenait la hauteur de l'image, et son `max-height: 100%` ne se
     résolvait contre rien — une photo en portrait débordait de la scène.
     L'image tient désormais dans le carré, en largeur ET en hauteur.
  */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  margin: 0;
  cursor: zoom-in;
}

/* --- Badge ----------------------------------------------------------- */

/*
   LE BADGE — relevé `118:5853`. Rien de ce qui était posé n'était juste :
   fond anthracite au lieu du jeton pastille, texte blanc au lieu du bleu
   pétrole, 14 px au lieu de 16, et 16 px de marge au lieu de 32. Le
   badge dessiné est DISCRET — un aplat bleu à 10 % avec du texte bleu
   dessus — et il avait été posé en pastille sombre à fort contraste.

   La variante Promo reste PROVISOIRE : la maquette ne dessine que
   « Nouveauté ». Elle garde le bleu pétrole plein, qui se distingue de
   la nouveauté sans inventer une couleur hors palette, en attendant la
   réponse du designer (tâche 67).
*/
.stv-galerie__badge {
  position: absolute;
  z-index: 2;
  top: var(--stv-espace-32);
  left: var(--stv-espace-32);
  padding: var(--stv-espace-4) var(--stv-espace-12);
  background: var(--stv-pastille);
  color: var(--stv-bleu-petrole);
  font-size: var(--stv-taille-texte);
  line-height: var(--stv-interligne-titre);
}

.stv-galerie__badge--promo {
  background: var(--stv-bleu-petrole);
  color: var(--stv-blanc);
}

/* --- Flèches, compteur, plein écran ---------------------------------- */

/*
   `.stv-fleches` est à `display: none` par défaut dans composants.css et
   ne s'allume qu'en dessous de 1025 px — c'est la règle du carrousel des
   collections, où les flèches montent sur la ligne des onglets en
   desktop. La galerie, elle, les veut en bas au centre de la scène à
   toutes les largeurs : on rallume, sans toucher à la règle d'origine.
*/
/*
   32 du bas, et non 16 : les deux commandes sont à y = 787 dans une
   scène de 859, soit 32 sous leurs 40 px de haut (`118:5850` et
   `118:5855`). Même marge que le badge — c'est la marge intérieure de
   la scène, pas trois valeurs indépendantes.
*/
.stv-galerie__fleches {
  display: flex;
  gap: var(--stv-espace-24);
  position: absolute;
  z-index: 2;
  bottom: var(--stv-espace-32);
  left: 50%;
  transform: translateX(-50%);
  margin-top: 0;
}

/* Même piège, même parade : c'est un <button>. */
.stv-galerie__plein.stv-galerie__plein {
  position: absolute;
  z-index: 2;
  right: var(--stv-espace-32);
  bottom: var(--stv-espace-32);
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--stv-rayon);
  /*
     FOND GRIS CLAIR depuis le 24/09, à la demande de Logan : le même
     carré que les deux flèches (`.stv-fleches .stv-fleches__bouton`,
     composants.css), pour que les trois commandes de la scène se lisent
     comme une famille. Il était transparent depuis le 15/09 — la maquette
     ne lui dessinait pas de fond — avec un voile au survol pour donner
     une cible au pointeur ; le carré gris est désormais cette cible.
     Icône : `plein-ecran` de inc/icones.php (17 × 17, MEDIAS).
  */
  background: var(--stv-gris-clair);
  color: var(--stv-texte);
  cursor: pointer;
}

/*
   Le survol et le focus ne changent RIEN, comme sur les flèches — mais la
   règle est écrite quand même : le `reset.css` de Hello pose
   `[type=button]:hover, [type=button]:focus` (0,2,0) en rose `#c36`, à
   égalité avec la règle de repos, et seul l'ordre des fichiers la fait
   perdre (relevé du 24/09 ; le kit, lui, n'écrit pas de survol de
   bouton). À 0,3,0, le résultat ne dépend plus de l'ordre.
*/
.stv-galerie__plein.stv-galerie__plein:hover,
.stv-galerie__plein.stv-galerie__plein:focus {
  background: var(--stv-gris-clair);
  color: var(--stv-texte);
}

.stv-galerie__plein.stv-galerie__plein:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

/*
   Le compteur est MASQUÉ VISUELLEMENT en desktop, pas retiré : c'est le
   seul retour dont disposent les lecteurs d'écran sur une piste
   défilante, et il porte `aria-live`.
*/
.stv-galerie__compteur {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Sous 980 : la règle d'empilement de la tâche 8 ------------------- */

@media (max-width: 979px) {
  /*
     Plus de colonne de vignettes : le doigt remplace le clic, et une
     rangée de vignettes sous l'image mangerait la hauteur du premier
     écran pour un gain nul.
  */
  /* Plus de colonne : on rend sa place au contenu. */
  .stv-galerie--multiple {
    padding-left: 0;
  }

  .stv-galerie__vignettes {
    display: none;
  }

  /* Le glissement suffit : les flèches sont un doublon au doigt. */
  .stv-galerie__fleches {
    display: none;
  }

  /*
     Le compteur reprend sa place, celle de la maquette mobile — relevé
     `118:5696` le 15/09 : pastille ANTHRACITE à texte blanc, Inter
     Medium 12 à 1,1, 6 / 12 de marge intérieure, à 12 du bas. La
     version précédente (blanc sur blanc, 14, à 16) était déduite.
  */
  .stv-galerie__compteur {
    position: absolute;
    z-index: 2;
    left: 50%;
    bottom: var(--stv-espace-12);
    transform: translateX(-50%);
    width: auto;
    height: auto;
    margin: 0;
    padding: 6px var(--stv-espace-12);
    overflow: visible;
    clip-path: none;
    background: var(--stv-anthracite);
    color: var(--stv-blanc);
    font-size: var(--stv-taille-copyright);
    font-weight: 500;
    line-height: var(--stv-interligne-titre);
  }
}

/*
   LA SCÈNE MOBILE — relevé `118:5686` le 15/09.

   L'image fait 353 × 580 : un PORTRAIT, pas le carré du desktop. La
   maquette la remplit en recouvrement (`object-fit: cover`, l'image
   carrée dépasse de 35 % de chaque côté) sur une photo d'ambiance ; le
   catalogue, lui, est fait de packshots sur fond blanc dont un
   recouvrement couperait les bords — un rameur perdrait ses deux
   extrémités. On garde donc le cadre de la maquette et `contain` : sur
   fond blanc, une photo à fond blanc centrée dans le cadre donne le
   même rendu que la maquette, sans rien couper. Une seule déclaration
   à changer si le client passe à des photos d'ambiance.

   Le badge et le plein écran viennent à 12 des bords (`118:5694`,
   `118:5688`), le plein écran en HAUT à droite et en 24 px : la
   maquette mobile n'a pas de flèches, il est seul à cet angle.
*/
@media (max-width: 767px) {
  .stv-galerie__scene {
    aspect-ratio: 353 / 580;
  }

  .stv-galerie__badge {
    top: var(--stv-espace-12);
    left: var(--stv-espace-12);
    font-size: var(--stv-taille-copyright);
  }

  .stv-galerie__plein.stv-galerie__plein {
    top: var(--stv-espace-12);
    right: var(--stv-espace-12);
    bottom: auto;
    width: 24px;
    height: 24px;
  }

  .stv-galerie__plein.stv-galerie__plein svg {
    width: 12px;
    height: 12px;
  }
}

/* =====================================================================
   LE BLOC RÉSUMÉ — tâche 72

   Marque, H1, référence, description, prix.

   TOUTES LES VALEURS SONT RELEVÉES, plus aucune n'est déduite : Dev Mode
   a été activé le 15/09 et les deux groupes ont été lus au nœud près —
   `118:5861` en desktop, `118:5699` en mobile. Les trois valeurs que la
   version précédente de ce fichier signalait comme « à confirmer »
   étaient fausses toutes les trois : le H1 fait 32 et non 50, les deux
   prix font 32 et 28 et non 20 et 16, et ils sont l'un SOUS l'autre et
   non côte à côte.

   Deux choses qui ne se voient pas dans une capture et qui structurent
   le bloc :

   1. L'écart n'est pas uniforme. Marque + titre + référence forment un
      groupe à 8, et c'est ce groupe, la description et le prix qui sont
      espacés de 24. D'où `.stv-resume__entete` dans le gabarit.
   2. Le prix est une COLONNE. `.stv-prix` sert aussi à la barre d'achat
      collante (76), d'où des règles attachées à `.stv-prix` et non à
      `.stv-resume` — mais les tailles, elles, descendent de
      `.stv-resume` par les variables ci-dessous, avec un repli pour les
      usages hors fiche.

   Les tailles du résumé ne sont PAS dans jetons.css : 32, 28, 24 et 20
   n'apparaissent nulle part ailleurs dans la maquette, et un jeton qui
   ne sert qu'à un bloc n'est pas un jeton, c'est une variable locale mal
   rangée. La couleur de la référence, elle, en est une — 0,5 rejoint
   les 0,8 / 0,7 / 0,6 déjà relevés, et `--stv-texte-tertiaire` a été
   ajouté à jetons.css.
   ================================================================== */

.stv-resume {
  --stv-resume-marque:    var(--stv-taille-texte);      /* 16 — 118:5863 */
  --stv-resume-titre:     32px;                         /* 118:5865 */
  --stv-resume-reference: var(--stv-taille-petit);      /* 14 — 118:5866 */
  --stv-resume-prix-ht:   32px;                         /* 118:5869 */
  --stv-resume-prix-ttc:  28px;                         /* 118:5870 */

  /* -0,16 px sur 32 et -0,14 px sur 28 : le même -0,005em que les H2. */
  --stv-resume-serrage: -0.005em;

  /* La largeur de la colonne dans la maquette : la mesure du texte. */
  --stv-resume-mesure: 672px;

  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-24);
}

/*
   Bascule mobile au point de rupture Elementor (767), comme jetons.css,
   et non au 979 où la grille de la fiche s'empile : entre les deux, la
   colonne résumé prend TOUTE la largeur du contenu, c'est le moment où
   un titre a le plus de place, pas le moins.
*/
@media (max-width: 767px) {
  .stv-resume {
    --stv-resume-marque:    var(--stv-taille-petit);      /* 14 — 118:5701 */
    --stv-resume-titre:     24px;                         /* 118:5703 */
    --stv-resume-reference: var(--stv-taille-copyright);  /* 12 — 118:5704 */
    --stv-resume-prix-ht:   24px;                         /* 118:5707 */
    --stv-resume-prix-ttc:  20px;                         /* 118:5708 */

    /* La maquette mobile ne serre plus : -0,12 px ne se voit pas. */
    --stv-resume-serrage: normal;
  }
}

/* --- Le groupe serré : marque, titre, référence ---------------------- */

.stv-resume__entete {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-8);
}

/* --- La marque, au-dessus du titre ----------------------------------- */

/*
   AUCUNE mise en capitales par le CSS. Les termes de la branche MARQUES
   sont déjà écrits ainsi en base — « WATERROWER® », « PILATES-MAD® » —
   et un `text-transform` ne ferait que masquer une marque qui, un jour,
   ne le serait pas. La maquette n'en pose pas non plus, ni d'interlettrage.
*/
.stv-resume__marque {
  margin: 0;
  color: var(--stv-texte-secondaire);
  font-size: var(--stv-resume-marque);
  line-height: var(--stv-interligne-texte);
}

/*
   Deux classes : le kit Elementor écrit `.elementor-kit-44301 a`
   (poids 0,1,1) et emporterait la couleur et le soulignement.
   Piège documenté — `piege-specificite-elementor.md`.
*/
.stv-resume .stv-resume__marque-lien {
  color: inherit;
  text-decoration: none;
}

.stv-resume .stv-resume__marque-lien:hover,
.stv-resume .stv-resume__marque-lien:focus-visible {
  text-decoration: underline;
}

/* --- Le titre -------------------------------------------------------- */

/*
   Deux classes (0,2,0), et la graisse déclarée explicitement. Deux
   règles le visent sinon, et toutes deux donneraient un H1 de page
   d'accueil : `style.css` pose `h1 { font-size: var(--stv-taille-hero) }`
   — 62 px — et `h1..h6 { font-weight: 500 }`, quand la maquette demande
   du Outfit Regular ; le kit Elementor pose `.elementor-kit-44301 h1`
   (0,1,1) par-dessus.
*/
.stv-resume .stv-resume__titre {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  font-size: var(--stv-resume-titre);
  font-weight: 400;
  line-height: var(--stv-interligne-titre);
  letter-spacing: var(--stv-resume-serrage);
}

/* --- La référence ---------------------------------------------------- */

.stv-resume__reference {
  margin: 0;
  color: var(--stv-texte-tertiaire);
  font-size: var(--stv-resume-reference);
  font-weight: 300;
  line-height: var(--stv-interligne-texte);
}

/* --- La description courte ------------------------------------------- */

/*
   Anthracite et non le texte secondaire : la maquette donne `#24262e`,
   la couleur pleine des boutons primaires, pas un noir voilé. Le
   contraste sur blanc est de 13,4:1 — largement au-delà du 4,5:1 exigé.
*/
/*
   LA MESURE DU TEXTE NE SUIT PAS LA COLONNE — demande de Logan du 15/09.

   Depuis que la colonne de droite remplit ce que la galerie lui laisse
   (voir `.stv-fiche__grille`), elle peut atteindre 900 px et plus sur un
   écran large et peu haut. Un paragraphe de 16 px sur 900 px fait des
   lignes de 110 caractères : l'œil perd la ligne suivante. On plafonne
   le TEXTE — en-tête et description — à la largeur de la colonne de la
   maquette, 672, qui donne ~85 caractères par ligne. Le prix, le
   formulaire et le bouton, eux, continuent de remplir : c'est le
   texte qui a une mesure, pas le bloc.
*/
.stv-resume__entete,
.stv-resume__description {
  max-width: var(--stv-resume-mesure);
}

.stv-resume__description {
  color: var(--stv-anthracite);
  font-size: var(--stv-taille-texte);
  line-height: var(--stv-interligne-texte);
}

.stv-resume__description > :first-child {
  margin-top: 0;
}

.stv-resume__description > :last-child {
  margin-bottom: 0;
}

/* =====================================================================
   LE PRIX

   Balisage posé par le filtre de la tâche 69 : `.stv-prix` contenant
   `__ht` et `__ttc`, ou `__unique` quand les deux montants sont égaux,
   et `__barre` — le prix barré d'une promotion — DANS la ligne HT.

   Colonne, et non ligne : relevé `118:5868`, écart 8. C'était l'erreur
   de la version précédente, qui les alignait sur une baseline commune.
   Le HT est le prix de décision en B2B ; le mettre en premier et en
   plus gros, c'est la hiérarchie que la maquette impose.

   Outfit et pas Inter : les deux montants sont dessinés dans la police
   des titres. Rien ne l'hérite ici — le corps de page est en Inter —
   donc `font-family` est déclarée sur le conteneur.
   ================================================================== */

.stv-prix {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--stv-espace-8);
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  line-height: var(--stv-interligne-titre);
  white-space: nowrap;
}

/*
   Les replis servent la barre d'achat collante (76) et tout autre usage
   hors `.stv-resume`, où les variables du bloc n'existent pas.
*/
.stv-prix__ht,
.stv-prix__unique {
  font-size: var(--stv-resume-prix-ht, var(--stv-taille-carte));
  font-weight: 500;
  letter-spacing: var(--stv-resume-serrage, normal);
}

.stv-prix__ttc {
  font-size: var(--stv-resume-prix-ttc, var(--stv-taille-texte));
  font-weight: 300;
  letter-spacing: var(--stv-resume-serrage, normal);
}

/* La mention « HT » / « TTC » fait corps avec le montant. */
.stv-prix__mention {
  font-size: inherit;
  font-weight: inherit;
}

/*
   PROVISOIRE — le prix barré n'est dessiné nulle part : la maquette ne
   montre aucun produit en promotion, et l'état définitif fait partie de
   la tâche 67. Taille du TTC, graisse légère, couleur voilée : lisible,
   clairement secondaire, et à revoir sur maquette.
*/
.stv-prix__barre {
  margin-right: var(--stv-espace-8);
  color: var(--stv-texte-secondaire);
  font-size: var(--stv-resume-prix-ttc, var(--stv-taille-texte));
  font-weight: 300;
  text-decoration: line-through;
}

/*
   Une fourchette porte deux montants et un tiret par ligne. La colonne
   et l'écart sont déjà ceux du prix simple — seule la coupure change :
   « 1 599,00 € – 2 890,00 € HT » à 32 px peut dépasser 353 px en
   mobile, et mieux vaut qu'elle passe à la ligne qu'elle déborde.
*/
.stv-prix--fourchette {
  white-space: normal;
}

/* =====================================================================
   LE BLOC D'ACHAT — tâche 73

   Relevé Dev Mode `118:5884` (desktop) et `118:5722` (mobile) : mêmes
   mesures, seules les largeurs changent. Colonne à 24 : état, puis la
   rangée quantité + bouton, puis le lien de devis.

   AUCUN GABARIT WOOCOMMERCE N'EST SURCHARGÉ. Ce qu'on style est le
   balisage natif de `simple.php` et `variable.php`, complété par
   crochets (inc/fiche-achat.php). Les sélecteurs visent donc les
   classes de WooCommerce — `.cart`, `.quantity`, `.variations`,
   `.single_add_to_cart_button` — à l'intérieur de notre enveloppe.

   LA RÈGLE DE POIDS DE CE BLOC — et cette fois ce n'est pas le kit.

   Le violet du bouton (#7f54b3) et les 4 px collés à la quantité
   viennent de `woocommerce.css` lui-même, dont les sélecteurs sont
   lourds : `.woocommerce div.product form.cart div.quantity` pèse
   0,4,3, `.woocommerce div.product form.cart .variations select` 0,4,3,
   `… form.cart .button` 0,4,2, `… form.cart` 0,3,2. Deux classes
   répétées (0,3,0) ne suffisaient plus — constaté le 15/09, deux fois.

   D'où le préfixe `.stv-fiche .stv-achat.stv-achat` (trois classes) sur
   tout, ET la classe de l'élément répétée quand WooCommerce vise cet
   élément avec quatre classes : `.quantity.quantity`,
   `.variations.variations`, `.single_add_to_cart_button` doublé. On
   arrive à 0,5,x, devant tout ce que WooCommerce et le kit écrivent,
   sans `!important` et sans dépendre de l'ordre des feuilles. Dixième
   occurrence du piège, à consigner dans `piege-specificite-elementor.md`
   — qui devra changer de titre : le piège n'est pas Elementor, c'est
   toute feuille tierce qui écrit sur nos éléments.

   Ordre des poids à respecter en modifiant ce bloc : le conteneur
   (`form.cart`, 0,4,1) < ses enfants génériques (`form.cart > *`,
   0,4,1, même poids, déclaré avant) < la quantité et le bouton (0,5,0).
   ================================================================== */

.stv-achat {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-24);
}

/* --- Ce que WooCommerce rend et que la maquette ne montre pas -------- */

/*
   Le prix et la disponibilité que `variations_form` injecte dans le
   formulaire au choix d'une option : le résumé et le bloc d'état les
   reprennent (achat.js), les laisser ferait tout en double. `display`
   plutôt que retirer du DOM : WooCommerce y écrit, et un nœud absent
   ferait échouer son script.
*/
.stv-achat .woocommerce-variation-price,
.stv-achat .woocommerce-variation-availability,
.stv-achat p.stock {
  display: none;
}

/* --- Le formulaire : une colonne à 24, une rangée à 16 --------------- */

/*
   `form.cart` (simple) et `.woocommerce-variation-add-to-cart`
   (variable) contiennent les mêmes enfants dans le même ordre : bloc
   d'état, quantité, bouton, et ce que les extensions y glissent. On les
   met en flex enveloppant : tout enfant prend la largeur entière SAUF
   la quantité et le bouton, qui partagent une rangée. Une extension qui
   ajoute un champ tombe donc sur sa propre ligne, pleine largeur —
   c'est le comportement le moins surprenant qu'on puisse lui offrir.

   Deux écarts : 24 entre les lignes (maquette `118:5884`), 16 entre la
   quantité et le bouton (`118:5888`).
*/
.stv-fiche .stv-achat.stv-achat form.cart,
.stv-fiche .stv-achat.stv-achat .woocommerce-variation-add-to-cart,
.stv-fiche .stv-achat.stv-achat .stv-achat__rangee {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--stv-espace-24) var(--stv-espace-16);
  margin: 0;
}

.stv-fiche .stv-achat.stv-achat form.cart > *,
.stv-fiche .stv-achat.stv-achat .woocommerce-variation-add-to-cart > * {
  flex: 0 0 100%;
  margin: 0;
}

/*
   LE CLEARFIX DE WOOCOMMERCE DEVIENT UN ENFANT FLEX.

   `woocommerce.css` pose `form.cart::after { content: ""; display:
   table; clear: both }` pour ses flottants. Dans un conteneur flex, un
   pseudo-élément avec `content` est un enfant à part entière : vide,
   mais il compte pour un écart de 16 px en bout de rangée — le bouton
   sortait à 528 au lieu de 544, constaté le 15/09. Plus de flottants
   ici, donc plus de clearfix.
*/
.stv-fiche .stv-achat.stv-achat form.cart::before,
.stv-fiche .stv-achat.stv-achat form.cart::after {
  content: none;
}

/*
   48 AVANT LE BLOC D'ÉTAT — relevé `118:5860` (desktop) et `118:5698`
   (mobile), lus le 15/09 au retour de Figma : la colonne de droite est
   DEUX groupes à 24 — de la marque à la liste « Choisir une option »,
   puis de l'état aux puces — séparés de 48 (y = 541 → 589 en desktop).
   La colonne, elle, est à 24 partout (relevé 72) ; les 24 qui manquent
   sont posés ici, sur le premier élément du second groupe : le
   formulaire d'un produit simple, l'enveloppe de variation d'un
   produit variable (sa table d'options appartient au premier groupe),
   ou le bloc d'état rendu à la main pour une rupture.

   `.single_variation_wrap` DOUBLÉ (0,5,0) : relevé le 26/10 en posant les
   pastilles, l'enveloppe d'un produit variable ne prenait PAS ses 24 —
   `form.cart > *` (0,4,1, plus haut) les lui reprenait, l'état tombait à
   24 de la liste au lieu de 48 sur toutes les fiches variables.
*/
.stv-fiche .stv-achat.stv-achat form.cart:not(.variations_form),
.stv-fiche .stv-achat.stv-achat .single_variation_wrap.single_variation_wrap,
.stv-fiche .stv-achat.stv-achat > .stv-achat__etat {
  margin-top: var(--stv-espace-24);
}

/* La variante variable : la table des options et l'enveloppe de la
   variation ne sont pas des rangées, elles contiennent des rangées. */
.stv-fiche .stv-achat.stv-achat form.variations_form.variations_form {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-24);
}

.stv-fiche .stv-achat.stv-achat .single_variation_wrap {
  display: block;
  width: 100%;
}

/* --- Les options (produit variable) ---------------------------------- */

/*
   Pastilles en V2 (décision du 14/09) : une liste déroulante pour tout
   attribut — SAUF LA COULEUR DEPUIS LE 26/10 (bloc suivant, « Les
   pastilles de couleur »). Relevé `118:5881` : fond blanc, 8 / 16 de marge intérieure,
   Inter Regular 16 à 1,5, chevron de 8 × 4 à 16 du bord droit.

   Le libellé au-dessus est une décision, pas un relevé : la maquette ne
   dessine pas de libellé pour la liste, mais une liste seule qui dit
   « Choisir une option » ne dit pas de quoi. Il reprend le style du
   libellé de la rangée de pastilles (`118:5873`, Inter Medium 16 à 1,1),
   qui est le voisin le plus proche dans la maquette.
*/
.stv-fiche .stv-achat.stv-achat table.variations.variations,
.stv-fiche .stv-achat.stv-achat table.variations.variations tbody,
.stv-fiche .stv-achat.stv-achat table.variations.variations tr,
.stv-fiche .stv-achat.stv-achat table.variations.variations th,
.stv-fiche .stv-achat.stv-achat table.variations.variations td {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations {
  margin: 0;
  border-collapse: collapse;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations tr + tr {
  margin-top: var(--stv-espace-16);
}

.stv-fiche .stv-achat.stv-achat table.variations.variations th.label {
  margin-bottom: var(--stv-espace-8);
  color: var(--stv-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-titre);
}

.stv-fiche .stv-achat.stv-achat table.variations.variations label {
  font: inherit;
  color: inherit;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations select {
  box-sizing: border-box;
  width: 100%;
  min-height: 40px;
  margin: 0;
  padding: var(--stv-espace-8) calc(var(--stv-espace-16) + 8px + var(--stv-espace-16)) var(--stv-espace-8) var(--stv-espace-16);
  border: 0;
  border-radius: var(--stv-rayon);
  background-color: var(--stv-blanc);
  /* Le chevron de la maquette, 8 × 4, à 16 du bord droit. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3E%3Cpath d='M0 0l4 4 4-4' fill='none' stroke='%23020a12' stroke-width='1.2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--stv-espace-16) center;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 400;
  line-height: var(--stv-interligne-texte);
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations select:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

/* « Effacer », le lien de réinitialisation : discret, sous la liste. */
.stv-fiche .stv-achat.stv-achat .reset_variations.reset_variations {
  display: inline-block;
  margin-top: var(--stv-espace-8);
  color: var(--stv-texte-tertiaire);
  font-size: var(--stv-taille-petit);
  text-decoration: underline;
}

/* --- Les pastilles de couleur (26/10, V2 avancée) ------------------- */

/*
   Maquette `303:10216` (fiche du 13/10) : une rangée pleine largeur entre
   deux filets bleu pétrole à 10 % (`--stv-pastille`), 16 de marge haute et
   basse ; à gauche le nom de la couleur choisie, Inter Medium 16 à 1,1,
   noir ; à droite les pastilles de 40 à 8 d'écart — le composant
   `stv-pastilles` de composants.css, à sa grande taille (filet 2, marge 3,
   aplat 30). Mobile `303:10067` : 32 (filet 1, marge 2, aplat 26), marge
   de 8. Hauteur : 74 en desktop, 50 en mobile, comme la maquette.

   La rangée suit la liste qu'elle commande (inc/fiche-achat.php) : la
   liste et son libellé « Couleur » sont retirés de l'écran (`:has()`),
   la liste reste dans le formulaire — cachée à l'œil, pas en `display:
   none`. « Effacer », lui, n'est pas rendu (filtre PHP) : WooCommerce le
   réaffiche en style en ligne, la CSS n'y pouvait rien.
*/
.stv-fiche .stv-achat.stv-achat table.variations.variations tr:has([data-stv-achat-couleurs]) th.label {
  display: none;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations td:has([data-stv-achat-couleurs]) {
  position: relative;
}

.stv-fiche .stv-achat.stv-achat table.variations.variations select[data-stv-achat-liste] {
  position: absolute;
  width: 1px;
  min-width: 0; /* woocommerce.css pose 75 % */
  height: 1px;
  min-height: 0;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
  white-space: nowrap;
}

.stv-fiche .stv-achat .stv-achat__couleurs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--stv-espace-8) var(--stv-espace-16);
  padding: var(--stv-espace-16) 0;
  border-top: 1px solid var(--stv-pastille);
  border-bottom: 1px solid var(--stv-pastille);
}

.stv-fiche .stv-achat .stv-achat__couleur-nom {
  margin: 0;
  color: var(--stv-noir);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-titre);
}

.stv-fiche .stv-achat .stv-achat__couleurs .stv-pastilles {
  --stv-pastille-taille: 40px;
  --stv-pastille-ecart: var(--stv-espace-8);

  justify-content: flex-end;
  margin-left: auto;
}

/* Indisponible (option désactivée par WooCommerce) : NON DESSINÉ — atténuée, sans clic. */
.stv-fiche .stv-achat .stv-achat__couleurs .stv-pastilles__pastille[aria-disabled="true"] {
  opacity: 0.3;
  cursor: not-allowed;
}

@media (max-width: 767px) {
  .stv-fiche .stv-achat .stv-achat__couleurs {
    padding: var(--stv-espace-8) 0;
  }

  .stv-fiche .stv-achat .stv-achat__couleurs .stv-pastilles {
    --stv-pastille-taille: 32px;
  }
}

/* --- Le bloc d'état -------------------------------------------------- */

/*
   Relevé `118:5885` : fond anthracite à 5 %, 12 de marge intérieure,
   deux lignes à 8 d'écart, Inter 16 à 1,1 — Medium pour le titre,
   Regular pour le texte, « Consultez nos équipes » souligné.

   Le 5 % est un jeton : `--stv-fond-etat`, ajouté à jetons.css. Il
   rejoint les autres voiles de l'anthracite (0,4 et 0,8).
*/
.stv-achat__etat {
  margin: 0;
  color: var(--stv-texte);
  font-size: var(--stv-taille-texte);
  line-height: var(--stv-interligne-titre);
}

.stv-achat__etat--commande,
.stv-achat__etat--rupture {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-8);
  box-sizing: border-box;
  padding: var(--stv-espace-12);
  background: var(--stv-fond-etat);
}

.stv-achat__etat-titre,
.stv-achat__etat-texte {
  margin: 0;
}

.stv-achat__etat-titre {
  font-weight: 500;
}

.stv-fiche .stv-achat.stv-achat .stv-achat__etat-lien {
  color: inherit;
  /*
     `line-height` HÉRITÉ, explicitement : une feuille tierce pose 1,5
     sur `a`, et un lien en ligne plus haut que sa ligne agrandit la
     boîte de ligne entière — le cadre sortait à 74 px au lieu de 68.
     Même leçon que la taille des maillons du fil : une valeur héritée
     n'est pas une valeur déclarée.
  */
  line-height: inherit;
  text-decoration: underline;
  text-underline-position: from-font;
}

/*
   « En stock » : la maquette ne le dessine pas, la décision du 14/09 dit
   « texte discret ». Petit, voilé, sans cadre — présent pour dire que
   l'information a été vérifiée, pas pour attirer l'œil.
*/
.stv-achat__etat--stock {
  color: var(--stv-texte-tertiaire);
  font-size: var(--stv-taille-petit);
}

/* --- La quantité ----------------------------------------------------- */

/*
   Relevé `118:5889` : un cadre de 1 px noir, 48 de haut ; trois cases
   de 34, 40 et 36 de large (le champ a 16 de marge intérieure de chaque
   côté autour d'un chiffre). Inter 16 à 1,1, serrage −0,08 px — le
   même −0,005em que partout ailleurs.

   Le champ reste un `input[type=number]` pour le clavier numérique
   mobile ; on retire seulement les flèches natives, remplacées par les
   deux boutons. `-moz-appearance: textfield` est encore nécessaire sur
   Firefox pour ce cas précis.
*/
.stv-fiche .stv-achat.stv-achat .quantity.quantity {
  display: inline-flex;
  align-items: stretch;
  flex: 0 0 auto;
  box-sizing: border-box;
  height: var(--stv-bouton-hauteur);
  margin: 0;
  border: 1px solid var(--stv-noir);
  background: var(--stv-blanc);
}

.stv-fiche .stv-achat.stv-achat .quantity.quantity input.qty {
  box-sizing: border-box;
  width: 40px;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--stv-blanc);
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 400;
  line-height: var(--stv-interligne-titre);
  letter-spacing: -0.005em;
  text-align: center;
  appearance: textfield;
  -moz-appearance: textfield;
}

.stv-fiche .stv-achat.stv-achat .quantity.quantity input.qty::-webkit-outer-spin-button,
.stv-fiche .stv-achat.stv-achat .quantity.quantity input.qty::-webkit-inner-spin-button {
  margin: 0;
  -webkit-appearance: none;
}

.stv-fiche .stv-achat.stv-achat .quantity.quantity input.qty:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: -2px;
}

.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton {
  display: grid;
  place-items: center;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--stv-blanc);
  color: var(--stv-texte);
  cursor: pointer;
}

.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton--moins { width: 34px; }
.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton--plus  { width: 36px; }

.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton:hover,
.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton:focus {
  background: var(--stv-gris-clair);
  color: var(--stv-texte);
}

.stv-fiche .stv-achat.stv-achat .stv-quantite__bouton:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: -2px;
}

/* --- Le bouton ------------------------------------------------------- */

/*
   Relevé `118:5896` : prend la largeur restante, 48 de haut, anthracite,
   12 / 24 de marge intérieure, Inter Medium 16 à 1,5, blanc. Ce sont
   exactement les jetons `--stv-bouton-*` — la maquette et le styleguide
   disent la même chose, ce qui n'était pas garanti.

   `flex: 1 1 auto` et non une largeur : 544 en desktop, 225 en mobile,
   c'est ce qui reste une fois la quantité posée — pas une mesure.
*/
.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button {
  flex: 1 1 auto;
  box-sizing: border-box;
  min-height: var(--stv-bouton-hauteur);
  margin: 0;
  padding: var(--stv-bouton-padding);
  border: 0;
  border-radius: var(--stv-rayon);
  background: var(--stv-bouton-primaire-fond);
  color: var(--stv-bouton-primaire-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  text-align: center;
  text-transform: none;
  cursor: pointer;
  transition: var(--stv-bouton-transition);
}

/* Survol du primaire (331:10551, R4 du 26/10) : jetons du bouton. */
.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button:hover,
.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button:focus {
  background: var(--stv-bouton-primaire-survol-fond);
  box-shadow: var(--stv-bouton-primaire-survol-lueur);
  color: var(--stv-bouton-primaire-texte);
}

.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

/*
   Désactivé — rupture de stock, ou produit variable sans option choisie
   (WooCommerce pose alors `disabled` et
   `.woocommerce-variation-add-to-cart-disabled`). Même rendu dans les
   deux cas : le bouton reste là, atténué, et le curseur le dit.
*/
.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button:disabled,
.stv-fiche .stv-achat.stv-achat .single_add_to_cart_button.single_add_to_cart_button.disabled,
.stv-fiche .stv-achat.stv-achat .woocommerce-variation-add-to-cart-disabled .single_add_to_cart_button {
  background: var(--stv-bouton-primaire-fond);
  opacity: 0.4;
  cursor: not-allowed;
}

/* --- Le devis -------------------------------------------------------- */

/*
   Relevé `118:5901` : pleine largeur, centré, capitales, souligné, Inter
   Medium 16 à 1,5, noir. Le soulignement vient de la police
   (`from-font`), comme dans la maquette — plus fin que celui du
   navigateur.

   DEPUIS LA FICHE 182 (21/10), c'est le bouton « Ajouter au devis » de
   YITH Request a Quote : un `<a href="#" role="button">` (surcharge
   `woocommerce/add-to-quote.php`) qui porte la même classe que l'ancien
   lien — le lien de contact, lui, ne revient que si YITH est coupée. Les
   remises à zéro d'un bouton (fond, bordure, marge intérieure, police)
   sont gardées : un essai en `<button>` a montré ce que Hello y pose
   (`[type=button]` : bordure et texte #c36, fond #c36 au survol), et le
   kit d'Elementor écrit `a` à 0,1,1. Une ligne de 24, centrée.
*/
.stv-fiche .stv-achat.stv-achat .stv-achat__devis {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  letter-spacing: 0;
  text-align: center;
  text-decoration: underline;
  text-underline-position: from-font;
  text-transform: uppercase;
  white-space: normal;
  cursor: pointer;
  transition: color 0.2s ease;
}

.stv-fiche .stv-achat.stv-achat .stv-achat__devis:hover,
.stv-fiche .stv-achat.stv-achat .stv-achat__devis:focus-visible {
  background: none;
  color: var(--stv-bleu-petrole);
}

.stv-fiche .stv-achat.stv-achat .stv-achat__devis:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

/*
   Produit variable sans option choisie : le script de YITH pose
   `disabled` (achat.js le recopie dans `aria-disabled`). La feuille de
   YITH l'atténue à 50 % en `!important` (`.add-request-quote-button.disabled`)
   — gardé : le bouton d'achat est à 40 %, l'écart ne se rattrape qu'avec
   un autre `!important`. Pas de bleu au survol : il ne fait rien.
*/
.stv-fiche .stv-achat.stv-achat .stv-achat__devis[aria-disabled="true"],
.stv-fiche .stv-achat.stv-achat .stv-achat__devis.disabled {
  color: var(--stv-texte);
  cursor: not-allowed;
}

/*
   Le bloc de YITH autour du bouton. Sa feuille en ligne (`layout/css.php`,
   option « à côté du bouton panier ») le pose en `inline-block` à 5 du
   haut, à 0,4,0 : la classe est doublée ici (0,5,0). Le bouton y prend la
   ligne ; le témoin de chargement que le script glisse après lui
   (`img.ywraq-loader`) se pose à droite, hors du flux.
*/
.stv-fiche .stv-achat.stv-achat .yith-ywraq-add-to-quote.yith-ywraq-add-to-quote {
  display: block;
  width: 100%;
  margin: 0;
}

.stv-fiche .stv-achat.stv-achat .yith-ywraq-add-button {
  position: relative;
}

.stv-fiche .stv-achat.stv-achat .yith-ywraq-add-button .ywraq-loader {
  position: absolute;
  top: 50%;
  right: 0;
  width: 16px;
  height: auto;
  margin: 0;
  transform: translateY(-50%);
}

/*
   Les messages de YITH (« Produit ajouté à la liste », « Produit déjà
   dans la liste », « Voir ma demande de devis ») — non dessinés. Avec le
   réglage de production « après le clic, aller à la liste », le visiteur
   ne les voit qu'en cas d'erreur : rendu sobre, centré sous le bouton,
   Inter 14, le lien souligné.
*/
.stv-fiche .stv-achat.stv-achat .stv-achat__devis-retour {
  color: var(--stv-texte);
  font-size: var(--stv-taille-petit);
  line-height: var(--stv-interligne-texte);
  text-align: center;
}

/* Chaque message affiché à 8 du précédent ; un message masqué (`display: none`) ne compte pas. */
.stv-fiche .stv-achat.stv-achat .stv-achat__devis-retour > * {
  margin: var(--stv-espace-8) 0 0;
}

.stv-fiche .stv-achat.stv-achat .stv-achat__devis-retour a {
  color: var(--stv-texte);
  font-weight: 500;
  text-decoration: underline;
  text-underline-position: from-font;
}

.stv-fiche .stv-achat.stv-achat .stv-achat__devis-retour a:hover,
.stv-fiche .stv-achat.stv-achat .stv-achat__devis-retour a:focus-visible {
  color: var(--stv-bleu-petrole);
}

/* --- Alma — tâche 74 ------------------------------------------------- */

/*
   Relevé `118:5871` : un cadre blanc de 506 × 89 sous le prix, qui ne
   prend PAS toute la largeur de la colonne — c'est un encart, pas une
   ligne. En mobile (`118:5709`) il prend la largeur et garde son rapport.

   Deux cibles : notre cadre de réservation (`.stv-alma`), et le
   conteneur d'Alma quand l'extension est active — lu dans son code
   (6.5.0) : `.alma-product-eligibility`, avec `.alma-default-widget`
   quand le widget est affiché ou `.alma-widget-excluded` pour le message
   de catégorie exclue. Sans l'une ni l'autre, c'est le div vide que
   l'extension écrit quand son widget est désactivé : masqué, sinon il
   coûterait un écart de 24 dans la colonne — le même mécanisme que le
   clearfix de WooCommerce plus haut.

   `min-height` sur la seule réservation : le widget réel a sa hauteur
   (il dessine son propre cadre, `hide_border: false` dans sa
   configuration), et la lui imposer donnerait un cadre blanc vide.
   Rendu du widget vu le 07/11 sur staging (Alma active, mode test) :
   ses boutons étaient habillés par le kit Elementor — corrigés dans
   composants.css (« Alma, le widget »), qui sert aussi au panier.
*/
.stv-alma,
.stv-fiche .stv-achat.stv-achat .alma-product-eligibility.alma-default-widget,
.stv-fiche .stv-achat.stv-achat .alma-product-eligibility.alma-widget-excluded {
  --stv-alma-largeur: 506px;

  box-sizing: border-box;
  width: 100%;
  max-width: var(--stv-alma-largeur);
  margin: 0;
  background: var(--stv-blanc);
}

/*
   Alma pose `padding-bottom: 10px` sur `.alma-default-widget`
   (alma-widget-block.css) : sur notre fond blanc, une bande de 10 sous
   le cadre que le widget dessine lui-même. Vu le 07/11 sur staging.
*/
.stv-fiche .stv-achat.stv-achat .alma-product-eligibility.alma-default-widget {
  padding-bottom: 0;
}

.stv-alma--reserve {
  display: grid;
  place-items: center;
  min-height: 89px;
  padding: var(--stv-espace-12) var(--stv-espace-16);
  color: var(--stv-texte-tertiaire);
  font-size: var(--stv-taille-petit);
  line-height: var(--stv-interligne-texte);
  text-align: center;
}

.stv-fiche .stv-achat.stv-achat .alma-product-eligibility:not(.alma-default-widget):not(.alma-widget-excluded) {
  display: none;
}

@media (max-width: 767px) {
  .stv-alma,
  .stv-fiche .stv-achat.stv-achat .alma-product-eligibility.alma-default-widget,
  .stv-fiche .stv-achat.stv-achat .alma-product-eligibility.alma-widget-excluded {
    max-width: none;
  }

  .stv-alma--reserve {
    min-height: 0;
    aspect-ratio: 506 / 89;
  }
}

/* --- Les messages WooCommerce après ajout ---------------------------- */

/*
   PROVISOIRE : la maquette ne dessine pas le message « Produit ajouté
   au panier ». Rendu sobre dans le cadre du bloc d'état, le temps que
   la tâche 67 dise mieux. Le message est posé par
   `woocommerce_before_single_product`, donc AU-DESSUS de la grille.
*/
.stv-fiche-page .woocommerce-message,
.stv-fiche-page .woocommerce-error,
.stv-fiche-page .woocommerce-info {
  box-sizing: border-box;
  max-width: var(--stv-largeur-contenu);
  margin: 0 auto var(--stv-espace-24);
  padding: var(--stv-espace-12);
  border: 0;
  background: var(--stv-blanc);
  color: var(--stv-texte);
  font-size: var(--stv-taille-texte);
  line-height: var(--stv-interligne-texte);
}

.stv-fiche-page .woocommerce-message .button,
.stv-fiche-page .woocommerce-error .button,
.stv-fiche-page .woocommerce-info .button {
  color: var(--stv-texte);
  text-decoration: underline;
  background: none;
  padding: 0;
  margin-left: var(--stv-espace-12);
}

/* =====================================================================
   LES PUCES DE LIVRAISON — tâche 75

   Relevé Dev Mode des nœuds 156:11123 (desktop) et 156:7749 (mobile) :
   deux cadres de largeur égale, écart 12 ; filet 1 px à 25 % du noir ;
   marge intérieure 12 puis 8 ; icône, écart 8, texte ; texte 14 puis
   12, interligne 1,3 ; la mention « Livraison estimée le » à 70 % du
   noir, la date en 500.

   ALIGNÉS EN HAUT et non au milieu. Le premier cadre tient sur deux ou
   trois lignes selon la largeur : une icône centrée verticalement irait
   flotter au milieu du paragraphe, et les deux icônes ne seraient plus
   sur la même ligne d'un cadre à l'autre. La maquette dit `items-start`.

   `flex: 1 1 0` et non deux colonnes de grille : la seconde puce n'est
   rendue que si le client l'a cochée — ce sera le cas rare — et la
   première doit alors prendre la ligne entière. Avec des colonnes, il
   aurait fallu une règle pour le dire ; ici c'est la conséquence.

   AUCUNE MARGE HAUTE : la colonne de droite porte déjà son écart de 24
   (voir « LA COLONNE DE DROITE EST UNE SEULE COLONNE À 24 », tâche 72).
   ================================================================== */

.stv-puces {
  display: flex;
  align-items: stretch;
  gap: var(--stv-espace-12);
}

.stv-puce {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: var(--stv-espace-8);
  padding: var(--stv-espace-12);
  border: 1px solid var(--stv-filet-puces);
  border-radius: var(--stv-rayon);
}

/*
   L'icône garde sa boîte. `flex: none` est indispensable : un SVG en
   enfant direct de flex se laisse étirer par `align-items: stretch` du
   parent et compresser par le texte du voisin — les deux à la fois ici.

   Le pixel de décalage vient du dessin : la première ligne de texte fait
   18,2 px de haut pour une icône de 17, et sans lui l'icône colle au
   filet du haut au lieu de s'aligner sur la ligne.
*/
.stv-puce .stv-puce__icone {
  flex: none;
  margin-block-start: 1px;
}

/*
   Les tailles sont celles du FICHIER (21 × 17 et 19 × 21) et non celles
   que la maquette annonce (20 × 16 et 18 × 20) : l'écart est le débord
   du trait, que Figma inclut dans le SVG exporté. Poser les secondes
   rétrécirait le dessin de 5 %.
*/
.stv-puce--livraison .stv-puce__icone {
  width: 21px;
  height: 17px;
}

.stv-puce--preassemblage .stv-puce__icone {
  width: 19px;
  height: 21px;
}

.stv-puce__corps {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-8);
  min-width: 0;
}

/*
   Trois classes — 0,3,0 — pour passer devant `.elementor-kit-44301 p`,
   qui pose sinon sa marge basse sous chaque paragraphe et décolle le
   texte du filet du bas. Le piège habituel du kit, relevé pour la
   septième fois sur ce projet.
*/
.stv-fiche .stv-puce .stv-puce__texte,
.stv-fiche .stv-puce .stv-puce__estimation {
  margin: 0;
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-petit);
  font-weight: 400;
  line-height: 1.3;
  color: var(--stv-texte);
}

/*
   `--stv-texte-champ` vaut rgba(2, 10, 18, .7), exactement la valeur de
   la maquette pour cette mention. Le jeton s'appelle « champ » parce
   qu'un champ de formulaire fut son premier usage, pas parce qu'il lui
   appartient : en créer un second à la même valeur donnerait deux
   vérités à tenir pour une seule couleur.
*/
.stv-puce__mention {
  display: block;
  color: var(--stv-texte-champ);
}

.stv-puce__date {
  display: block;
  font-weight: 500;
  color: var(--stv-texte);
}

/* --- Mobile — nœud 156:7749 ------------------------------------------ */

@media (max-width: 767px) {
  .stv-puce {
    padding: var(--stv-espace-8);
  }

  /*
     `--stv-taille-copyright` est le jeton des 12 px du système. Même
     remarque que pour la couleur ci-dessus : c'est son premier usage qui
     l'a nommé, pas sa portée.
  */
  .stv-fiche .stv-puce .stv-puce__texte,
  .stv-fiche .stv-puce .stv-puce__estimation {
    font-size: var(--stv-taille-copyright);
  }

  /* 0,8 du desktop, comme la maquette mobile. */
  .stv-puce--livraison .stv-puce__icone {
    width: 17px;
    height: 14px;
  }

  .stv-puce--preassemblage .stv-puce__icone {
    width: 15px;
    height: 17px;
  }
}

/* =====================================================================
   LES ONGLETS — tâche 77 : ce qui n'appartient qu'à la fiche

   LA RANGÉE ET LES PANNEAUX ONT DÉMÉNAGÉ — assets/css/composants.css,
   « ONGLETS », le 25/09/2026 (lot FAQ). Mêmes valeurs, mêmes comptes :
   ils y sont réglés par variables, parce que la page FAQ les reprend en
   variante (`stv-onglets--sobre`) et que ce fichier n'est chargé que sur
   la fiche. Le balisage aussi est partagé (`stelvoren_onglets_html()`,
   inc/onglets.php) ; le script est mis en file par lui.

   Relevé `118:5911` (desktop) et `118:5749` (mobile). Ce qui reste ici :
   la description sur deux colonnes de 912 et 624 séparées de 48 (l'image
   en portrait 624 × 789, au-dessus du texte en 321 × 180 en mobile), le
   tableau des attributs, les vidéos, l'accordéon de la fiche et les avis.
   ================================================================== */

/* --- Les contenus ---------------------------------------------------- */

/*
   `.stv-prose` A DÉMÉNAGÉ — assets/css/socle.css, § 8, le 21/09/2026.

   Les règles étaient ici depuis la tâche 77, et deux choses les y
   rendaient inopérantes ailleurs : ce fichier n'est mis en file que sur
   la fiche produit, et les titres comme les liens étaient portés par
   `.stv-onglets .stv-prose`, donc par les seuls onglets. Or la prose
   sert sept contextes — fiche, article, textes et FAQ de catégorie,
   éditeurs des pages de contenu, réponses de la FAQ, pages légales.

   Rien n'a changé de valeur au passage : mêmes tailles, mêmes couleurs,
   même rythme vertical, relevé `118:5927` / `118:5767` du 15/09. Seule
   la portée s'élargit, et la spécificité passe de 0,2,1 à 0,3,1 par
   répétition de classe. Le tableau et les trois styles de liste s'y
   ajoutent (lot « socle »).

   Ce qui reste dans ce fichier : la mise en page à deux colonnes de la
   description et les onglets eux-mêmes, qui n'appartiennent qu'à la
   fiche.
*/

/* --- Description : deux colonnes ------------------------------------- */

/*
   L'IMAGE PREND LA HAUTEUR DU TEXTE. Relevé `118:5926` : la colonne de
   droite est étirée (`self-stretch`) et l'image la remplit en
   recouvrement — 789 de haut parce que le texte fait 789, pas parce que
   l'image le voudrait. D'où l'image en position absolue dans une figure
   étirée : c'est le texte seul qui donne la hauteur de la rangée.

   Le rapport 624 / 789 reste posé sur la figure comme PLANCHER : une
   description courte ne réduit pas l'image à une bande — la rangée
   prend au moins la hauteur du portrait, et le texte a de l'air.

   REMPLACÉ LE 24/09 en deux colonnes : l'image ne s'étire plus, elle
   est bornée à 720 et collante — voir plus bas, après l'image.
*/
.stv-onglets__description--illustree {
  display: grid;
  grid-template-columns: minmax(0, 912fr) minmax(0, 624fr);
  gap: var(--stv-gouttiere);
  align-items: stretch;
}

/*
   `width: 100%` EXPLICITE, et non le simple étirement par défaut :
   constaté en Chrome le 15/09, un élément de grille étiré dans les deux
   axes avec un `aspect-ratio` prend sa LARGEUR de la hauteur de la
   rangée — la figure sortait à 759 (960 × 624 / 789) dans une colonne
   de 624 et débordait sur la carte. Une largeur définie renverse le
   calcul : c'est la hauteur qui découle de la largeur, puis l'étirement
   l'amène à celle du texte.
*/
.stv-onglets__figure {
  position: relative;
  width: 100%;
  margin: 0;
  aspect-ratio: 624 / 789;
}

/* Deux classes : woocommerce.css écrit `.woocommerce img { height: auto }`. */
.stv-onglets .stv-onglets__image {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}

/*
   720 AU PLUS, ET COLLANTE — demande de Logan du 24/09, deux colonnes
   seulement (dès 768 : sous ce seuil l'image passe au-dessus du texte,
   en paysage, et rien ne change).

   Ce qui remplace « l'image prend la hauteur du texte » ci-dessus : sur
   une description longue, l'image s'étirait sur toute la hauteur du
   texte — 1 008 px sur le tapis Horizon, bien davantage ailleurs — et un
   `cover` aussi haut n'en montrait qu'une bande verticale, loin sous la
   ligne de flottaison. Elle ne s'étire plus (`align-items: start`) : sa
   hauteur vient du rapport 624 / 789, bornée à 720, et elle reste
   collée sous l'en-tête pendant que le texte défile, jusqu'au bas de la
   description.

   DEUX BORNES, pas une. 720 est la demande ; la seconde garde l'image
   ENTIÈRE à l'écran — sous l'en-tête fixe (barre d'administration
   comprise, `--stv-decalage-admin`), et au-dessus de la barre d'achat
   collante, qui est visible pendant toute la lecture (le bouton
   principal est alors hors de l'écran) et occupe le bas de la même
   colonne : 128 de haut à 24 du bord, plus 24 d'air. Une image collante
   plus haute que la place libre aurait son bas caché sous la barre
   pendant tout le défilement. À 1840 × 1080 : 720 ; sur un écran de
   portable, moins.

   `position: sticky` remplace `relative` : l'image en `absolute` garde
   la figure pour boîte de référence. Aucun ancêtre en `overflow`
   (vérifié le 24/09) — la condition pour que le collant opère.
*/
@media (min-width: 768px) {
  .stv-onglets__description--illustree {
    align-items: start;
  }

  .stv-onglets__figure {
    /* `--stv-annonce-visible` : la barre d'annonce part en défilant (R1, 26/10). */
    --stv-onglets-image-haut: calc(var(--stv-decalage-admin, 0px) + var(--stv-annonce-visible, var(--stv-annonce-h)) + var(--stv-barre-h) + var(--stv-espace-32));
    --stv-onglets-image-reserve: calc(128px + 2 * var(--stv-espace-24));

    position: sticky;
    /* La navigation se retire en descendant sur ordinateur (Logan, 26/10, 17 h 42) : l'image remonte avec elle
       (`--stv-barre-visible`, composants.css). La borne de hauteur garde la barre entière : l'image ne change
       pas de taille quand la navigation va et vient. */
    top: calc(var(--stv-onglets-image-haut) - var(--stv-barre-h) + var(--stv-barre-visible, var(--stv-barre-h)));
    max-height: min(720px, calc(100vh - var(--stv-onglets-image-haut) - var(--stv-onglets-image-reserve)));
    transition: top 0.35s cubic-bezier(0.4, 0, 0.2, 1);
  }
}

@media (min-width: 768px) and (prefers-reduced-motion: reduce) {
  .stv-onglets__figure {
    transition: none;
  }
}

/* --- Les intertitres des panneaux (179) ------------------------------ */

/*
   « Caractéristiques techniques : » (`315:10763`) et « FAQ <produit> »
   (`315:10889`) : Inter Medium 24 à 1,5, noir — 20 en mobile, par le jeton.
   Un H3, sous le H2 du panneau. Deux classes contre le kit (`h3`, 0,1,1).
*/
.stv-onglets .stv-onglets__intertitre {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-intertitre);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  letter-spacing: normal;
}

/* --- Caractéristiques : des groupes de définitions (179) ------------ */

/*
   RELEVÉ `315:10746` / `315:10762` : un bloc par groupe, 48 entre deux
   blocs ; le titre, puis à 16 deux colonnes — les libellés (Inter Semi
   Bold 16, anthracite) dans une colonne de 218, les valeurs (Inter Regular
   16, anthracite) 48 plus loin ; 8 entre deux lignes de 24.

   ÉCART : le libellé en Medium (500) et non Semi Bold (600) — la graisse
   600 n'est pas livrée (polices auto-hébergées 300 / 400 / 500), le
   navigateur la simulerait en graissant à la main. Même règle que la
   question de la FAQ et les intertitres des pages légales.

   La colonne des libellés fait 218 AU MOINS, et s'élargit au plus long
   libellé du groupe : deux groupes aux libellés courts s'alignent, un
   libellé de 300 ne se coupe pas. Les paires sont enveloppées d'un `<div>`
   que la grille ignore (`display: contents`).
*/
.stv-caracteristiques {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-48);
}

.stv-caracteristiques__groupe {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-16);
}

.stv-onglets .stv-caracteristiques__liste {
  display: grid;
  grid-template-columns: minmax(218px, max-content) minmax(0, 1fr);
  gap: var(--stv-espace-8) var(--stv-espace-48);
  margin: 0;
  padding: 0;
}

.stv-caracteristiques__ligne {
  display: contents;
}

.stv-onglets .stv-caracteristiques__libelle,
.stv-onglets .stv-caracteristiques__valeur {
  margin: 0;
  padding: 0;
  color: var(--stv-anthracite);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 400;
  line-height: var(--stv-interligne-texte);
}

.stv-onglets .stv-caracteristiques__libelle {
  font-weight: 500;
}

/* Mobile (non dessiné) : deux colonnes resserrées, 2 / 3, 16 entre elles. */
@media (max-width: 767px) {
  .stv-caracteristiques {
    gap: var(--stv-espace-32);
  }

  .stv-onglets .stv-caracteristiques__liste {
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    column-gap: var(--stv-espace-16);
  }
}

/* --- Informations complémentaires : la table des attributs ----------- */

/*
   Le balisage est celui de WooCommerce (`product-attributes.php`) : on
   n'habille que ce qu'il rend. Une ligne par attribut, un filet léger
   entre deux, le libellé en Medium.

   Le préfixe `.stv-fiche` et la classe doublée sont pour woocommerce.css,
   qui écrit `.woocommerce table.shop_attributes th` (0,2,2) et surtout
   `.woocommerce table.shop_attributes tr:nth-child(2n) td` (0,3,2) — le
   fond alterné, l'italique, le libellé à 150px en gras et les pointillés.
   Il faut 0,4,x pour passer devant sans !important — voir le piège de
   spécificité, même mécanique que le bloc d'achat.
*/
.stv-fiche .stv-onglets table.shop_attributes.shop_attributes {
  width: 100%;
  max-width: 912px;
  margin: 0;
  border: 0;
  border-collapse: collapse;
}

.stv-fiche .stv-onglets table.shop_attributes.shop_attributes tr {
  border-bottom: 1px solid var(--stv-pastille);
}

.stv-fiche .stv-onglets table.shop_attributes.shop_attributes th,
.stv-fiche .stv-onglets table.shop_attributes.shop_attributes td {
  padding: var(--stv-espace-12) 0;
  border: 0;
  background: none;
  color: var(--stv-texte);
  font-size: var(--stv-taille-texte);
  font-style: normal;
  line-height: var(--stv-interligne-texte);
  text-align: left;
  vertical-align: top;
}

.stv-fiche .stv-onglets table.shop_attributes.shop_attributes th {
  width: 40%;
  padding-right: var(--stv-espace-24);
  font-weight: 500;
}

.stv-fiche .stv-onglets table.shop_attributes.shop_attributes td p {
  margin: 0;
  padding: 0;
}

/* --- Vidéos : la grille portrait de la maquette (179) ---------------- */

/*
   RELEVÉ `315:10831` (fiche 179, 23/10) : quatre vignettes PORTRAIT par
   rangée, 359,5 × 564 dans les 1 582 du panneau, 48 entre elles et 48 entre
   deux rangées ; la légende Inter Medium 16 noire, centrée, 32 sous la
   vignette ; au centre un disque de 96 (`315:10851`) — bleu pétrole à 40 %,
   flou d'arrière-plan de 11, triangle blanc. Trois par rangée sous 1025,
   deux en mobile (non dessinés, § 5 de l'inventaire).

   UN CADRE QUI NE BOUGE PAS. La vignette est en 360 / 564 (celle du client
   aussi : au moins 722 × 1 128, le même rapport). Le lecteur qui la remplace
   au clic (onglets.js) garde ce cadre : une vidéo verticale le remplit, une
   vidéo paysage s'y lit avec des bandes — la grille ne saute pas.

   Le disque est rond : c'est un disque, pas un angle (la règle « aucun
   arrondi » vise les boîtes).
*/
.stv-videos {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--stv-espace-48);
}

.stv-videos__element {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-32);
  min-width: 0;
  margin: 0;
}

/* Deux classes contre le kit (`a`). */
.stv-onglets .stv-video {
  position: relative;
  display: block;
  aspect-ratio: 360 / 564;
  overflow: hidden;
  background: var(--stv-bleu-petrole);
  color: var(--stv-blanc);
  text-decoration: none;
}

/* Deux classes : woocommerce.css écrit `.woocommerce img { height: auto }`. */
.stv-onglets .stv-video__vignette {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}

/*
   LA VIGNETTE DE YOUTUBE (repli, quand le client n'en a pas fourni) : une
   image 4:3 dont la vidéo 16:9 occupe le milieu, bandes noires en haut et en
   bas. Recadrée en portrait, ce sont les côtés qui partent, pas les bandes :
   agrandie d'un tiers (4/3 ÷ 16/9 = 0,75), elle les sort du cadre.
*/
.stv-onglets .stv-video__vignette--service {
  transform: scale(1.34);
}

.stv-video__lecture {
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  place-items: center;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: rgba(0, 29, 61, 0.4);
  -webkit-backdrop-filter: blur(11px);
  backdrop-filter: blur(11px);
  color: var(--stv-blanc);
  transition: background-color 0.15s ease;
}

.stv-video__lecture .stv-video__glyphe {
  display: block;
  width: 100%;
  height: 100%;
}

/* Survol et focus : NON DESSINÉS — le disque s'assombrit. */
.stv-onglets .stv-video:hover .stv-video__lecture,
.stv-onglets .stv-video:focus-visible .stv-video__lecture {
  background: rgba(0, 29, 61, 0.7);
}

.stv-onglets .stv-video:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

.stv-video--lecteur iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.stv-onglets .stv-videos__legende {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  text-align: center;
}

@media (max-width: 1024px) {
  .stv-videos {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--stv-espace-24);
  }
}

@media (max-width: 767px) {
  .stv-videos {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--stv-espace-24) var(--stv-espace-16);
  }

  .stv-videos__element {
    gap: var(--stv-espace-12);
  }

  .stv-video__lecture {
    width: 56px;
    height: 56px;
  }

  .stv-onglets .stv-videos__legende {
    font-size: var(--stv-taille-petit);
  }
}

/* --- FAQ : l'accordéon des catégories (179) -------------------------- */

/*
   DEPUIS LA 179 (23/10), l'onglet FAQ prend la variante `stv-faq--plus`
   (composants.css) — celle des catégories et de la page FAQ, que la
   maquette du 19/10 dessine ici aussi (`315:10873`) : filet anthracite à
   20 % sous chaque question, « + » qui devient « − », premier panneau
   ouvert. Le chevron et le filet bleuté que ce fichier dessinait pour la
   seule fiche depuis la 77 sont RETIRÉS : chargés après composants.css,
   ils auraient ajouté un filet au-dessus de la première question et
   décroché l'icône de sa place. Un accordéon, un habillage.

   Ne restent ici que la largeur de la liste (898, `315:10890`) et l'écart
   sous le titre « FAQ <produit> » (24, `315:10888`).
*/
.stv-onglets .stv-faq--fiche {
  max-width: 898px;
  margin-top: var(--stv-espace-24);
}

/* --- Avis : le formulaire WooCommerce, au minimum ------------------- */

/*
   Les avis sont fermés (relevé 68) et leur état définitif attend la
   décision du client. Le peu qui suit empêche le formulaire natif de
   casser la carte le jour où ils s'ouvrent ; l'habillage complet
   viendra avec la décision.
*/
.stv-onglets #reviews {
  max-width: 912px;
}

.stv-onglets #reviews .comment-form textarea,
.stv-onglets #reviews .comment-form input[type="text"],
.stv-onglets #reviews .comment-form input[type="email"] {
  box-sizing: border-box;
  width: 100%;
  border: 1px solid var(--stv-vignette-filet, rgba(0, 29, 61, 0.25));
  border-radius: var(--stv-rayon);
  background: var(--stv-blanc);
}

/* --- Mobile ---------------------------------------------------------- */

@media (max-width: 767px) {

  /*
     48 sous la rangée et 48 sous l'image, comme en desktop (`118:5750`
     et `118:5765`, `gap-[48px]` tous les deux) : le panneau garde sa
     marge, seule la grille passe en colonne. L'image au-dessus du
     texte, en paysage (`118:5766`, 321 × 180).
  */
  .stv-onglets__description--illustree {
    grid-template-columns: minmax(0, 1fr);
  }

  .stv-onglets__figure {
    order: -1;
    aspect-ratio: 321 / 180;
  }

  .stv-fiche .stv-onglets table.shop_attributes.shop_attributes th {
    width: 45%;
  }
}

/* =====================================================================
   « DANS LA MÊME COLLECTION » — tâche 78

   Relevé `118:5929` (desktop, 1840 × 609) et `118:5768` (mobile) : une
   bande blanche pleine largeur, 80 de marge intérieure sur les quatre
   côtés — donc un contenu de 1680, le bandeau de la page — et une
   rangée à 16 : le H2 sur la colonne restante (844), centré sur la
   hauteur des cartes, puis deux cartes catalogue de 402. En mobile,
   32 dessus et dessous (`--stv-section-y` y vaut déjà 32), titre à 24
   puis les cartes empilées, à 16.

   Le titre est en Outfit Medium **40** à 1,5 — pas le H2 de section à
   50 : c'est un intertitre de fiche, pas un titre de page. Valeur
   locale, comme les tailles du résumé.

   Les cartes sont celles du catalogue (composants.css) : rien n'est
   réglé ici sur la carte elle-même, seulement sa colonne.
   ================================================================== */

.stv-fiche__collection {
  background: var(--stv-blanc);
  padding-block: var(--stv-section-y);
}

.stv-collection {
  --stv-collection-titre: 40px;   /* 118:5932 */
  --stv-collection-carte: 402px;  /* 118:5933 */

  box-sizing: border-box;
  max-width: calc(var(--stv-largeur-contenu) + 2 * var(--stv-fiche-marge));
  margin-inline: auto;
  padding-inline: var(--stv-fiche-marge);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--stv-espace-16);
  align-items: stretch;
}

/* Deux classes : le kit écrit sur `h2` (0,1,1). */
.stv-collection .stv-collection__titre {
  display: flex;
  align-items: center;
  min-width: 0;
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  font-size: var(--stv-collection-titre);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  letter-spacing: normal;
}

/*
   Une colonne PAR CARTE, pas deux colonnes d'avance : `grid-auto-flow:
   column` crée les pistes au fil des cartes, si bien qu'une collection
   à une seule carte (la Combo Chair III n'a qu'un voisin de catégorie,
   constaté le 15/09) ne laisse pas 402 px de vide à droite du titre.
*/
.stv-collection__cartes {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--stv-collection-carte);
  gap: var(--stv-espace-16);
}

/*
   Sous 1025, la marge de page tombe à 32 et la colonne du titre
   n'aurait plus que 124 px sur un écran de 1024 : le titre passe
   au-dessus et les cartes se partagent la largeur. La maquette n'a pas
   de frame tablette ; c'est la lecture la plus proche des deux qu'elle a.
*/
@media (max-width: 1024px) {
  .stv-collection {
    grid-template-columns: minmax(0, 1fr);
  }

  .stv-collection__cartes {
    grid-auto-flow: row;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Mobile — `118:5769` : titre 24, cartes empilées, tout à 16. */
@media (max-width: 767px) {
  .stv-collection {
    --stv-collection-titre: 24px; /* 118:5771 */
  }

  .stv-collection__cartes {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =====================================================================
   LA BARRE D'ACHAT COLLANTE — tâche 76

   Relevé `118:5976` : cadre blanc de 672 × 128, ombre `0 0 6
   rgba(0,0,0,.12)`, 24 de marge intérieure, 24 d'écart ; l'image en
   128 × 80, le nom (Outfit Regular 20 à 1,5) au-dessus du prix HT
   (Outfit Medium 24 à 1,1, −0,12 px), 16 entre les deux ; le bouton
   179 × 48 calé en bas. La maquette la dessine sous le bloc d'achat,
   sans dire où elle se fixe : fixée en bas de la fenêtre, à 24 du bord,
   ALIGNÉE SUR LA COLONNE DE DROITE — barre-achat.js lit la colonne et
   pose `--stv-barre-gauche` / `--stv-barre-largeur`. Rien n'est codé en
   dur : 672 sur la maquette, 544 à 1440, la largeur du contenu sous 980.

   Pas de frame mobile : fixée en bas sur toute la largeur, le prix HT à
   gauche et le bouton sur tout le reste ; image et nom s'effacent — le
   nom est deux lignes plus haut, la fiche l'a déjà dit.

   Elle n'entre que par `transform` : aucun décalage de mise en page,
   jamais. `visibility` suit, pour qu'une barre sortie ne soit ni
   focusable ni cliquable par accident ; `inert` et `hidden` (posés par
   le script) font le reste pour les lecteurs d'écran.
   ================================================================== */

.stv-barre {
  /* Le prix reprend le balisage du résumé : ces variables lui donnent la taille de la maquette. */
  --stv-resume-prix-ht: 24px;          /* 118:5980 */
  --stv-resume-serrage: -0.005em;
  --stv-barre-bas: var(--stv-espace-24);

  position: fixed;
  z-index: 90; /* sous l'en-tête (100) et le menu mobile (99) */
  left: var(--stv-barre-gauche, var(--stv-fiche-marge));
  bottom: var(--stv-barre-bas);
  box-sizing: border-box;
  width: var(--stv-barre-largeur, auto);
  max-width: calc(100vw - 2 * var(--stv-fiche-marge));
  transform: translateY(calc(100% + var(--stv-barre-bas)));
  visibility: hidden;
  transition: transform 0.2s ease, visibility 0s linear 0.2s;
}

.stv-barre--visible {
  transform: translateY(0);
  visibility: visible;
  transition-delay: 0s, 0s;
}

@media (prefers-reduced-motion: reduce) {
  .stv-barre {
    transition: none;
  }
}

.stv-barre__cadre {
  display: flex;
  align-items: stretch;
  gap: var(--stv-espace-24);
  box-sizing: border-box;
  min-height: 128px;
  padding: var(--stv-espace-24);
  background: var(--stv-blanc);
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.12);
}

.stv-barre__media {
  flex: 0 0 128px;
  width: 128px;
  height: 80px;
  align-self: center;
}

/* Deux classes : woocommerce.css écrit `.woocommerce img { height: auto }`. */
.stv-barre .stv-barre__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.stv-barre__texte {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  justify-content: center;
  gap: var(--stv-espace-16);
  min-width: 0;
}

.stv-barre .stv-barre__nom {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  font-size: var(--stv-taille-carte);
  font-weight: 400;
  line-height: var(--stv-interligne-texte);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* Seule la ligne HT : la barre est un rappel, pas le résumé. */
.stv-barre__prix .stv-prix__ttc {
  display: none;
}

/* Deux classes contre le kit, qui écrit sur `button`. */
.stv-barre .stv-barre__bouton {
  flex: 0 0 auto;
  align-self: flex-end;
  box-sizing: border-box;
  width: 179px;
  min-height: var(--stv-bouton-hauteur);
  margin: 0;
  padding: var(--stv-bouton-padding);
  border: 0;
  border-radius: var(--stv-rayon);
  background: var(--stv-bouton-primaire-fond);
  color: var(--stv-bouton-primaire-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  text-align: center;
  text-transform: none;
  white-space: nowrap;
  cursor: pointer;
  transition: var(--stv-bouton-transition);
}

/* Survol du primaire (331:10551, R4 du 26/10) : jetons du bouton. */
.stv-barre .stv-barre__bouton:hover,
.stv-barre .stv-barre__bouton:focus {
  background: var(--stv-bouton-primaire-survol-fond);
  box-shadow: var(--stv-bouton-primaire-survol-lueur);
  color: var(--stv-bouton-primaire-texte);
}

.stv-barre .stv-barre__bouton:focus-visible {
  outline: 2px solid var(--stv-anthracite);
  outline-offset: 2px;
}

/* Variation manquante : le bouton suit celui du formulaire (barre-achat.js). */
.stv-barre .stv-barre__bouton:disabled {
  background: var(--stv-bouton-primaire-fond);
  opacity: 0.4;
  cursor: not-allowed;
}

/*
   MOBILE — relevé `118:5819`, transmis par Logan le 17/09. La frame
   existait dans le fichier mais hors de l'artboard mobile, à côté :
   c'est pourquoi elle n'était pas dans le relevé du 15/09, et la
   première version mobile — une seule rangée, vignette et nom masqués —
   était une invention. Celle-ci est mesurée.

   393 × 148, collée en bas, 16 de marge intérieure sur les quatre côtés
   et 16 entre les deux rangées :

     rangée 1   vignette 64 × 64, puis le nom (Outfit Regular 14, 1,5)
                au-dessus du prix HT (Outfit Medium 14, 1,1), écart 8 ;
     rangée 2   le bouton, PLEINE LARGEUR, en variante « primarySmall »
                du système (`118:5239`) : 6 / 12 de marge intérieure,
                Inter Medium 16 — 36 de haut, contre 48 en desktop.

   Aucun changement de balisage : le cadre passe en `flex-wrap` et le
   bouton prend une base de 100 %, ce qui le renvoie seul sur la seconde
   ligne. Une rangée de plus dans le HTML n'aurait servi qu'ici.

   `env(safe-area-inset-bottom)` en plus de la maquette : sur un iPhone
   à encoche, sans lui, le bouton tombe sous la barre d'accueil.
*/
@media (max-width: 767px) {
  .stv-barre {
    --stv-resume-prix-ht: var(--stv-taille-petit); /* 14 — 118:5823 */
    --stv-barre-bas: 0px;

    left: 0;
    right: 0;
    width: auto;
    max-width: none;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--stv-blanc);
    box-shadow: 0 0 6px rgba(0, 0, 0, 0.12);
  }

  .stv-barre__cadre {
    flex-wrap: wrap;
    align-items: center;
    gap: var(--stv-espace-16);
    min-height: 0;
    padding: var(--stv-espace-16);
    box-shadow: none;
  }

  .stv-barre__media {
    flex: 0 0 64px;
    width: 64px;
    height: 64px;
  }

  .stv-barre__texte {
    gap: var(--stv-espace-8);
  }

  .stv-barre .stv-barre__nom {
    font-size: var(--stv-taille-petit);
  }

  /* Base de 100 % : le bouton passe seul sur la seconde ligne. */
  .stv-barre .stv-barre__bouton {
    flex: 0 0 100%;
    align-self: auto;
    width: auto;
    min-height: 36px;
    padding: 6px var(--stv-espace-12);
  }
}

/* =====================================================================
   PROVISOIRE — plus rien

   Pendant la phase 3, les emplacements non montés laissaient le rendu
   natif de WooCommerce tenir la place (voir stelvoren_fiche_partie()).
   La 71 a retiré la galerie, la 72 le résumé, la 73 le formulaire, la 77
   les onglets et la 78 la collection, le 15/09 : il ne reste aucune
   règle provisoire dans ce fichier. Le bloc est gardé vide pour que le
   prochain à chercher « où est le provisoire » tombe sur la réponse.

   Les deux règles sur `.entry-summary` / `.summary` ont été retirées le
   15/09 : elles ne s'appliquaient plus depuis la 70. Ces classes
   viennent du `content-single-product.php` de WooCommerce, que notre
   gabarit remplace — plus aucun élément de la page ne les porte.
   ================================================================== */

/* =====================================================================
   LE CŒUR DE LA SCÈNE — lot « Favoris », 13/10
   Nouvelle maquette de la fiche, `303:10168` : un bouton de 40 à 32 du coin
   haut droit de la scène (`303:10194`), le pendant du plein écran en bas —
   même carré gris clair (`--stv-gris-clair`, celui que Logan a voulu pour
   les trois commandes de la scène le 24/09), cœur en contour de 1,67 au
   bleu pétrole, 15 × 12,76 : l'icône `favori` de la carte agrandie de
   1,125 — d'où le trait réécrit en unités de sa boîte (1,67 / 1,125). La
   maquette lui dessine une ombre portée, comme au plein écran ; elle n'est
   pas plus posée ici que là.

   C'est le bouton `.stv-favori` de la carte (composants.css, 0,3,0 ; 0,4,0
   au survol) : ses règles sont battues AU COMPTE (0,5,0), pas à l'ordre des
   feuilles. Dans la liste : le même tracé rempli, au bleu pétrole (non
   dessiné, provisoire).

   MOBILE — NON DESSINÉ : `303:10045`, au coin haut droit, est le plein
   écran (24). Provisoire, à Estevan : le cœur se range à sa gauche, à 8,
   même taille, glyphe au rapport du plein écran (0,6).
   ================================================================== */

.stv-galerie .stv-galerie__favori.stv-favori.stv-favori.stv-favori {
  z-index: 2;
  top: var(--stv-espace-32);
  right: var(--stv-espace-32);
  width: 40px;
  height: 40px;
  background: var(--stv-gris-clair);
  color: var(--stv-bleu-petrole);
}

.stv-galerie .stv-galerie__favori.stv-favori.stv-favori.stv-favori:focus-visible {
  outline-color: var(--stv-anthracite);
}

.stv-galerie .stv-galerie__favori .stv-favori__glyphe {
  width: 16.5px;
  height: 14.262px;
  stroke-width: 1.4815;
}

@media (max-width: 767px) {
  .stv-galerie .stv-galerie__favori.stv-favori.stv-favori.stv-favori {
    top: var(--stv-espace-12);
    right: calc(var(--stv-espace-12) + 24px + var(--stv-espace-8));
    width: 24px;
    height: 24px;
  }

  .stv-galerie .stv-galerie__favori .stv-favori__glyphe {
    width: 9.9px;
    height: 8.557px;
  }
}

/* =====================================================================
   « PRODUITS SOUVENT ACHETÉS ENSEMBLE » — fiche 178 (23/10/2026)

   Relevé `315:18924` (1840 × 706) : une bande BLANCHE pleine largeur, 48
   en haut et en bas, 80 sur les côtés (la marge de la fiche) ; le titre
   (`315:18927`, Outfit Medium 40 à 1,5 — l'intertitre de « Dans la même
   collection ») puis, 48 plus bas, une rangée : la piste des cartes, et à
   48 d'elle la colonne d'achat de 261 (`315:18949`) — « Prix total : » en
   Inter 16, le montant en 20, le bouton 24 plus bas, 261 × 48.

   LA PISTE : cartes de 402 à 16 l'une de l'autre, une case de 24 centrée
   16 sous chaque carte (`315:18934`), flèches de 40 de part et d'autre
   (`315:18930`, `315:18947` : le carré gris clair et la flèche des tuiles).
   Les flèches ne paraissent que si la piste déborde (carrousels.js pose
   `data-stv-piste-deborde`) : la place leur est alors faite, 40 + 16 de
   chaque côté. À trois cartes sur un écran de 1840, tout tient : pas de
   flèches, les cartes s'alignent sur le titre (point 4 du lot).

   LES CARTES sont celles du catalogue (composants.css) : rien n'est réglé
   ici sur la carte, seulement sa largeur et son atténuation quand sa case
   est décochée (`315:18943`, la troisième de la maquette).

   SANS SCRIPT, les cases et la colonne d'achat sont INVISIBLES, place
   gardée : leur apparition ne fait rien sauter. Le script les montre
   (`stv-ensemble--pret`) quand il peut ajouter.

   Mobile et tablette non dessinés (§ 5 de l'inventaire) : sous 1025, la
   colonne d'achat passe sous la piste ; en mobile, piste au doigt bord à
   bord, sans flèches, total et bouton pleine largeur.
   ================================================================== */

.stv-fiche__ensemble {
  background: var(--stv-blanc);
  padding-block: var(--stv-espace-48);
  /* La carte des onglets suit, blanche elle aussi : 80 de gris entre les
     deux, comme entre la grille et la carte. */
  margin-block-end: var(--stv-section-y);
}

.stv-ensemble {
  --stv-ensemble-titre: 40px;            /* 315:18927 */
  --stv-ensemble-carte: 402px;           /* 315:18933 */
  --stv-ensemble-ecart: var(--stv-espace-16);
  --stv-ensemble-fleche: 40px;           /* 315:18930 */
  --stv-ensemble-achat: 261px;           /* 315:18949 */

  box-sizing: border-box;
  max-width: calc(var(--stv-largeur-contenu) + 2 * var(--stv-fiche-marge));
  margin-inline: auto;
  padding-inline: var(--stv-fiche-marge);
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-48);
}

/* Deux classes : le kit écrit sur `h2` (0,1,1). */
.stv-ensemble .stv-ensemble__titre {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  font-size: var(--stv-ensemble-titre);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  letter-spacing: normal;
}

.stv-ensemble__corps {
  display: flex;
  align-items: center;
  gap: var(--stv-espace-48);
}

.stv-ensemble__piste {
  flex: 1 1 auto;
  min-width: 0;
}

.stv-ensemble__cadre {
  position: relative;
}

/* La place des flèches, seulement quand elles sont là. */
.stv-ensemble__piste[data-stv-piste-deborde] .stv-ensemble__cadre {
  padding-inline: calc(var(--stv-ensemble-fleche) + var(--stv-ensemble-ecart));
}

.stv-ensemble .stv-ensemble__liste {
  display: flex;
  gap: var(--stv-ensemble-ecart);
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.stv-ensemble .stv-ensemble__liste::-webkit-scrollbar {
  display: none;
}

/*
   `position: relative` SUR L'ÉLÉMENT, et ce n'est pas décoratif : les
   libellés masqués des cases (`stv-cache`, en position absolue) prenaient
   sinon le cadre pour boîte de référence — HORS de la piste qui défile,
   donc jamais rognés par elle. Ceux des cartes hors champ élargissaient
   toute la page : barre de défilement horizontale, mesuré le 23/10 (3 616
   de large pour une fenêtre de 2 545).
*/
.stv-ensemble .stv-ensemble__element {
  position: relative;
  display: flex;
  flex: 0 0 var(--stv-ensemble-carte);
  flex-direction: column;
  align-items: center;
  gap: var(--stv-espace-16);
  min-width: 0;
  margin: 0;
  padding: 0;
  scroll-snap-align: start;
}

/* Pleine largeur de l'élément ; sa hauteur est la sienne — constante d'une
   carte à l'autre (composants.css) : les cases s'alignent d'elles-mêmes. */
.stv-ensemble__element > .stv-carte-produit {
  align-self: stretch;
  transition: opacity 0.2s ease;
}

/* Décochée, ou en attente d'une option : la carte s'efface (315:18943). */
.stv-ensemble__element--decoche > .stv-carte-produit {
  opacity: 0.5;
}

/*
   LES FLÈCHES — trois classes (0,3,0) contre le `reset.css` de Hello
   (`[type=button]`, survol et focus roses à 0,2,0) et le kit (`button`,
   0,1,1), comme celles des tuiles et de la frise. Centrées sur la CARTE et
   non sur la ligne entière : la case et ses 16 (40 en tout) sont retirées
   de la hauteur avant de prendre le milieu.
*/
.stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche {
  position: absolute;
  z-index: 1;
  top: calc((100% - var(--stv-espace-16) - 24px - var(--stv-ensemble-fleche)) / 2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--stv-ensemble-fleche);
  height: var(--stv-ensemble-fleche);
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--stv-rayon);
  background: var(--stv-gris-clair);
  color: var(--stv-noir);
  cursor: pointer;
  transition: opacity 0.2s, background-color 0.2s;
}

.stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche[hidden] {
  display: none;
}

.stv-ensemble .stv-ensemble__fleche--precedent.stv-ensemble__fleche--precedent {
  left: 0;
}

.stv-ensemble .stv-ensemble__fleche--suivant.stv-ensemble__fleche--suivant {
  right: 0;
}

/* En bout de course, à 50 % (`315:18930` : la précédente au départ). */
.stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche[aria-disabled="true"] {
  opacity: 0.5;
  cursor: default;
}

/* Survol : NON DESSINÉ — celui des tuiles. */
.stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche:not([aria-disabled="true"]):hover {
  background: var(--stv-bleu-clair);
  color: var(--stv-noir);
}

.stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche:focus-visible {
  outline: 2px solid var(--stv-noir);
  outline-offset: 2px;
}

.stv-ensemble .stv-ensemble__glyphe {
  display: block;
  flex-shrink: 0;
  width: 21.5px;
  height: 9.5px;
}

/*
   LA CASE (`315:18934`) : 24 × 24, filet de 2 bleu pétrole à 20 %, angles
   de 4 ; cochée, un aplat bleu pétrole et une coche blanche au trait de 2 —
   tracé EXPORTÉ de l'asset du Dev Mode. La case native garde son rôle et son
   clavier ; seul le dessin change, comme celle des filtres (catalogue.css).

   LES ANGLES DE 4 : la maquette les dessine sur cette case, et sur elle
   seule — écart à la règle « aucun arrondi », gardé parce qu'il est dessiné
   (à confirmer par Estevan, 67).

   Quatre classes (0,4,0) contre le champ du kit (0,3,1).

   UNE RANGÉE DE 24, TOUJOURS : « Choisir une option » se pose à côté de
   la case désactivée, pas dessous. Toutes les lignes ont ainsi la même
   hauteur — la carte, 16, 24 —, et les flèches se centrent sur la carte
   par un calcul juste (mesuré à 1840 : une ligne à lien dessous les
   décalait de 15).
*/
.stv-ensemble__choix {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--stv-espace-8);
  height: 24px;
  visibility: hidden;
}

.stv-ensemble--pret .stv-ensemble__choix {
  visibility: visible;
}

.stv-ensemble.stv-ensemble .stv-ensemble__case.stv-ensemble__case {
  -webkit-appearance: none;
  appearance: none;
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 2px solid rgba(0, 29, 61, 0.2);
  border-radius: 4px;
  background: var(--stv-blanc);
  cursor: pointer;
}

.stv-ensemble.stv-ensemble .stv-ensemble__case.stv-ensemble__case:checked {
  border-color: var(--stv-bleu-petrole);
  background: var(--stv-bleu-petrole) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M18.0036 8L10.0036 16L6.36719 12.3636' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 0 0 / 24px 24px no-repeat border-box;
}

.stv-ensemble.stv-ensemble .stv-ensemble__case.stv-ensemble__case:disabled {
  background: var(--stv-gris-clair);
  cursor: not-allowed;
}

.stv-ensemble.stv-ensemble .stv-ensemble__case.stv-ensemble__case:focus-visible {
  outline: 2px solid var(--stv-noir);
  outline-offset: 2px;
}

/* « Choisir une option » (non dessiné) : un lien à côté de la case désactivée. */
.stv-ensemble .stv-ensemble__option.stv-ensemble__option {
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-petit);
  line-height: var(--stv-interligne-texte);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.stv-ensemble .stv-ensemble__option.stv-ensemble__option[hidden] {
  display: none;
}

/* --- La colonne d'achat (315:18949) ----------------------------------- */

.stv-ensemble__achat {
  display: flex;
  flex: 0 0 var(--stv-ensemble-achat);
  flex-direction: column;
  gap: var(--stv-espace-24);
  min-width: 0;
  visibility: hidden;
}

.stv-ensemble--pret .stv-ensemble__achat {
  visibility: visible;
}

.stv-ensemble .stv-ensemble__total {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-texte);
  font-weight: 400;
  line-height: var(--stv-interligne-texte);
  text-align: center;
}

/* Le montant : Inter Semi Bold 20 dans la maquette, Medium ici (600 non livré). */
.stv-ensemble .stv-ensemble__total-montant {
  font-size: var(--stv-taille-carte);
  font-weight: 500;
  white-space: nowrap;
}

.stv-ensemble .stv-ensemble__bouton.stv-ensemble__bouton {
  width: 100%;
}

.stv-ensemble .stv-ensemble__bouton.stv-ensemble__bouton[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}

.stv-ensemble .stv-ensemble__message {
  margin: 0;
  color: var(--stv-message-erreur);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-petit);
  line-height: var(--stv-interligne-texte);
  text-align: center;
}

.stv-ensemble .stv-ensemble__message[hidden] {
  display: none;
}

@media (max-width: 1024px) {
  .stv-ensemble {
    --stv-ensemble-carte: 300px;
  }

  .stv-ensemble__corps {
    flex-direction: column;
    align-items: stretch;
    gap: var(--stv-espace-32);
  }

  .stv-ensemble__achat {
    flex: 0 0 auto;
    align-self: flex-end;
    width: var(--stv-ensemble-achat);
  }
}

@media (max-width: 767px) {
  .stv-fiche__ensemble {
    padding-block: var(--stv-espace-32);
    margin-block-end: var(--stv-espace-32);
  }

  .stv-ensemble {
    --stv-ensemble-titre: 24px;          /* l'intertitre mobile de la collection, 118:5771 */
    --stv-ensemble-carte: 75%;

    gap: var(--stv-espace-24);
  }

  /* La piste va d'un bord à l'autre de l'écran, la première carte calée sur le titre. */
  .stv-ensemble .stv-ensemble__liste {
    margin-inline: calc(-1 * var(--stv-fiche-marge));
    padding-inline: var(--stv-fiche-marge);
    scroll-padding-inline: var(--stv-fiche-marge);
  }

  .stv-ensemble .stv-ensemble__fleche.stv-ensemble__fleche {
    display: none;
  }

  .stv-ensemble__piste[data-stv-piste-deborde] .stv-ensemble__cadre {
    padding-inline: 0;
  }

  .stv-ensemble__achat {
    align-self: stretch;
    width: auto;
  }
}

/* =====================================================================
   LES SECTIONS DE PRÉSENTATION — fiche 177 (23/10/2026)

   Deux sections facultatives et indépendantes, saisies sur l'écran du
   produit (extension 1.4.0), rendues après les onglets : les arguments
   (`315:17617`), puis l'image + texte (`315:17636`). La seconde est le
   bandeau du socle en variante à texte latéral (`stv-bandeau--lateral`,
   socle.css § 3) : rien ici ne la règle.

   LES ARGUMENTS — relevé `315:17617` (1840 × 826) : sur le gris clair des
   cartes, 80 de marge ; le H2 de section centré sur 816 (Outfit Medium 50,
   1,1, −0,25 px), 48 au-dessus d'une rangée de 1 392 où les cartes se
   centrent, à 48 l'une de l'autre. Une carte (`315:17621`) : 385 de large,
   fond blanc, l'image en 385 × 248 recadrée, puis à 32 des bords le titre
   (Inter Semi Bold 20 — Medium ici, 600 non livré) et à 16 le texte (Inter
   Regular 20, anthracite). Les cartes d'une rangée prennent la hauteur de
   la plus haute.

   À QUATRE (175.8, point 1 du lot) : la rangée prend les 1 680 du contenu,
   en cartes de 384 (`--stv-colonne` : 4 × 384 + 3 × 48). À valider par
   Estevan (67). Le nombre de cartes est posé par le gabarit
   (`stv-arguments--N`) : la grille a autant de colonnes que de cartes, et
   un argument seul reste une carte de 385 au milieu.

   SOUS 1025 (non dessiné) : une piste au doigt, bord à bord — deux cartes
   et demie en tablette, une et un bout en mobile.
   ================================================================== */

.stv-fiche__arguments {
  background: var(--stv-gris-clair);
  padding-block: var(--stv-section-y);
}

/* Sous la carte des onglets, ses 80 de marge font déjà l'écart — même gris des deux côtés. */
.stv-fiche__onglets + .stv-fiche__arguments {
  padding-block-start: 0;
}

.stv-arguments {
  --stv-arguments-nombre: 3;
  --stv-arguments-carte: 385px;          /* 315:17621 */
  --stv-arguments-rangee: 1392px;        /* 315:17620 */

  box-sizing: border-box;
  max-width: calc(var(--stv-largeur-contenu) + 2 * var(--stv-fiche-marge));
  margin-inline: auto;
  padding-inline: var(--stv-fiche-marge);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--stv-espace-48);
}

.stv-arguments--1 {
  --stv-arguments-nombre: 1;
}

.stv-arguments--2 {
  --stv-arguments-nombre: 2;
}

.stv-arguments--4 {
  --stv-arguments-nombre: 4;
  --stv-arguments-carte: var(--stv-colonne);
  --stv-arguments-rangee: var(--stv-largeur-contenu);
}

/* Deux classes : le kit écrit sur `h2` (0,1,1). */
.stv-arguments .stv-arguments__titre {
  margin: 0;
  max-width: var(--stv-largeur-texte);
  color: var(--stv-texte);
  font-family: var(--stv-police-titres);
  font-size: var(--stv-taille-h2);
  font-weight: 500;
  line-height: var(--stv-interligne-titre);
  letter-spacing: -0.005em;
  text-align: center;
  text-wrap: balance;
}

.stv-arguments__piste {
  width: 100%;
}

.stv-arguments .stv-arguments__liste {
  display: grid;
  grid-template-columns: repeat(var(--stv-arguments-nombre), minmax(0, var(--stv-arguments-carte)));
  justify-content: center;
  gap: var(--stv-gouttiere);
  max-width: var(--stv-arguments-rangee);
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.stv-arguments .stv-arguments__carte {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin: 0;
  padding: 0;
  background: var(--stv-blanc);
}

/* Deux classes : woocommerce.css écrit `.woocommerce img { height: auto }`. */
.stv-arguments .stv-arguments__image {
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
  aspect-ratio: 385 / 248;
  object-fit: cover;
}

.stv-arguments__texte {
  display: flex;
  flex-direction: column;
  gap: var(--stv-espace-16);
  padding: var(--stv-espace-32);
}

/* Deux classes : le kit écrit sur `h3` (0,1,1). */
.stv-arguments .stv-arguments__intitule {
  margin: 0;
  color: var(--stv-texte);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-carte);
  font-weight: 500;
  line-height: var(--stv-interligne-texte);
  letter-spacing: normal;
}

.stv-arguments .stv-arguments__description {
  color: var(--stv-anthracite);
  font-family: var(--stv-police-texte);
  font-size: var(--stv-taille-carte);
  font-weight: 400;
  line-height: var(--stv-interligne-texte);
}

.stv-arguments .stv-arguments__description p {
  margin: 0;
}

.stv-arguments .stv-arguments__description p + p {
  margin-top: 0.75em;
}

@media (max-width: 1024px) {
  .stv-arguments .stv-arguments__liste {
    display: flex;
    justify-content: flex-start;
    justify-content: safe center;
    gap: var(--stv-espace-16);
    max-width: none;
    margin-inline: calc(-1 * var(--stv-fiche-marge));
    padding-inline: var(--stv-fiche-marge);
    scroll-padding-inline: var(--stv-fiche-marge);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }

  .stv-arguments .stv-arguments__liste::-webkit-scrollbar {
    display: none;
  }

  .stv-arguments .stv-arguments__carte {
    flex: 0 0 min(var(--stv-arguments-carte), 40%);
    scroll-snap-align: start;
  }
}

@media (max-width: 767px) {
  .stv-arguments {
    gap: var(--stv-espace-32);
  }

  .stv-arguments .stv-arguments__carte {
    flex-basis: 85%;
  }

  .stv-arguments__texte {
    gap: var(--stv-espace-12);
    padding: var(--stv-espace-24);
  }

  .stv-arguments .stv-arguments__description {
    font-size: var(--stv-taille-texte);
  }
}
