/* ══════════════════════════════════════════════════════════════════════
   DevisVox — css/desktop/devisvox-desktop.css
   DESKTOP — surcharges uniquement, ≥ 1025px
   CSS pur. Aucun préprocesseur, aucun JS, aucune modification de balisage.
   Le socle mobile (theme/base/layout/components.css) reste la référence.
   À charger APRÈS la feuille tablette.
   ══════════════════════════════════════════════════════════════════════ */

@media (min-width: 1025px) {

/* ══════════════════════════════════════════════════════════════
   SECTION A — LAYOUT
   Sélecteurs vérifiés dans css/mobile/layout.css, css/mobile/menu.css
   et index.html. Sûrs.
   ══════════════════════════════════════════════════════════════ */

/* ── LOT 47 : overflow:hidden neutralisé sur desktop ───────────────
   overflow:clip posé aux lots 42/43 sur body puis html supprimait le
   port de défilement, empêchant tout retour en arrière après un
   défilement programmatique (titre de page rendu inaccessible, blocs
   dépliables du tableau de bord irréversibles) — vérifié en console :
   overflow:auto sur les deux éléments restaure un comportement fluide
   et réversible, identique au mobile. overflow:hidden d'origine
   souffrant de la même limite que clip (port de défilement manipulable
   mais sans retour possible), neutralisé ici aussi. Les hauteurs et
   positionnements des lots 44/45/46 restent inchangés. */
html, body { height: 100%; overflow: auto; }

/* ── Bottom nav masquée ───────────────────────────────────────── */
#bottom-nav, .bnav, .home-bar { display: none !important; }

/* ── Nav du header masquée : #sidebar est la seule navigation ──── */
.nav-desktop { display: none !important; }

/* ── Logo du header agrandi (lot 31) ─────────────────────────────
   .logo img{height:32px} (style.css:23, non scopée) reste actif partout ;
   #header .logo img (id + descendant) l'emporte par spécificité. Largeur
   non fixée : reste auto, le ratio se préserve automatiquement. Centrage
   vertical déjà assuré par .header-container{align-items:center}
   (style.css:22, non touché) — s'applique de la même façon à une image
   plus haute. */
#header .logo img { height: 58px; }

/* ── Mode sombre : mot "Devis" illisible (lot 33) ─────────────────
   logo-devisvox.svg est chargé via <img> (auth-logo-img, .logo-img) :
   currentColor est inopérant, le document SVG référencé n'a aucun
   accès au DOM/aux classes de la page hôte. Seule solution qui
   fonctionne réellement : substituer le fichier via content:url(),
   même mécanisme déjà en place pour .brand-logo-img (css/mobile/vocal.css:275).
   assets/logo-devisvox-dark.svg existe déjà ("Devis" en blanc, "Vox"
   en #FF3300 inchangé) — référencé tel quel ici, non modifié (hors
   périmètre autorisé de ce lot). Limite connue et assumée : son
   alignement (text-anchor:start, texte collé au bord gauche du
   viewBox) diffère de celui du fichier clair (text-anchor:middle,
   centré) — le rendu en mode sombre ne sera pas optiquement centré
   dans sa boîte comme en mode clair. Correction impossible sans
   éditer/créer un fichier SVG, hors périmètre de ce lot. */
body.dark-mode #header .logo img,
body.dark-mode .auth-logo-img {
    content: url('../../assets/logo-devisvox-dark.svg');
}

/* ── Cloche du header poussée à droite, groupée avec le toggle ───
   même mécanisme que .dark-mode-toggle-container (style.css:27) :
   margin-left:auto pousse .header-bell et tout ce qui suit (le
   toggle) vers la droite ; le margin-left:auto du toggle devient
   sans effet supplémentaire, sans conflit. */
.header-bell {
  margin-left: auto;
  margin-right: var(--sp-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Neutralise le margin-left:auto de style.css:27 : deux marges auto
   successives se partageraient l'espace libre au lieu de tout pousser
   à droite. Seule celle de .header-bell doit rester active. */
.dark-mode-toggle-container { margin-left: 0; }
/* .dark-mode-switch (style.css:28) est inline-block sans centrage : son
   contenu (.dark-mode-slider) suit l'alignement de ligne par défaut au
   lieu d'être centré dans sa propre boîte — même mécanisme que .bell-btn
   corrigé au lot 5d. L'input checkbox n'est pas en cause (width/height:0,
   style.css:29) : il reste dans le flux, sans emprise dimensionnelle. */
.dark-mode-switch { display: flex; align-items: center; }

/* ── En-tête interne de #vocal-mobile-page masqué sur desktop ────
   .brand-info (logo) et .bell-btn (cloche) sont redondants avec
   #header une fois .header-bell ajouté (LOT 5). .brand n'a que ces
   deux enfants : les masquer individuellement laisserait un espace
   vertical résiduel (son propre padding) — on masque .brand entier
   à la place, conformément à la consigne. */
#vocal-mobile-page .brand { display: none !important; }


/* ── Sidebar bureau : #sidebar réactivé, tiroir mobile masqué ──
   css/mobile/layout.css contient #sidebar{display:none!important} sans
   media query. On le réaffiche ici. Le tiroir #menu-mobile reste un
   tiroir, masqué comme sur mobile.
   Le conteneur de nav est ciblé de façon large (nav, .sidebar-nav, ul)
   car son nom exact varie selon ce que rend js/nav.js.
   ─────────────────────────────────────────────────────────────── */

#menu-mobile { display: none !important; }
#mob-menu-overlay { display: none !important; }

#sidebar {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  background: var(--card);
  border-right: 1px solid var(--b2);
}

/* Le logo/en-tête : bloc autonome, jamais recouvert, jamais chevauché */
#sidebar > a:first-child,
#sidebar > div:first-child {
  position: static !important;
  display: flex !important;
  align-items: center;
  flex-shrink: 0 !important;
  width: 100%;
  padding: 16px 14px;
  margin: 0 !important;
}
#sidebar > a:first-child img { max-width: 140px; height: auto; display: block; }

/* Tout conteneur de liens devient une colonne — quel que soit son nom */
#sidebar > nav,
#sidebar nav,
#sidebar .sidebar-nav,
#sidebar > ul,
#sidebar ul {
  display: flex !important;
  flex-direction: column !important;
  flex-wrap: nowrap !important;
  align-items: stretch !important;
  justify-content: flex-start !important;
  gap: 2px;
  width: 100% !important;
  margin: 0 !important;
  padding: 12px 14px;
  list-style: none;
  /* LOT 52 : hauteur définie requise pour que margin-top:auto (ligne ~185,
     #sidebar .nav-sidebar-footer) ait un espace réel à consommer — #sidebar
     est désormais flex (ci-dessus), .sidebar-nav en occupe l'espace restant
     une fois le bloc identité (flex-shrink:0, plus bas) déduit. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}
#sidebar li { display: block !important; width: 100% !important; margin: 0 !important; float: none !important; }

/* Un lien = une ligne pleine largeur, jamais en flux inline */
#sidebar a,
#sidebar .sidebar-link,
#sidebar button {
  position: static !important;
  display: flex !important;
  align-items: center;
  gap: 10px;
  width: 100% !important;
  box-sizing: border-box;
  font-size: 15px;
  padding: 11px 12px;
  margin: 0 !important;
  float: none !important;
  white-space: nowrap;
  text-align: left;
  border-radius: var(--r-sm);
}
#sidebar a svg,
#sidebar .sidebar-link svg,
#sidebar button svg { flex-shrink: 0; width: 18px; height: 18px; }

/* Couleur neutre par défaut — seul .active passe en orange */
#sidebar a:not(.active),
#sidebar .sidebar-link:not(.active) { color: var(--text) !important; }
#sidebar a:not(.active) svg,
#sidebar .sidebar-link:not(.active) svg { stroke: var(--text2); }
#sidebar a:not(.active):hover,
#sidebar .sidebar-link:not(.active):hover { background: var(--b); }
#sidebar a.active,
#sidebar .sidebar-link.active { color: var(--accent) !important; background: #FFF4EC; }
#sidebar a.active svg,
#sidebar .sidebar-link.active svg { stroke: var(--accent); }

/* Poignée de glisser-déposer : invisible au repos, visible au survol du lien */
#sidebar .sidebar-link .nav-drag-handle { opacity: 0; transition: opacity .15s ease; }
#sidebar .sidebar-link:hover .nav-drag-handle { opacity: 1; }

#sidebar > *:last-child:not(nav):not(ul) { margin-top: auto; flex-shrink: 0; }

/* ── LOT 52b/53 : déconnexion repositionnée au-dessus du bloc identité ──
   .nav-sidebar-footer (déconnexion) est imbriquée dans .sidebar-nav
   (js/nav.js:309-314), donc rendue avant .sidebar-identity, son frère
   suivant — l'ordre visuel "identité puis déconnexion" demandé est
   impossible par simple ordre de flux. Sortie du flux de .sidebar-nav
   et ancrée au bas de #sidebar (position:fixed, plus haut — sert de
   bloc conteneur pour ce position:absolute) ; #sidebar réserve l'espace
   correspondant via padding-bottom ci-dessous. LOT 53 : bouton porté à
   padding:var(--sp-md) (comparable au bloc identité, contre 11px/12px
   hérité de la règle générique #sidebar .sidebar-link) + respiration
   sous le bouton (bottom:var(--sp-sm) au lieu de 0). Hauteur mesurée
   (padding 16px+16px + contenu ~20px ≈ 52px) + respiration var(--sp-sm)
   (8px) ≈ 60px : 64px retenu par sécurité, signalé faute de token
   correspondant à cette hauteur composite. */
#sidebar { padding-bottom: 64px; }
#sidebar .nav-sidebar-footer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--sp-sm);
  background: var(--card);
  border-top: 1px solid var(--b2);
}
/* LOT 53 : mise en évidence de l'action de sortie, même logique que le
   tiroir mobile (css/mobile/menu.css:74-75, var(--er) sur libellé+icône).
   !important nécessaire sur color pour l'emporter sur la règle générique
   #sidebar .sidebar-link:not(.active){color:var(--text)!important}. */
#sidebar .nav-sidebar-footer .nav-logout-btn {
  padding: var(--sp-md);
  color: var(--er) !important;
}
#sidebar .nav-sidebar-footer .nav-logout-btn svg { stroke: var(--er); }
#sidebar .nav-sidebar-footer .nav-logout-btn:hover { background: var(--er-bg); }

/* ── LOT 52/52b : bloc identité en bas de sidebar ──────────────────
   Frère de .sidebar-nav (jamais à l'intérieur : écrasé sinon par
   nav.innerHTML dans _renderSidebar(), js/nav.js:316). Alimenté par
   _mobMenuPopulateHeader() (index.html) en plus du tiroir mobile
   depuis le lot 52b — voir rapport de ce lot pour la limite connue
   sur le déclenchement (jamais appelée au chargement sur desktop). */
#sidebar .sidebar-identity {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: var(--sp-md);
  border-top: 1px solid var(--b2);
}
#sidebar .sidebar-identity .mob-menu-av {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: var(--r-full);
  background: var(--accent-tint);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-sm);
  font-weight: 800;
}
#sidebar .sidebar-identity-info { min-width: 0; }
#sidebar .sidebar-identity .mob-menu-name {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#sidebar .sidebar-identity-metier {
  font-size: var(--fs-xs);
  color: var(--text3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Zone de contenu décalée ───────────────────────────────────── */
/* margin retiré ici : laisse style.css:39 (margin-left var(--sidebar-width),
   margin-top var(--header-height)) décaler .main-content sous le header
   et à droite de la sidebar.
   padding-top neutralisé (lot 23) : css/mobile/base.css:140 pose
   padding-top:var(--header-height) sur .main-content, non scopé donc actif
   ici aussi — s'additionne au margin-top ci-dessus et double le décalage
   sous le header. Seule cette propriété est modifiée ; padding-bottom
   (même règle source) s'auto-neutralise déjà via --bottom-nav-height:0px
   à ≥769px, rien à y changer. */
.main-content {
  width: auto !important;
  padding-top: 0;
  overflow-y: auto !important;
  overflow-x: hidden !important;
  background: var(--bg);
}
/* ── LOT 44 : hauteur de .main-content ────────────────────────────
   #app{height:100dvh} (css/mobile/layout.css:81, non scopée) s'ajoute
   à margin-top:var(--header-height) (css/style.css:39, actif ≥769px) :
   total 100dvh + 70px, déborde le viewport de 70px (lot 43). Hauteur
   réduite du montant exact de cette marge pour que marge + hauteur
   correspondent au viewport. #app a une spécificité d'id (1,0,0)
   supérieure à .main-content seul (0,1,0) : #app.main-content (même
   élément, index.html:303) l'emporte par spécificité (1,1,0), sans
   !important. Seule la propriété height est posée ici — position,
   margin, padding, top/left/right/bottom, overflow non touchés. */
#app.main-content { height: calc(100dvh - var(--header-height)); }
.main-content::-webkit-scrollbar { width: 7px; display: block; }
.main-content::-webkit-scrollbar-track { background: transparent; }
.main-content::-webkit-scrollbar-thumb { background: var(--b2); border-radius: 4px; }

/* ── Pages : hauteurs mobiles fixes libérées ───────────────────── */
.page-content:not(.hidden) { min-height: 100%; height: auto !important; max-height: none !important; }
.page:not(.hidden), .page.active { height: auto !important; max-height: none !important; min-height: 0 !important; }
#cockpit-mobile-page:not(.hidden), #devis-mobile-page:not(.hidden),
#factures-mobile-page:not(.hidden), #clients-mobile-page:not(.hidden),
#equipe-mobile-page:not(.hidden), #notifications-mobile-page:not(.hidden),
#ia-chat-mobile-page:not(.hidden) {
  height: auto !important;
  max-height: none !important;
  min-height: 100%;
}

/* ── LOT 41 : hauteur figée héritée du mobile, sur les sept pages
   ci-dessus — chaque feuille mobile pose height:calc(100dvh - ...) sans
   media query sur #XXX-mobile-page (id seul, sans !important) :
   css/mobile/cockpit.css:7, devis.css:20, factures.css:22, clients.css:13,
   equipe.css:25, notifications.css:8, ia-chat.css:6. Priorité obtenue par
   spécificité (deux id) plutôt que !important, sur le modèle du lot 17b
   (#nouveau-devis-page #df-btn-prev, ligne ~532 plus bas). ───────────── */
#dashboard-page     #cockpit-mobile-page,
#devis-page         #devis-mobile-page,
#factures-page      #factures-mobile-page,
#clients-page       #clients-mobile-page,
#equipe-page        #equipe-mobile-page,
#assistant-page     #ia-chat-mobile-page,
#notifications-page #notifications-mobile-page {
  height: auto;
  max-height: none;
}
/* #cockpit-mobile-page est seule à poser son propre défilement interne
   (overflow-y:auto, css/mobile/cockpit.css:7) : un conteneur de
   défilement imbriqué n'a plus lieu d'être une fois sa hauteur libérée
   ci-dessus — le défilement global reste assuré par #app. Les six autres
   pages posent overflow:hidden ou rien, pas un conteneur de défilement,
   non concernées — à l'exception de devis ci-dessous (LOT 64b). */
#dashboard-page #cockpit-mobile-page { overflow-y: visible; }

/* Devis — overflow:hidden neutralisé (LOT 64b). #devis-mobile-page et
   .dv-body (mobile, css/mobile/devis.css:20-21) posent overflow:hidden,
   sans effet tant que rien n'en débordait (hauteur libérée en auto
   ci-dessus, comme les six pages ci-dessus). Le menu #dv-period-dropdown
   ajouté au LOT 64 (position:absolute, hors du flux) en déborde
   désormais : n'étant pas pris en compte dans le calcul de hauteur
   automatique de ses ancêtres, il se retrouvait tronqué au bord de leur
   boîte. overflow:visible sur les deux ancêtres laisse le menu se
   peindre au-delà sans changer leur propre dimensionnement (le
   défilement de la liste, assuré par #dv-list lui-même, css/mobile/
   devis.css:36, n'est pas concerné par l'overflow de ses parents). */
#devis-page #devis-mobile-page { overflow: visible; }
#devis-page #devis-mobile-page .dv-body { overflow: visible; }

/* Factures — même neutralisation que devis ci-dessus (LOT 65), même
   cause : #factures-mobile-page et .fac-body (mobile, css/mobile/
   factures.css:22-23) posent overflow:hidden, débordé par le nouveau
   menu #fac-period-dropdown (position:absolute, hors du flux). */
#factures-page #factures-mobile-page { overflow: visible; }
#factures-page #factures-mobile-page .fac-body { overflow: visible; }

/* ── Page vocale ────────────────────────────────────────────────
   La page masquée DOIT rester masquée : ne jamais neutraliser son
   position:fixed sans vérifier qu'elle est active, sinon son anneau
   de micro fuit par-dessus les autres pages.
   ─────────────────────────────────────────────────────────────── */
#vocal-mobile-page:not(.hidden):not([hidden]) {
  position: fixed !important;
  top: var(--header-height) !important;
  left: var(--sidebar-width) !important;
  right: 0 !important;
  bottom: 0 !important;
  z-index: auto !important;
  height: auto !important;
  overflow-y: auto;
}

