/* ============================================================================
   CLIENTIA — SOCLE MOBILE PARTAGÉ                              (2026-09-02)
   ----------------------------------------------------------------------------
   Pourquoi ce fichier : styles.css n'est chargé que par index.html et
   honoraires.html. Les 5 autres pages (secretariat, facturier, recouvrement,
   huissier, login) embarquent chacune leur CSS en ligne, avec leurs propres
   conteneurs, tables, champs et points de rupture. Il n'existait donc AUCUN
   endroit unique où corriger un défaut mobile : chaque page le reproduisait.

   Ce fichier est ce point unique. Il ne porte QUE des fondamentaux mobiles —
   pas d'identité visuelle, pas de couleurs, pas de refonte. Il est chargé en
   DERNIER par les 7 pages.

   Points de rupture canoniques (les seuls à utiliser désormais) :
       ≤ 640 px   mobile
       ≤ 1024 px  tablette
       > 1024 px  desktop
   Les seuils 380 / 430 restent autorisés pour des AJUSTEMENTS typographiques,
   jamais pour un changement de mise en page (sinon on recrée le trou 381-640
   qui laissait les tableaux bloqués à 600 px sur tous les téléphones récents).
   ========================================================================== */

:root {
    --pad-x: 24px;          /* padding horizontal du conteneur de page */
    --touch-min: 44px;      /* plancher de cible tactile (WCAG 2.5.8 AAA) */
    --safe-b: env(safe-area-inset-bottom, 0px);
    --safe-t: env(safe-area-inset-top, 0px);
}

@media (max-width: 640px) {
    :root { --pad-x: 16px; }
}

/* ---------------------------------------------------------------------------
   1. TABLEAUX — une seule stratégie : la table garde sa largeur naturelle et
      c'est le CONTENEUR qui défile. Jamais l'inverse.

      Avant : la largeur minimale était portée par la table (min-width:600px),
      puis annulée à 380 px (min-width:100%). Entre 381 et 640 px — c'est-à-dire
      sur la quasi-totalité du parc iPhone/Android — la table restait clouée à
      600 px dans un conteneur de 346 px, et html/body{overflow-x:hidden}
      rendait les colonnes de droite définitivement inatteignables. Mesuré :
      14 colonnes, en-tête « Relance » replié sur 16 lignes dans 20 px.
   --------------------------------------------------------------------------- */
.table-scroll,
.table-wrap,
.hc-table-scroll,
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
}

@media (max-width: 1024px) {
    /* La table prend la largeur qu'il lui faut ; le conteneur défile. */
    .table-scroll > table,
    .table-wrap > table,
    .table-responsive > table,
    table.data-table,
    table.hc-table,
    table.list {
        width: max-content;
        min-width: 100%;
    }
    /* Les cellules ne se replient plus en tranches illisibles. */
    .table-scroll th, .table-scroll td,
    .table-wrap th,   .table-wrap td,
    table.data-table th, table.data-table td,
    table.hc-table th,   table.hc-table td {
        white-space: nowrap;
        word-break: normal;
        overflow-wrap: normal;
    }
    /* Sauf les colonnes explicitement rédactionnelles. */
    .table-scroll td.wrap, .table-wrap td.wrap,
    table.data-table td.wrap, td[data-wrap] {
        white-space: normal;
        min-width: 12rem;
    }
    /* Repère visuel : le bord droit s'estompe quand il reste à faire défiler. */
    .table-scroll, .table-wrap { scrollbar-width: thin; }
}

/* Les hauteurs maximales étaient calées sur le chrome desktop
   (calc(100vh - 290px)) : sur mobile le chrome empilé dépasse largement 290 px,
   ce qui créait une zone de défilement imbriquée dans une page qui défile déjà. */
@media (max-width: 1024px) {
    .table-wrap, .table-scroll { max-height: none; }
}

/* ---------------------------------------------------------------------------
   2. CHAMPS DE SAISIE — plancher 16 px.
      Sous 16 px, iOS Safari zoome automatiquement à chaque focus et ne
      dézoome jamais : l'utilisateur se retrouve coincé dans une page élargie.
      C'était le cas sur l'écran de connexion et sur tout le générateur de
      factures, qui n'avaient aucune surcharge mobile.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
    select,
    textarea {
        font-size: 16px;
        max-width: 100%;
    }
    /* Un champ ne doit jamais imposer sa largeur intrinsèque à sa ligne. */
    input, select, textarea { min-width: 0; }
}

