/* ==========================================================================
   Primitives partagées de la couche mobile FO.

   Chargé avec media="(max-width: 768px)" sur le <link> : tout ce fichier est
   mobile par construction, il n'y a donc pas de @media à l'intérieur et le
   desktop ne peut pas être atteint par accident.

   Ce fichier ne connaît AUCUN écran. Il ne décrit que des motifs réutilisables,
   dont les feuilles nav.css / tunnel.css se servent. Un motif employé par deux
   écrans appartient ici ; un motif employé par un seul reste chez lui.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Panneau plein écran (.m-panel)

   Motif unique pour le menu burger, le panneau compte, le détail de commande
   et, à venir, le petit panier et la mise au panier. Ces quatre panneaux
   avaient chacun leur copie du même bloc de règles (position, offset sur la
   tabbar, z-index, défilement) : une divergence sur l'un ne se voyait pas sur
   les autres.

   Le corps s'arrête au bord haut de la tabbar, qui reste visible et au-dessus.
   -------------------------------------------------------------------------- */
/* Les !important de ce bloc sont assumés et centralisés. Un panneau peut être
   posé sur un élément du legacy déjà stylé par son ID (#mon_compte_detail :
   position absolue, z-index 1000, fond gris) — une classe perd contre un ID
   quelle que soit sa place dans la cascade. Le `display` doit en plus battre
   le style inline que les hovers desktop posent en JS.

   Mesuré : sans eux, le panneau compte retombe en absolute/z1000/gris.

   L'alternative serait un bloc de surcharge par élément adopté, donc autant de
   copies que de panneaux — précisément la duplication qu'on supprime. Cinq
   !important ici valent mieux que N blocs ailleurs. Les déclarations non
   structurelles (padding, filets, typo) n'en portent pas. */
.m-panel {
    display: none !important;
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: var(--m-tabbar-h) !important;
    z-index: var(--m-z-panel) !important;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    background: var(--m-bg) !important;
}

.m-panel.is-open {
    display: block !important;
}

/* Un seul verrou de défilement, quel que soit le panneau ouvert. Auparavant
   chaque panneau posait sa propre classe sur <body> (m-nav-open,
   m-account-open, m-order-open) et devait retirer à la main celles des
   autres — un croisement en O(n²) dès qu'on ajoute un panneau. */
body.m-panel-open {
    overflow: hidden;
}

.m-panel-head {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: var(--m-space-3);
    padding: 14px var(--m-space-4);
    border-bottom: 1px solid var(--m-line);
    background: var(--m-bg-alt);
}

.m-panel-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-family: var(--m-font-bold);
    font-weight: bold;
    font-size: var(--m-fs-field);
    color: var(--m-navy);
}

.m-panel-close {
    flex: 0 0 auto;
    width: var(--m-tap);
    height: var(--m-tap);
    border: 0;
    background: transparent;
    color: var(--m-navy);
    font-size: 30px;
    line-height: 1;
    cursor: pointer;
}

.m-panel-body {
    padding: var(--m-space-3) var(--m-space-4);
}

/* --------------------------------------------------------------------------
   Variante ancrée (.m-panel--anchored)

   Tous les panneaux ne remplacent pas l'écran. Le petit panier est le DÉTAIL
   d'une icône de la tabbar : il se pose au-dessus d'elle, se dimensionne à son
   contenu et la désigne d'une flèche — sinon rien ne dit de quelle icône il
   sort, et le tunnel d'achat derrière disparaît alors qu'il est le contexte de
   la sélection qu'on vérifie.

   Les !important répondent aux mêmes ID que ceux du bloc .m-panel : il s'agit
   d'annuler ses propres déclarations plein écran, elles-mêmes !important.
   -------------------------------------------------------------------------- */
.m-panel--anchored {
    top: auto !important;
    left: var(--m-space-3) !important;
    right: var(--m-space-3) !important;
    bottom: calc(var(--m-tabbar-h) + 12px) !important;
    max-height: 70vh;
    border-radius: var(--m-radius);
    box-shadow: 0 8px 28px var(--m-shadow-panel);
}

/* La flèche est un carré pivoté, pas un triangle de bordures : elle doit
   porter le même fond et la même ombre que le panneau, ce qu'un triangle
   obtenu par `border-color: transparent` ne sait pas faire.

   `position: fixed` la sort du défilement du panneau — elle reste collée à
   l'icône même quand on fait défiler une longue sélection. --m-anchor-x est
   posé à l'ouverture par Mobile.Panel ; le repli à 50% couvre le cas où
   l'icône a disparu entre-temps. */
.m-panel--anchored::after {
    content: '';
    position: fixed;
    bottom: calc(var(--m-tabbar-h) + 5px);
    left: var(--m-anchor-x, 50%);
    width: 14px;
    height: 14px;
    margin-left: -7px;
    transform: rotate(45deg);
    background: var(--m-bg);
    box-shadow: 3px 3px 5px var(--m-shadow);
}