/* ── Page recharges ─────────────────────────────────────────────
   Même mécanisme que #vocal-mobile-page ci-dessus : le calque plein écran défini pour le
   mobile (top:0;left:0;height:100%, css/mobile/abonnement.css:21-31) reste sous la sidebar
   (z-index:100 < 700, css/style.css:41) et height:auto!important (ligne 305 de ce fichier)
   le réduit à la hauteur de son contenu. Repositionné sous l'en-tête et à droite de la
   sidebar, quatre côtés fixés, comme la page vocale.
   z-index:100 (LOT CSS-b octies) : reprend la valeur mobile (css/mobile/abonnement.css:29)
   au lieu de auto. #ia-fab n'a aucun z-index propre (position:fixed;right/bottom, ligne
   537 ci-dessous) : sur mobile le fond opaque de #recharges-page (z-index:100) le recouvre
   déjà de fait, sans règle dédiée au bouton — passer ce z-index à auto ici le laissait
   réapparaître au-dessus. Sans risque de recouvrir la sidebar/le header désormais : depuis
   left:var(--sidebar-width), #recharges-page ne chevauche plus géométriquement ni l'une ni
   l'autre, quelle que soit sa position dans l'ordre d'empilement.
   ─────────────────────────────────────────────────────────────── */
#recharges-page:not(.hidden):not([hidden]) {
  position: fixed !important;
  top: var(--header-height) !important;
  left: var(--sidebar-width) !important;
  right: 0 !important;
  bottom: 0 !important;
  z-index: 100 !important;
  width: auto !important;
  height: auto !important;
  overflow-y: auto;
}

/* ── Recharges : cartes de pack (LOT CSS-b bis) ───────────────────
   Structure produite par js/recharges.js:renderHTML() : #recharges-page > .abo-page >
   .abo-packs-grid > deux .abo-pack-card. .abo-packs-grid est display:flex sans largeur
   maximale (css/mobile/abonnement.css:452-455) et .abo-pack-card est flex:1
   (css/mobile/abonnement.css:457-467) — sur mobile les deux cartes se partagent la largeur
   étroite de l'écran ; une fois #recharges-page élargi à toute la zone de contenu desktop
   (règle ci-dessus), le même flex:1 sans borne les étire/déborde sur la largeur disponible.
   Seul le conteneur est borné et centré ; les cartes (flex:1, inchangé) se répartissent
   alors cette largeur bornée. ─────────────────────────────────────────────────────────── */
#recharges-page .abo-packs-grid {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
}

/* ── Page abonnement (LOT CSS-c) ───────────────────────────────────
   Structure produite par js/abonnement.js:renderHTML() : #abonnement-page > .abo-page >
   (.abo-back-btn, .abo-header, .abo-toggle-wrap, .abo-plans > deux .abo-plan-card,
   .abo-trial-banner?, .abo-cta-wrap > .abo-cta-btn). Classes propres à cette page — non
   partagées avec #recharges-page (js/recharges.js n'utilise que .abo-page/.abo-header/
   .abo-packs-grid/.abo-pack-card).

   Cartes de plan : .abo-plans est display:flex;flex-direction:column (empilées, pas de
   grille comme les packs) et .abo-plan-card n'a pas de largeur propre — flex item d'un
   conteneur colonne, il s'étire par défaut (align-items:stretch) sur toute la largeur de
   .abo-plans, elle-même sans max-width. Seul .abo-plans est borné/centré ci-dessous ;
   .abo-plan-card (inchangée) se redimensionne alors avec son conteneur. width:100% requis
   avant max-width/margin:auto : sans elle, les marges automatiques désactivent le stretch
   flex sur un item et la boîte revient à sa taille de contenu — même mécanisme identifié et
   corrigé pour #recharges-page .abo-packs-grid (LOT CSS-b sexies) ci-dessus. */
#abonnement-page .abo-plans {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
}

/* Bouton "Changer de plan" : .abo-cta-wrap (css/mobile/abonnement.css:347-358) est
   position:fixed;left:0;right:0 — plein viewport, y compris sous la sidebar. Recalé sur la
   zone de contenu (comme #recharges-page, LOT CSS-b) pour que le centrage du bouton se
   fasse sur la même référence que .abo-plans ci-dessus, plutôt que sur la largeur totale du
   viewport (qui aurait décentré le bouton du même écart que la sidebar — leçon du LOT
   CSS-b septies). .abo-cta-wrap n'a pas de largeur propre (seulement left/right/bottom) :
   pas de sur-contrainte à lever ici. */
#abonnement-page .abo-cta-wrap {
  left: var(--sidebar-width);
}
#abonnement-page .abo-cta-btn {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
}

/* Bande claire en haut : .main-content{margin-top:var(--header-height)} (css/style.css:46)
   réserve la hauteur de l'en-tête pour tout le contenu à ≥769px, mais #header est masqué
   spécifiquement sur cette page par une règle déjà existante et non scopée au mobile
   (body:has(#abonnement-page:not(.hidden)) #header{display:none!important},
   css/mobile/abonnement.css:10, dans un fichier chargé sans media query) : l'espace réservé
   par la marge ne montre alors plus l'en-tête, mais le fond clair de .main-content
   (var(--bg), css/desktop/devisvox-desktop.css:287) au-dessus du fond sombre de .abo-page.
   Neutralisée uniquement pour cette page, avec la même technique body:has() que la règle
   qui masque déjà l'en-tête ; height recalculée en conséquence (elle dépend de
   var(--header-height) via #app.main-content, ligne 299 ci-dessus) pour ne pas déplacer le
   manque vers le bas de la page. */
body:has(#abonnement-page:not(.hidden)) #app.main-content {
  margin-top: 0;
  height: 100dvh;
}
#vocal-mobile-page .page {
  height: auto !important;
  max-height: none !important;
  min-height: calc(100vh - 64px);
  overflow: visible !important;
}
#vocal-mobile-page .mic-zone {
  max-width: 900px;
  margin: 0 auto;
  padding: 48px 40px 56px !important;
  justify-content: flex-start;
}

/* ── LOT 34 : mise à l'échelle desktop de l'écran vocal ──────────
   .mic-zone élargie ci-dessus (640→900px) ; .tr-area (vocal.css:161,
   width:100%) suit automatiquement, aucun ajout nécessaire pour la
   largeur de la transcription. Tout le reste ci-dessous n'était
   jamais surchargé sur desktop avant ce lot. */

/* Titre — .mic-hint (vocal.css:117, 17px) */
#vocal-mobile-page .mic-hint { font-size: var(--fs-xl); }

/* Bouton micro +25% — .mic-main (vocal.css:129, 96px) et son icône
   (vocal.css:131, 38px) */
#vocal-mobile-page .mic-main { width: 120px; height: 120px; }
#vocal-mobile-page .mic-main svg { width: 48px; height: 48px; }

/* Anneaux/lueur, rapport ×1.25 conservé — pulse-wrap (vocal.css:119,
   min(170px,44vw)), p-glow (vocal.css:120, 176px), vocal-ring
   (vocal.css:122-125, 136/166/196/220px). Valeurs mesurées et
   transposées proportionnellement, comme autorisé pour le micro. */
#vocal-mobile-page .pulse-wrap { width: min(212px, 55vw); height: min(212px, 55vw); }
#vocal-mobile-page .p-glow { width: 220px; height: 220px; }
#vocal-mobile-page .vocal-ring:nth-child(2) { width: 170px; height: 170px; }
#vocal-mobile-page .vocal-ring:nth-child(3) { width: 208px; height: 208px; }
#vocal-mobile-page .vocal-ring:nth-child(4) { width: 245px; height: 245px; }
#vocal-mobile-page .vocal-ring:nth-child(5) { width: 275px; height: 275px; }

/* État "après transcription" : le micro y est déjà réduit par rapport
   à sa taille de base (vocal.css:198-202, 88px!important/78px!important/
   30px) — même rapport conservé avec la nouvelle base ci-dessus */
#vocal-mobile-page #p-home.done-screen .pulse-wrap { width: 110px !important; height: 110px !important; }
#vocal-mobile-page #p-home.done-screen .mic-main { width: 98px !important; height: 98px !important; }
#vocal-mobile-page #p-home.done-screen .mic-main svg { width: 38px; height: 38px; }

/* Zone de transcription, élément prioritaire — tr-box (vocal.css:168,
   min-height:60px ; r162 vocal.css:252, max-height:14vh) */
#vocal-mobile-page .tr-box { min-height: 90px; max-height: 28vh; }
/* tr-box.done hors état "après transcription" (vocal.css:172, min-height:140px) */
#vocal-mobile-page .tr-box.done { min-height: 200px; }
/* tr-box.done en état "après transcription" — r165 (vocal.css:257, max-height:26vh) */
#vocal-mobile-page #p-home.done-screen .tr-box.done { max-height: 46vh; }

/* Libellés de la transcription — tr-section-lbl (vocal.css:162, 11px),
   tr-lbl (vocal.css:164, 13px) */
#vocal-mobile-page .tr-section-lbl { font-size: var(--fs-xs); }
#vocal-mobile-page .tr-lbl { font-size: var(--fs-sm); }

/* Boutons d'action — btn-generate (vocal.css:177, 46px/14px), btn-validate
   (vocal.css:178, 52px/15px), btn-ghost (vocal.css:179, 44px/15px),
   btn-manual (vocal.css:181, 38px/13px). Hauteurs mesurées et
   transposées proportionnellement (mêmes principe que le micro) */
#vocal-mobile-page .btn-generate { height: 56px; font-size: var(--fs-md); }
#vocal-mobile-page .btn-validate { height: 64px; font-size: var(--fs-lg); }
#vocal-mobile-page .btn-ghost { height: 52px; font-size: var(--fs-md); }
#vocal-mobile-page .btn-manual { height: 44px; font-size: var(--fs-sm); }

/* Boutons en état "après transcription" : hauteurs déjà réduites par
   rapport à leur base (vocal.css:207-208, 48px/42px) — même rapport
   conservé avec les nouvelles bases ci-dessus */
#vocal-mobile-page #p-home.done-screen .btn-validate { height: 60px; }
#vocal-mobile-page #p-home.done-screen .btn-ghost { height: 50px; }

/* Garde-fou : aucun SVG décoratif ne peut dépasser son conteneur */
.main-content svg { max-width: 100%; max-height: 100%; }
.mic-ring, .mic-pulse, .pulse-ring, .mic-halo {
  max-width: 260px !important;
  max-height: 260px !important;
  pointer-events: none;
}

/* ── Formulaire e-mail : champs en colonne, jamais en ligne ────── */
.email-sheet-overlay.open form,
.email-sheet-overlay.open .email-sheet-body { display: flex; flex-direction: column; gap: 14px; }
.email-sheet-overlay.open .field,
.email-sheet-overlay.open .form-row,
.email-sheet-overlay.open label {
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  gap: 5px;
  width: 100%;
}
.email-sheet-overlay.open input,
.email-sheet-overlay.open textarea,
.email-sheet-overlay.open select {
  width: 100% !important;
  box-sizing: border-box;
  height: 42px;
  padding: 0 12px;
  border: 1.5px solid var(--b2);
  border-radius: var(--r-sm);
  font-size: 14.5px;
}
.email-sheet-overlay.open textarea { height: auto; min-height: 90px; padding: 10px 12px; }
.email-sheet-overlay.open .email-sheet-footer { display: flex; gap: 10px; }

/* ── FAB hors flux de scroll ───────────────────────────────────── */
.fab {
  position: fixed !important;
  bottom: 30px !important;
  right: 30px !important;
  width: 54px !important;
  height: 54px !important;
  z-index: 500;
}
.fab:hover { transform: scale(1.06); box-shadow: 0 8px 26px rgba(255, 107, 0, .5); }

/* ── LOT 45 : #ia-fab hors du flux ──────────────────────────────
   Aucune règle CSS ne cible #ia-fab/.ia-fab nulle part dans le projet
   (recherche exhaustive) — reste position:static par défaut, occupe
   44px dans le flux du body juste après #app, d'où le débordement de
   70px du viewport. Sorti du flux ici, ancré en bas à droite du
   viewport : le contenu se trouvant à droite de #sidebar (fixed, à
   gauche), un ancrage depuis le bord droit tombe naturellement dans la
   zone de contenu sans avoir besoin de tenir compte de --sidebar-width. */
#ia-fab {
  position: fixed;
  right: var(--sp-xl);
  bottom: var(--sp-xl);
  left: auto;
  top: auto;
}

/* ── Bottom sheet e-mail → modal centré ─────────────────────────
   TOUTES les règles sont scopées à .open : sans ça, neutraliser le
   transform révèle la modale sur toutes les pages.
   ─────────────────────────────────────────────────────────────── */
.email-sheet-overlay.open { display: flex !important; align-items: center; justify-content: center; }

/* LOT MODALES-2 : état fermé posé ici pour que la transition ne parte
   plus de l'ancrage mobile (bottom:0;left:0;right:0, css/mobile/
   layout.css:117) — sans cela, .open ci-dessous ne redéfinissant que
   lui-même, la transition existante (transform .3s ease, même fichier
   :118) interpolait depuis translateY(100%) (hors écran, en bas)
   jusqu'au centre, d'où la montée brutale constatée. Position/taille
   identiques à l'état ouvert : seuls l'échelle et l'opacité animent. */
.email-sheet-overlay.open .email-sheet,
.email-sheet.open,
.email-sheet {
  position: fixed !important;
  inset: 50% auto auto 50% !important;
  width: calc(100% - 48px) !important;
  max-width: 620px !important;
  height: auto !important;
  max-height: 86vh !important;
  border-radius: 16px !important;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28) !important;
}
.email-sheet {
  transform: translate(-50%, -50%) scale(.96) !important;
  opacity: 0;
  pointer-events: none;
  transition: transform .25s ease, opacity .25s ease !important;
  overflow-y: auto;
}
.email-sheet.open {
  transform: translate(-50%, -50%) scale(1) !important;
  opacity: 1;
  pointer-events: auto;
}
.email-sheet-overlay.open .email-sheet-handle,
.email-sheet.open .email-sheet-handle { display: none !important; }
.email-sheet-overlay.open .email-sheet-footer { padding: 14px 20px !important; }

/* ── LOT MODALES-1 : panneaux catalogue v2 sans règle desktop (audit
   MODALES) — #catv2-picker-sheet et #catv2-detail-sheet partagent la
   même base que .email-sheet ci-dessus (position:fixed;bottom:0;
   left:0;right:0;transform:translateY(100%), css/mobile/catalogue.css
   :199/226) : même technique de centrage, ciblée directement par id
   (#catv2-picker-overlay/#catv2-detail-overlay restent inchangés —
   déjà position:fixed;inset:0, ils assombrissent tout le viewport
   sans dépendre d'un parent flex, aucune reprise nécessaire).
   #catv2-picker-sheet : sélecteur de fournitures (recherche + liste) —
   largeur alignée sur .sg-sheet (720px) pour laisser respirer les
   lignes d'articles ; min-height/max-height (60vh/75vh, catalogue.css
   :226) non repris ici, inchangés, la liste interne (.catv2-picker-
   list{flex:1}) en dépend pour défiler correctement.
   #catv2-detail-sheet : fiche d'un seul article (3 lignes + prix) —
   contenu court, largeur alignée sur le plafond générique .modal-
   content (620px).
   #catv2-add-modal : formulaire "Nouvel article" — même largeur
   (620px), mais pas d'overlay propre (il est lui-même position:fixed;
   inset:0 sur fond plein, css/mobile/catalogue.css:130-134) : le
   recentrer en fait un panneau flottant sans fond assombri derrière —
   aucun fichier HTML/JS n'étant modifiable depuis cette feuille pour
   lui donner un calque de fond, état accepté tel quel.
   LOT MODALES-2 : #catv2-picker-sheet/#catv2-detail-sheet utilisent le
   même mécanisme que .email-sheet (transform:translateY(100%) →
   .open{translateY(0)}, transition:transform .3s ease, css/mobile/
   catalogue.css:199-200/226-227) — état fermé posé ci-dessous pour la
   même raison : sans lui, la transition héritée du mobile animait
   depuis l'ancrage bas-écran jusqu'au centre (montée brutale constatée
   sur #catv2-picker-sheet). #catv2-add-modal n'a ni transition ni
   animation (bascule display:none/flex seule, catalogue.css:130-135) :
   examiné, non concerné, rien à corriger ici. ───────────────────── */