/* ---------------------------------------------------------------------------
   3. CIBLES TACTILES — plancher 44 px sur les commandes réelles.
      Mesuré avant correction : cases à cocher de sélection en masse 13×13 px
      (workflow « préparer un dépôt de chèques » inutilisable au doigt),
      boutons secondaires 26 px, croix de suppression 9×14 px.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    button, .btn, [role="button"], a.btn, summary, details > summary {
        min-height: var(--touch-min);
    }
    /* Les boutons compacts gardent leur look, gagnent une zone tactile. */
    .btn.sm, .btn-sm, .btn.ghost.sm, button.del, .icon-btn, .btn-icon {
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* 13 x 13 px avant correction : la selection en masse des echeances et des
       impayes — le workflow « preparer un depot de cheques » — etait inutilisable
       au doigt. La case reste visuellement dense (adaptee a un tableau financier)
       mais la marge porte l'espacement entre deux cibles au-dela de 44 px, ce que
       WCAG 2.5.8 accepte au titre de l'exception d'espacement. */
    input[type="checkbox"], input[type="radio"] {
        width: 24px;
        height: 24px;
        margin: 10px;
        flex: 0 0 auto;
    }
    /* La cellule qui porte la case reste centrée sur la ligne. */
    td:has(> input[type="checkbox"]), th:has(> input[type="checkbox"]) {
        text-align: center;
    }
}

/* ---------------------------------------------------------------------------
   4. LIGNES D'ACTIONS ET DE FILTRES — elles doivent s'enrouler.
      Une rangée flex sans flex-wrap pousse ses enfants hors écran ; combinée à
      overflow-x:hidden, le contenu devient inatteignable. C'est ce qui rendait
      « Déconnexion », « Mot de passe » et « Recouvrement » injoignables depuis
      un téléphone sur le poste secrétariat (mesuré : boutons à x=656..763 sur
      un écran de 320 px, sans défilement possible).
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    .header,
    .header > div,
    .view-header,
    .filters,
    .filter-row,
    .actions,
    .toolbar,
    .btn-row {
        flex-wrap: wrap;
    }
}

/* ---------------------------------------------------------------------------
   5. GRILLES auto-fit — un plancher minmax() supérieur à la largeur
      disponible déborde sans qu'aucune media query puisse le rattraper,
      parce que auto-fit ne descend jamais sous le minimum demandé.
      min(Xpx, 100%) neutralise le plancher dès que le conteneur est plus étroit.
   --------------------------------------------------------------------------- */
.pv2-grid      { grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); }
.recouv-canaux { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.ged-recos     { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }

/* Grilles à colonnes fixes restées à 2-3 colonnes sur téléphone : des montants
   sur 85 px de large ne sont pas lisibles. */
@media (max-width: 640px) {
    .recouv-canal-stats,
    .recouv-canal-stats.triple,
    .quick-fields-grid,
    .ged-gen-vars,
    .ftm-stats-panel,
    .dash-cadrage-line-4 {
        grid-template-columns: 1fr 1fr;
    }
}
@media (max-width: 430px) {
    .recouv-canal-stats,
    .recouv-canal-stats.triple,
    .quick-fields-grid,
    .ged-gen-vars,
    .dash-cadrage-line-4 {
        grid-template-columns: 1fr;
    }
}

/* ---------------------------------------------------------------------------
   6. ENCOMBREMENT — rien ne doit dépasser sa boîte, et le contenu doit pouvoir
      passer sous les boutons flottants (assistant + chat) ancrés en bas.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    img, video, canvas, svg, iframe, pre { max-width: 100%; }
    /* Les chiffres longs (1 248 158,90 €) ne doivent pas élargir leur colonne. */
    .kpi-value, .dash-statut-value, .dash-cat-value, .stat-value {
        overflow-wrap: anywhere;
    }
}