/* --------------------------------------------------------------------------
   Reflow des tables denses en cartes (.m-cards)

   La liste des tables concernées était écrite DEUX fois : dans le sélecteur
   CSS et dans le querySelectorAll du JS. Ajouter un écran demandait de penser
   aux deux. Désormais le JS pose la classe .m-cards depuis un registre
   (Mobile.registerTable) et le CSS ne connaît que cette classe : un nouvel
   écran est une ligne de registre, zéro CSS.

   Les data-label viennent des <th> de la table, posés par Mobile.labelTable.
   -------------------------------------------------------------------------- */
table.m-cards,
table.m-cards tbody {
    display: block;
    width: 100%;
    border: 0;
}

/* Une table reflowée en cartes suit le barème, comme le reste du contenu : le
   legacy dessine la sienne pour une largeur de bureau et descend sous
   l'échelon le plus bas. Mesuré sur le récapitulatif de l'étape de paiement,
   14px pour un barème qui va de 13 à 17.

   Les points d'entrée du registre sont listés : ces tables sont habillées par
   leur ID ou leur classe côté legacy, et une classe seule y perdrait. Ajouter
   une table au registre (Mobile.registerTable) demande donc d'ajouter son
   sélecteur ici — c'est le prix d'une échelle qui tient sur tous les écrans. */
table.m-cards,
table#tableau_recapitulatif.m-cards,
table#big_cart_details_table.m-cards,
table.TableFacture.m-cards,
table.table_order.m-cards {
    font-size: var(--m-fs);
}

/* Le récapitulatif de commande porte ses libellés de ligne en
   <th class="titre_maj">, que panier.css dessine en 12px — sous l'échelon le
   plus bas du barème, et c'est le texte qui nomme chaque montant. Ils restent
   secondaires par les capitales et la graisse, pas par une taille illisible.
   Le sélecteur reprend l'ID pour battre `#tableau_recapitulatif th`. */
table#tableau_recapitulatif.m-cards th,
table#tableau_recapitulatif.m-cards th.titre_maj {
    font-size: var(--m-fs-sm);
}

table.m-cards tr {
    display: block;
    width: auto;
    margin: 0 0 var(--m-space-3);
    border: 1px solid var(--m-line-4);
    border-radius: var(--m-radius);
    overflow: hidden;
    background: var(--m-bg);
    box-shadow: 0 1px 4px var(--m-shadow);
}

table.m-cards th {
    display: none;
}

/* Masquer les <th> ne suffit pas : leur <tr> garde son habillage de carte —
   bordure, fond blanc, 27px de haut — pour un contenu devenu invisible. Une
   ligne sans aucune cellule de données n'a rien à afficher. */
table.m-cards tr:not(:has(> td)) {
    display: none;
}

/* Le rembourrage porte un !important pour la même raison que .m-panel : les
   tables reflowées sont dimensionnées par leur ID côté desktop
   (`#tableau_recapitulatif td { padding: 3px }`), et un id bat n'importe quel
   nombre de classes. Mesuré : les valeurs se retrouvaient à 3px du filet.

   L'alternative serait un bloc de surcharge par table dans chaque feuille
   d'écran — donc autant de copies que de tables au registre, précisément la
   duplication que le registre supprime. */
table.m-cards td {
    display: flex;
    justify-content: space-between;
    gap: var(--m-space-3);
    width: auto;
    padding: 9px var(--m-space-3) !important;
    border: 0;
    border-bottom: 1px solid var(--m-line-2);
    text-align: right;
}

table.m-cards td:last-child {
    border-bottom: 0;
}

/* Libellé et valeur en encre franche. Le legacy les rend en #777 et #6d6d6d —
   deux gris indiscernables l'un de l'autre, qui effacent la structure de
   lecture d'un tableau financier. La maquette pose un libellé sombre en gras
   face à une valeur sombre ; seul l'accessoire reste en retrait. */
table.m-cards td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-weight: bold;
    color: var(--m-ink);
    text-align: left;
}

/* !important pour la même raison que le rembourrage juste au-dessus : ces
   tables sont colorées par leur id (`#tableau_recapitulatif td { color:
   #6d6d6d }`), et un id bat n'importe quel nombre de classes. */
table.m-cards td {
    color: var(--m-ink) !important;
}

/* Cellule fusionnée : en-tête de section ou total, pleine largeur, sans label.

   Le fond n'est PAS posé ici. Certaines de ces cellules portent déjà leur
   couleur côté legacy — `.big_cart_align_line_offre` est en ardoise avec un
   texte blanc — et cette feuille étant chargée après elles, un fond
   inconditionnel les repeignait en gris clair : texte blanc sur #f6f6f6,
   soit un rapport de contraste de 1,07 et un intitulé de rayon invisible.

   C'est donc mobile/core.js qui pose .m-band, après avoir constaté que la
   cellule n'a pas de fond propre. Une condition qui ne s'exprime pas en CSS
   se décide à la mesure, pas en aveugle. */
table.m-cards td[colspan] {
    justify-content: flex-start;
    text-align: left;
    font-weight: bold;
}