#catv2-picker-sheet,
#catv2-detail-sheet {
  position: fixed !important;
  inset: 50% auto auto 50% !important;
  border-radius: 16px !important;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28) !important;
  opacity: 0;
  pointer-events: none;
  transition: transform .25s ease, opacity .25s ease !important;
}
#catv2-picker-sheet {
  width: calc(100% - 48px) !important;
  max-width: 720px !important;
  transform: translate(-50%, -50%) scale(.96) !important;
}
#catv2-picker-sheet.open {
  transform: translate(-50%, -50%) scale(1) !important;
  opacity: 1;
  pointer-events: auto;
}
#catv2-detail-sheet {
  width: calc(100% - 48px) !important;
  max-width: 620px !important;
  height: auto !important;
  max-height: 86vh !important;
  overflow-y: auto;
  transform: translate(-50%, -50%) scale(.96) !important;
}
#catv2-detail-sheet.open {
  transform: translate(-50%, -50%) scale(1) !important;
  opacity: 1;
  pointer-events: auto;
}
#catv2-add-modal.open {
  position: fixed !important;
  inset: 50% auto auto 50% !important;
  transform: translate(-50%, -50%) !important;
  width: calc(100% - 48px) !important;
  max-width: 620px !important;
  height: auto !important;
  max-height: 86vh !important;
  border-radius: 16px !important;
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28) !important;
  overflow-y: auto;
}
/* ── Centrage desktop des modales .modal et des bottom sheets .v2-bs
   (LOT 37) — même approche que .email-sheet ci-dessus. Spécificité
   classe seule : #planifier-modal et #modal-edit-event redéfinissent
   déjà align-items:stretch et leur propre .modal-content plein écran
   via un sélecteur #id .modal-content (spécificité id+classe), donc
   restent protégés sans avoir besoin d'être exclus explicitement.
   #modal-paiement-validation, #modal-acompte-encaisse et
   #modal-fiche-employe ne stylent que .modal-header/.modal-actions/etc.,
   jamais .modal ni .modal-content — aucun chevauchement. Ne touche pas
   .email-sheet, déjà traité au bloc précédent.
   LOT MODALES-2 : .modal-content rejoue à chaque ouverture le keyframe
   animation:dvSlideUp .35s ease-out (css/mobile/base.css:104/160,
   from{translateY(100%)}to{translateY(0)}) — il s'applique ici même,
   puisque .modal-content est créé par innerHTML à chaque ouverture
   (une keyframe animation se joue au montage, sans avoir besoin d'un
   changement de classe comme .email-sheet ci-dessus) : centré par
   .modal en display:flex (aucun transform propre à neutraliser), il
   produit une montée depuis le bas de sa position déjà centrée —
   remplacée ici par un fondu avec léger agrandissement.
   #onboarding-modal (.modal-onboarding-content, qui porte aussi la
   classe .modal-content) n'est pas concerné : neutralisé sans condition
   d'écran par .modal-onboarding .modal-content,.modal-onboarding-content
   {animation:none!important} (css/mobile/base.css:248), plus fort que
   la règle non-!important ci-dessous. #planifier-modal/#modal-edit-
   event/#commande-modal (plein écran, voir plus haut) restent sur
   .modal-content : ils recevront donc eux aussi le fondu à la place de
   la montée (aucune exclusion locale de leur part sur `animation`) —
   signalé, non traité séparément, la CONTEXTE de ce lot visant les
   panneaux centrés sans exclure nommément ces trois-là. ───────────── */
@keyframes dvDesktopFadeScale {
  from { opacity: 0; transform: scale(.96); }
  to   { opacity: 1; transform: scale(1); }
}
.modal { align-items: center; }
.modal-content {
  max-width: 620px;
  max-height: 86vh;
  border-radius: var(--r-lg);
  overflow-y: auto;
  animation: dvDesktopFadeScale .25s ease-out;
}

/* ── LOT MODALES-4 : #planifier-modal/#modal-edit-event/#commande-modal
   restent plein écran sur ordinateur (exclus ci-dessus) via leur propre
   #id .modal-content{max-width:100%;height:100dvh;...} (css/mobile/
   devis.css:152-159/188-194, css/mobile/agenda.css:309-314) — même
   spécificité (id+classe) que les règles ci-dessous, mais posées en
   premier dans le document : à spécificité égale, la règle chargée en
   dernier l'emporte, donc les surpasser depuis cette feuille (chargée
   après toutes les feuilles mobiles, index.html:35-58 puis :66) suffit
   sans !important — sauf #commande-modal, dont le max-width mobile est
   lui-même posé avec !important (devis.css:192) : seule la valeur ici
   doit l'être aussi pour rivaliser (!important ne cède jamais face à
   une règle non-!important, quel que soit l'ordre ou la spécificité).
   620px retenu pour les trois : simples formulaires (champs, un
   textarea, une liste d'équipe courte pour #planifier-modal ; un
   embedded Point par fournisseur, pas de tableau, pour #commande-modal)
   — aucun ne justifie davantage. padding existant (14px 16px 20px,
   propre à chacun) non touché : déjà posé sur leur .modal-content,
   conservé tel quel, les champs ne touchent pas leurs bords. ───────── */
#planifier-modal .modal-content,
#modal-edit-event .modal-content {
  max-width: 620px;
  height: auto;
  max-height: 86vh;
  border-radius: var(--r-lg);
}
#commande-modal .modal-content {
  max-width: 620px !important;
  height: auto;
  max-height: 86vh;
  border-radius: var(--r-lg);
}

/* ── LOT MODALES-5 : hauteur excessive de #modal-edit-event malgré
   height:auto (ci-dessus) — constaté : #modal-edit-event{align-items:
   stretch} (css/mobile/agenda.css:308, id seul, sans condition d'écran)
   étire .modal-content sur toute la hauteur de .modal (position:fixed;
   inset:0, donc 100% du viewport) quelle que soit sa propre hauteur —
   align-items:stretch s'applique à tout enfant dont la taille calculée
   en travers (height) vaut auto, ce qui reste le cas même avec
   height:auto explicite. .modal-actions{margin-top:auto} (agenda.css:
   286) absorbait ensuite tout cet espace libre, d'où le vide constaté
   entre le dernier champ et les boutons. #planifier-modal/#commande-
   modal portent le même align-items:stretch sur leur propre id (css/
   mobile/devis.css:150/186) : probablement concernés par le même
   symptôme, non traités ici — non nommés par ce lot.
   Correction : #modal-edit-event{align-items:center} (même sélecteur,
   même spécificité id seule que la règle mobile, mais chargée après —
   l'emporte sans !important) rétablit un .modal-content réellement
   dimensionné par son contenu ; margin-top:auto n'a alors plus d'espace
   à absorber, sans action supplémentaire nécessaire sur .modal-actions
   pour ce point précis. ───────────────────────────────────────────── */
#modal-edit-event { align-items: center; }

/* ── LOT MODALES-5 ter : disposition des boutons de #modal-edit-event
   — remplace l'approche du lot MODALES-5 (suppression séparée à gauche,
   annulation/enregistrement serrés à droite), signalée ne pas s'accorder
   avec le reste de l'application. #modal-add-event dispose ses deux
   boutons ainsi (lot MODALES-4) : #modal-add-event .btn-primary,
   #modal-add-event .btn-secondary{width:auto;flex:1} — largeur partagée
   à parts égales, occupant toute la ligne. Reprise à l'identique ici
   pour .btn-secondary (Annuler) et .btn-success (Enregistrer), dans le
   même ordre de lecture (Annuler puis Enregistrer, ce dernier le plus
   à droite). .btn-danger (Supprimer) reste visible mais n'occupe plus
   la même ligne : flex-wrap:wrap le renvoie seul sur une ligne distincte
   (le couple Annuler/Enregistrer, flex:1 chacun, occupe déjà toute la
   largeur de la première ligne, laissant structurellement aucune place
   pour un troisième élément). Sur cette seconde ligne, retrait visuel
   pour éviter un clic malheureux : largeur naturelle centrée (flex:0 0
   auto;margin:4px auto 0, au lieu de flex:1 sur toute la largeur),
   hauteur et texte réduits, fond et bordure retirés (transparent) —
   .btn-danger reste coloré (var(--er), agenda.css:324) donc identifiable,
   mais visuellement en retrait par rapport au couple du haut. ───────── */
#modal-edit-event .modal-actions {
  flex-direction: row;
  justify-content: flex-end;
  flex-wrap: wrap;
}
/* LOT MODALES-5 quater : constaté — #modal-edit-event .modal-actions
   button{width:100%;flex:none} (agenda.css:287, id+classe+type, sans
   condition d'écran) a une spécificité de (1,1,1). Les sélecteurs
   posés au lot précédent pour .btn-secondary/.btn-success/.btn-danger
   (id+classe seule, (1,1,0), SANS le type "button") sont donc moins
   spécifiques : flex:none l'emportait sur flex:1 quel que soit l'ordre
   de chargement, les boutons restaient à leur largeur naturelle sans
   grossir — et comme aucun des trois ne grossissait, ils tenaient tous
   les trois sur la même ligne, flex-wrap:wrap n'ayant jamais de
   dépassement à renvoyer à la ligne. Corrigé en reprenant le type
   "button" dans chaque sélecteur ci-dessous (id+2 classes+type =
   (1,2,1), supérieur à (1,1,1)) : aucun !important nécessaire. */
#modal-edit-event .modal-actions button.btn-secondary {
  width: auto;
  flex: 1;
  order: 1;
}
#modal-edit-event .modal-actions button.btn-success {
  width: auto;
  flex: 1;
  order: 2;
}
/* LOT MODALES-5 octies : la suppression d'un chantier est désormais
   accessible depuis la carte de l'agenda sur ordinateur, vue mois
   (index.html:5202-5209, bouton posé seulement si window.innerWidth
   >= 1025) — le bouton de #modal-edit-event y devient redondant, sur
   ordinateur uniquement (sur mobile, cette carte n'offre pas ce
   bouton : seul #modal-edit-event permet encore de supprimer, d'où
   son maintien là-bas). Masqué ici ; le repère de saut de ligne et la
   mise en forme en retrait du lot MODALES-5 quinquies n'ont alors plus
   d'objet, retirés avec lui. Annuler/Enregistrer (ci-dessus) restent
   seuls dans .modal-actions, à parts égales. */
#modal-edit-event .modal-actions button.btn-danger {
  display: none;
}

/* ── LOT MODALES-5 bis : même cause que ci-dessus (align-items:stretch,
   id seul, sans condition d'écran) également posée sur #planifier-modal
   (css/mobile/devis.css:150) et #commande-modal (même fichier:186) —
   même correctif, par les mêmes id, chargés après donc l'emportant sans
   !important. Boutons vérifiés pour les deux, aucun correctif requis :
   #planifier-modal .modal-actions{display:flex;gap:10px} + .btn-
   secondary{flex:1}/.btn-primary{flex:2} (devis.css:170-179) partagent
   déjà la ligne sans width:100% ni empilement — pas de chevauchement.
   #commande-modal .modal-footer (pas .modal-actions) ne contient qu'un
   seul bouton ("Envoyer", js/commande-fournisseur.js:164-166) : rien à
   empiler ni à chevaucher, la règle .btn-secondary/.btn-primary de
   devis.css:204-208 n'y trouve aucune paire de boutons à s'appliquer
   actuellement. ─────────────────────────────────────────────────── */
#planifier-modal,
#commande-modal {
  align-items: center;
}

/* ── LOT MODALES-4 : chevauchement des boutons de #modal-add-event
   ("Nouveau chantier") — constaté : .btn-primary{width:100%} (css/
   style.css:7, sans condition d'écran) entre en conflit avec le passage
   en ligne de .modal-actions sur ordinateur (.modal-actions{display:
   flex;justify-content:flex-end;gap:...}, règle générique plus bas
   dans ce fichier) : un enfant flex forcé à 100% de largeur alors que
   son voisin (.btn-secondary{flex:1}, css/mobile/base.css:194) tente
   aussi d'occuper la ligne produit le chevauchement rapporté. Aucune
   règle propre à #modal-add-event n'existe nulle part (vérifié) pour
   neutraliser ce width:100% comme le font déjà #planifier-modal/
   #commande-modal pour leurs propres boutons (css/mobile/devis.css:
   175-178/204-207) — corrigé ici en reprenant le même comportement
   (flex:1, largeur réellement partagée). #modal-add-event n'a pas non
   plus de .modal-body : ses champs (.form-group/.form-row-2, enfants
   directs de .modal-content) n'ont donc aucune marge horizontale —
   ajoutée ci-dessous avec var(--sp-lg), la valeur déjà employée pour
   le même usage par .modal-header/.modal-body/.modal-actions (ligne
   ~2020 et suivantes, famille .modal générique). ───────────────────── */
#modal-add-event .btn-primary,
#modal-add-event .btn-secondary {
  width: auto;
  flex: 1;
}
#modal-add-event .modal-content > .form-group,
#modal-add-event .modal-content > .form-row-2 {
  padding: 0 var(--sp-lg);
}

/* ── Modaux rapport d'intervention (A : saisie, B : envoi) — #modal-nouveau-rapport et
   #modal-envoi-rapport (js/rapports.js). Retraits horizontaux, pied de page, formulaire et zones
   de texte, scopés par identifiant : aucune règle générique (.modal-actions, .btn-*, .form-input)
   n'est redéfinie hors de ces deux modaux. ───────────────────────────────────────────────── */
#modal-nouveau-rapport .rapport-form-grid {
  padding: 0 var(--sp-lg);
}
#modal-envoi-rapport .modal-content > :not(.modal-header):not(.modal-actions) {
  padding-left: var(--sp-lg);
  padding-right: var(--sp-lg);
}

#modal-nouveau-rapport .modal-actions .btn-secondary,
#modal-envoi-rapport .modal-actions .btn-secondary {
  flex: 0 0 auto;
  padding: 0 var(--sp-md);
  white-space: nowrap;
}
#modal-nouveau-rapport .modal-actions .btn-primary,
#modal-envoi-rapport .modal-actions .btn-primary {
  flex: 1 1 auto;
  width: auto;
  padding: 0 var(--sp-md);
  white-space: nowrap;
}

#modal-nouveau-rapport .form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

#modal-nouveau-rapport .materiaux-rapport-list,
#modal-nouveau-rapport .mo-lines-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}
#modal-nouveau-rapport .materiau-rapport-ligne,
#modal-nouveau-rapport .mo-line {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
}
#modal-nouveau-rapport .materiau-rapport-ligne .form-input,
#modal-nouveau-rapport .mo-line .form-input {
  width: auto;
  min-width: 0;
}

#modal-nouveau-rapport textarea.form-input,
#modal-envoi-rapport textarea.form-input {
  height: auto;
  resize: vertical;
  padding-top: var(--sp-sm);
  padding-bottom: var(--sp-sm);
}

#modal-nouveau-rapport .rapport-section-title {
  font-size: 11px;
  font-weight: var(--fw-bold);
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: .07em;
  margin: var(--sp-lg) 0 var(--sp-sm);
}
#modal-nouveau-rapport .rapport-form-col:first-child > .rapport-section-title:first-child {
  margin-top: var(--sp-md);
}

/* ── Modale de signature (css/components/signature.css) — fenêtre centrée sur desktop, au lieu du
   plein écran mobile/tablette. #signature-modal/.sg-sheet ne réutilisent pas .modal-content
   (structure .sg-* dédiée), d'où des sélecteurs propres ; #signature-modal doit être ciblé par id
   (même spécificité que sa propre règle align-items:stretch dans le fichier surchargé, l'emporte
   par ordre de chargement, sans !important). Contenu et pied restent inchangés (défilement,
   pied collé en bas) : seules la taille et la forme de la feuille changent. */
#signature-modal { align-items: center; justify-content: center; }
.sg-sheet {
  width: 720px;
  max-width: calc(100% - 48px);
  height: auto;
  max-height: 90vh;
  max-height: 90dvh;
  border-radius: var(--r-lg);
}
.sg-canvas-wrap { height: 300px; }

.v2-bs-overlay.open { display: flex; align-items: center; justify-content: center; }
/* ── LOT 38 : #dv-bs/#fac-bs sont frères de leur overlay dans le HTML
   (index.html:510-511), jamais imbriqués — position:relative (LOT 37)
   les repassait donc dans le flux du document à l'emplacement de leur
   balise, sans lien avec l'overlay. .v2-bs reste position:fixed (hérité
   de css/mobile/devis.css:150) et se centre directement par rapport au
   viewport ; bottom/right neutralisés pour que seuls top/left pilotent
   la position. animation:none neutralise dvSlideUp (translation verticale
   pensée pour un panneau ancré en bas), qui produirait un déplacement
   incohérent combinée au transform de centrage ci-dessous. ─────────── */
.v2-bs.open {
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%);
  animation: none;
  width: calc(100% - var(--sp-xxl));
  max-width: 420px;
  max-height: 86vh;
  border-radius: var(--r-lg);
  overflow-y: auto;
  overflow-x: hidden;
}

/* ── LOT 39 : poignée de glissement sans objet sur desktop (aucun
   gestionnaire JS attaché, css/mobile/devis.css:152, index.html:512,1283
   — fermeture desktop via clic sur l'overlay) ; overflow-x ci-dessus
   découpe les séparateurs .v2-bs-info/.v2-bs-btn à la silhouette
   désormais arrondie sur les quatre côtés (LOT 37), qui les laissait
   déborder légèrement du contour incurvé faute de découpe horizontale. */
.v2-bs-handle { display: none; }
/* Sans la poignée (qui apportait 12 + 14 px au-dessus), le bloc d'info démarre au bord supérieur du
   panneau (.v2-bs{padding:0 …}, css/mobile/devis.css:122) : marge haute rétablie pour #dv-bs et #fac-bs. */
.v2-bs-info { padding-top: var(--sp-lg); }

/* ── Liens de prose uniquement : jamais les liens de nav ni de liste ──
   Un a{color:...} global repeignait la sidebar et les lignes de liste.
   ─────────────────────────────────────────────────────────────── */
.main-content p a:not([class]) { color: var(--accent); }
.main-content p a:not([class]):hover { color: #D85500; }


/* ══════════════════════════════════════════════════════════════
   SECTION B — FORMULAIRE NOUVEAU DEVIS
   Balisage df-* dans #nouveau-devis-page. TROIS PANNEAUX PRÉSERVÉS.
   Aucune règle ne rend deux panneaux visibles simultanément.
   ══════════════════════════════════════════════════════════════ */

/* animation:none : .page-content:not(.hidden){animation:dvPageFade .25s ease}
   (css/mobile/base.css:112, non scopée) anime opacity, ce qui promeut
   #nouveau-devis-page dans son propre contexte d'empilement (déclencheur
   listé par la spécification CSS) et isole la comparaison de z-index entre
   .df-header et le reste du contenu. Neutralisée ici, sur desktop uniquement. */
#nouveau-devis-page {
  animation: none;
}