@media (max-width: 640px) {
    /* Deux FAB de 56 px + marges : on libère la hauteur correspondante. */
    body { padding-bottom: calc(96px + var(--safe-b)); }
    .container, main, .hc-page { padding-bottom: 24px; }
}

/* ---------------------------------------------------------------------------
   7. ZONES SÛRES iOS — encoche en haut, barre d'accueil en bas.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    .mobile-topbar, .header.sticky, .app-topbar {
        padding-top: calc(var(--safe-t) + 0px);
    }
    .bulk-bar, .sticky-actions, .modal-footer, .fab, .fab-stack {
        margin-bottom: var(--safe-b);
    }
}

/* ---------------------------------------------------------------------------
   8. HAUTEURS DE FENÊTRE — sur mobile, 100vh vaut la hauteur AVEC la barre
      d'adresse rétractée : un élément en 100vh dépasse donc l'écran tant que
      la barre est visible. dvh suit la hauteur réellement disponible.
   --------------------------------------------------------------------------- */
@supports (height: 100dvh) {
    @media (max-width: 1024px) {
        .modal, .modal-overlay, .drawer, .sidebar, .fullscreen {
            max-height: 100dvh;
        }
    }
}

/* ---------------------------------------------------------------------------
   9. ÉCHELLE DE PLANS — il n'en existait aucune : 23 valeurs de z-index de 30 à
      99999, posées indépendamment par chaque script. Conséquence mesurée : la
      barre supérieure mobile (1090) passait AU-DESSUS des modales (100) et du
      tiroir de navigation (1080). Le titre d'une modale plein écran était
      invisible sous la barre, et toute commande située dans ses 52 premiers
      pixels recevait les clics de la barre — dont le bouton Déconnexion.
   --------------------------------------------------------------------------- */
:root {
    --z-fab: 900;
    --z-topbar: 1000;
    --z-backdrop: 1090;
    --z-drawer: 1100;
    --z-modal: 1200;
    --z-toast: 1300;
}

.mobile-topbar   { z-index: var(--z-topbar); }
.sidebar-backdrop{ z-index: var(--z-backdrop); }
.sidebar         { z-index: var(--z-drawer); }
.modal, .modal-overlay, .rlz-overlay, .hf-overlay,
.ged-overlay, .hc-modal-backdrop { z-index: var(--z-modal); }
.toast, .rlz-toast, .ai-toast    { z-index: var(--z-toast); }

/* Le voile doit couvrir TOUT l'écran, barre supérieure comprise : sinon un
   appui sur la barre ne ferme pas le tiroir et déclenche une action derrière. */
.sidebar-backdrop { inset: 0; }

/* Le tiroir répète la marque déjà affichée par la barre, et se faisait couper
   par elle. On récupère la hauteur pour la navigation, qui en manque
   (mesuré : 1386 px de nav pour 699 px disponibles). */
@media (max-width: 900px) {
    .sidebar .brand { display: none; }
}

/* ---------------------------------------------------------------------------
   10. BOUTONS FLOTTANTS — trois scripts (assistant, connexions, profil) posaient
       chacun le sien à des coordonnées codées en dur, à 8 px d'écart pour des
       disques de 52 px, avec des plans 75 fois différents (9000 vs 120).
       Résultat mesuré : le bouton « Connexions » n'était cliquable que sur ~15 %
       de sa surface, entièrement recouvert par celui de l'assistant.
       On les range dans une pile verticale déterministe.
   --------------------------------------------------------------------------- */
.ai-fab      { z-index: var(--z-fab); bottom: calc(16px + var(--safe-b)) !important; right: 16px !important; }
#cnx-fab     { z-index: var(--z-fab); bottom: calc(84px + var(--safe-b)) !important; right: 16px !important; }
#pw-fab      { z-index: var(--z-fab); bottom: calc(152px + var(--safe-b)) !important; right: 16px !important; }