/* En-tête de groupe. La maquette en fait un bandeau sombre à texte clair —
   il nomme le choix ou le bénéficiaire auquel se rattachent les lignes qui
   suivent. Un gris très clair, comme précédemment, ne le distinguait pas des
   lignes de données. */
table.m-cards td.m-band {
    background: var(--m-navy);
    color: var(--m-bg) !important;
}

/* Même bandeau, mais peint par le legacy : on garde son fond et on lui rend
   seulement un texte clair, que la remise en encre lui avait pris. */
table.m-cards td.m-band-legacy {
    color: var(--m-bg) !important;
}

table.m-cards td[colspan]::before {
    content: '';
}

/* Cellule vide : masquée, sinon on affiche une ligne de libellé sans valeur */
table.m-cards td.m-empty {
    display: none;
}

/* Cellule sans libellé : le mappeur en prive celles dont l'en-tête de colonne
   ne décrit pas le contenu (préfixe d'une ligne de totaux, intertitre de
   groupe). Sans cela le ::before vide laisse la valeur collée à droite, comme
   si un libellé manquait à l'affichage. */
table.m-cards td:not([data-label]) {
    justify-content: flex-start;
    text-align: left;
}

table.m-cards td:not([data-label])::before {
    display: none;
}


/* --------------------------------------------------------------------------
   Groupe collectif d'un tableau financier (mobile/tunnel.js)

   Ces tables sont un REGISTRE DE CALCUL : chaque ligne se lit horizontalement
   — sur telle base, tel taux, donnant tel montant, qui va en « à payer » ou
   « à recevoir ». Les colonnes sont génériques à dessein, une ligne de produit
   et une ligne de subvention étant deux étapes du même calcul.

   « Subventions / Plafonds » n'est pas un intertitre égaré : c'est une VALEUR
   de la colonne Bénéficiaire(s), qui couvre ses lignes par rowspan pour dire
   qu'elles n'ont pas de bénéficiaire — donc qu'elles sont COLLECTIVES. Une
   subvention individuelle est portée par la ligne de la personne.

   En desktop c'est la géométrie qui porte ce sens ; empilée en cartes elle
   disparaît, et le groupement doit le porter à sa place. Le libellé de colonne
   est retiré : il faisait lire « Bénéficiaire(s) : Subventions / Plafonds ».
   -------------------------------------------------------------------------- */
/* Une cellule seule COIFFE les lignes qui la suivent. Sans cela l'écran est
   une suite de cartes blanches identiques : rien ne dit que les cinq lignes
   sous « Franck » sont les siennes, ni où commence Carole. En desktop le
   rowspan le disait ; empilé, il faut le rendre au groupement.

   Même traitement pour « Subventions / Plafonds » : il occupe la même colonne
   et joue le même rôle — coiffer un bloc. Le distinguer visuellement, comme je
   l'avais d'abord fait, laissait croire à deux natures différentes. Ce qui les
   distingue est déjà dans le texte : un prénom d'un côté, la mention qui dit
   l'absence de bénéficiaire de l'autre. C'est ainsi que le desktop procède. */
table.m-cards tr.m-rowspan-solo {
    margin-bottom: 0;
    border-bottom: 0;
    border-radius: var(--m-radius) var(--m-radius) 0 0;
    background: var(--m-bg-alt);
}

table.m-cards tr.m-rowspan-solo td {
    border-bottom: 0;
    font-family: var(--m-font-bold);
    font-weight: bold;
}

/* Les lignes rattachées ferment le groupe : pas de filet entre l'en-tête et
   elles, angles arrondis seulement en bas. */
table.m-cards tr.m-rowspan-solo + tr {
    border-top: 0;
    border-radius: 0 0 var(--m-radius) var(--m-radius);
}


/* Chaque bloc du groupe porte son nom — « Subvention Séjours Individuels »,
   « Plafond Vacances Individuelles » — dans une cellule libellée
   « Détail/Subvention », qui ne le désigne pas. Deux blocs se suivaient sans
   qu'on sache ce que chacun calcule : le nom devient leur titre. */
table.m-cards td.m-section-titre {
    justify-content: flex-start;
    font-family: var(--m-font-bold);
    font-weight: bold;
}


/* Le repli d'une ligne (bigcart.js, commande.js) pose display:none EN INLINE.
   Le display:block du reflow l'écraserait et la flèche de repli n'aurait plus
   d'effet : ce sélecteur d'attribut, plus spécifique, respecte le masquage.
   Le !important est ici indispensable — il ne s'agit pas de gagner sur une
   feuille, mais sur un style inline. */
table.m-cards tr[style*="display: none"],
table.m-cards tr[style*="display:none"] {
    display: none !important;
}