/* Fond opaque + empilement renforcés : le bandeau (sticky) ne doit jamais
   laisser transparaître le contenu qui défile dessous. Les valeurs top
   restent celles de css/mobile/nouveau-devis.css (0 et 57px) : le
   padding-top redondant qu'elles compensaient au lot 22 a été neutralisé
   à sa source par padding-top:0 sur .main-content (lot 23). */
#nouveau-devis-page .df-header { padding: 16px 32px 10px; background: var(--card) !important; z-index: 20 !important; }
#nouveau-devis-page .df-header-center { text-align: left; }
#nouveau-devis-page .df-header-title { font-size: 17px; }
#nouveau-devis-page .df-tab-bar { padding-inline: 32px; background: var(--card) !important; z-index: 19 !important; }
#nouveau-devis-page .df-tab { padding: 12px 4px; }
#nouveau-devis-page .df-tab-txt { font-size: 13px; }
#nouveau-devis-page .df-tab-num { width: 24px; height: 24px; }

/* Un seul panneau visible à la fois — la navigation reste maîtresse */
#nouveau-devis-page .df-panel { max-width: 840px; margin-inline: auto; padding: 22px 0 28px; }
#nouveau-devis-page .df-panel:not(.df-panel-active) { display: none; }

/* Champs — confort pointeur */
#nouveau-devis-page .df-input,
#nouveau-devis-page #df-prenom, #nouveau-devis-page #df-nom,
#nouveau-devis-page #df-tel, #nouveau-devis-page #df-email,
#nouveau-devis-page #df-adresse, #nouveau-devis-page #df-cp,
#nouveau-devis-page #df-ville, #nouveau-devis-page #df-lieu-chantier { height: 44px; }
#nouveau-devis-page .df-body { padding: 14px 18px; }
#nouveau-devis-page .df-card-title { padding: 14px 18px 10px; }

/* Lignes de devis — plus d'air, suppression révélée au survol */
#nouveau-devis-page .df-item-row { padding: 12px 18px; }
#nouveau-devis-page .df-item-row:hover { background: var(--b); }
#nouveau-devis-page .df-item-desc { font-size: 15px; }
#nouveau-devis-page .df-item-del { opacity: 0; transition: opacity .15s; }
#nouveau-devis-page .df-item-row:hover .df-item-del { opacity: 1; }
#nouveau-devis-page .df-item-del:focus-visible { opacity: 1; }
#nouveau-devis-page .df-item-del:hover { color: var(--er); background: #FEF2F2; }
#nouveau-devis-page .df-add-btn { width: calc(100% - 36px); margin-inline: 18px; }
#nouveau-devis-page .df-add-btn:hover { background: rgba(255, 107, 0, .18); }

/* Totaux */
#nouveau-devis-page .df-ttc-val { font-size: 28px; }

/* Footer aligné sur la colonne */
/* position:fixed (lot 25) : ancre le footer en bas de la zone de contenu,
   décalé de la sidebar, pleine largeur à droite. Sort #df-footer du flux
   flex de #nouveau-devis-page — supprime l'effet de rétrécissement décrit
   ci-dessus (margin-inline:auto retiré, devenu sans objet hors flux). */
/* padding horizontal calculé (lot 26) : #df-footer n'a pas de conteneur
   interne dédié aux boutons (ceux-ci sont ses enfants directs) — ce padding
   ramène leur boîte de contenu à 840px, centrée (même largeur que .df-panel),
   tout en laissant le fond peindre sous le padding jusqu'aux bords réels du
   bandeau. Un pourcentage simple (calc(50% - 420px)) se résout, pour un
   élément position:fixed, contre la largeur du viewport et non contre la
   largeur réelle de #df-footer (viewport - var(--sidebar-width)) : la valeur
   serait donc fausse. On calcule à la place directement en vw, la seule unité
   qui référence sans ambiguïté la largeur du viewport, en retranchant la
   moitié de var(--sidebar-width) (déjà utilisée pour décaler ce bandeau,
   ligne ci-dessus) et la moitié de 840 (420px, déjà la largeur de .df-panel) :
   50vw - var(--sidebar-width)/2 - 420px. */
/* justify-content:flex-end (lot 30b) : regroupe les boutons réellement dans
   le flux (jamais ceux en display:none) contre le bord de fin, séparés du
   seul gap:8px hérité — fonctionne identiquement à 1 ou 2 boutons visibles,
   sans dépendre d'une marge automatique par bouton (lot 30, abandonné : deux
   marges auto actives simultanément se partagent l'espace libre à parts
   égales au lieu de l'annuler pour l'une des deux — même défaut que le lot
   5c sur la cloche/le toggle du header). */
#nouveau-devis-page #df-footer {
  position: fixed;
  left: var(--sidebar-width);
  right: 0;
  bottom: 0;
  padding: 14px calc(50vw - (var(--sidebar-width) / 2) - 420px);
  justify-content: center;
  background: var(--card) !important;
  z-index: 20 !important;
}
#nouveau-devis-page #df-footer button { height: 50px; }
/* Bouton retour élargi (lot 28) : ~1/6 de la largeur utile du footer
   (840px, même valeur que .df-panel/.df-btn-next, réutilisée — calc(840px/6)
   plutôt que la valeur littérale pour tracer la dérivation). !important
   nécessaire pour l'emporter sur .df-btn-prev{flex:0 0 52px!important}
   (css/mobile/nouveau-devis.css:178) : la spécificité supérieure de
   #nouveau-devis-page #df-btn-prev (deux id) suffit face à un !important
   de spécificité classe, mais on aligne le style pour rester lisible.
   Alignement à droite : voir justify-content:flex-end sur #df-footer
   ci-dessus (lot 30b) — remplace la marge automatique posée au lot 30,
   qui se cumulait avec celle de #df-btn-next/#df-btn-generate au lieu de
   la neutraliser. */
#nouveau-devis-page #df-btn-prev {
  flex: 0 0 calc(840px / 6) !important;
  width: calc(840px / 6) !important;
}
/* Bouton principal contraint (lot 29) : #df-btn-next (flex:1 hérité de
   css/mobile/nouveau-devis.css:179) et #df-btn-generate (flex:1 posé au
   lot 28) prenaient tout l'espace restant — largeur fixe à la place.
   300px : aucun token n'existant pour cette largeur, valeur mesurée
   signalée conformément à la consigne.
   Alignement à droite : voir justify-content:flex-end sur #df-footer
   (lot 30b) — remplace la marge automatique posée au lot 30. */
#nouveau-devis-page #df-btn-next,
#nouveau-devis-page #df-btn-generate {
  flex: 0 0 300px;
  width: 300px;
}
/* Espace réservé en bas du formulaire pour ne pas être masqué par le
   footer désormais fixed, sur #df-body (enveloppe les trois .df-panel,
   s'applique donc identiquement aux trois étapes).
   Hauteur réelle du footer : padding 14px+14px + bouton 52px (.df-btn-prev,
   css/mobile/nouveau-devis.css:178, height:52px!important — l'emporte sur
   le height:50px ci-dessus malgré une spécificité moindre) + bordure 1px =
   81px, revu depuis les 79px du lot 25 (le !important n'avait pas été pris
   en compte). Aucun token ne représente cette hauteur composite, valeur
   mesurée signalée. Marge de respiration ajoutée : var(--sp-xl) (32px,
   tokens.css:32). Total : 81px + 32px = 113px. */
#nouveau-devis-page #df-body { padding-bottom: calc(81px + var(--sp-xl)); }
#nouveau-devis-page #df-btn-next:hover,
#nouveau-devis-page #df-btn-generate:hover { opacity: .92; transform: translateY(-1px); }

/* ── ÉTATS FONCTIONNELS — porteurs d'information, jamais neutralisés ── */

/* Badges « Mon catalogue » / « Standard » : ni écrasés ni tronqués */
#nouveau-devis-page .df-item-row [class*="badge"],
#nouveau-devis-page .df-item-row .badge-catalogue,
#nouveau-devis-page .df-item-row .badge-standard {
  flex-shrink: 0;
  white-space: nowrap;
}

/* Lignes non trouvées (triangle jaune) : fond ambré conservé, y compris au survol.
   Sélecteur corrigé (lot 54) ; règle sans effet observable : ni la classe
   "unmatched" ni l'attribut data-unmatched ne sont jamais posés par le JS
   du projet (recherche exhaustive) — fonctionnalité non câblée, signalée. */
#nouveau-devis-page .df-item-row.unmatched,
#nouveau-devis-page .df-item-row[data-unmatched] { background: rgba(217, 119, 6, .06); }
#nouveau-devis-page .df-item-row.unmatched:hover,
#nouveau-devis-page .df-item-row[data-unmatched]:hover { background: rgba(217, 119, 6, .10); }

/* Le triangle reste visible même quand l'action de suppression est masquée */
#nouveau-devis-page .df-item-row.unmatched [class*="warn"],
#nouveau-devis-page .df-item-row[data-unmatched] [class*="warn"] { opacity: 1 !important; }

/* Prix verrouillé (article du catalogue global) : reste visiblement inerte */
#nouveau-devis-page .df-item-row input[readonly],
#nouveau-devis-page .df-item-row input[disabled] {
  background: var(--b);
  color: var(--text2);
  cursor: not-allowed;
}
#nouveau-devis-page .df-item-row input[readonly]:hover,
#nouveau-devis-page .df-item-row input[disabled]:hover { background: var(--b); }


/* ══════════════════════════════════════════════════════════════
   SECTION C — PAGES — SÉLECTEURS À VÉRIFIER
   Je n'ai pas reçu le CSS ni le HTML de ces pages : ces noms de classes
   sont déduits de la nomenclature du design system, pas lus dans ton
   dépôt. Chaque règle est inerte si le sélecteur n'existe pas — aucun
   risque de casse, mais aucun effet non plus. Voir README point 3.
   ══════════════════════════════════════════════════════════════ */

/* Devis — montant, badge et date alignés en colonnes à droite */
#devis-mobile-page .dv-hdr { padding: 20px 32px 14px; }
#devis-mobile-page input[type="search"] { max-width: 480px; }
#devis-mobile-page .lr { padding: 14px 32px; align-items: center; }
#devis-mobile-page .lr:hover { background: var(--b); }
#devis-mobile-page .lr-r {
  flex-direction: row !important;
  align-items: center;
  gap: 16px;
  min-width: 340px;
  justify-content: flex-end;
}
#devis-mobile-page .lr-r .l-amt { min-width: 110px; text-align: right; }
#devis-mobile-page .lr-r .l-date { min-width: 90px; text-align: right; }

/* Page devis — plafond de largeur commun (LOT 56), même approche que
   #cockpit-mobile-page (lot 49) : plafond et centrage sur le conteneur,
   remplissage latéral unique, marges/paddings latéraux propres à chaque
   bloc (css/mobile/devis.css:22,24,33,38 — 20px/16px/16px/20px selon le
   bloc) neutralisés pour un alignement identique, titre compris. À
   1280px (< 1550), max-width ne contraint pas encore : padding-inline
   continue seul d'assurer la marge latérale. */
#devis-mobile-page .dv-body {
  width: 100%;
  max-width: 1550px;
  margin-inline: auto;
  padding-inline: var(--sp-xl);
}
#devis-mobile-page .dv-ph { padding-left: 0; padding-right: 0; }
#devis-mobile-page .dv-search-row { padding-left: 0; padding-right: 0; }
#devis-mobile-page .dv-filters { padding-left: 0; padding-right: 0; }

/* Boutons de filtre — dimensionnés à leur libellé, groupés à gauche
   (LOT 63). Vestige mobile : .dv-filter-btn est flex:1 1 auto dans
   .dv-filters (css/mobile/devis.css:33-34), chaque bouton s'étirant pour
   occuper une part égale de la largeur du conteneur — correct sur un
   écran étroit à 4 boutons, mais donne ~400px par bouton une fois
   .dv-filters à pleine largeur de page (lot 56). flex:none le ramène à
   la largeur de son contenu ; .dv-filters (déjà display:flex, sans
   justify-content) les regroupe alors naturellement à gauche avec son
   gap:8px existant, aucun changement de conteneur nécessaire. Forme,
   bordure, couleurs et état actif (mobile, css/mobile/devis.css:34-35)
   non repris ici, seuls flex/padding changent. Aucun survol n'existait :
   ajouté, exclu de l'état actif pour ne pas concurrencer son fond
   accent. */
#devis-mobile-page .dv-filter-btn { flex: none; padding: var(--sp-sm) var(--sp-md); }
#devis-mobile-page .dv-filter-btn:hover:not(.active) { background: var(--b); }

/* Filtre période — bouton et menu déroulant, desktop uniquement (LOT 64).
   #dv-period-wrap est masqué par défaut en ligne dans index.html
   (style="display:none") faute de pouvoir déclarer un état "masqué sous
   1025px" dans ce fichier, entièrement contenu dans
   @media(min-width:1025px) : !important reproduit ici le même mécanisme
   déjà utilisé pour l'inverse (#equipe-mobile-page, plus haut, masqué sur
   desktop). #dv-period-btn porte la classe .dv-filter-btn : il hérite
   donc directement du style compact et du survol posés au lot 63,
   « même langage » que les filtres de statut sans règle dupliquée.
   .period-dropdown/.period-opt (css/mobile/devis.css:12-16, non scopé)
   sont déjà complets ; seul le positionnement relatif du déclencheur
   (posé en ligne dans index.html) et le panneau de plage personnalisée
   ci-dessous sont propres à ce lot. L'ouverture/fermeture est gérée en
   JS (js/devis.js, devisTogglePeriod) via display 'block'/'none' sur
   .period-dropdown et 'flex'/'none' sur .dv-period-custom — la valeur
   display de ce dernier n'est donc pas redéclarée ici, seules les
   propriétés de mise en forme le sont. */
#devis-mobile-page #dv-period-wrap { display: inline-block !important; }
#devis-mobile-page .dv-period-custom {
  flex-direction: column;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md) var(--sp-md);
  border-top: 1px solid var(--b);
}
#devis-mobile-page .dv-period-custom input[type="date"] {
  height: 36px;
  border: 1.5px solid var(--b2);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-sm);
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--card);
  font-family: inherit;
}
#devis-mobile-page .dv-period-custom button {
  height: 34px;
  border: none;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  font-family: inherit;
}

#devis-mobile-page .dv-row { padding-left: 0; padding-right: 0; }

/* Lignes devis — colonnes alignées sur une ligne (LOT 62). Structure
   réelle (_renderDvList, js/devis.js:1513-1526) : .dv-row > .dv-row-left
   (.dv-num, .dv-client, .dv-desc — optionnelle) + .dv-row-right
   (.dv-amount, .dv-badges-row > 1-2 .dv-badge, .dv-date), empilés en
   colonne par le mobile (css/mobile/devis.css:41,45 : display:flex;
   flex-direction:column). .dv-row-left/.dv-row-right neutralisés en
   display:contents pour exposer directement leurs enfants comme cellules
   de la grille posée sur .dv-row, sans toucher au markup. .dv-desc étant
   optionnelle, chaque colonne est assignée explicitement par classe
   (grid-column) plutôt que par ordre d'apparition : une ligne sans
   .dv-desc laisse sa 3e colonne vide au lieu de décaler les suivantes.
   .dv-badges-row reste display:flex (mobile, déjà justify-content:
   flex-end) à l'intérieur de sa propre cellule — pas d'unwrap nécessaire.
   Couleurs/graisses (.dv-num gris majuscule, .dv-client gras, .dv-desc
   gris, .dv-amount gras, .dv-date gris, badges de statut) déjà conformes
   à la maquette dans le mobile non scopé (css/mobile/devis.css:42-55) —
   non reprises ici, seule la disposition change.
   Largeurs de colonnes : valeurs en dur signalées, aucun token de largeur
   adapté dans tokens.css. */
#devis-mobile-page .dv-row {
  display: grid;
  /* 5e colonne (badges) : largeur fixe de 200px (LOT DIVERS-4 quinquies), recalculée sur le
     couple de badges le plus large ("Accepté" + "Acompte reçu") plutôt qu'une valeur
     arbitraire — voir le calcul dans le commentaire de .dv-badges-row ci-dessous. Remplace
     minmax(130px, max-content) (LOT DIVERS-4 quater) : une largeur fixe identique sur
     toutes les lignes est nécessaire pour que les colonnes montant/date restent alignées
     entre elles quel que soit le contenu du badge. Seule la 3e colonne (.dv-desc, 1fr) cède
     la place — les autres colonnes restent fixes. */
  grid-template-columns: 140px 200px 1fr 110px 200px 90px;
  gap: var(--sp-md);
  align-items: center;
  padding-block: var(--sp-sm);
}
#devis-mobile-page .dv-row:hover { background: var(--b); }
#devis-mobile-page .dv-row-left,
#devis-mobile-page .dv-row-right { display: contents; }
#devis-mobile-page .dv-num        { grid-column: 1; }
#devis-mobile-page .dv-client     { grid-column: 2; min-width: 0; }
#devis-mobile-page .dv-desc       { grid-column: 3; min-width: 0; }
#devis-mobile-page .dv-amount     { grid-column: 4; text-align: right; }
/* Calcul de la largeur de colonne ci-dessus (200px), à partir des propriétés réelles de
   .dv-badge (css/mobile/devis.css:49 : font-size:11px, font-weight:700, padding:3px 9px) et
   de .dv-badges-row (css/mobile/devis.css:48 : gap:4px) — police système (tokens.css:27,
   -apple-system/Segoe UI/system-ui), largeur de caractère non mesurable ici sans rendu réel,
   approximée à 0.6 × la taille de police pour du gras :
     "Accepté" (7 car.) : 7 × 6,6px ≈ 46px + remplissage 18px (9px × 2)      ≈ 64px
     "Acompte reçu" (12 car.) : 12 × 6,6px ≈ 79px + remplissage 18px         ≈ 97px
     + espacement entre les deux badges (gap 4px)                           =  4px
     sous-total                                                             ≈ 165px
     + marge de sécurité (~20%, police système non mesurée précisément)     ≈  33px
     total, arrondi                                                        ≈ 200px
   justify-self:start (remplace l'absence d'ancrage, qui héritait de la valeur par défaut
   stretch + justify-content:flex-end de .dv-badges-row) : la ligne de badges se dimensionne
   maintenant à son contenu et se cale au bord gauche de la colonne, point fixe identique sur
   toutes les lignes quelle que soit la longueur du ou des badges. */