/* Un bouton flottant ne doit jamais rester posé sur une modale ouverte.

   ⚠ CORRECTIF 02/09/2026 — LA CONDITION ÉTAIT VRAIE EN PERMANENCE SUR DEUX
   PAGES, ET LES BULLES Y ÉTAIENT DONC INVISIBLES EN TOUTES CIRCONSTANCES.
   `.hidden` ne porte pas toujours sur le même élément :
     • index.html          le pose sur la `.modal` elle-même ;
     • secretariat.html    le pose sur le CONTENEUR `.modal-overlay` ;
     • facturier.html      le pose sur le CONTENEUR `.overlay`.
   Sur les deux dernières, la `.modal` INTERNE ne porte jamais `.hidden` :
   `body:has(.modal:not(.hidden))` était donc satisfait dès le chargement.
   Mesuré : assistant + messages + connexions masqués en permanence sur
   secretariat.html, assistant masqué en permanence sur facturier.html —
   aucun utilisateur de ces écrans n'a jamais pu ouvrir ces bulles.

   On ignore désormais une `.modal` posée DANS un conteneur masqué, et on
   traite les deux familles de conteneurs pour les TROIS bulles : la règle ne
   couvrait `.modal-overlay` que pour `.ai-fab`, si bien qu'une fois le défaut
   corrigé les deux autres seraient restées posées sur la fiche du secrétariat.
   `.overlay` est ajouté pour la même raison, et parce que son plan (60) passe
   SOUS celui des bulles (900) : sans cette règle, la bulle de l'assistant se
   poserait sur les modales du facturier. */
body:has(.modal:not(.hidden):not(.hidden .modal)) .ai-fab,
body:has(.modal:not(.hidden):not(.hidden .modal)) #cnx-fab,
body:has(.modal:not(.hidden):not(.hidden .modal)) #pw-fab,
body:has(.modal-overlay:not(.hidden)) .ai-fab,
body:has(.modal-overlay:not(.hidden)) #cnx-fab,
body:has(.modal-overlay:not(.hidden)) #pw-fab,
body:has(.overlay:not(.hidden)) .ai-fab,
body:has(.overlay:not(.hidden)) #cnx-fab,
body:has(.overlay:not(.hidden)) #pw-fab { display: none !important; }

/* ---------------------------------------------------------------------------
   11. INFOBULLES ET MESSAGES — cause racine commune : `position:fixed` +
       `left:50%` + `translateX(-50%)`. Un élément fixe dont `left` est renseigné
       et la largeur automatique prend une largeur « shrink-to-fit » bornée par
       « largeur du conteneur − left », soit 50vw. Sur un écran de 390 px la
       boîte était donc plafonnée à 195 px — et comme les boutons portaient
       `white-space:nowrap`, c'est la colonne de texte qui absorbait tout
       l'écrasement (mesurée à 58 px, soit 1 à 3 mots par ligne, sur 247 px de
       haut, recouvrant les filtres en dessous).
       C'est le même défaut sur l'astuce contextuelle et sur les messages
       d'erreur métier du facturier et du recouvrement.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
    .ai-toast, .toast, .rlz-toast {
        left: 12px !important;
        right: 12px !important;
        transform: none !important;
        max-width: none !important;
        width: auto !important;
    }
    .ai-toast {
        /* Au-dessus de la pile de boutons flottants : sinon « ne plus afficher »
           passe sous le bouton de l'assistant et devient intappable. */
        bottom: calc(84px + var(--safe-b)) !important;
        flex-wrap: wrap;
        align-items: flex-start;
        row-gap: 6px;
    }
    .ai-toast > span { flex: 1 1 100%; min-width: 0; }
    /* Le ✕ de fermeture mesurait 9 a 11 px de large : intappable au doigt. */
    .ai-toast button { min-height: 44px; min-width: 44px; font-size: 13px;
                       display: inline-flex; align-items: center; justify-content: center; }
    .ai-toast button[data-off] { min-width: 0; padding: 0 10px; }
    .ai-toast button[data-x] { margin-left: auto; }
    /* L'animation d'entrée reposait sur le même translateX(-50%). */
    .ai-toast { animation: none; }
}

/* ---------------------------------------------------------------------------
   12. CIBLES DE NAVIGATION — le hamburger et la déconnexion de la barre mobile
       faisaient 40 px de large. Le visuel reste à 40 px, la zone tactile monte
       à 44 px sans décaler la mise en page.
   --------------------------------------------------------------------------- */