/* --------------------------------------------------------------------------
   Barre d'actions (.m-actions / .m-action)

   Quatre écrans posaient la même question — navigation du tunnel, boutons des
   popins, actions de l'écran de confirmation, actions de « Ma sélection » — et
   chacun y répondait par son propre bloc : pleine largeur, hauteur de cible
   tactile, gouttière, hiérarchie principale/secondaire. Quatre copies à
   maintenir, donc quatre endroits à retrouver si le client fait varier un
   espacement.

   Le legacy dispose ces actions de façons très différentes (cellules de
   tableau, <div> flottants, <a> dans un pavé coloré). Plutôt que de deviner
   ces formes en CSS, les gabarits déclarent l'intention :

     .m-actions   sur le conteneur de la rangée — ses enfants directs
                  deviennent les actions, quel que soit leur balisage ;
     .m-action    sur une action isolée, quand les boutons n'ont pas de
                  conteneur commun et ne peuvent pas en recevoir un sans
                  déplacer du DOM (écran de confirmation).

   Les couleurs, libellés et gestionnaires restent au legacy : cette primitive
   ne décrit que la géométrie et le confort tactile.
   -------------------------------------------------------------------------- */
/* Les popins font exception au marquage : 105 fichiers ouvrent des dialogs, on
   ne les marquera pas un par un. Leur structure d'actions est en revanche
   toujours la même — une <table> de boutons dans .corps_popin. Elle est donc
   BRANCHÉE sur la primitive comme second point d'entrée, plutôt que d'en
   recopier les déclarations : une seule règle à modifier, deux façons d'y
   accéder. */
.m-actions,
.ui-dialog .corps_popin > table > tbody > tr,
#resume_petit_panier #footer_little_cart_table > tbody > tr {
    display: flex;
    flex-wrap: wrap;
    gap: var(--m-space-3);
    width: 100%;
    box-sizing: border-box;
}

/* Empilement quand les actions ne tiennent pas côte à côte, ou quand l'une
   d'elles est destructive et doit être écartée de l'autre. */
.m-actions--stack {
    flex-direction: column;
}

/* Le legacy termine ses rangées par un <div style="clear:both"> : inutile en
   flex, il y prenait un emplacement à parts égales avec les vrais boutons —
   mesuré, un bloc vide de 93px collé à « Étape suivante ». Un emplacement
   sans contenu n'est jamais une action. */
.m-actions > :empty:not(.m-action) {
    display: none;
}

/* Enfant direct = emplacement. Il ne porte que la répartition, pas l'habillage :
   le legacy intercale souvent une cellule de tableau entre la rangée et le
   bouton coloré, et un rembourrage posé ici retrécirait ce dernier. */
/* Base nulle plutôt qu'auto : les emplacements se partagent la largeur à
   parts égales au lieu de se dimensionner à leur texte — sans quoi
   « Enregistrer » et « Annuler » font 166 et 148px côte à côte, ce qui se
   lit comme une hiérarchie qui n'existe pas. Un écran qui veut vraiment
   distinguer ses deux actions le fait explicitement, comme le tunnel avec
   « Étape précédente ». */
.m-actions > *,
.ui-dialog .corps_popin > table > tbody > tr > td,
.ui-dialog .corps_popin .btnconteneur,
.ui-dialog .corps_popin .btnconteneur .btn,
#resume_petit_panier #footer_little_cart_table > tbody > tr > td {
    flex: 1 1 0;
    display: flex;
    min-height: var(--m-tap);
    padding: 0;
    box-sizing: border-box;
}

/* Le bouton lui-même. Les gabarits posent cette classe sur l'élément coloré,
   qu'il soit l'enfant direct de la rangée ou imbriqué dans une cellule. */
/* Le `display` porte un !important, comme le fond de .m-panel : le legacy
   habille ces boutons par leur id (`#commande_etape_suivante { display:
   inline-block }`), et un id bat n'importe quel nombre de classes. Sans lui,
   le bouton restait inline-block : le chevron, pseudo-élément dimensionné en
   8x8, voyait sa largeur et sa hauteur ignorées faute de contexte flex, et
   seule sa bordure droite se peignait — un trait vertical collé au libellé. */
.m-action,
.button_add_to_cart,
.ui-dialog .corps_popin .btnconteneur .btn > div,
#resume_petit_panier #button_little_cart {
    flex: 1 1 auto;
    display: flex !important;
    align-items: center;
    justify-content: center;
    gap: var(--m-space-2);
    float: none;
    width: auto;
    min-height: var(--m-tap);
    /* !important pour la même raison que le `display` ci-dessus : le legacy
       habille certains de ces boutons par leur id (`#button_big_cart_validate
       { padding: 5px }`). Ces 5px verticaux s'ajoutaient à la hauteur de
       cible — mesuré : 54px dans le panier contre 44px dans le tunnel, pour
       la même primitive. La cible tactile fixe la hauteur, le rembourrage ne
       doit agir qu'horizontalement. */
    padding: 0 var(--m-space-4) !important;
    box-sizing: border-box;
    border-radius: var(--m-radius-sm);
    text-align: center;
}

/* Le libellé est parfois un <a> inline à l'intérieur du pavé coloré : sans
   cela, seule la surface du texte serait cliquable et non tout le bouton. */