#devis-mobile-page .dv-badges-row { grid-column: 5; justify-self: start; }
#devis-mobile-page .dv-date       { grid-column: 6; text-align: right; }

/* Barre de recherche devis — contrainte et alignée à gauche (maquette
   LOT 62). #devis-mobile-page input[type="search"] ci-dessus (ligne 747)
   est un sélecteur orphelin : le champ réel (#dv-search-input,
   index.html:513) n'a pas d'attribut type="search", cette règle ne s'est
   donc jamais appliquée. .dv-sw est display:flex (mobile,
   css/mobile/devis.css:25) mais reste un bloc sans largeur déclarée : il
   s'étirait sur toute la largeur de .dv-search-row (elle-même pleine
   largeur de .dv-body, lot 56). max-width le plafonne ; aucune marge
   automatique n'étant posée, il reste naturellement aligné à gauche. */
#devis-mobile-page .dv-sw { max-width: 790px; }

/* Factures */
#factures-mobile-page .fac-hdr { padding: 20px 32px 14px; }
#factures-mobile-page input[type="search"] { max-width: 480px; }
#factures-mobile-page .lr { padding: 14px 32px; align-items: center; }
#factures-mobile-page .lr:hover { background: var(--b); }
#factures-mobile-page .lr-r {
  flex-direction: row !important;
  align-items: center;
  gap: 16px;
  min-width: 340px;
  justify-content: flex-end;
}

/* Page factures — plafond de largeur commun (LOT 57), même mécanisme que
   la page devis (LOT 56/56b) : #factures-mobile-page est display:flex en
   colonne (css/mobile/factures.css:22) et .fac-body en est un enfant flex
   (flex:1, ligne 23) — le flex ne gouverne que la hauteur sur cet axe, la
   largeur se réduit donc au contenu sans width:100% explicite (même cause
   que le lot 56b sur .dv-body). Marges/paddings latéraux propres à chaque
   bloc (css/mobile/factures.css:24,26,32 — 20px/16px/16px — et .fac-row
   ligne 37 — 20px) neutralisés pour un alignement identique, titre compris. */
#factures-mobile-page .fac-body {
  width: 100%;
  max-width: 1550px;
  margin-inline: auto;
  padding-inline: var(--sp-xl);
}
#factures-mobile-page .fac-ph { padding-left: 0; padding-right: 0; }
#factures-mobile-page .fac-search-row { padding-left: 0; padding-right: 0; }
#factures-mobile-page .fac-filters { padding-left: 0; padding-right: 0; }

/* Boutons de filtre — même correction que devis ci-dessus (LOT 63),
   même cause : .fac-filter-btn est flex:1 1 auto dans .fac-filters
   (css/mobile/factures.css:32-33). */
#factures-mobile-page .fac-filter-btn { flex: none; padding: var(--sp-sm) var(--sp-md); }
#factures-mobile-page .fac-filter-btn:hover:not(.active) { background: var(--b); }

/* Filtre période — bouton et menu déroulant, desktop uniquement (LOT 65),
   même mécanisme que devis (LOT 64) : #fac-period-wrap est masqué par
   défaut en ligne dans index.html (style="display:none"), révélé ici en
   !important pour la même raison que #dv-period-wrap ci-dessus (fichier
   entièrement contenu dans @media(min-width:1025px)). #fac-period-btn
   porte la classe .fac-filter-btn : il hérite du style compact et du
   survol posés au lot 63. .period-dropdown/.period-opt (non scopées,
   partagées avec devis) déjà complètes. */
#factures-mobile-page #fac-period-wrap { display: inline-block !important; }
#factures-mobile-page .fac-period-custom {
  flex-direction: column;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md) var(--sp-md);
  border-top: 1px solid var(--b);
}
#factures-mobile-page .fac-period-custom input[type="date"] {
  height: 36px;
  border: 1.5px solid var(--b2);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-sm);
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--card);
  font-family: inherit;
}
#factures-mobile-page .fac-period-custom button {
  height: 34px;
  border: none;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  font-family: inherit;
}

#factures-mobile-page .fac-row { padding-left: 0; padding-right: 0; }

/* Lignes factures — colonnes alignées sur une ligne (LOT 62). Même
   structure et même traitement que les lignes devis ci-dessus
   (_renderFacList, js/facturation.js:1894-1904) : .fac-row-left/
   .fac-row-right neutralisés en display:contents, colonnes assignées
   explicitement par classe (.fac-desc optionnelle, même raison que
   .dv-desc). Seule différence de structure : un badge unique (ou deux en
   cas d'avoir partiel), direct enfant de .fac-row-right (pas de wrapper
   équivalent à .dv-badges-row) — justify-self:start (LOT DIVERS-4
   quinquies, remplace justify-self:end) pour un point d'ancrage à gauche
   identique sur toutes les lignes, voir le calcul de largeur ci-dessous.
   Couleurs/graisses déjà conformes à la maquette dans le mobile non
   scopé (css/mobile/factures.css:41-52) — non reprises ici. */
#factures-mobile-page .fac-row {
  display: grid;
  /* 5e colonne (badges) : largeur fixe de 200px (LOT DIVERS-4 quinquies), recalculée sur le
     libellé le plus long ("Annulée — à rembourser", badge unique) plutôt qu'une valeur
     arbitraire — voir le calcul dans le commentaire de .fac-badges ci-dessous. Remplace
     minmax(130px, max-content) (LOT DIVERS-4 quater) : une largeur fixe identique sur
     toutes les lignes est nécessaire pour que les colonnes montant/date restent alignées
     entre elles quel que soit le contenu du badge. Seule la 3e colonne (.fac-desc, 1fr) cède
     la place. */
  grid-template-columns: 180px 200px 1fr 110px 200px 90px;
  gap: var(--sp-md);
  align-items: center;
  padding-block: var(--sp-sm);
}
#factures-mobile-page .fac-row:hover { background: var(--b); }
#factures-mobile-page .fac-row-left,
#factures-mobile-page .fac-row-right { display: contents; }
#factures-mobile-page .fac-num    { grid-column: 1; }
#factures-mobile-page .fac-client { grid-column: 2; min-width: 0; }
#factures-mobile-page .fac-desc   { grid-column: 3; min-width: 0; }
#factures-mobile-page .fac-amount { grid-column: 4; text-align: right; }
/* Calcul de la largeur de colonne ci-dessus (200px), à partir des propriétés réelles de
   .fac-badge (css/mobile/factures.css:47 : font-size:11px, font-weight:700, padding:3px 9px)
   — police système (tokens.css:27, -apple-system/Segoe UI/system-ui), largeur de caractère
   non mesurable ici sans rendu réel, approximée à 0.6 × la taille de police pour du gras :
     "Annulée — à rembourser" (22 car., espaces et tiret cadratin inclus) : 22 × 6,6px ≈ 145px
     + remplissage 18px (9px × 2)                                                    ≈ 163px
     + marge de sécurité (~20%, police système non mesurée précisément)              ≈  33px
     total, arrondi                                                                 ≈ 200px
   justify-self:start (remplace justify-self:end) : le badge se dimensionne à son contenu et
   se cale au bord gauche de la colonne, point fixe identique sur toutes les lignes. */
#factures-mobile-page .fac-badges { grid-column: 5; justify-self: start; }
#factures-mobile-page .fac-date   { grid-column: 6; text-align: right; }

/* Barre de recherche factures — même correction que devis ci-dessus.
   #factures-mobile-page input[type="search"] (ligne 780) est un
   sélecteur orphelin (le champ réel, .fac-search-input, index.html:1284,
   n'a pas d'attribut type="search"). */
#factures-mobile-page .fac-sw { max-width: 790px; }

/* Cockpit / tableau de bord — plafond de largeur commun + 2 colonnes (LOT 49)
   Remplace les marges/paddings de 32px auparavant dupliqués sur chaque bloc
   (.ck2-kpi, .ck2-grid, .ck2-sat-btn/.ck2-ia-btn, .ck2-sat-body/.ck2-conseils)
   par un seul plafond posé sur le conteneur #cockpit-mobile-page : garantit
   l'alignement de tous les blocs, titre .ck2-hdr compris, par construction.
   À 1280px (< 1550), max-width ne contraint pas encore : padding-inline
   continue seul d'assurer la marge latérale.
   #cockpit-mobile-page passe de display:flex (css/mobile/cockpit.css:7,
   non scopé) à display:grid ici : .ck2-sat-btn/.ck2-sat-body/.ck2-ia-btn/
   .ck2-conseils sont des frères séparés dans le DOM (index.html:357-398,
   pas imbriqués bouton+panneau) — le placement grid explicite par
   grid-column/grid-row les répartit en 2 colonnes sans dépendre de leur
   ordre dans le HTML, contrairement à un simple display:flex séquentiel. */
#cockpit-mobile-page {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--sp-xl);
  max-width: 1550px;
  margin-inline: auto;
  padding-inline: var(--sp-xl);
}
#cockpit-mobile-page .ck2-hdr,
#cockpit-mobile-page .ck2-kpi,
#cockpit-mobile-page .ck2-grid,
#cockpit-mobile-page .ck2-evolution { grid-column: 1 / -1; }
/* Marges/paddings latéraux propres à chaque bloc (css/mobile/cockpit.css:14,16,24
   — 20px/16px/16px selon le bloc) neutralisés : seul le padding-inline du
   conteneur ci-dessus doit gouverner l'inset latéral, pour un alignement
   identique sur les 3 blocs pleine largeur. */
#cockpit-mobile-page .ck2-hdr { padding-left: 0; padding-right: 0; grid-row: 1; }
#cockpit-mobile-page .ck2-kpi { margin-left: 0; margin-right: 0; grid-row: 2; }
#cockpit-mobile-page .ck2-grid { padding-left: 0; padding-right: 0; grid-template-columns: repeat(4, 1fr) !important; grid-row: 3; }
/* LOT 50 : graphique inséré entre la grille et les blocs dépliables — rangée
   explicite requise, .ck2-sat-btn/.ck2-ia-btn portant déjà une rangée fixe
   (l'auto-placement grid ignorerait sinon l'ordre du DOM et repousserait ce
   bloc après les deux accordéons). Rangées des accordéons décalées de 4/5 à
   5/6 en conséquence. */
#cockpit-mobile-page .ck2-evolution { grid-row: 4; }
#cockpit-mobile-page .ck2-sat-btn  { grid-column: 1 / -1; grid-row: 5; width: auto !important; margin-left: 0; margin-right: 0; }
#cockpit-mobile-page .ck2-sat-body { grid-column: 1 / -1; grid-row: 6; }
#cockpit-mobile-page .ck2-ia-btn   { grid-column: 1 / -1; grid-row: 7; width: auto !important; margin-left: 0; margin-right: 0; }
#cockpit-mobile-page .ck2-conseils { grid-column: 1 / -1; grid-row: 8; }
#cockpit-mobile-page .ck2-sat-comment { white-space: normal; }

/* LOT 50 : habillage du graphique d'évolution — mêmes tokens de fond/bordure/
   ombre que .ck2-card (css/mobile/cockpit.css:25 : background:var(--card)),
   mais texte/bordure sur les tokens globaux var(--text*)/var(--b2) plutôt
   que sur les propriétés --ck-text/--ck-border locales à #cockpit-mobile-page
   (css/mobile/cockpit.css:2-12) : ces dernières sont des valeurs hex fixes,
   jamais redéfinies en mode sombre nulle part dans le projet (recherche
   exhaustive) — un défaut préexistant sur .ck2-card/.ck2-kpi, hors périmètre
   de ce lot, que je ne reproduis pas ici pour que cet ajout reste correct
   dans les deux thèmes. */
#cockpit-mobile-page .ck2-evolution {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 360px;
  background: var(--card);
  border: 1px solid var(--b2);
  border-radius: var(--r-lg);
  box-shadow: var(--sh);
  padding: var(--sp-lg);
}
#cockpit-mobile-page .ck2-evolution-title { flex-shrink: 0; font-size: var(--fs-lg); font-weight: 800; color: var(--text); }
#cockpit-mobile-page .ck2-evolution-sub { flex-shrink: 0; font-size: var(--fs-xs); color: var(--text3); margin-bottom: var(--sp-md); }
/* LOT 50c : flex:0 0 280px (lot 50b) réglait la même propriété (height/
   flex-basis) que celle que Chart.js réécrit en style inline à chaque
   redimensionnement — l'inline l'emportait toujours, boucle non rompue,
   aggravée par .ck2-evolution restée en hauteur automatique (grandissait
   avec le canvas). .ck2-evolution reçoit maintenant une hauteur explicite
   ci-dessus (360px) ; max-height/max-width !important ci-dessous agissent
   sur une propriété différente de celle que Chart.js écrit (height/width) :
   un plafond, pas une valeur concurrente, donc jamais écrasé par l'inline. */
#cockpit-mobile-page #chart-evolution { display: block; max-height: 280px !important; max-width: 100% !important; }

/* LOT 40 : bande CA encaissé plus compacte, valeur plus présente.
   Base mobile (css/mobile/cockpit.css:16,18) : padding 20px 16px,
   .ck2-kpi-val à 36px. Aucun token tokens.css n'atteint 36px (max
   --fs-xxl:32px, déjà inférieur à la valeur mobile actuelle) : valeur
   en dur signalée, seule échappatoire au token pour ce cas précis. */
#cockpit-mobile-page .ck2-kpi { padding: var(--sp-md) var(--sp-xl); }
#cockpit-mobile-page .ck2-kpi-val { font-size: 44px; }

/* LOT 40 : cartes d'indicateurs — respiration accrue. grid-auto-rows
   est une hauteur : aucun token de hauteur n'existe dans tokens.css,
   valeur en dur signalée (152px, contre 112px hérités du mobile,
   css/mobile/cockpit.css:24). */
#cockpit-mobile-page .ck2-grid { grid-auto-rows: 152px; }
#cockpit-mobile-page .ck2-card { padding: var(--sp-lg); }
#cockpit-mobile-page .ck2-card-val { font-size: var(--fs-xxl); }
#cockpit-mobile-page .ck2-card-hdr span,
#cockpit-mobile-page .ck2-card-sub { font-size: var(--fs-sm); }

/* LOT 40 : états de survol — cartes non cliquables (aucun onclick,
   index.html) : élévation d'ombre seule, pas de curseur pointeur.
   Boutons dépliables déjà cliquables : opacité, même traitement que
   leur propre :active existant (css/mobile/cockpit.css:31,45). */
#cockpit-mobile-page .ck2-card { transition: box-shadow var(--tr); }
#cockpit-mobile-page .ck2-card:hover { box-shadow: var(--sh2); }
#cockpit-mobile-page .ck2-sat-btn:hover,
#cockpit-mobile-page .ck2-ia-btn:hover { opacity: .85; }

/* Catalogue — 3 colonnes */
/* Page catalogue — plafond de largeur commun (LOT 57), même mécanisme que
   le cockpit (LOT 49) : .catv2-ui est lui-même display:flex en colonne
   (css/mobile/catalogue.css:2) et n'est pas un enfant flex d'un autre
   conteneur flex (#catalogue-page, son parent, n'est pas flex) — sa
   largeur suit donc le comportement normal d'un bloc (100% du parent par
   défaut), aucun width:100% requis, contrairement à .dv-body/.fac-body
   (lots 56b/57). padding-inline 32px déjà posé (LOT 3) converti en token
   --sp-xl (valeur identique, tokens.css:32) et complété du plafond et du
   centrage. Marges/paddings latéraux propres à chaque bloc enfant
   (css/mobile/catalogue.css:7,89 — .catv2-header 16px, .catv2-list 16px —
   et .catv2-empty ligne 82, 24px) neutralisés pour un alignement
   identique, titre compris. */
#catalogue-page .catv2-ui { max-width: 1550px; margin-inline: auto; padding-inline: var(--sp-xl); }
#catalogue-page .catv2-ui .catv2-header { padding-left: 0; padding-right: 0; }
#catalogue-page .catv2-ui .catv2-list { padding-left: 0; padding-right: 0; }
#catalogue-page .catv2-ui .catv2-empty { padding-left: 0; padding-right: 0; }
#catalogue-page .catv2-ui input[type="search"] { max-width: 480px; }
#catalogue-page .catv2-ui .cat-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 32px; }
#catalogue-page .catv2-ui .cat-row:hover { background: var(--b); }

/* Planning / Agenda — disposition desktop 2 colonnes (LOT 60)
   Remplace les 3 règles ci-dessus (id #agenda-page, jamais présent dans
   le DOM réel — l'id réel est #ag-page, construit par _agInit,
   index.html:4517, à l'intérieur de #planning-page). Aucune règle
   n'était donc effective pour cet écran au-delà de 1025px, qui reposait
   entièrement sur css/mobile/agenda.css (non scopé, aucune media query).
   Colonne gauche : navigation + calendrier mensuel, largeur restante.
   Colonne droite : liste des interventions, largeur fixe 420px.
   Défilement propre à chaque colonne plutôt qu'un défilement unique sur
   .ag-body (mobile, agenda.css:37) : .ag-body devient elle-même la
   grille, chaque zone défilant indépendamment. */