@media (max-width: 900px) {
    .mobile-burger, .mobile-logout { position: relative; }
    .mobile-burger::after, .mobile-logout::after {
        content: "";
        position: absolute;
        inset: -2px;
        min-width: var(--touch-min);
        min-height: var(--touch-min);
    }
}

/* ---------------------------------------------------------------------------
   13. TIROIR DE NAVIGATION — la page continuait de défiler derrière lui, et la
       position de lecture était perdue à la fermeture. La classe posée par le
       script d'ouverture (`has-mobile-sidebar-open`) suffit à verrouiller le
       défilement, sans toucher au JavaScript.
   --------------------------------------------------------------------------- */
body.has-mobile-sidebar-open { overflow: hidden; overscroll-behavior: contain; }

/* ---------------------------------------------------------------------------
   14. FACTURIER — le tableau « Factures émises » était dans un conteneur
       défilant, mais la table portait `width:100%` sans largeur minimale : elle
       se rétractait à la largeur de l'écran, donc il n'y avait rien à faire
       défiler et les 9 colonnes se compressaient. Une colonne était même
       mesurée à 0 px de large (le libellé de prestation, invisible).
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    table:has(> tbody#liste) { min-width: 820px; width: max-content; }
    table:has(> tbody#liste) th, table:has(> tbody#liste) td { white-space: nowrap; }
}

/* ---------------------------------------------------------------------------
   15. ONGLETS ET SEGMENTS des pages autonomes — mesurés entre 24 et 35 px de
       haut, sans aucun plancher tactile (ces pages ne chargent pas styles.css).
   --------------------------------------------------------------------------- */
@media (max-width: 900px) {
    .tabs .tab, .seg button, .chip-lien, .btn.ghost.sm, .btn.sm {
        min-height: var(--touch-min);
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .tabs { flex-wrap: wrap; gap: 8px; }
}

/* ---------------------------------------------------------------------------
   16. PLAGES DE DATES « Du … Au … » — deux champs `date` font environ 160 px
       chacun à 16 px de police ; posés sur une seule ligne avec leurs libellés,
       ils dépassaient l'écran et le champ « Au » se retrouvait coupé, donc
       impossible à renseigner. Ils passent chacun sur leur ligne.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
    .ech-period-dates, .fin-date-range, .ftm-date-range {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        width: 100%;
    }
    .ech-period-dates > label, .fin-date-range > label, .ftm-date-range > label {
        display: flex;
        align-items: center;
        gap: 8px;
        flex: 1 1 100%;
        min-width: 0;
    }
    input[type="date"], input[type="datetime-local"], input[type="month"] {
        flex: 1 1 auto;
        min-width: 0;
        max-width: 100%;
    }
    /* Quand le libellé « Du » / « Au » est un élément frère du champ (et non son
       parent), il faut que chaque paire occupe sa propre ligne : sinon le second
       libellé se retrouve orphelin en fin de première ligne. */
    .dash-filter-group > input[type="date"],
    .filter-group > input[type="date"] {
        flex: 1 1 calc(100% - 34px);
    }
}

/* ---------------------------------------------------------------------------
   17. PLANCHER TYPOGRAPHIQUE — 190 déclarations du produit descendent sous
       12 px, dont les libellés de tous les indicateurs financiers (10 px), les
       en-têtes de tableaux (10-11 px), les numéros de dossier des cartes kanban
       (10 px) et les libellés du formulaire client (10,5 px). Sur téléphone
       cela n'est pas « compact », c'est illisible : ce sont précisément les
       éléments qui portent la donnée métier.
       On ne relève que les porteurs d'information, pas la décoration.
   --------------------------------------------------------------------------- */
@media (max-width: 900px) {
    .kpi-label, .fin-kpi-label, .ca-hero-stat-label, .recouv-cadrage-label,
    .recouv-canal-stat .label, .tva-strip-label, .modal-mini-stat .label,
    .kanban-card-code, .kanban-card-meta, .ech-recap-cell .lbl,
    .dash-statut-cat, .dash-cat-meta, .nav-badge, .badge,
    #client-form .form-grid label, .form-grid label,
    .depot-cheques-list th, .ged-badge-arch, .huissier-focus-label {
        font-size: 12px;
    }
    .data-table thead th { font-size: 11.5px; }
    .nav-section-title { font-size: 11.5px; }
    .pill, .muted, .fin-period-info-label, .hc-coord-badge,
    .dash-ca-label, th, th.num { font-size: 11.5px; }
}