.m-action > a {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    min-height: var(--m-tap);
    text-decoration: none;
}

/* Le lien enveloppe parfois l'action au lieu d'être dedans — la mise au panier
   entoure « Voir ma sélection » d'un <a href="/cart/bigCart">. Inline par
   défaut, ce lien se dimensionne à son texte : le bouton principal se
   retrouvait rétréci et calé à gauche, sous une action secondaire pleine
   largeur. Les deux imbrications sont donc prévues. */
.m-actions a {
    display: flex;
    flex: 1 1 auto;
    text-decoration: none;
}

/* Action destructive : elle garde son rouge — c'est son signal — mais cesse
   d'avoir le poids visuel d'un bouton plein collé à la validation. Placée
   AVANT la rangée principale, comme sur la maquette : elle quitte ainsi la
   zone basse où le pouce se repose. */
/* `flex: 0 0 100%` la met seule sur sa ligne, et `order: -1` place cette ligne
   au-dessus de la rangée principale. */
.m-actions > .m-action--destructive,
.m-actions > *:has(> .m-action--destructive) {
    order: -1;
    flex: 0 0 100%;
}

/* Ces deux déclarations portent un !important, comme le fond de .m-panel et
   le rembourrage de .m-cards : le legacy habille ces boutons par leur id
   (`#delete_big_cart { background: red }`), et un id bat n'importe quel
   nombre de classes, quelle que soit la place dans la cascade. Le !important
   est ici sur la PRIMITIVE, donc à un seul endroit — c'est précisément ce qui
   évite un bloc de surcharge par bouton et par écran. */
.m-action--destructive {
    background: transparent !important;
    color: var(--m-danger) !important;
    font-weight: bold;
    text-decoration: underline;
}

/* Action secondaire : elle reste un bouton — c'est un chemin légitime, pas un
   repli — mais en contour, pour que l'action qui fait avancer l'achat se
   distingue. Sans quoi deux pavés identiques laissent l'utilisateur choisir au
   hasard : mesuré sur la mise au panier d'une offre à souhaits, « Faire un
   choix supplémentaire » et « Voir ma sélection » faisaient tous deux 304x44
   dans le même bleu nuit.

   Le contour passe par une ombre interne plutôt qu'une bordure : il ne
   modifie pas la géométrie et n'entre donc pas en conflit avec les bordures
   que le legacy pose sur ces boutons. */
.m-action--secondary {
    background: transparent !important;
    color: var(--m-navy) !important;
    box-shadow: inset 0 0 0 1px currentColor;
}

/* --------------------------------------------------------------------------
   Page de message d'erreur

   Le bloc et sa liste portent des largeurs de bureau en dur — 1000 et 980px —
   et #bloc_secondaire, le conteneur de page, en fait autant. Mesuré sur un
   écran de 390 : document à 1020px, soit 630px de débordement, le texte de
   l'erreur coupé à droite et le bouton de retour hors écran. Sur la page même
   dont le seul rôle est d'expliquer ce qui vient de se passer.

   La contrainte est portée au bloc d'erreur et à ses ancêtres via :has(), et
   non à #bloc_secondaire en général : les pages qui vivent en largeur fixe
   doivent y rester — la fiche produit n'est pas responsive, c'est voulu.
   -------------------------------------------------------------------------- */
body:has(.bloc_message_erreur) #bloc_secondaire,
.bloc_message_erreur,
.bloc_message_erreur ul,
.bloc_message_erreur li {
    width: auto;
    max-width: 100%;
    box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Listes de contenu

   Le legacy ne donne ni retrait ni rembourrage à ses <ul> de contenu. Avec
   list-style-position: outside — la valeur par défaut — la puce se peint À
   GAUCHE du bord du conteneur, donc dans la marge, et le texte revient à la
   ligne sans retrait dessous : la puce se lit comme un caractère orphelin en
   début de paragraphe. Mesuré sur les conditions de vente du tunnel : le <ul>
   a padding-left: 0 et commence à x=12, soit le bord exact de la carte.

   Branché sur les zones de contenu et non sur toutes les <ul> : le pied de
   page en rend une, qui est une liste de navigation et n'a pas à s'indenter.
   -------------------------------------------------------------------------- */
#commande_etape_contenu ul,
#commande_etape_contenu ol,
.bloc_message_erreur ul {
    padding-left: var(--m-space-5);
}

#commande_etape_contenu li + li {
    margin-top: var(--m-space-1);
}

/* --------------------------------------------------------------------------
   Dé-flottement générique (.m-stack)

   Le legacy dispose ses blocs en float + largeurs en pourcentage ou en pixels.
   Sur mobile ils doivent tous s'empiler. Plutôt que de répéter ce quatuor de
   déclarations sur chaque famille de sélecteurs (colonnes d'assurance, blocs
   de prestations, colonnes de livraison), on le nomme une fois.
   -------------------------------------------------------------------------- */