/* Alignement sur la référence Commandes (#fournisseurs-page .frs-shell) : largeur maximale
   1550px et centrage, appliqués séparément à .ag-hdr et .ag-body (frères directs de #ag-page,
   sans conteneur commun à cibler en une seule règle). width:100% nécessaire sur les deux : sans
   elle, des marges auto sur l'axe secondaire d'un enfant de #ag-page (flex-direction:column,
   css/mobile/agenda.css:5-9) désactivent le stretch par défaut et l'élément se dimensionne sur
   son propre contenu au lieu de remplir le conteneur avant d'être plafonné par max-width — même
   mécanisme déjà rencontré et corrigé pour #mn-export-title/.mn-ov-body plus haut dans ce fichier
   (recherche "auto margins block stretch" dans l'historique de ce fichier). Sans ce correctif,
   .ag-hdr en particulier (display:flex;justify-content:space-between, css/mobile/agenda.css:12-17)
   se rétrécirait sur son contenu et casserait la répartition titre/onglets. */
/* align-items:flex-start (au lieu de center, mobile, css/mobile/agenda.css:12-17) : .ag-title
   (31px de haut) était centré sur la hauteur de .ag-hdr, elle-même dictée par .ag-tabs (50px,
   le plus haut des deux enfants) — ce centrage repoussait le titre 9-10px sous le haut du
   padding au lieu d'y être calé, décalage mesuré en navigateur (100px contre 90px pour la
   référence Commandes). Avec flex-start, les deux enfants démarrent au même bord haut ; .ag-tabs
   étant le plus grand des deux, sa position ne change pas (un élément aussi haut que la ligne
   flex est déjà calé en haut quel que soit align-items) — vérifié, aucun correctif individuel
   n'est donc nécessaire sur .ag-tabs : sa hauteur et sa mise en forme restent inchangées. */
#planning-page #ag-page .ag-hdr {
  width: 100%;
  max-width: 1550px;
  margin-inline: auto;
  align-items: flex-start;
  padding-left: var(--sp-xl);
  padding-right: var(--sp-xl);
  padding-top: max(20px, env(safe-area-inset-top, 0px));
  padding-bottom: 16px;
}
#planning-page #ag-page .ag-title {
  line-height: 1.1;
}
#planning-page #ag-page .ag-body {
  width: 100%;
  max-width: 1550px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 420px;
  grid-template-rows: auto 1fr;
  gap: var(--sp-xl);
  padding-inline: var(--sp-xl);
  padding-bottom: 0;
  overflow: hidden;
}
/* FAB "+" — aligné sur le bord droit du contenu (pas du viewport) : .ag-fab reste position:fixed
   (css/mobile/agenda.css:174, non scopé donc actif ici aussi, seul "right" est surchargé). Le
   contenu (.ag-hdr/.ag-body ci-dessus) commence après la sidebar, se plafonne à 1550px et se
   centre avec un padding de --sp-xl : son bord droit se trouve donc à
   max(0, (largeur utile - 1550px) / 2) + --sp-xl du bord droit du viewport, où
   "largeur utile" = 100vw - var(--sidebar-width). En dessous de 1550px + sidebar (fenêtre
   étroite), le contenu occupe toute la largeur utile et cette expression se réduit à --sp-xl
   (32px) — le bouton reste à distance du bord de l'écran, jamais collé ni hors champ, et ne
   recouvre jamais le contenu puisqu'il s'aligne pile sur son bord. */
#planning-page #ag-page .ag-fab {
  right: max(var(--sp-xl), calc((100vw - var(--sidebar-width) - 1550px) / 2 + var(--sp-xl)));
}

#planning-page #ag-page .ag-cal-nav { grid-column: 1; grid-row: 1; padding-left: 0; padding-right: 0; }
#planning-page #ag-page .ag-cal-grid {
  grid-column: 1; grid-row: 2;
  padding-left: 0; padding-right: 0;
  overflow-y: auto;
  align-content: start;
}
#planning-page #ag-page #ag-list {
  grid-column: 2; grid-row: 1 / -1;
  overflow-y: auto;
  padding-bottom: 88px;
}

/* Cases du calendrier — confinées à la colonne de gauche ci-dessus au
   lieu de s'étirer sur toute la largeur de page (#ag-cal-grid en
   repeat(7,1fr) sur jusqu'à ~1900px, cases très plates). Hauteur minimale
   plus généreuse pour un rendu proche du carré ; align-content:start
   ci-dessus empêche en plus l'étirement vertical des lignes. Aucun token
   de hauteur adapté dans tokens.css : valeur en dur signalée, sur le
   modèle du lot 40 (cockpit). */
#planning-page #ag-page .ag-day { min-height: 88px; border-radius: var(--r); }
#planning-page #ag-page .ag-day:hover { background: var(--b); }

/* Cartes d'intervention — vue Mois desktop uniquement. Scopées via
   :has(.ag-month-day-hdr) (déjà utilisé dans le projet,
   css/mobile/nouveau-devis.css) : cette classe n'est posée que par la
   nouvelle _agMonthList (index.html) ; les vues Semaine/Jour, qui
   réutilisent la même _agCardHtml, ne la portent jamais et restent donc
   visuellement inchangées, y compris sur desktop.
   Fond teinté : --ag-card-bg posée en ligne par _agCardHtml (couleur du
   chantier + suffixe alpha hex si couleur réelle, sinon var(--card) —
   même logique que border-left-color juste au-dessus).
   Nom du client mis en avant, horaire en dessous : ordre visuel inversé
   par flex-direction/order, DOM inchangé. Détail (lieu, description,
   équipe, actions) rendu toujours visible plutôt que replié au clic pour
   que le lieu demandé soit visible sans interaction — description/
   équipe/actions s'affichent avec, faute de marqueur dans ce markup
   permettant d'isoler la seule ligne d'adresse. */
#planning-page #ag-page #ag-list:has(.ag-month-day-hdr) .ag-int-card {
  background: var(--ag-card-bg, var(--card));
}
#planning-page #ag-page #ag-list:has(.ag-month-day-hdr) .ag-int-compact {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-xs);
}
#planning-page #ag-page #ag-list:has(.ag-month-day-hdr) .ag-int-client { order: 1; }
#planning-page #ag-page #ag-list:has(.ag-month-day-hdr) .ag-int-time-compact {
  order: 2;
  font-weight: var(--fw-normal);
  color: var(--text2);
}
#planning-page #ag-page #ag-list:has(.ag-month-day-hdr) .ag-int-detail {
  max-height: none;
  opacity: 1;
  overflow: visible;
  transition: none;
  margin-top: var(--sp-sm);
  padding-top: var(--sp-sm);
  border-top: 1px solid var(--b2);
}

/* Libellés de regroupement par jour — vue Mois desktop */
#planning-page #ag-page .ag-month-day-hdr {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--text3);
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: var(--sp-md) 0 var(--sp-xs);
  border-top: 1px solid var(--b2);
}
#planning-page #ag-page .ag-month-day-hdr:first-child { border-top: none; padding-top: 0; }

/* Bouton Supprimer — carte d'intervention, vue Mois desktop uniquement
   (posé par _agCardHtml quand _agView==='mois' et window.innerWidth>=1025,
   index.html — LOT 61). À la suite des deux autres boutons dans la ligne
   d'actions existante (display:flex;gap:8px;flex-wrap:wrap, inline,
   index.html) — LOT 61b : plus de margin-left:auto, qui le détachait à
   l'extrémité droite et le faisait déborder du cadre de la carte ; le
   gap:8px du conteneur assure le même écart qu'entre les deux premiers
   boutons, et flex-wrap:wrap gère déjà le retour à la ligne si la
   largeur de la carte est insuffisante. Couleur d'erreur des tokens pour
   signaler l'action destructrice. */
#planning-page #ag-page .ag-int-del-btn {
  display: inline-flex;
  align-items: center;
  padding: 5px 11px;
  border: 1.5px solid var(--er);
  border-radius: 20px;
  background: transparent;
  font-size: 11px;
  font-weight: 600;
  color: var(--er);
  cursor: pointer;
  font-family: inherit;
}
#planning-page #ag-page .ag-int-del-btn:hover { background: var(--er-bg); }

/* Équipe — #equipe-mobile-page masqué sur desktop : initEquipePage()
   (js/equipe.js:110-118) affiche #equipe-admin-content sur desktop sans
   masquer #equipe-mobile-page en retour, les deux s'affichaient donc
   empilés. #equipe-admin-content couvre l'intégralité des fonctions du
   bloc mobile (ajout de collaborateur, liste des membres, accès fiche). */
#equipe-mobile-page { display: none !important; }

/* Équipe — membres sur 2 colonnes */
#equipe-mobile-page { padding-inline: 32px; }
#equipe-mobile-page .eq-stats { max-width: 640px; }
#equipe-mobile-page .eq-list { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
#equipe-mobile-page .eq-card:hover,
#equipe-mobile-page .eq-member:hover { background: var(--b); }
#equipe-mobile-page .eq-cta { max-width: 420px; }

/* ══════════════════════════════════════════════════════════════
   Équipe — vue admin (#equipe-admin-content), LOT 8
   Vocabulaire repris de la référence mobile : titre gras aligné à
   gauche, bande d'indicateurs horizontale sans emoji, onglets à trait
   accent, cartes membres déjà stylées par css/mobile/equipe.css,
   bouton principal contraint. Tokens tokens.css exclusivement.
   ══════════════════════════════════════════════════════════════ */

/* Page équipe — plafond de largeur commun (LOT 57), même mécanisme que le
   cockpit (LOT 49) : #equipe-admin-content n'est déclaré flex nulle part
   et son parent #equipe-page (.page-content) ne l'est pas non plus — sa
   largeur suit donc le comportement normal d'un bloc, aucun width:100%
   requis, contrairement à .dv-body/.fac-body (lots 56b/57). Aucun bloc
   enfant direct (.ck2-hdr, .factures-stats-grid, .equipe-tabs,
   .equipe-coming-soon…) ne porte de marge/remplissage latéral propre dans
   ce fichier ni dans css/mobile/equipe.css (recherche exhaustive) : rien à
   neutraliser, alignement déjà garanti par construction, titre compris. */
#equipe-admin-content {
  max-width: 1550px;
  margin-inline: auto;
  padding-inline: var(--sp-xl);
  padding-top: max(20px, env(safe-area-inset-top, 0px));
}

/* Titre de page — classes .ck2-hdr/.ck2-title reprises de #cockpit-mobile-page
   (dashboard.css), seul écran confirmé 100% stylé par l'audit précédent.
   Rescopées ici sous #equipe-admin-content : la règle mobile d'origine est
   scopée #cockpit-mobile-page, donc inapplicable telle quelle.
   Alignement sur la référence Commandes (#fournisseurs-page .frs-shell) : marge basse ramenée
   à var(--sp-md) (16px, au lieu de var(--sp-lg), 24px) pour l'espacement titre → premier bloc ;
   mise en forme du titre alignée (taille 28px au lieu de var(--fs-xxl)/32px, letter-spacing et
   line-height ajoutés — absents jusqu'ici). Graisse (var(--fw-black), 900) et couleur
   (var(--text)) déjà conformes à la référence, non modifiées. */
#equipe-admin-content .ck2-hdr { margin-bottom: var(--sp-md); }
#equipe-admin-content .ck2-title {
  font-size: 28px;
  font-weight: var(--fw-black);
  color: var(--text);
  letter-spacing: -.5px;
  line-height: 1.1;
}

/* Bande d'indicateurs horizontale — 3 colonnes, séparateurs fins, sans icône */
#equipe-admin-content .factures-stats-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border: 1px solid var(--b2);
  border-radius: var(--r);
  overflow: hidden;
}
#equipe-admin-content .stat-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-xs);
  padding: var(--sp-lg) var(--sp-md);
  text-align: center;
}
#equipe-admin-content .stat-card + .stat-card { border-left: 1px solid var(--b2); }
#equipe-admin-content .stat-card-value { font-size: var(--fs-xxl); font-weight: var(--fw-black); color: var(--text); }
#equipe-admin-content .stat-card-label { font-size: var(--fs-sm); font-weight: var(--fw-normal); color: var(--text2); }
#equipe-admin-content .stat-card-warning .stat-card-value { color: var(--wa); }
#equipe-admin-content .stat-card-danger .stat-card-value { color: var(--er); }

/* Onglets principaux et onglets RH — même vocabulaire : libellé gris,
   actif en couleur accent avec trait accent sous le texte */
#equipe-admin-content .equipe-tabs,
#equipe-admin-content .rh-tabs {
  display: flex;
  gap: var(--sp-lg);
  border-bottom: 1px solid var(--b2);
  margin: var(--sp-lg) 0;
}
#equipe-admin-content .equipe-tab-btn,
#equipe-admin-content .rh-tab-btn {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  padding: var(--sp-sm) var(--sp-xs);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--text2);
  cursor: pointer;
  font-family: inherit;
  transition: var(--tr);
}
#equipe-admin-content .equipe-tab-btn:hover,
#equipe-admin-content .rh-tab-btn:hover { color: var(--text); }
#equipe-admin-content .equipe-tab-btn.active,
#equipe-admin-content .rh-tab-btn.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Badge "Bientôt disponible" — pastille encadrée au lieu de texte nu */
#equipe-admin-content .badge-soon {
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  color: var(--text2);
  background: var(--b);
  border-radius: var(--r-full);
  padding: var(--sp-xs) var(--sp-sm);
  white-space: nowrap;
}

/* Blocs "Bientôt disponible" — encadré discret */
#equipe-admin-content .equipe-coming-soon {
  border: 1px solid var(--b2);
  border-radius: var(--r);
  padding: var(--sp-lg);
  background: var(--bg);
}
#equipe-admin-content .equipe-coming-soon-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-sm);
  font-size: var(--fs-md);
  font-weight: var(--fw-semibold);
  color: var(--text);
}
#equipe-admin-content .equipe-coming-soon-desc {
  margin-top: var(--sp-sm);
  font-size: var(--fs-sm);
  color: var(--text2);
  line-height: var(--lh-body);
}

/* Bouton principal — largeur contrainte au contenu, pas étiré */
#equipe-admin-content #rh-page-actions .btn-primary { width: auto; padding: 0 var(--sp-xl); }
#equipe-admin-content #rh-page-actions .btn-primary:hover { opacity: .92; }

/* Bouton "Fiche" et cartes employé — états de survol */
#equipe-admin-content .btn-secondary:hover { background: var(--b); }
#equipe-admin-content .employe-card:hover { border-color: var(--accent); }

/* Carte employé — mise à plat horizontale, compacte (LOT 9).
   css/mobile/equipe.css:88 ne déclare pas display:flex sur .employe-card :
   ses enfants restent empilés en bloc, d'où la hauteur ~200px constatée.
   .employe-info a déjà flex:1 (equipe.css:91), actif dès que le parent
   devient flex. */
#equipe-admin-content .employe-card {
  display: flex;
  align-items: center;
  gap: var(--sp-md);
  padding: var(--sp-sm) var(--sp-md);
  margin-bottom: var(--sp-sm);
}
#equipe-admin-content .employe-info { margin-top: 0; }

/* Seule métadonnée présente dans ce markup : le téléphone
   (js/rh.js:169, .employe-meta > span, affiche "—" si absent — c'est
   l'origine du tiret constaté). Stylée en pastille. Les indicateurs
   heures/congés/statut de la référence mobile n'existent pas dans ce
   markup (ils appartiennent à .eq-stats de js/equipe.js, un autre
   composant, désormais masqué sur desktop) — non ajoutés, non inventés. */
#equipe-admin-content .employe-meta {
  margin-top: var(--sp-xs);
  display: flex;
  gap: var(--sp-xs);
}
#equipe-admin-content .employe-meta span {
  font-size: var(--fs-xs);
  color: var(--text2);
  background: var(--b);
  border-radius: var(--r-full);
  padding: var(--sp-xs) var(--sp-sm);
}

/* Bouton Fiche — compact, aligné à droite : neutralise le flex:1 de
   .btn-secondary (css/mobile/base.css:186) qui l'étirait sur toute la
   largeur puisque .employe-actions est déjà display:flex (equipe.css:95). */
#equipe-admin-content .employe-actions { margin-top: 0; flex-shrink: 0; }
#equipe-admin-content .employe-actions .btn-secondary {
  flex: 0 0 auto;
  height: auto;
  padding: var(--sp-xs) var(--sp-md);
  font-size: var(--fs-sm);
}

/* Paramètres — disposition desktop 2 colonnes (LOT 70). Remplace les 3
   règles ci-dessus (id réel #parametres-page conservé, mais .stg-grid/
   .settings-grid, .stg-panel/.settings-panel, .stg-footer/.settings-footer
   n'existent dans aucun markup réel — les vraies classes produites par
   _stgBuildPage, index.html:5296-5322, sont .stg-ci-list, .stg-fs et
   .stg-fs-foot). #parametres-page a pour enfants directs et FRÈRES
   .stg-cards-pg (en-tête + liste) et les 9 panneaux .stg-fs : il devient
   lui-même la grille, chacun assigné à sa colonne individuellement, sans
   toucher au markup. Les 9 panneaux partagent la même cellule
   (grid-column:2;grid-row:1) : seul celui portant .stg-fs-open
   (display:flex) est visible — .stg-fs-open n'étant retirée des autres
   panneaux que depuis le LOT 70 (index.html, window._stgOpen), sans quoi
   plusieurs auraient pu se superposer simultanément sur desktop. */
