/* ============================================================================
   fp-responsive.css — couche responsive commune à tout le site.

   POURQUOI CE FICHIER EXISTE
   Le site a été assemblé zone par zone (public, dashboard client, administration,
   finance, CRM, panel) et chaque zone a son propre CSS. L'adaptation mobile, elle,
   n'a jamais été faite au même endroit : sur 59 feuilles de style de page, 14
   n'avaient aucune media query et 25 n'en avaient qu'une. Résultat : sur téléphone
   les tableaux sortaient de l'écran, les grilles à colonnes fixes écrasaient le
   texte à zéro pixel de large, et les pages devenaient inutilisables.

   Corriger 564 vues une par une aurait produit 564 correctifs divergents. Ce
   fichier pose donc les règles communes une seule fois, et les pages n'ont plus à
   traiter que ce qui leur est vraiment particulier.

   CE QU'IL FAIT, ET CE QU'IL NE FAIT PAS
   Les règles de la section 1 sont des garde-fous : elles s'appliquent partout et ne
   font que rendre impossible un débordement horizontal. Les sections suivantes
   transforment la mise en page (grilles repliées, tableaux empilés) et sont donc
   volontairement LIMITÉES aux zones de contenu — jamais au chrome (barre latérale,
   barre du haut, navigation), qui a sa propre logique mobile et qu'un repli
   automatique casserait.

   Les zones de contenu sont :
     #main-content   dashboard client, administration, finance, CRM
     .fpr-contenu    public (posé par layouts/app.blade.php)
   Le panel v2 n'est PAS inclus : il a déjà ses propres media queries, complètes.

   ÉCHAPPATOIRES
   Une grille, un tableau ou une rangée flex qui doit garder sa forme sur mobile
   porte [data-fpr-garder] et aucune règle de mise en page ne la touche.

   CHARGEMENT
   Ce fichier vient APRÈS @stack('styles') dans chaque layout : il doit pouvoir
   écraser le CSS de page. Les !important ne sont pas de la paresse — ils sont
   nécessaires pour battre les styles `style="..."` posés dans le HTML, qui sont
   justement l'origine de la majorité des débordements.
   ========================================================================== */


/* ============================================================================
   1. GARDE-FOUS UNIVERSELS
   S'appliquent à toutes les zones, chrome compris. Aucune ne change une mise en
   page : elles empêchent seulement de sortir de l'écran.
   ========================================================================== */