.m-stack {
    float: none;
    width: auto;
    max-width: 100%;
    display: block;
    box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Confort tactile

   Ciblage volontairement restreint aux éléments déjà block/inline-block : une
   règle générale sur `a` casserait les liens en ligne dans les paragraphes.
   -------------------------------------------------------------------------- */
.m-tap {
    display: flex;
    align-items: center;
    min-height: var(--m-tap);
    box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Popins jQuery UI

   Filet complémentaire au patch de scripts/mobile/dialog.js : il attrape les
   dialogs créés avant que notre script ne s'exécute, et ceux dont un code
   tiers repositionne la largeur après coup.

   Le position:fixed n'est pas cosmétique. En jQuery UI 1.10 le voile est en
   position:absolute dimensionné au document : sur une page de tunnel de
   plusieurs milliers de pixels, une modale ouverte après défilement atterrit
   hors écran et le tap « à l'extérieur » ne tombe pas au bon endroit.
   -------------------------------------------------------------------------- */
/* Le placement (position:fixed + centrage sur le viewport) est posé par
   Popins.anchor dans layout.js, pour desktop comme pour mobile : une seule
   logique, un seul endroit. Il ne reste ici que ce qui est propre au petit
   écran — la largeur et la hauteur maximale, qui doivent tenir compte de la
   tabbar. Pas de top/left forcés, sinon le centrage partagé serait écrasé. */
.ui-dialog {
    width: calc(100vw - 2 * var(--m-space-3)) !important;
    max-width: calc(100vw - 2 * var(--m-space-3)) !important;
    box-sizing: border-box;
}

/* Une SEULE zone de défilement, sur le contenu. jQuery UI pose déjà
   overflow:auto sur .ui-dialog-content ; ajouter overflow sur l'enveloppe
   créait deux conteneurs imbriqués, d'où des barres de défilement visibles
   alors même que le contenu tenait dans la boîte. L'enveloppe ne défile
   donc pas, et le contenu ne défile que s'il dépasse la hauteur utile. */
.ui-dialog .ui-dialog-content {
    width: auto !important;
    height: auto !important;
    max-height: calc(100dvh - var(--m-tabbar-h) - 4 * var(--m-space-2)) !important;
    box-sizing: border-box;
}

.ui-widget-overlay {
    position: fixed !important;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto !important;
    height: auto !important;
}

/* Fermeture native : plusieurs templates suppriment la barre de titre, la
   seule sortie restante doit donc être confortable au doigt. */
.ui-dialog .ui-dialog-titlebar-close {
    min-width: var(--m-tap);
    min-height: var(--m-tap);
}

/* Les croix des popins legacy sont des sprites : `.img_sprite` pose
   `icon_web_ce.png` à un décalage au pixel près, en `background-repeat:
   repeat`. Agrandir la boîte à 44px n'agrandit donc pas l'icône — elle
   DÉVOILE les pictogrammes voisins de la planche, et on voyait un demi-glyphe
   collé à droite de la croix.

   La boîte garde sa taille d'origine ; seule la zone tapable est étendue, en
   surimpression. Le pseudo-élément étant un enfant du <div> cliquable, le tap
   remonte à lui : la cible s'élargit sans rien changer au comportement.

   Ne pas toucher au `background-repeat` : la croix n'est PAS dessinée par le
   décalage déclaré, c'est une répétition de la planche qui l'amène dans la
   boîte. Passé en `no-repeat`, le bouton devient invisible — mesuré. */
.close_popin,
.close_popin_alert {
    position: relative;
}

.close_popin::after,
.close_popin_alert::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--m-tap);
    height: var(--m-tap);
    transform: translate(-50%, -50%);
}

/* Boutons des popins alert / confirm : dimensionnés sobrement pour la souris
   dans layout.css, relevés ici à la cible tactile et empilables. */
.popup_panel {
    flex-wrap: wrap;
}

.popup_panel input[type="button"] {
    flex: 1 1 auto;
    min-height: var(--m-tap);
    padding: 10px 18px;
}

.popup_content input[type="text"] {
    min-height: var(--m-tap);
}

/* Habillage de ces mêmes popins. Il vit ici et non dans layout.css : cette
   feuille-là sert aussi le bureau, où le rendu par défaut du navigateur est
   celui d'avant le ticket, et le ticket ne porte que sur le petit écran. */
.popup_content {
    color: var(--m-ink-2);
    font-family: "klavika_regular";
    font-size: var(--m-fs);
    line-height: 1.4;
}

.popup_message {
    margin-bottom: 18px;
}