/* LOT 70d : gouttière unique (sidebar↔colonne, colonne↔panneau,
   panneau↔bord toutes à var(--sp-xl)) — la colonne 2 passe de
   minmax(0,840px) à 1fr : elle occupe l'espace restant au lieu de
   laisser tout excédent devenir une marge de centrage extérieure plus
   grande que le remplissage latéral (cause du déséquilibre du lot 70c,
   la colonne de gauche « flottant » loin de la sidebar). Plafond global
   porté à 1550px (LOT 70e), même valeur, en dur, que les autres pages
   (devis, factures, cockpit, catalogue, équipe — lignes 790, 932, 1036,
   1135, 1294) pour un alignement identique d'un écran à l'autre ;
   margin-inline:auto ne redevient actif, de façon symétrique, qu'au-delà
   de ce plafond. */
#parametres-page {
  display: grid;
  grid-template-columns: 300px 1fr;
  max-width: 1550px;
  margin-inline: auto;
  gap: var(--sp-xl);
  align-items: start;
  padding-inline: var(--sp-xl);
}
#parametres-page .stg-cards-pg { grid-column: 1; grid-row: 1; }

/* Liste des sections — grille 2×2 mobile (css/mobile/parametres.css:28)
   devenue liste verticale. Largeur fixée à 300px (valeur en dur, aucun
   token adapté) plutôt que var(--sidebar-width, 250px) : la ligne
   affiche un libellé ET un sous-titre, plus exigeant que le libellé
   seul de la sidebar principale de l'application.
   .stg-ci passe de son empilement mobile (icône au-dessus, texte centré,
   flex-direction:column) à une grille 2 colonnes : icône à gauche sur 2
   lignes, libellé et sous-titre empilés à droite, chacun sur une seule
   ligne (nowrap + ellipsis) — la largeur de colonne ne permet pas un
   agencement icône/libellé/sous-titre sur une seule ligne (libellé
   tronqué, sous-titre écrasé). La structure identique pour les 9 lignes
   donne une hauteur uniforme sans valeur forcée.
   .stg-ci-sub passe de var(--text3) (mobile, contraste insuffisant sur
   fond clair) à var(--text2).
   .stg-ci-on (mobile, css/mobile/parametres.css:33) marque déjà la
   section active (bordure + fond teintés) : conservée telle quelle. */
#parametres-page .stg-ci-list { display: flex; flex-direction: column; padding: 0; }
#parametres-page .stg-ci {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: var(--sp-md);
  align-items: center;
  text-align: left;
  padding: var(--sp-sm) var(--sp-md);
}
#parametres-page .stg-ci-ico-wrap { grid-column: 1; grid-row: 1 / 3; }
#parametres-page .stg-ci-lbl { grid-column: 2; grid-row: 1; }
#parametres-page .stg-ci-sub { grid-column: 2; grid-row: 2; color: var(--text2); }
#parametres-page .stg-ci-lbl,
#parametres-page .stg-ci-sub { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#parametres-page .stg-ci-lbl { font-size: var(--fs-md); }
#parametres-page .stg-ci-sub { font-size: var(--fs-sm); }
#parametres-page .stg-card:hover { transform: translateY(-2px); }

/* Bouton Enregistrer du pied de panneau — la surcharge mobile (non
   scopée, css/mobile/parametres.css:21) le peint en vert (réservé aux
   états de succès, ex. facture payée) ; restauré ici en orange accent,
   couleur de l'action primaire ailleurs dans l'application (même
   dégradé que .stg-btn-s de base, css/mobile/parametres.css:19). */
#parametres-page .stg-fs-foot .stg-btn-s {
  background: linear-gradient(135deg, var(--accent-h), var(--accent) 50%, var(--accent-d));
  box-shadow: 0 3px 12px rgba(255, 107, 0, .28);
}

/* Panneaux — position fixe et glissement mobile (css/mobile/parametres.css:
   40-41) neutralisés : panneau normal dans la colonne de droite, largeur
   plafonnée à 840px pour la lisibilité des champs (repère déjà utilisé
   dans ce fichier pour .df-panel/.exp-body). Le pied .stg-fs-foot, simple
   dernier enfant flex de .stg-fs (mobile, lignes 9 et 44), se retrouve de
   fait ancré dans cette même boîte plutôt qu'en barre pleine largeur,
   sans règle séparée nécessaire. Remplissages agrandis (point 7),
   32px/24px au lieu de 14-16px hérités du mobile. */
#parametres-page .stg-fs {
  position: relative;
  inset: auto;
  transform: none;
  transition: none;
  display: none;
  grid-column: 2;
  grid-row: 1;
  background: var(--card);
  border-radius: var(--r);
  box-shadow: var(--sh);
}
#parametres-page .stg-fs.stg-fs-open { display: flex; }
#parametres-page .stg-ph { padding: var(--sp-lg) var(--sp-xl); border-radius: var(--r) var(--r) 0 0; }
#parametres-page .stg-ph-title { font-size: var(--fs-lg); }
#parametres-page .stg-body { padding: var(--sp-xl); }
#parametres-page .stg-card { padding: var(--sp-xl); }
#parametres-page .stg-lbl2 { font-size: var(--fs-sm); }

/* LOT 70d : rythme vertical des formulaires. min-height:32px sur .stg-lbl2
   (mobile, css/mobile/parametres.css:13) gonflait l'écart visuel
   libellé→champ au-delà du gap réel de .stg-fi (5px, ligne 12) ; neutralisé
   ici pour que le gap redevienne l'unique écart mesurable, ramené à
   var(--sp-sm) (8px). .stg-card (ligne 11) régit l'écart entre un champ et
   le libellé suivant, porté à var(--sp-lg) (24px) — même règle que la
   surcharge #stg-body-donnees, spécificité identique, l'emporte par ordre
   de chargement (uniformité entre sections, cf. point 4). */
#parametres-page .stg-lbl2 { min-height: 0; }
#parametres-page .stg-fi { gap: var(--sp-sm); }
#parametres-page .stg-card { gap: var(--sp-lg); }

/* Intertitres de section (.stg-sig-section-lbl, mobile, css/mobile/
   parametres.css:63, réutilisée pour Identité/Assurance/Coordonnées,
   index.html) : premier enfant de .stg-card, l'espace au-dessus est donc
   le padding du haut de la carte (var(--sp-xl), 32px, inchangé — point 4).
   La marge basse mobile (8px) s'ajoutait au gap de .stg-card ci-dessus
   pour l'espace en dessous ; supprimée pour ne laisser que ce gap seul
   (24px), inférieur au padding du haut — rapport désormais inversé. */
#parametres-page .stg-sig-section-lbl { margin-bottom: 0; }

/* Clients */
#clients-mobile-page .cli-body { padding-inline: 32px; }
#clients-mobile-page .cli-row { align-items: center; }
#clients-mobile-page .cli-row:hover { background: var(--b); }
#clients-mobile-page .cli-row-r {
  flex-direction: row !important;
  align-items: center;
  gap: 20px;
  min-width: 300px;
  justify-content: flex-end;
}

/* Commandes (LOT 71) — tout le markup est généré en JS avec styles en
   ligne (js/fournisseurs.js), sans classe sur la plupart des conteneurs
   avant le LOT 66, qui a posé les accroches utilisées ici. Un style en
   ligne l'emportant sur une règle externe sans !important : display et
   padding, fixés en ligne sur .frs-shell/.frs-row, sont surchargés en
   !important ci-dessous ; les propriétés non fixées en ligne (max-width,
   grid-column, background au survol...) n'en ont pas besoin. */
#fournisseurs-page .frs-shell {
  max-width: 1550px;
  margin-inline: auto;
  padding-left: var(--sp-xl) !important;
  padding-right: var(--sp-xl) !important;
}
#fournisseurs-page .frs-sw { max-width: 790px; }

/* Lignes de commandes — colonnes alignées, même principe que .dv-row/
   .fac-row (LOT 62) : .frs-row-left (nouvelle classe, LOT 71, posée sur
   le bloc jusque-là non ciblable regroupant numéro/infos secondaires/
   client) neutralisé en display:contents pour exposer ses enfants comme
   cellules de la grille posée sur .frs-row. Les trois éléments d'action
   (lien PDF, boutons œil/corbeille), frères sans enveloppe commune,
   reçoivent une nouvelle enveloppe .frs-row-actions-wrap (LOT 71) pour
   tenir dans une seule colonne. Espacement vertical réduit (var(--sp-sm)
   au lieu des 10px en ligne). */
#fournisseurs-page .frs-row {
  display: grid !important;
  grid-template-columns: 140px minmax(160px, 1fr) minmax(160px, 1fr) 100px auto;
  gap: var(--sp-md);
  align-items: center;
  padding: var(--sp-sm) 0 !important;
}
#fournisseurs-page .frs-row:hover { background: var(--b); }
#fournisseurs-page .frs-row-left { display: contents; }
#fournisseurs-page .frs-row-num { grid-column: 1; grid-row: 1; }
#fournisseurs-page .frs-row-client { grid-column: 2; grid-row: 1; }
#fournisseurs-page .frs-row-meta { grid-column: 3; grid-row: 1; }
#fournisseurs-page .frs-row-num,
#fournisseurs-page .frs-row-client,
#fournisseurs-page .frs-row-meta { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#fournisseurs-page .frs-row-statut { grid-column: 4; grid-row: 1; justify-self: end; }
#fournisseurs-page .frs-row-actions-wrap { grid-column: 5; grid-row: 1; display: flex; align-items: center; gap: var(--sp-xs); justify-self: end; }

/* Filtre période — bouton et menu déroulant, desktop uniquement (LOT 71),
   même mécanisme que devis/factures (LOT 64/65). #frs-period-wrap est
   masqué par défaut en ligne dans js/fournisseurs.js (style="display:
   none"), révélé ici en !important pour la même raison que les autres
   pages (fichier entièrement contenu dans @media(min-width:1025px)).
   .frs-filter-btn est une classe neuve (aucun équivalent mobile sur cet
   écran, qui n'a jamais eu de barre de filtres) : stylée intégralement
   ici, même langage visuel que .dv-filter-btn/.fac-filter-btn.
   .period-dropdown/.period-opt (non scopées, partagées avec devis/
   factures) déjà complètes. */
#fournisseurs-page #frs-period-wrap { display: inline-block !important; }
#fournisseurs-page .frs-filter-btn {
  flex: none;
  padding: var(--sp-sm) var(--sp-md);
  border-radius: var(--r-full);
  border: 1.5px solid var(--b2);
  background: var(--card);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text2);
  cursor: pointer;
  font-family: inherit;
}
#fournisseurs-page .frs-filter-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
#fournisseurs-page .frs-filter-btn:hover:not(.active) { background: var(--b); }
#fournisseurs-page .frs-period-custom {
  flex-direction: column;
  gap: var(--sp-sm);
  padding: var(--sp-sm) var(--sp-md) var(--sp-md);
  border-top: 1px solid var(--b);
}
#fournisseurs-page .frs-period-custom input[type="date"] {
  height: 36px;
  border: 1.5px solid var(--b2);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-sm);
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--card);
  font-family: inherit;
}
#fournisseurs-page .frs-period-custom button {
  height: 34px;
  border: none;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  font-family: inherit;
}

}

@media (max-width: 1024px) {
  /* LOT 52 : bloc identité sidebar — même raison que ci-dessus, masquage
     sous 1025px impossible depuis l'intérieur du wrapper ≥1025px du fichier.
     #sidebar est déjà display:none sous 768px (css/mobile/layout.css:93) ;
     cette règle est donc utile spécifiquement pour la tablette (769-1024px),
     où #sidebar reste visible (css/style.css:36). */
  #sidebar .sidebar-identity { display: none; }
}

/* SOCLE MODALES GENERIQUE - desktop uniquement (fichier charge >=1025px via link media) - tablette 769-1024px a traiter dans css/tablet/ - mobile non concerne */
.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-md) var(--sp-lg);
  border-bottom: 1px solid var(--b);
}

.btn-close-modal {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text2);
  font-size: var(--fs-lg);
  font-family: inherit;
  cursor: pointer;
}

.btn-close-modal:hover {
  background: var(--b);
  color: var(--text);
}

.modal-body {
  padding: var(--sp-lg);
  overflow-y: auto;
}

.modal-footer,
.modal-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-sm);
  padding: var(--sp-md) var(--sp-lg);
  border-top: 1px solid var(--b);
}

/* HABILLAGE ONBOARDING - desktop uniquement (fichier charge >=1025px via link media) - mobile non concerne */
#onboarding-modal.modal-onboarding {
  align-items: center;
}

#onboarding-modal.modal-onboarding .modal-onboarding-content {
  max-width: 960px;
  margin: 0 auto;
  height: auto;
  min-height: 640px;
  max-height: 85vh;
  border-radius: var(--r-lg);
  box-shadow: var(--sh2);
}

.metiers-grid {
  grid-template-columns: repeat(4, 1fr);
}

.onboarding-actions {
  justify-content: flex-end;
}

.onboarding-actions .btn-primary {
  flex: none;
}

/* OVERLAY EXPORT COMPTABLE - desktop uniquement (fichier charge >=1025px via link media) - mobile non concerne */
/* #mn-export-ov est en position:fixed;inset:0 (css/mobile/export-comptable.css) : il démarre au
   sommet et au bord gauche du viewport, recouvrant le header (#header, css/style.css:21,
   position:fixed;height:var(--header-height)) et la sidebar (#sidebar, css/style.css:34,
   position:fixed;width:var(--sidebar-width)). Même mécanisme déjà utilisé pour d'autres éléments
   fixed en desktop (#vocal-mobile-page ligne ~367, #df-footer ligne ~672 de ce fichier) :
   décaler via les tokens plutôt qu'une valeur en dur. right/bottom (déjà à 0 via inset)
   volontairement non repris ici — bas et centrage réservés à un lot ultérieur. */
#mn-export-ov {
  top: var(--header-height);
  left: var(--sidebar-width);
}

/* .mn-ov-hdr n'est plus utilisée nulle part dans le balisage (retirée de #mn-export-ov puis de
   #mn-frais-ov) — règle de masquage devenue sans objet, retirée. Vérifié par recherche
   exhaustive de .mn-ov-hdr/.mn-ov-back/.mn-ov-title dans index.html avant suppression. */

/* #mn-export-title (anciennement #mn-ov-title-desktop, renommé — visible sur les deux supports
   depuis ce lot, l'attribut "hidden" et le display:block qui le contrait ont été retirés avec
   le balisage index.html). width:100% : occupe toute la largeur disponible (alignée à gauche
   du contenu) plutôt que de se dimensionner sur son texte. */
#mn-export-title {
  width: 100%;
  max-width: 1550px;
  margin-left: auto;
  margin-right: auto;
}

/* Titre — .mn-ov-ph/.mn-ov-ph-title (css/mobile/export-comptable.css, non scopé, classe
   partagée par #mn-export-title ET #mn-frais-title) : une seule règle ici couvre les deux
   écrans, pas de duplication par id nécessaire.
   Alignement sur la référence Commandes (#fournisseurs-page .frs-shell) :
   - padding horizontal porté à var(--sp-xl) (32px), contre 20px en mobile (jamais surchargé
     jusqu'ici) ;
   - padding-top aligné sur max(20px, env(safe-area-inset-top, 0px)), identique à la référence ;
   - padding-bottom ramené à 0 (contre 12px en mobile) : c'est .mn-ov-body (padding-top:16px,
     mobile, inchangé) qui porte à lui seul l'espacement de 16px avant le premier bloc, plutôt
     que de cumuler les deux (12px + 16px = 28px en mobile) — évite de faire porter cet
     espacement par deux éléments distincts pour une seule valeur cible.
   .mn-ov-ph-title n'a pas besoin d'être touchée : taille (28px), graisse (900), couleur
   (var(--text)), letter-spacing (-.5px) et line-height (1.1) sont déjà strictement identiques
   à la référence (css/mobile/export-comptable.css:21) — vérifié avant modification. */
.mn-ov-ph {
  padding-left: var(--sp-xl);
  padding-right: var(--sp-xl);
  padding-top: max(20px, env(safe-area-inset-top, 0px));
  padding-bottom: 0;
}

/* Une seule colonne large (mise en grille à deux colonnes du lot précédent annulée) : .mn-ov-body
   redevient un simple empilement vertical, comme sur mobile (aucun display/grid-* défini ici,
   le display:flex;flex-direction:column hérité du CSS mobile s'applique donc tel quel).
   width:100% ajouté (autorisation explicite, hors périmètre initial de ce lot) : sans cette
   propriété, .mn-ov-body est un enfant flex de #mn-export-ov (flex-direction:column) dont les
   deux marges horizontales sont auto — en flexbox, des marges auto sur l'axe secondaire
   neutralisent le stretch par défaut (align-items:stretch hérité) et l'élément se dimensionne
   alors sur son contenu (ses cartes, elles-mêmes en stretch, se calent sur cette largeur
   réduite) au lieu de remplir le conteneur avant d'être plafonné par max-width. width:100%,
   déjà présent sur #mn-export-title juste au-dessus, force ce remplissage et fait donc
   coïncider les deux bords gauches.
   padding-left/right ajoutés (var(--sp-xl), 32px) : la valeur mobile (16px, css/mobile/
   export-comptable.css:24) n'était jusqu'ici jamais surchargée en desktop. */
.mn-ov-body {
  width: 100%;
  max-width: 1550px;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--sp-xl);
  padding-right: var(--sp-xl);
}