/* ---------------------------------------------------------------------------
   18. FILTRES MULTI-VALEURS (menus à cases à cocher greffés sur les <select>) —
       le bouton porte `min-width: 165px`, ce qui l'empêche d'entrer dans une
       demi-colonne sur un écran de 320 px, et le menu déroulant est en largeur
       fixe. Sur téléphone le bouton suit la grille de filtres et le menu occupe
       la largeur du bouton.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
    .ms { display: flex; }
    .ms-btn {
        min-width: 0;
        max-width: none;
        width: 100%;
        min-height: var(--touch-min);
    }
    .ms-menu {
        left: 0;
        right: 0;
        width: auto;
        min-width: 0;
        max-height: 50dvh;
        overflow-y: auto;
    }
    .ms-menu label, .ms-menu .ms-opt { min-height: var(--touch-min); display: flex; align-items: center; }
}

/* ---------------------------------------------------------------------------
   19. VUE CARTES — pour les écrans qu'on ouvre avec le client au téléphone
       (dossiers, fiche client, impayés, relances, recouvrement), lire une ligne
       ne doit pas demander un balayage horizontal. Le tableau devient une pile
       de cartes ; les libellés viennent du <thead>, stampés par mobile-cards.js.

       Les champs secondaires sont disposés sur deux colonnes, libellé au-dessus
       de la valeur : une carte de 15 champs tient ainsi en ~250 px au lieu des
       ~470 px qu'imposerait une ligne par champ. Rien n'est masqué.

       Les tableaux de comparaison (cadrage, balance, grand livre, CA par année)
       gardent volontairement le défilement horizontal : on y compare des
       colonnes, la carte n'aurait aucun sens. Un bouton permet de revenir au
       tableau quand on a besoin de comparer, et le choix est mémorisé.
   --------------------------------------------------------------------------- */