.popup_panel {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

.popup_panel input[type="button"] {
    padding: 7px 18px;
    border: 1px solid var(--m-navy);
    border-radius: var(--m-radius-sm);
    background: var(--m-navy);
    color: var(--m-bg);
    font-family: "klavika_regular";
    font-size: var(--m-fs-sm);
    cursor: pointer;
}

/* Le second bouton d'un confirm est l'action d'abandon : elle ne doit pas
   peser autant que la validation. */
.popup_panel input[type="button"] + input[type="button"] {
    background: var(--m-bg);
    color: var(--m-navy);
}

.popup_panel input[type="button"]:hover {
    opacity: .85;
}

/* Le champ de saisie d'un jPrompt porte un size=30 en dur dans le plugin */
.popup_content input[type="text"] {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
}

/* Certaines popins ne portent pas leurs actions en <input type=button> mais
   en <div class="bouton_validation"> dans des cellules de tableau, avec un
   rembourrage de 10px posé en attribut inline. Mesurés sur la confirmation
   d'abandon du panier : « CONFIRMER » et « ANNULER » à 34px de haut, sous le
   seuil — sur la popin qui décide de vider un panier. */
.ui-dialog .bouton_validation {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--m-tap);
    box-sizing: border-box;
}

/* Zones de saisie d'une popin : le legacy les dimensionne par `cols`/`size`,
   attributs de présentation que le CSS de la page ne couvre pas puisque
   jQuery UI déplace la popin sur <body>, hors de tout conteneur stylé. Mesuré
   sur le commentaire de commande : `cols="60"` donne 501px de champ dans une
   popin de 366 — le contenu débordait de 165px et la popin s'ouvrait avec une
   barre de défilement horizontale, le texte saisi partant hors écran. */
.ui-dialog textarea,
.ui-dialog input[type="text"],
.ui-dialog input[type="email"],
.ui-dialog input[type="password"],
.ui-dialog input[type="number"],
.ui-dialog select {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

/* Actions d'une popin : la géométrie et la gouttière viennent de la primitive
   .m-actions / .m-action, à laquelle cette structure est branchée plus haut.
   Ne reste ici que la <table> qui les porte : centrée et dimensionnée à son
   contenu par le gabarit, elle doit sortir de sa mise en page de tableau pour
   que la rangée puisse s'exprimer. */
.ui-dialog .corps_popin > table,
.ui-dialog .corps_popin > table > tbody {
    display: block;
    width: 100%;
}

/* Titre d'une popin : le bouton de fermeture est un flottant à droite, donc
   hors flux — le libellé passait dessous dès qu'il tenait sur deux lignes,
   ce qui est systématique à 390px. */
.ui-dialog .titre_popin {
    display: flex;
    align-items: flex-start;
    gap: var(--m-space-2);
}

.ui-dialog .titre_popin > .close_popin {
    flex: 0 0 auto;
    float: none;
    margin-left: auto;
}

/* --------------------------------------------------------------------------
   Sélecteur de date jQuery UI

   Les champs date du tunnel sont en lecture seule : le calendrier est le SEUL
   moyen de les renseigner, donc un passage obligé du parcours. Mesuré en
   390px : cellules de jour à 39x26, sélecteur de mois à 25px, flèches à 29px
   — on vise à côté au doigt.
   -------------------------------------------------------------------------- */
#ui-datepicker-div {
    max-width: calc(100vw - 2 * var(--m-space-3));
    box-sizing: border-box;
}

#ui-datepicker-div table {
    width: 100%;
}

#ui-datepicker-div td a,
#ui-datepicker-div td span {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--m-tap);
    padding: 0;
    text-align: center;
}

#ui-datepicker-div .ui-datepicker-prev,
#ui-datepicker-div .ui-datepicker-next {
    width: var(--m-tap);
    height: var(--m-tap);
}

#ui-datepicker-div .ui-datepicker-title select {
    min-height: var(--m-tap);
    font-size: var(--m-fs-field);
}

/* --------------------------------------------------------------------------
   Boutons générés par LogiCE\Helper\Html::getBtn

   L'helper écrit une largeur en dur dans l'attribut style
   (`<div class="bleu" style="width:200px">`), donc inatteignable autrement
   qu'avec !important. Une seule règle couvre tous les boutons de toute
   l'application — les 200px des popins d'assurance comme les autres — sans
   toucher au PHP.
   -------------------------------------------------------------------------- */
.btnconteneur {
    float: none !important;
    width: 100% !important;
}

.btnconteneur .btn > div[style] {
    width: auto !important;
    min-width: 0 !important;
    box-sizing: border-box;
}

/* Sous 16px, Safari iOS zoome au focus et ne dézoome jamais. Le legacy est en
   12-15px un peu partout : on relève tous les champs du FO d'un coup. */
input:not([type=checkbox]):not([type=radio]):not([type=button]):not([type=submit]):not([type=hidden]),
select,
textarea {
    font-size: var(--m-fs-field);
}

/* --------------------------------------------------------------------------
   Confirmation d'ajout au panier

   Le socle la pose en `position: absolute` à left:42% / top:20% de la PAGE :
   elle est calée sur le document et non sur l'écran, donc selon l'endroit où
   l'on a défilé elle atterrit à cheval sur le bandeau de subvention, voire
   hors du champ de vision. Sur un écran de 390, ses 268px de large laissent
   par ailleurs le contenu à l'étroit.

   Traité ici et non dans applicatif/produit.css : cette feuille-là sert aussi
   le bureau, où le placement d'origine reste celui du ticket précédent.

   Le voile passe par une ombre portée démesurée plutôt que par un élément
   supplémentaire — produit.js appelle bien $("#flou").show(), mais aucun
   élément #flou n'existe dans le socle, l'assombrissement n'a donc jamais eu
   lieu. Cette forme évite d'ajouter du DOM et de toucher au script.
   -------------------------------------------------------------------------- */