/* .mn-ov-footer, comme .mn-ov-body/#mn-export-title plus haut, est un enfant flex de
   #mn-export-ov (flex-direction:column) avec marges auto : sans width:100%, il se dimensionne
   sur le contenu de ses boutons (flex:1 chacun, mobile) au lieu de remplir le conteneur — les
   boutons se retrouvaient donc centrés au milieu d'un pied rétréci plutôt qu'alignés à droite
   du contenu élargi à 1550px. padding-left/right alignés sur var(--sp-xl), en cohérence avec
   .mn-ov-body. justify-content:flex-end regroupe les boutons contre le bord droit — sans effet
   sur #mn-frais-ov (bouton unique en width:100% mobile, non modifié : il occupe déjà toute la
   largeur utile, rien à "pousser" à droite, voir vérification dans le rapport).
   flex:none sur les boutons (au lieu de flex:1, mobile) : ils ne s'étirent plus pour remplir le
   pied — leur largeur suit désormais leur contenu, plancher à 180px déjà imposé par
   #mn-export-ov .mn-btn-csv/.mn-btn-pdf ci-dessous, ordre et espacement (gap:10px, mobile)
   inchangés. */
.mn-ov-footer {
  width: 100%;
  max-width: 1550px;
  margin-left: auto;
  margin-right: auto;
  padding-left: var(--sp-xl);
  padding-right: var(--sp-xl);
  justify-content: flex-end;
}

.mn-chips-row {
  grid-template-columns: 1fr 1fr 1fr;
}

.mn-kpi-grid {
  /* minmax(0, 1fr) plutôt que 1fr seul : une piste dimensionnée en 1fr a un minimum implicite
     "auto" (taille du contenu), qu'un contenu long peut faire dépasser sa part équitable et
     élargir une colonne au détriment des autres. minmax(0, 1fr) fixe ce minimum à 0 et force
     les quatre colonnes à rester strictement égales, quel que soit le contenu. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* flex:none (au lieu de flex:1, mobile) : avec .mn-ov-footer désormais width:100% (voir
   ci-dessus), flex:1 aurait continué à étirer les deux boutons pour occuper toute la largeur
   du pied élargi — flex:none leur rend leur largeur naturelle (plancher 180px ci-dessous). */
.mn-btn-csv,
.mn-btn-pdf {
  white-space: nowrap;
  flex: none;
}

/* min-width seul, scopé à #mn-export-ov (spécificité ID) : la règle globale
   button:not(.mic-main):not(.bell-btn):not(.btn-edit):not(.vc-mic-main) (css/mobile/layout.css,
   spécificité 0-4-1) impose min-width:44px et l'emportait sur .mn-btn-csv/.mn-btn-pdf seules
   (0-1-0). Un sélecteur ID (1-1-0) l'emporte sur toute combinaison de classes, sans !important. */
#mn-export-ov .mn-btn-csv,
#mn-export-ov .mn-btn-pdf {
  min-width: 180px;
}

.mn-kpi-lbl {
  color: var(--text2);
  text-transform: uppercase;
  letter-spacing: .05em;
}

/* ── LIGNES DU BLOC AVOIRS ÉMIS (.mn-stat-row) — la carte a gardé sa pleine largeur (1550px,
   .mn-ov-body ci-dessus) mais justify-content:space-between (mobile, inchangé) écartait libellé
   et montant sur toute cette largeur. Regroupés à gauche : gap fixe et raisonnable (var(--sp-md),
   16px) entre .mn-stat-key et .mn-stat-val, au lieu du space-between. min-width sur .mn-stat-key
   (100px, aucun token n'existe pour cette largeur de colonne de libellé — valeur en dur signalée,
   même situation déjà rencontrée pour .ag-day plus haut dans ce fichier) : les deux libellés de
   ce bloc ("Total HT"/"Total TTC") tiennent tous deux dans cette largeur, ce qui aligne
   verticalement les montants d'une ligne à l'autre sans les coller au libellé le plus court.
   Ne touche ni .mn-tva-row (détail TVA, traité séparément) ni .mn-apercu-empty/.mn-canet-row. */
.mn-stat-row {
  justify-content: flex-start;
  gap: var(--sp-md);
}
.mn-stat-key {
  min-width: 100px;
  flex-shrink: 0;
}

/* ── BANDEAU CA NET — pleine largeur (comportement par défaut d'un bloc dans .mn-ov-body, non
   modifié), fond dégradé orange repris des mêmes tokens que le bouton Export PDF
   (css/mobile/export-comptable.css : linear-gradient(135deg,#ff9500,#ff6b00)), coins arrondis.
   #ff9500 n'a pas de token exact dans css/tokens.css (--accent-h vaut #FF8533, proche mais pas
   identique) — signalé : --accent-h est repris comme valeur la plus proche disponible plutôt
   que d'écrire #ff9500 en dur. --accent correspond en revanche exactement à #ff6b00. Couleur de
   texte #fff non tokenée : aucun token blanc n'existe dans tokens.css, #fff est déjà utilisé
   ainsi pour le texte de .mn-btn-pdf sur ce même dégradé (css/mobile/export-comptable.css),
   repris ici à l'identique plutôt qu'inventé. */
.mn-canet-row {
  background: linear-gradient(135deg, var(--accent-h), var(--accent));
  border-radius: var(--r-lg);
  border-top: none;
  padding: 20px 24px;
}

.mn-canet-lbl {
  color: #fff;
  text-transform: uppercase;
  letter-spacing: .05em;
}

.mn-canet-val {
  color: #fff;
  font-size: 24px;
}

/* ── DÉTAIL TVA — présentation deux colonnes (taux+compteur à gauche, HT gras puis TTC gris en
   dessous à droite), sans toucher à la structure à 3 spans par ligne (rate/HT/TVA) : elle est
   conservée pour ne rien changer au rendu mobile (les sélecteurs mobiles .mn-tva-row
   span:first-child / span:not(:first-child) ciblent ces 3 spans directs et ne doivent pas se
   mettre à cibler d'autres éléments). La 3e colonne (montant TVA seul) ne fait pas partie de la
   maquette desktop : masquée ici, elle reste inchangée et visible sur mobile.
   .mn-tva-head (ligne d'en-têtes Taux/Base HT/TVA) n'a pas d'équivalent dans la maquette
   (rendu two-colonnes auto-descriptif) : masquée sur desktop, inchangée sur mobile. */
.mn-tva-head {
  display: none;
}

.mn-tva-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.mn-tva-row span:nth-child(3) {
  display: none;
}

.mn-tva-row span:nth-child(2) {
  font-weight: 700;
  color: var(--text);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

/* Compteur de factures et TTC par taux — enfants imbriqués marqués "hidden" dans le HTML généré
   (index.html), masqués sur mobile par la règle native du navigateur [hidden]{display:none}
   (priorité toujours inférieure à une règle d'auteur, ici révélée sans !important).
   Sélecteur .mn-tva-row span.mn-tva-count / .mn-tva-ttc (specificité 0-2-1) requis, et non
   .mn-tva-count seul (0-1-0) : chacun est l'unique enfant-élément de son span (rate/HT), donc
   :first-child de son propre parent — la règle mobile .mn-tva-row span:first-child (0-2-1) les
   cible déjà pour le gras/couleur foncée, et l'emporterait sur un sélecteur moins spécifique. */
.mn-tva-row span.mn-tva-count {
  display: block;
  font-size: 11px;
  font-weight: 500;
  color: var(--text3);
  text-align: left;
}

.mn-tva-row span.mn-tva-ttc {
  display: block;
  font-size: 11px;
  font-weight: 500;
  color: var(--text3);
  text-align: right;
}

/* OVERLAY MES FRAIS - desktop uniquement (fichier chargé >=1025px via link media) - mobile non concerné.
   Même traitement que #mn-export-ov ci-dessus : décalage sous le header/à côté de la sidebar,
   largeur du titre plafonnée et centrée. .mn-ov-body et .mn-ov-footer n'ont pas besoin d'être
   répétés ici : ce sont des sélecteurs de classe partagés (pas scopés par id), déjà appliqués à
   #mn-frais-ov par les règles .mn-ov-body/.mn-ov-footer définies plus haut pour Export comptable —
   même plafond de 1200px, même centrage, pour les deux overlays. Bouton de pied (.mn-btn-generate)
   vérifié : sa règle mobile (#mn-frais-ov .mn-btn-generate, css/mobile/frais.css) est déjà scopée
   par id (spécificité 1-1-0), qui l'emporte déjà sur la règle globale
   button:not(...):not(...):not(...):not(...) (css/mobile/layout.css, 0-4-1) sans intervention ici —
   contrairement à .mn-btn-csv/.mn-btn-pdf (classes seules, 0-1-0) qui avaient dû être re-scopées par
   id plus haut.
   .mn-ov-ph (padding, marge supérieure) et .mn-ov-body (padding horizontal, largeur) sont
   également des classes partagées, déjà alignées sur la référence Commandes par les règles
   posées plus haut pour Export comptable — aucune répétition nécessaire ici non plus.
   Mise à jour (lot formulaire) : .mn-btn-generate gardait jusqu'ici width:100% (mobile), ce qui
   annulait tout effet de justify-content:flex-end posé sur .mn-ov-footer — corrigé ci-dessous. */
#mn-frais-ov {
  top: var(--header-height);
  left: var(--sidebar-width);
}

#mn-frais-title {
  width: 100%;
  max-width: 1550px;
  margin-left: auto;
  margin-right: auto;
}

/* Bouton de pied "Ajouter ce frais" — width:100% (mobile, css/mobile/frais.css, non modifié) le
   faisait remplir tout .mn-ov-footer, rendant sans effet le justify-content:flex-end déjà posé
   sur .mn-ov-footer (règle partagée avec Export comptable, plus haut). width:auto + min-width lui
   rendent une largeur raisonnable et l'alignent sur le bord droit du contenu, même traitement que
   #mn-export-ov .mn-btn-csv/.mn-btn-pdf (min-width:180px) — valeur un peu supérieure ici (220px)
   pour accueillir le texte plus long ("Ajouter ce frais" contre "Export CSV"/"Export PDF"). */
#mn-frais-ov .mn-btn-generate {
  width: auto;
  min-width: 220px;
}

/* ── FORMULAIRE "NOUVEAU FRAIS" — mise en grille ──────────────────────────────────────────
   Carte identifiée par :has(.mn-fr-row2) (pattern déjà utilisé dans ce fichier, ex. ligne ~1240
   #ag-list:has(.ag-month-day-hdr)) faute d'id/classe propre distinguant cette carte de celle de
   la liste "Frais enregistrés" (même classe .mn-ov-card pour les deux, index.html).
   Structure relevée avant écriture (index.html) : la carte contient, à plat, quatre enfants —
   .mn-fr-row2 (contient lui-même deux .mn-fr-field : Date et Catégorie, déjà groupés par le
   balisage), un .mn-fr-field autonome pour la Description, un .mn-fr-field autonome pour le
   Montant HT, un .mn-fr-field autonome pour la TVA (contenant .mn-fr-tva-row). Montant et TVA
   sont bien deux frères distincts, jamais enveloppés ensemble — aucun conteneur commun à leur
   assigner une même ligne autrement qu'en les plaçant explicitmement sur les deux colonnes d'une
   grille posée sur leur parent commun (la carte elle-même) : réalisable en CSS seul via
   grid-column, sans modification du balisage.
   Troisième colonne 1fr ajoutée (au lieu de deux colonnes 1fr symétriques) : avec deux colonnes
   égales (~700px chacune sur une carte à 1550px), Montant (grid-column:1) et TVA (grid-column:2)
   se retrouvaient chacun calés à gauche de leur propre moitié de carte, séparés par un vide de
   plusieurs centaines de pixels. Les deux premières colonnes en max-content se dimensionnent
   désormais sur le contenu réel de Montant et TVA (sans toucher aux largeurs déjà posées sur
   #mn-fr-montant/.mn-fr-tva-row, inchangées) : les deux blocs se rapprochent, séparés par le seul
   column-gap (var(--sp-xl), inchangé). La troisième colonne (1fr) absorbe le reste de la largeur
   de la carte — .mn-fr-row2 et la Description, tous deux en grid-column:1/-1, continuent donc de
   s'étendre sur la largeur complète de la carte (1/-1 désigne toujours la dernière ligne de
   grille, quel que soit le nombre de colonnes), disposition inchangée pour ces deux champs. */
#mn-frais-ov .mn-ov-card:has(.mn-fr-row2) {
  display: grid;
  grid-template-columns: max-content max-content 1fr;
  column-gap: var(--sp-xl);
}

/* Date + Catégorie : déjà groupées par le balisage (.mn-fr-row2), simple passage de l'empilement
   vertical (mobile, grid-template-columns:1fr) à deux colonnes fixes et modestes — une date ou
   une catégorie n'a pas besoin de la largeur d'une des deux colonnes 1fr de la grille du
   formulaire (≈700px chacune sur une carte à 1550px). Occupe les deux colonnes de cette grille
   (grid-column:1/-1) puisqu'elle constitue sa propre sous-grille à 2 colonnes. */
#mn-frais-ov .mn-fr-row2 {
  grid-column: 1 / -1;
  grid-template-columns: 200px 260px;
  column-gap: var(--sp-lg);
}

/* Description : ligne pleine largeur (grid-column:1/-1), mais champ plafonné — une description
   de frais n'a pas besoin de dépasser une longueur de ligne confortable à lire (contre plus de
   1400px aujourd'hui). */
#mn-frais-ov .mn-fr-field:has(#mn-fr-desc) {
  grid-column: 1 / -1;
}
#mn-fr-desc {
  max-width: 600px;
}

/* Montant HT et groupe de TVA : même ligne, sur les deux colonnes de la grille du formulaire.
   Champ Montant plafonné (220px) pour la même raison que Date/Catégorie ci-dessus. */
#mn-frais-ov .mn-fr-field:has(#mn-fr-montant) {
  grid-column: 1;
}
#mn-fr-montant {
  max-width: 220px;
}
#mn-frais-ov .mn-fr-field:has(.mn-fr-tva-row) {
  grid-column: 2;
}

/* Boutons de TVA — ne changent pas de comportement (flex:1 chacun, mobile, non modifié).
   max-width:300px (lot précédent) ne fixait qu'un plafond, sans effet dans une colonne de grille
   en max-content : celle-ci se dimensionne sur le contenu réel de .mn-fr-tva-row (trois boutons
   sans padding propre, ~13px de texte chacun), très en-deçà de 300px — d'où des boutons resserrés
   à ~45px, plus étroits que le champ Montant (220px) juste à gauche. width:220px (au lieu de
   max-width:300px) fixe une taille déterminée : une largeur définie contribue telle quelle au
   calcul max-content de la colonne (contrairement à une largeur intrinsèque plafonnée), ce qui
   porte la colonne TVA à 220px, identique à celle du Montant. flex:1 (mobile, non modifié)
   répartit ensuite cette largeur également entre les trois boutons — mise en forme, hauteur et
   espacement (gap:6px, mobile) inchangés. */
#mn-frais-ov .mn-fr-tva-row {
  width: auto;
  max-width: 100%;
}
/* Boutons de taux : largeur de leur texte, marge intérieure horizontale de 14px (comme « Sans TVA »), largeur minimale 56px ;
   hauteur (40px) et espacement (gap) hérités, inchangés. Ordinateur uniquement (feuille desktop). */
#mn-frais-ov .mn-fr-tva-row .mn-fr-tva-btn {
  flex: 0 0 auto;
  width: auto;
  min-width: 56px;
  padding: 0 14px;
  box-sizing: border-box;
  white-space: nowrap;
}

/* ── LISTE "FRAIS ENREGISTRÉS" — rapprocher montant et libellé ───────────────────────────────
   Même principe que .mn-stat-row (bloc Avoirs émis, Export comptable, plus haut) : ce n'est pas
   un justify-content:space-between qui écarte ici montant et libellé (.mn-fr-item n'en porte pas,
   mobile), mais .mn-fr-info en flex:1 (mobile) qui s'étire pour occuper tout l'espace entre
   l'icône et le montant. flex-grow retiré (flex-shrink conservé, donc la troncature du nom et
   l'ellipsis existants restent actifs si le contenu est long) ; min-width modeste, sur le même
   principe que .mn-stat-key, pour garder les montants à peu près alignés d'une ligne à l'autre
   sans empêcher un libellé plus long de s'étendre au besoin. .mn-fr-del récupère l'espace
   ainsi libéré via margin-left:auto (technique standard flexbox), ce qui le maintient calé au
   bord droit de la carte — seul élément qui a réellement besoin d'y rester ancré. */
#mn-frais-ov .mn-fr-info {
  flex: 0 1 auto;
  min-width: 140px;
}

/* Une fois .mn-fr-info privée de sa croissance ci-dessus, le montant se retrouvait immédiatement
   collé au libellé (seul le gap uniforme de .mn-fr-item, 12px mobile, les séparait — le même
   espacement qu'entre icône et libellé). margin-left ajouté spécifiquement sur .mn-fr-amt, sans
   toucher au gap partagé par les autres enfants de la ligne : le montant se lit à une distance
   raisonnable du libellé, ni collé, ni renvoyé à l'autre bout — le bouton de suppression, lui,
   garde .mn-fr-del{margin-left:auto} ci-dessous et récupère naturellement moins d'espace
   résiduel côté droit, la ligne se répartit donc plus régulièrement dans son ensemble. */
#mn-frais-ov .mn-fr-amt {
  margin-left: var(--sp-lg);
}
#mn-frais-ov .mn-fr-del {
  margin-left: auto;
}