.vue-bascule {
    display: none;
    font: inherit;
    font-size: 13px;
    background: var(--bg-3, #2d3854);
    color: var(--text-soft, #c5cbd9);
    border: 1px solid var(--border, #3a4666);
    border-radius: 8px;
    padding: 0 14px;
    min-height: var(--touch-min);
    margin: 0 0 10px;
    cursor: pointer;
}

@media (max-width: 640px) {
    .cartes-mobile { position: relative; overflow-x: visible; border: none; background: none; max-height: none; }
    .cartes-mobile > .vue-bascule { display: inline-flex; align-items: center; }

    /* ---- mode cartes (par défaut) ---- */
    .cartes-mobile:not(.vue-tableau) > table { display: block; width: 100%; min-width: 0; }
    .cartes-mobile:not(.vue-tableau) thead { display: none; }
    .cartes-mobile:not(.vue-tableau) tbody { display: flex; flex-direction: column; gap: 10px; }

    .cartes-mobile:not(.vue-tableau) tbody > tr {
        display: grid;
        grid-template-columns: 1fr 1fr auto;
        align-items: start;
        gap: 10px 14px;
        background: var(--bg-2, #232c44);
        border: 1px solid var(--border, #3a4666);
        border-radius: 10px;
        padding: 12px 14px;
    }
    .cartes-mobile:not(.vue-tableau) tbody > tr[data-pleine] { display: block; text-align: center; }

    .cartes-mobile:not(.vue-tableau) tbody > tr > td {
        display: block;
        padding: 0;
        border: none;
        white-space: normal;
        text-align: left;
        min-width: 0;
        font-size: 14px;
        line-height: 1.35;
        overflow-wrap: anywhere;
    }
    /* Le libellé de colonne devient l'étiquette du champ, posée au-dessus. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-l]::before {
        content: attr(data-l);
        display: block;
        font-size: 11.5px;
        font-weight: 600;
        letter-spacing: .02em;
        color: var(--text-dim, #adb6ca);
        margin-bottom: 1px;
    }

    /* En-tête de carte : ce qui identifie la ligne, sur toute la largeur. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-titre] {
        grid-column: 1 / 3;
        font-size: 16px;
        font-weight: 700;
        line-height: 1.2;
        color: var(--text, #f0f3fa);
    }
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-titre]::before { display: none; }
    /* Deux cellules de titre (nom puis prénom, ou n° puis date) forment un seul
       bloc d'identité : elles s'empilent sur toute la largeur plutôt que de se
       partager deux colonnes étroites, où « ANDRIAMANANTENA » se couperait en
       trois. La seconde est allégée, comme un prénom sous un patronyme. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-titre] + td[data-titre] {
        grid-column: 1 / 3;
        font-size: 14.5px;
        font-weight: 500;
        color: var(--text-soft, #c5cbd9);
        margin-top: -8px;
    }

    /* Contrôles (case à cocher, bouton d'action) : colonne de droite, en haut. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-ctrl] {
        grid-column: 3;
        grid-row: 1;
        align-self: center;
    }
    /* Une cellule vide n'occupe pas de place dans la carte. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td[data-vide] { display: none; }
    /* Une cellule sans étiquette et sans contrôle (action textuelle) passe à droite. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td:not([data-l]):not([data-ctrl]):not([data-titre]) {
        grid-column: 3;
        grid-row: 1;
        align-self: center;
        text-align: right;
    }
    /* Les montants gardent des chiffres tabulaires et du poids. */
    .cartes-mobile:not(.vue-tableau) tbody > tr > td.num {
        font-variant-numeric: tabular-nums;
        font-weight: 600;
    }

    /* ---- mode tableau (au choix de l'utilisateur) ---- */
    .cartes-mobile.vue-tableau {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        border: 1px solid var(--border, #3a4666);
        border-radius: 8px;
        background: var(--bg-2, #232c44);
    }
    .cartes-mobile.vue-tableau > .vue-bascule { position: sticky; left: 0; margin: 8px; }
}

/* ---------------------------------------------------------------------------
   20. CLAVIER VIRTUEL — une barre `position:fixed` ancrée en bas n'est pas
       remontée par le clavier iOS : elle reste sous lui, au moment précis où
       l'on s'en sert (saisir un commentaire puis « Appliquer » sur une sélection
       d'échéances). mobile-shell.js expose la hauteur réellement masquée dans
       --clavier ; les barres ancrées se replacent au-dessus.
   --------------------------------------------------------------------------- */
:root { --clavier: 0px; }

@media (max-width: 900px) {
    .bulk-bar, .pb-barre, .sticky-actions {
        bottom: calc(var(--clavier) + var(--safe-b));
        transition: bottom .12s ease-out;
    }
    /* Clavier ouvert, l'écran est déjà réduit de moitié : la barre se fait
       discrète pour laisser voir le champ en cours de saisie. */
    .clavier-ouvert .bulk-bar { max-height: 30dvh; }
    /* Les boutons flottants n'ont rien à faire par-dessus le clavier. */
    .clavier-ouvert .ai-fab,
    .clavier-ouvert #cnx-fab,
    .clavier-ouvert #pw-fab { display: none !important; }
}

/* ---------------------------------------------------------------------------
   21. RELIQUATS TACTILES relevés en paysage et à 375 px — trois sélecteurs plus
       spécifiques échappaient au plancher posé au §3.
   --------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    /* Cases de sélection de la liste des dossiers : 15 × 15 px, portées par un
       sélecteur à trois classes qui l'emportait sur le plancher générique. */
    .data-table td.col-sel input,
    .data-table th.col-sel input { width: 24px; height: 24px; }
    .data-table th.col-sel, .data-table td.col-sel { width: 44px; }

    /* Le ✕ de l'infobulle en paysage : le bloc de assistant.js s'arrête à
       640 px, or un téléphone en paysage fait 667 à 932 px de large et reste
       un écran tactile. */
    .ai-toast button { min-width: 44px; }
    .ai-toast button[data-off] { min-width: 0; padding: 0 12px; }

    /* Retour à l'accueil depuis Honoraires : lien de 24 px de haut. */
    .hc-topbar a, .hc-header a { min-height: var(--touch-min); display: inline-flex; align-items: center; }
}