@media (max-width: 991px) {

    /* La page ne défile jamais horizontalement. Sans cela, un seul élément trop
       large décale tout le contenu et la lecture verticale devient bancale. */
    html,
    body {
        max-width: 100%;
        overflow-x: hidden;
    }

    /* Les médias se contentent de la place disponible plutôt que d'imposer leur
       taille intrinsèque. `height:auto` évite l'écrasement des proportions. */
    img,
    svg,
    video,
    canvas,
    iframe,
    embed,
    object {
        max-width: 100%;
    }

    img,
    video,
    canvas {
        height: auto;
    }

    /* Le code et les blocs préformatés défilent dans leur propre boîte au lieu
       d'élargir la page. */
    pre {
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Une adresse IP, un identifiant de serveur, un courriel ou une URL n'a aucun
       point de césure naturel : sans ceci un seul mot long élargit toute la page. */
    td,
    th,
    dd,
    p,
    li,
    figcaption,
    blockquote {
        overflow-wrap: break-word;
    }

    /* Les titres cassent aussi, mais sans césure typographique arbitraire. */
    h1, h2, h3, h4, h5, h6 {
        overflow-wrap: break-word;
    }
}


/* ============================================================================
   2. TABLEAUX
   Un tableau est la structure la plus hostile au téléphone : sa largeur est dictée
   par la somme de ses colonnes, qu'il y ait 320 pixels d'écran ou 1600.

   Deux traitements, choisis par fp-responsive.js selon la forme du tableau :

     .fpr-tableau-empile  — le tableau devient une liste de fiches, une par ligne,
                            chaque cellule affichée « intitulé : valeur ». C'est
                            l'adaptation la plus lisible, réservée aux tableaux
                            réguliers (un seul niveau d'en-tête, pas de fusion).
     défilement           — le tableau garde sa forme et défile horizontalement
                            dans son enveloppe. Utilisé quand l'empilement
                            fausserait la lecture (en-têtes fusionnés, matrices).

   Dans les deux cas le tableau est enveloppé dans .fpr-tableau-boite, ce qui
   confine le débordement à cette boîte au lieu de le propager à la page.
   ========================================================================== */

.fpr-tableau-boite {
    max-width: 100%;
}

@media (max-width: 991px) {

    .fpr-tableau-boite {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        /* Indique que la boîte défile : sans cela rien ne signale au doigt qu'il
           reste des colonnes à droite. */
        scrollbar-width: thin;
    }

    /* Le tableau qui défile ne doit pas être comprimé par son enveloppe. `width:
       auto` ne suffit pas : la mise en page automatique des tableaux resserre
       alors les colonnes pour tenir dans la boîte, et on obtient des en-têtes
       coupés au milieu d'un mot au lieu d'un défilement. `max-content` lui rend
       la largeur qu'exige réellement son contenu ; `min-width: 100%` évite
       qu'un tableau court ne se recroqueville à gauche. */
    .fpr-tableau-boite > table {
        width: max-content;
        min-width: 100%;
        max-width: none;
    }

    /* Un tableau empilé n'est plus un tableau : il ne doit surtout pas prendre la
       largeur de son contenu, sinon les fiches débordent de l'enveloppe. */
    .fpr-tableau-boite > table.fpr-tableau-empile {
        width: 100%;
        max-width: 100%;
    }
}

@media (max-width: 767px) {

    /* --- Mode fiches empilées --- */
    table.fpr-tableau-empile {
        display: block;
        width: 100%;
        min-width: 0;
        border: 0;
        border-collapse: separate;
        border-spacing: 0;
    }

    /* Les intitulés de colonnes ne sont plus affichés en haut : ils sont répétés
       devant chaque valeur (voir ::before plus bas). */
    table.fpr-tableau-empile > thead {
        display: none;
    }

    table.fpr-tableau-empile > tbody,
    table.fpr-tableau-empile > tfoot {
        display: block;
        width: 100%;
    }

    /* Une ligne devient une fiche. Les teintes sont neutres et semi-transparentes
       pour rester lisibles sur les zones claires (finance, public) comme sur les
       zones sombres (dashboard, administration). */
    table.fpr-tableau-empile > tbody > tr,
    table.fpr-tableau-empile > tfoot > tr {
        display: block;
        width: 100%;
        margin: 0 0 12px;
        padding: 4px 12px;
        border: 1px solid rgba(128, 138, 150, 0.28);
        border-radius: 12px;
        background: rgba(128, 138, 150, 0.06);
    }

    table.fpr-tableau-empile > tbody > tr:last-child,
    table.fpr-tableau-empile > tfoot > tr:last-child {
        margin-bottom: 0;
    }

    /* Chaque cellule est une ligne « intitulé à gauche, valeur à droite ». */
    table.fpr-tableau-empile > tbody > tr > td,
    table.fpr-tableau-empile > tbody > tr > th,
    table.fpr-tableau-empile > tfoot > tr > td,
    table.fpr-tableau-empile > tfoot > tr > th {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 14px;
        width: auto;
        min-width: 0;
        max-width: none;
        padding: 9px 0;
        border: 0;
        border-bottom: 1px solid rgba(128, 138, 150, 0.16);
        text-align: right;
        white-space: normal;
    }

    table.fpr-tableau-empile > tbody > tr > td:last-child,
    table.fpr-tableau-empile > tbody > tr > th:last-child,
    table.fpr-tableau-empile > tfoot > tr > td:last-child,
    table.fpr-tableau-empile > tfoot > tr > th:last-child {
        border-bottom: 0;
    }

    /* L'intitulé de colonne, recopié depuis <thead> par fp-responsive.js. */
    table.fpr-tableau-empile > tbody > tr > td::before,
    table.fpr-tableau-empile > tfoot > tr > td::before {
        content: attr(data-fpr-intitule);
        flex: 0 0 40%;
        min-width: 0;
        text-align: left;
        font-weight: 600;
        opacity: 0.72;
        overflow-wrap: break-word;
    }

    /* Colonne sans intitulé (cases à cocher, boutons d'action) : la valeur prend
       toute la largeur, sans laisser un blanc de 40 % à sa gauche. */
    table.fpr-tableau-empile > tbody > tr > td[data-fpr-intitule=""]::before,
    table.fpr-tableau-empile > tfoot > tr > td[data-fpr-intitule=""]::before {
        display: none;
    }

    table.fpr-tableau-empile > tbody > tr > td[data-fpr-intitule=""],
    table.fpr-tableau-empile > tfoot > tr > td[data-fpr-intitule=""] {
        justify-content: flex-start;
        text-align: left;
    }

    /* Une cellule vide n'a pas à occuper une ligne. */
    table.fpr-tableau-empile > tbody > tr > td.fpr-cellule-vide {
        display: none;
    }

    /* Les contenus internes ne débordent pas de leur cellule empilée. */
    table.fpr-tableau-empile > tbody > tr > td > * {
        max-width: 100%;
    }

    /* Une rangée d'actions reste groupée et enroulable plutôt qu'alignée à droite
       sur une seule ligne trop longue. */
    table.fpr-tableau-empile > tbody > tr > td[data-fpr-intitule=""] {
        flex-wrap: wrap;
    }
}


/* ============================================================================
   3. GRILLES DE CONTENU
   Les grilles à colonnes fixes sont la première cause de casse constatée. Exemple
   relevé sur /cgu-cgv : `grid-template-columns: minmax(0,290px) minmax(0,1fr)`
   donnait, sur un conteneur de 310 pixels, une première colonne de 284 pixels et
   une seconde de ZÉRO — tout le texte des conditions sortait de l'écran.

   Sur téléphone, une colonne unique est la bonne réponse dans la quasi-totalité
   des cas : c'est le même contenu, dans le même ordre, simplement empilé.
   ========================================================================== */
@media (max-width: 767px) {

    /* QUI DÉCIDE DU REPLI

       Ce n'est PAS un sélecteur, c'est fp-responsive.js, sur mesure : une grille
       n'est repliée que si elle déborde réellement, ou si ses colonnes sont
       devenues trop étroites pour porter une valeur lisible.

       La première version repliait toute grille de plus d'une colonne, sans
       mesurer. Elle cassait les petits assemblages qui se servent d'une grille
       pour poser une icône à côté d'un libellé : ceux-là ne débordent pas et
       n'ont rien à empiler. Et elle ramenait à une colonne des paires de tuiles
       d'indicateurs qui tenaient parfaitement côte à côte.

       Le repli s'applique aux grilles écrites en style="..." comme à celles
       déclarées en feuille de style : le script lit le style CALCULÉ, la seule
       source qui sache ce que le navigateur a réellement appliqué. Le !important
       reste nécessaire pour battre un style en ligne. */
    #main-content .fpr-grille-repliee:not([data-fpr-garder]),
    .fpr-contenu .fpr-grille-repliee:not([data-fpr-garder]) {
        grid-template-columns: minmax(0, 1fr) !important;
    }

    /* Une grille repliée ne doit pas voir ses lignes tenues par la largeur de
       leur contenu. */
    #main-content .fpr-grille-repliee > *,
    .fpr-contenu .fpr-grille-repliee > * {
        min-width: 0;
        max-width: 100%;
    }

    /* Certaines grilles positionnent explicitement leurs enfants par colonne. Une
       fois la grille sur une seule colonne, ces consignes n'ont plus de sens et
       laisseraient des trous. */
    #main-content .fpr-grille-repliee > [style*="grid-column"],
    .fpr-contenu .fpr-grille-repliee > [style*="grid-column"] {
        grid-column: 1 / -1 !important;
    }
}


/* ============================================================================
   4. RANGÉES FLEX DE CONTENU
   Une rangée flex sans retour à la ligne pousse ses éléments hors de l'écran. En
   contenu, l'enroulement est presque toujours ce qu'on veut sur téléphone.
   ========================================================================== */
@media (max-width: 767px) {

    /* L'enroulement N'EST PAS posé ici, mais par fp-responsive.js, et seulement
       sur les rangées.

       Raison : `flex-wrap: wrap` sur un conteneur en COLONNE dont la hauteur est
       contrainte ne fait pas retourner à la ligne — il répartit le contenu sur
       PLUSIEURS COLONNES, qui débordent alors latéralement. Le cas s'est présenté
       tel quel sur /base-connaissance : l'encart « pas de vidéo » est un flex en
       colonne borné par aspect-ratio:16/9 ; enroulé, son icône et son texte se
       sont retrouvés côte à côte, 56 pixels hors de la carte.

       Aucun sélecteur CSS ne sait lire flex-direction. Seul le script peut donc
       distinguer une rangée d'une colonne — voir .fpr-flex-enroule plus bas. */

    /* Un élément flex refuse par défaut de descendre sous la taille de son
       contenu ; c'est ce qui fait déborder les rangées contenant un mot long.
       Cette règle-ci est sans danger dans les deux directions. */
    #main-content [style*="display:flex"]:not([data-fpr-garder]) > *,
    #main-content [style*="display: flex"]:not([data-fpr-garder]) > *,
    .fpr-contenu [style*="display:flex"]:not([data-fpr-garder]) > *,
    .fpr-contenu [style*="display: flex"]:not([data-fpr-garder]) > * {
        min-width: 0;
    }

    /* Marqué par fp-responsive.js sur les rangées flex venues du CSS de page. */
    #main-content .fpr-flex-enroule:not([data-fpr-garder]),
    .fpr-contenu .fpr-flex-enroule:not([data-fpr-garder]) {
        flex-wrap: wrap;
    }

    /* Marqué par fp-responsive.js sur les éléments flex qui sortent de leur place,
       que ce soit par leur contenu ou par rapport à leur parent.

       `min-width: auto` est la valeur par défaut d'un élément flex : elle
       l'empêche de descendre sous la largeur de son contenu. C'est la cause
       première des rangées qui sortent de l'écran, et la lever est le remède
       standard. On ne le fait QUE là où le débordement a été mesuré — l'appliquer
       partout écraserait des éléments qui n'avaient aucun problème.

       Exemple relevé : le fil d'étapes du panier à 320 pixels. Les pastilles
       « Panier / Paiement / Confirmation » sont en white-space: nowrap ; leur
       conteneur gardait sa largeur de contenu, 336 pixels, dans une boîte de 288. */
    #main-content .fpr-flex-souple:not([data-fpr-garder]),
    .fpr-contenu .fpr-flex-souple:not([data-fpr-garder]),
    #main-content .fpr-flex-enroule:not([data-fpr-garder]) > *,
    .fpr-contenu .fpr-flex-enroule:not([data-fpr-garder]) > * {
        min-width: 0;
    }
}


/* ============================================================================
   5. LARGEURS IMPOSÉES EN CONTENU
   Un `min-width: 760px` sur une carte, ou un `width: 900px`, tient l'écran ouvert
   quoi qu'on fasse par ailleurs. En contenu mobile, ces largeurs n'ont plus de
   raison d'être : la colonne unique donne déjà la largeur utile.
   ========================================================================== */
@media (max-width: 767px) {

    #main-content [style*="min-width"]:not([data-fpr-garder]):not(table),
    .fpr-contenu [style*="min-width"]:not([data-fpr-garder]):not(table) {
        min-width: 0 !important;
    }

    /* Les largeurs fixes en pixels deviennent des largeurs maximales : l'élément
       peut rétrécir, il ne peut plus imposer sa taille. */
    #main-content [style*="width:"]:not([data-fpr-garder]):not(table):not(col):not(colgroup),
    .fpr-contenu [style*="width:"]:not([data-fpr-garder]):not(table):not(col):not(colgroup) {
        max-width: 100%;
    }

    /* Marqué par fp-responsive.js quand la contrainte vient du CSS de page. */
    #main-content .fpr-largeur-liberee,
    .fpr-contenu .fpr-largeur-liberee {
        min-width: 0 !important;
        max-width: 100% !important;
    }

    /* Un positionnement absolu calculé pour un écran large sort de l'écran
       étroit. On ne le corrige que là où il a été marqué comme fautif. */
    #main-content .fpr-position-relachee,
    .fpr-contenu .fpr-position-relachee {
        position: static !important;
    }
}


/* ============================================================================
   6. FORMULAIRES
   Deux problèmes distincts. D'abord iOS zoome automatiquement sur tout champ dont
   le texte fait moins de 16 pixels, et ne dézoome jamais : la page reste décalée.
   Ensuite les champs à largeur fixe débordent.
   ========================================================================== */
@media (max-width: 767px) {

    input,
    select,
    textarea {
        /* 16px est le seuil exact au-delà duquel Safari iOS ne zoome plus. */
        font-size: max(16px, 1em);
        max-width: 100%;
    }

    #main-content input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([data-fpr-garder]),
    #main-content select:not([data-fpr-garder]),
    #main-content textarea:not([data-fpr-garder]),
    .fpr-contenu input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([data-fpr-garder]),
    .fpr-contenu select:not([data-fpr-garder]),
    .fpr-contenu textarea:not([data-fpr-garder]) {
        width: 100%;
        min-width: 0;
    }

    /* Un champ et son bouton côte à côte tiennent rarement sur 320 pixels. */
    #main-content .input-group:not([data-fpr-garder]),
    .fpr-contenu .input-group:not([data-fpr-garder]) {
        display: flex;
        flex-wrap: wrap;
    }
}


/* ============================================================================
   7. CIBLES TACTILES
   Un lien de 18 pixels de haut se rate au doigt. On agrandit la zone cliquable
   sans toucher à l'apparence, via le remplissage vertical et une hauteur minimale
   sur les seuls éléments réellement destinés au clic.
   ========================================================================== */
@media (max-width: 767px) {

    /* `a[class*="btn"]` n'est pas de la paresse : dans ce dépôt la très grande
       majorité des boutons sont des liens — admin-btn (157 emplois),
       admin-btn-secondary (102), action-btn, pv2-btn-*, fd-btn… Ne viser que
       <button> et .btn laissait donc de côté l'essentiel des commandes, et c'est
       ce qui faisait rester les boutons d'une carte de service sous 40 pixels. */
    #main-content button:not([data-fpr-garder]),
    #main-content .btn:not([data-fpr-garder]),
    #main-content [role="button"]:not([data-fpr-garder]),
    #main-content a[class*="btn"]:not([data-fpr-garder]),
    .fpr-contenu button:not([data-fpr-garder]),
    .fpr-contenu .btn:not([data-fpr-garder]),
    .fpr-contenu [role="button"]:not([data-fpr-garder]),
    .fpr-contenu a[class*="btn"]:not([data-fpr-garder]) {
        min-height: 40px;
    }

    /* Un lien-bouton dont on relève la hauteur minimale garderait son libellé
       collé en haut de la zone agrandie. On centre donc verticalement — mais SANS
       toucher au `display` : imposer inline-flex à tous les liens-boutons
       changerait la disposition de ceux qui sont en block pleine largeur ou en
       colonne. `align-items` et `justify-content` sont sans effet sur un élément
       qui n'est ni flex ni grid, donc sans risque ; ils n'agissent que là où la
       feuille de page a déjà choisi une de ces dispositions. */
    #main-content a[class*="btn"]:not([data-fpr-garder]),
    .fpr-contenu a[class*="btn"]:not([data-fpr-garder]) {
        align-items: center;
    }

    /* Les liens en liste de navigation, pas les liens dans un paragraphe : un
       min-height sur un lien en ligne casserait la justification du texte. */
    #main-content li > a:not([data-fpr-garder]),
    .fpr-contenu li > a:not([data-fpr-garder]) {
        display: inline-block;
        padding-top: 2px;
        padding-bottom: 2px;
    }
}


/* ============================================================================
   8. FENÊTRES MODALES
   Une modale dimensionnée pour un écran large déborde et, plus grave, ses boutons
   de validation se retrouvent hors écran sans moyen de défiler jusqu'à eux.
   ========================================================================== */
@media (max-width: 767px) {

    /* Ce bloc reste volontairement étroit.

       Les modales de ce dépôt ont été vérifiées une par une : la quasi-totalité
       borne déjà correctement sa hauteur (max-height: 90vh + overflow-y: auto) et
       sa largeur. Les trois panneaux qui ne le faisaient pas ont été corrigés dans
       LEUR fichier — .code-modal-box, .panel-modal-content et la .modal-box des
       fiches de prospect — parce qu'une règle générale sur [class*="modal"]
       toucherait aussi les en-têtes, pieds et titres de modale, et transformerait
       chacun en conteneur de défilement capable de rogner un menu déroulant.

       Ne restent ici que les modales Bootstrap, dont le gabarit est connu. */
    .modal-dialog,
    .fpr-modale {
        max-width: 100% !important;
        margin: 8px auto !important;
    }

    .modal-content,
    .fp-modal-content {
        max-width: 100%;
        /* La modale ne dépasse jamais l'écran et défile en son sein : les actions
           du bas restent toujours atteignables. */
        max-height: calc(100vh - 16px);
        max-height: calc(100dvh - 16px);
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    .modal-body {
        max-width: 100%;
        overflow-x: hidden;
    }

    /* Les pieds de modale passent en colonne : deux boutons larges valent mieux
       que deux boutons tronqués. */
    .modal-footer {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
    }

    .modal-footer > .btn,
    .modal-footer > button {
        flex: 1 1 auto;
    }
}


/* ============================================================================
   9. DÉFILEMENTS HORIZONTAUX ASSUMÉS
   Certaines structures se lisent mieux en défilant qu'empilées : une barre
   d'onglets, un fil de sous-navigation, une frise. Elles portent .fpr-defile et
   défilent proprement, avec calage sur les éléments.
   ========================================================================== */
@media (max-width: 991px) {

    .fpr-defile {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x proximity;
        /* Masque la barre de défilement sans empêcher le geste. */
        scrollbar-width: none;
    }

    .fpr-defile::-webkit-scrollbar {
        display: none;
    }

    .fpr-defile > * {
        flex: 0 0 auto;
        scroll-snap-align: start;
    }
}


/* ============================================================================
   10. UTILITAIRES
   ========================================================================== */

/* Masquer sur téléphone / n'afficher que sur téléphone. Utilisés par les pages
   qui doivent proposer deux présentations d'une même donnée. */
@media (max-width: 767px) {
    .fpr-sans-mobile { display: none !important; }
}

@media (min-width: 768px) {
    .fpr-mobile-seul { display: none !important; }
}

/* Respect du réglage système : pas d'animation pour qui n'en veut pas. */
@media (prefers-reduced-motion: reduce) {
    .fpr-tableau-boite,
    .fpr-defile {
        scroll-behavior: auto;
    }
}