/* Le placement — ancrage à l'écran, centrage, voile — vaut pour les deux
   rendus et vit dans applicatif/produit.css. Ne reste ici que ce qui tient au
   petit écran : 268px y laissent le libellé à l'étroit.

   `min()` et non une largeur nue : la borne doit être là où est la valeur.
   applicatif/produit.css pose bien un max-width, mais dans une autre feuille
   — la popin ne débordait que parce que celle-là était chargée. Mesuré à
   trois largeurs après correction : 280 rend 248, 390 rend 358, 768 rend 380. */
#add_to_cart {
    width: min(380px, calc(100vw - 2 * var(--m-space-4)));
    border-radius: var(--m-radius);
}

/* Voile. Obtenu par une ombre portée démesurée plutôt que par un élément
   supplémentaire : produit.js appelle bien $("#flou").show(), mais aucun
   élément #flou n'existe dans le socle — l'assombrissement n'a donc jamais eu
   lieu. Cette forme évite d'ajouter du DOM et de toucher au script.

   Mobile seulement : c'est de l'habillage, pas du placement. Au bureau la
   popin n'assombrissait rien, et une ombre n'intercepte pas les clics — elle
   laisserait croire à une page bloquée qui ne l'est pas.

   Le conteneur est rendu VIDE dans la page produit et rempli plus tard par
   l'AJAX : sans la garde de vacuité, l'ombre assombrirait tout l'écran dès le
   chargement, pour une popin inexistante. */
#add_to_cart:not(:empty) {
    box-shadow: 0 10px 40px var(--m-shadow-panel), 0 0 0 100vmax rgba(0, 0, 0, .45);
}

#add_to_cart_header {
    padding: var(--m-space-3) var(--m-space-4);
    font-size: var(--m-fs);
}

/* Libellé de l'offre ajoutée : c'est l'information de la popin, elle doit se
   lire avant l'action. */
.add_to_cart_produit {
    color: var(--m-navy);
    font-size: var(--m-fs-field);
    font-weight: bold;
    line-height: 1.35;
}

#add_to_cart_details {
    border-bottom: 0;
    background: var(--m-bg);
    padding: var(--m-space-5) var(--m-space-5) 0;
}

#footer_add_to_cart {
    padding: var(--m-space-5);
}

#footer_add_to_cart_table {
    width: 100%;
    border-spacing: var(--m-space-2) 0;
}

/* Les actions faisaient 5px de rembourrage : injouables au doigt. */
.button_add_to_cart {
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    padding: 7px 14px;
    border-radius: var(--m-radius-sm);
}

/* Confirmation d'ajout au panier, variante subvention : le gabarit y liste
   une quantité codée en dur à 1, qui n'a pas de sens pour une demande de
   subvention, suivie de deux cellules vides. Sur 390px, ce « 1 » orphelin et
   ses colonnes fantômes prennent la moitié de la ligne au libellé. Le tableau
   reste tel quel côté bureau : c'est le rendu d'avant le ticket. */
#add_to_cart .m-cart-subsidy .add_to_cart_details_quantity,
#add_to_cart .m-cart-subsidy td.add_to_cart_details_product_name + td.add_to_cart_details_product_name,
#add_to_cart .m-cart-subsidy td.little_cart_align_right {
    display: none;
}

/* La ligne restante n'a plus qu'une cellule utile : elle prend la largeur. */
#add_to_cart .m-cart-subsidy td.add_to_cart_details_product_name {
    width: auto;
}

/* --------------------------------------------------------------------------
   Pages offre et produit : contenu réduit à la largeur de l'écran

   Le template d'une offre ou d'un produit, et le panneau d'achat qui le
   flanque, sont mis en page pour le bureau sur 1000px. Le cadre de page
   quitte ses 1000px pour que le bandeau de l'offre tienne dans l'écran ;
   scripts/mobile/produit.js regroupe les blocs du contenu dans
   .m-produit-reduit, qui garde les 1000px et que le script réduit (zoom) à
   la largeur disponible — le rendu qu'avaient ces pages avant la version
   mobile. Le bandeau de l'offre est ramené à l'écran lui aussi : des
   templates lui imposent 1000px dans leur propre <style>. flow-root : le
   conteneur englobe le panneau d'achat et la colonne
   du template, tous deux flottants.
   -------------------------------------------------------------------------- */
body.m-ctx-produit #bloc_secondaire,
body.m-ctx-produit #bandeau_offre {
    width: auto;
}

body.m-ctx-produit .m-produit-reduit {
    display: flow-root;
    width: 1000px;
}
