/* =====================================================================
   GREEN VISION — Interface tablette
   =====================================================================
   Conçue pour un usage tactile debout chez le client : cibles larges,
   contrastes forts (écran vu en plein jour), pas de survol requis.
   ===================================================================== */

/* Les polices sont hébergées avec l'application (fonts/, licence OFL) : elles marchent hors
   ligne, et aucune adresse IP ne part chez un service tiers. Lexend (le dessin du mot
   ExcelPros) pour les titres et les chiffres clés ; Atkinson Hyperlegible, conçue pour
   la basse vision, pour le texte : plein soleil, gants, écran sale. */
@font-face { font-family: 'Atkinson Hyperlegible'; font-weight: 400; font-style: normal; font-display: swap; src: url('../fonts/atkinson-400.woff2') format('woff2'); }
@font-face { font-family: 'Atkinson Hyperlegible'; font-weight: 700; font-style: normal; font-display: swap; src: url('../fonts/atkinson-700.woff2') format('woff2'); }
@font-face { font-family: 'Lexend'; font-weight: 100 900; font-style: normal; font-display: swap; src: url('../fonts/lexend.woff2') format('woff2'); }

:root {
  --font-texte: 'Atkinson Hyperlegible', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-titre: 'Lexend', var(--font-texte);
  --ink:        #0f2430;
  --ink-soft:   #50606a;
  --forest:     #1f4630;
  --forest-2:   #163524;
  --leaf:       #2f7a4f;
  --leaf-2:     #3f9760;
  --leaf-pale:  #e7f2ea;
  --sky:        #2c6e8c;
  --sky-pale:   #e5f0f4;
  --ember:      #a8501f;
  --ember-pale: #f6e7dd;
  --alerte:     #b3261e;
  --alerte-pale: #fdecea;
  --paper:      #f4f6f5;
  --surface:    #ffffff;
  --line:       #dde4e2;
  /* Une ombre à peine posée : les cartes tiennent par leur filet, pas par un halo. */
  --ombre:      0 1px 2px rgba(15,36,48,.06);
  --radius:     12px;
  /* Cible tactile minimale confortable */
  --touch:      48px;

  /* Le sens des couleurs d'état : gris = à venir ou clos, bleu = en cours ou envoyé,
     vert = fait ou signé, ambre = une action attend, rouge = refusé ou en erreur. */
  --etat-neutre: #46555c;    --etat-neutre-fond: #eceff0;
  --etat-info: #1a5f84;      --etat-info-fond: #e3f0f7;
  --etat-ok: #12684d;        --etat-ok-fond: #e1f2ea;
  --etat-attention: #9a4a00; --etat-attention-fond: #fdecdc;
  --etat-erreur: #a3221b;    --etat-erreur-fond: #fbe6e4;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* L'attribut hidden doit toujours l'emporter.
   Plusieurs éléments ne sont masqués que par lui : l'écran de connexion
   une fois l'utilisateur identifié, et les onglets interdits à son rôle.
   Une règle d'auteur comme « .connexion { display: flex } » suffit à
   neutraliser le display:none du navigateur — l'écran de connexion
   restait alors affiché par-dessus l'application, donnant l'impression
   que le bouton « Se connecter » ne faisait rien. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  font-family: var(--font-texte);
  font-variant-numeric: tabular-nums;
  background: var(--paper);
  color: var(--ink);
  font-size: 16px;
  line-height: 1.5;
  overscroll-behavior: none;
}

button, input, select, textarea { font: inherit; }
img { max-width: 100%; display: block; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------- Structure ---------- */
/* TROIS HAUTEURS, ET LA DERNIÈRE COMPRISE L'EMPORTE.

   Un navigateur ignore une déclaration qu'il ne sait pas lire : les
   trois lignes se relaient donc du plus ancien au plus juste.

   `100dvh` est la bonne valeur — la hauteur RÉELLEMENT visible, barres
   du navigateur déduites — mais elle n'existe pas sur iOS avant 15.4.
   Là, `100vh` vaut la hauteur de l'écran BARRES COMPRISES : la colonne
   déborde par le bas d'une soixantaine de pixels, et la barre de
   navigation se retrouve hors de l'écran. C'est le défaut signalé sur
   iPhone — les boutons ne sont plus en bas.

   `-webkit-fill-available` comble cet intervalle : ces iOS-là le
   comprennent, et il vaut la hauteur utile. Partout ailleurs, `100dvh`
   passe après et gagne. */
.app {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: -webkit-fill-available;
  height: 100dvh;
}

/* L'EN-TÊTE PASSAIT SOUS L'ENCOCHE, FAUTE DE CE CALCUL.

   L'application se déclare en plein écran sur iOS — `black-translucent`
   dans index.html — et la barre d'état y devient transparente : le
   contenu commence AU BORD de l'écran, donc sous la caméra. La barre du
   bas était protégée depuis toujours ; celle du haut ne l'a jamais été,
   et le logo disparaissait derrière l'encoche.

   Les côtés comptent autant : téléphone couché, l'encoche mange la
   gauche ou la droite selon le sens de rotation. Le repli `0` sert
   partout ailleurs, où ces variables n'existent pas. */
.barre-haut {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(10px + env(safe-area-inset-top, 0)) 16px 10px;
  padding-left:  calc(16px + env(safe-area-inset-left, 0));
  padding-right: calc(16px + env(safe-area-inset-right, 0));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.barre-haut .logo { height: 38px; width: 38px; object-fit: contain; }
/* Avec un logo déposé (theme.js › appliquerLogo), l'image remplace les initiales. */
.barre-haut .monogramme.avec-logo {
  background-color: #fff; background-size: cover; background-position: center;
  color: transparent; border: 1px solid var(--line);
}
/* Le monogramme de l'entreprise : ses initiales, à sa couleur (theme.js › initiales). */
.barre-haut .monogramme {
  display: flex; align-items: center; justify-content: center;
  border-radius: 10px; background: var(--forest); color: #fff;
  font-weight: 700; font-size: .95rem; letter-spacing: .02em; flex-shrink: 0;
  user-select: none; -webkit-user-select: none;
}
.barre-haut .titre { font-family: var(--font-titre); font-weight: 600; color: var(--forest-2); font-size: 1.05rem; margin: 0; line-height: 1.2; }
.barre-haut .espace { flex: 1; }

/* Indicateur de synchronisation */
/* Icone seule, discrete : ce bouton sert rarement, et n'a pas a rivaliser
   avec l'etat de synchronisation qui, lui, se regarde en passant. */
.bouton-signaler {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  color: var(--ink-soft);
  flex-shrink: 0;
}
.bouton-signaler svg { width: 19px; height: 19px; }
.bouton-signaler:active { background: var(--leaf-pale); color: var(--forest); }

.etat-sync {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .82rem;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--leaf-pale);
  color: var(--forest);
  font-weight: 600;
}
.etat-sync .point { width: 9px; height: 9px; border-radius: 50%; background: var(--leaf); }
.etat-sync[data-etat="hors_ligne"] { background: #fdf0e3; color: var(--ember); }
.etat-sync[data-etat="hors_ligne"] .point { background: var(--ember); }
.etat-sync[data-etat="en_cours"] .point { animation: pulse 1s ease-in-out infinite; }
.etat-sync[data-etat="echec"] { background: #fbe9e7; color: var(--alerte); }
.etat-sync[data-etat="echec"] .point { background: var(--alerte); }
@keyframes pulse { 50% { opacity: .35; } }

.contenu { flex: 1; overflow-y: auto; padding: 20px 16px 90px; -webkit-overflow-scrolling: touch; }
.contenu-large { max-width: 900px; margin: 0 auto; }

/* ---------------------------------------------------------------
   LE DEVIS QU'ON A LAISSÉ OUVERT
   ---------------------------------------------------------------
   On quitte rarement un devis pour de bon : on va voir un prix au
   catalogue, une adresse chez le client, une date au planning — et
   on revient. Sans cette barre, il faut retourner dans la liste et
   rechercher le devis à chaque fois.

   Elle se pose au-dessus de la navigation, donc sous le pouce, et
   sur tous les écrans sauf le devis lui-même.
   --------------------------------------------------------------- */
.bandeau-reprise {
  display: flex;
  align-items: stretch;
  background: var(--leaf-pale);
  border-top: 1px solid var(--leaf);
  flex-shrink: 0;
}
.bandeau-reprise[hidden] { display: none; }
.bandeau-reprise #reprise-ouvrir {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 8px 14px;
  border: none;
  background: none;
  color: var(--forest);
  font: inherit;
  font-weight: 600;
  font-size: .86rem;
  text-align: left;
  cursor: pointer;
}
.bandeau-reprise #reprise-ouvrir svg { width: 19px; height: 19px; flex-shrink: 0; }
/* Le numéro et le client sur une seule ligne : la barre doit rester
   fine, c'est un rappel, pas un écran. */
.bandeau-reprise #reprise-texte {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bandeau-reprise #reprise-fermer {
  width: 46px;
  border: none;
  background: none;
  color: var(--ink-soft);
  cursor: pointer;
}
.bandeau-reprise #reprise-fermer svg { width: 17px; height: 17px; }

/* « Nouvelle version disponible » — voir js/mise-a-jour.js. Même place
   que le rappel du devis : visible, sans masquer l'écran. */
.bandeau-maj {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 6px 8px 6px 14px;
  background: var(--surface);
  border-top: 1px solid var(--line);
  color: var(--ink);
  font-size: .86rem;
  font-weight: 600;
  flex-shrink: 0;
}
.bandeau-maj span { flex: 1; }
.bandeau-maj .btn { min-height: 38px; padding: 6px 14px; font-size: .86rem; }
/* L'annonce de l'éditeur (maintenance, nouveauté) : même place, teinte selon l'urgence. */
.bandeau-annonce span { font-weight: 500; line-height: 1.35; }
.bandeau-annonce-maintenance, .bandeau-annonce-urgent { background: var(--alerte-pale); color: var(--alerte); }

/* Navigation basse, atteignable au pouce */
.barre-nav {
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--line);
  flex-shrink: 0;
  padding-bottom: env(safe-area-inset-bottom, 0);
  /* Le directeur a huit onglets. Sur un téléphone de 390 px, huit
     boutons à parts égales laissent 48 px chacun — « Entretiens » n'y
     tient pas et chevauche son voisin. La barre défile donc au doigt
     dès qu'elle déborde, au lieu d'écraser les libellés. Tant qu'il
     reste de la place, rien ne change : min-width n'entre en jeu qu'une
     fois l'espace épuisé. */
  overflow-x: auto;
  scrollbar-width: none;
}
.barre-nav::-webkit-scrollbar { display: none; }
.barre-nav button {
  flex: 1 0 auto;
  min-width: 64px;
  min-height: 62px;
  border: none;
  background: none;
  color: var(--ink-soft);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: .72rem;
  font-weight: 600;
  cursor: pointer;
  border-top: 3px solid transparent;
}
.barre-nav button svg { width: 23px; height: 23px; }
.barre-nav button[aria-current="page"] { color: var(--forest); border-top-color: var(--leaf); }
/* Libellés lisibles : 13 px au lieu de 11,5 (quatre onglets au plus sur un téléphone). */
.barre-nav button { font-size: .8125rem; }

/* Téléphone : les onglets rangés derrière « Plus » ne sont pas dans la barre. */
.barre-nav button.nav-secondaire { display: none; }

/* « Plus » : les autres onglets, en grosses touches. */
.plus-grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.plus-entree {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  min-height: 104px; padding: 14px 8px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--forest-2);
  font: inherit; font-weight: 700; cursor: pointer;
}
.plus-entree svg { width: 30px; height: 30px; }
.plus-entree:active { transform: scale(.98); }

/* ORDINATEUR : le menu passe sur le côté. Tous les onglets y tiennent, avec leur nom
   écrit, et le contenu retrouve toute la hauteur de l'écran. */
@media (min-width: 1024px) {
  .app { padding-left: 104px; }
  .barre-nav {
    position: fixed; left: 0; top: 0; bottom: 0; width: 104px; z-index: 5;
    flex-direction: column; gap: 2px; padding: 14px 6px;
    border-top: none; border-right: 1px solid var(--line);
    overflow-x: hidden; overflow-y: auto;
  }
  .barre-nav button, .barre-nav button.nav-secondaire {
    display: flex; flex: 0 0 auto; width: 100%; min-width: 0; min-height: 66px;
    border-top: none; border-left: 3px solid transparent; border-radius: 10px;
  }
  .barre-nav button[hidden] { display: none; }
  .barre-nav button.nav-plus { display: none; }
  .barre-nav button[aria-current="page"] { border-left-color: var(--leaf); background: var(--leaf-pale); }
  .barre-nav button:hover { background: var(--leaf-pale); }
  .contenu { padding-bottom: 28px; }
}

/* ---------- « Aujourd'hui » : l'accueil des gens de terrain ---------- */
.aujourdhui-date { margin: 0 0 12px; font-weight: 700; color: var(--forest-2); font-size: 1.05rem; }
.aujourdhui-hero {
  background: var(--ink); color: #fff; border-radius: 16px; padding: 20px;
  display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px;
}
.aujourdhui-hero h2 { margin: 0; color: #fff; font-size: 1.5rem; line-height: 1.2; }
.aujourdhui-hero p { margin: 0; }
.aujourdhui-quand { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 1.15rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.aujourdhui-jour { padding: 3px 10px; border-radius: 99px; background: rgba(255,255,255,.16); font-size: .85rem; }
.aujourdhui-client { font-size: 1.1rem; }
.aujourdhui-adresse { font-size: 1.15rem; font-weight: 700; padding: 10px 12px; border-radius: 10px; background: rgba(255,255,255,.1); }
.aujourdhui-actions { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 10px; margin-top: 4px; }
.aujourdhui-actions .btn { min-height: 56px; display: flex; align-items: center; justify-content: center; gap: 8px; text-decoration: none; font-size: 1.05rem; }
.aujourdhui-actions svg { width: 22px; height: 22px; }
.aujourdhui-actions .aujourdhui-principal { background: #fff; color: var(--ink); }
.aujourdhui-actions .aujourdhui-secondaire { background: transparent; color: #fff; border: 1.5px solid rgba(255,255,255,.45); }
.aujourdhui-ouvrir { min-height: 56px; font-size: 1.05rem; }
.aujourdhui-titre { margin: 22px 0 8px; font-size: 1.1rem; color: var(--forest-2); }
.aujourdhui-ligne {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: 14px 16px; margin-bottom: 8px; min-height: 56px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--ink);
  font: inherit; cursor: pointer;
}
.aujourdhui-ligne-quand { font-weight: 700; font-variant-numeric: tabular-nums; }
.aujourdhui-ligne-titre { color: var(--ink-soft); }

/* ---------- Typographie ---------- */
h1 { font-family: var(--font-titre); font-size: 1.5rem; margin: 0 0 16px; color: var(--forest-2); }
h2 { font-family: var(--font-titre); font-weight: 600; font-size: 1.2rem; margin: 26px 0 12px; color: var(--forest-2); }
h3 { font-family: var(--font-titre); font-weight: 600; font-size: 1rem; margin: 0 0 6px; }
a { color: var(--forest-2); }
p  { margin: 0 0 12px; }
.discret { color: var(--ink-soft); font-size: .88rem; }
.lien-client { cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }

/* ---------- Cartes ---------- */
.carte {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 12px;
  box-shadow: var(--ombre);
}
.carte-action { cursor: pointer; }
.carte-action:active { transform: scale(.995); }

/* Le titre d'une catégorie du sommaire des Réglages. */
.titre-reglages {
  margin: 24px 0 8px;
  font-family: var(--font-titre);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--forest-2);
}
/* Le bloc qu'une recherche de réglage vient d'ouvrir. */
.reglage-vise {
  background: var(--leaf-pale);
  box-shadow: 0 0 0 3px var(--leaf);
  border-radius: var(--radius);
  transition: background 1s, box-shadow 1s;
  scroll-margin: 16px;
}
/* Ce qu'il faut TOUCHER pour atteindre le réglage : il pulse, pour qu'on
   voie que c'est un geste à faire et non le réglage lui-même. */
.reglage-geste { animation: reglage-pulse 1.1s ease-in-out 3; }
@keyframes reglage-pulse {
  50% { box-shadow: 0 0 0 7px var(--leaf-pale), 0 0 0 9px var(--leaf); }
}
@media (prefers-reduced-motion: reduce) {
  .reglage-geste { animation: none; }
}
.carte .entete { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.carte .nom { font-weight: 700; font-size: 1.02rem; }

/* La vignette photo d'une référence du catalogue. */
.cat-vignette {
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--paper);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.cat-vignette img { width: 100%; height: 100%; object-fit: contain; background: #fff; }

/* ---------- Étiquettes de statut ---------- */
.badge {
  display: inline-block;
  font-size: .74rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
}
.badge-brouillon { background: var(--etat-neutre-fond); color: var(--etat-neutre); }
.badge-envoye { background: var(--etat-info-fond); color: var(--etat-info); }
.badge-accepte { background: var(--etat-ok-fond); color: var(--etat-ok); }
.badge-refuse { background: var(--etat-erreur-fond); color: var(--etat-erreur); }
.badge-planifie { background: var(--etat-neutre-fond); color: var(--etat-neutre); }

/* L'avertissement de tarif ancien — une pastille, pas une note de bas
   de page. Les couleurs viennent du JS (tarif-age.js), qui seul sait si
   le tarif est « ancien » ou « périmé » ; la forme est ici. */
.tarif-age {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 4px 9px;
  border: 1px solid;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 700;
  line-height: 1.2;
}
/* Périmé : un halo en plus. « Ancien », on le note ; « périmé », on ne
   chiffre pas avec avant d'avoir vérifié. */
.tarif-perime { box-shadow: 0 0 0 3px rgba(179, 38, 30, .10); }
.badge-attente { background: var(--etat-attention-fond); color: var(--etat-attention); }

/* La suite de la vie d'un devis : chantier fini, facture, encaissement.
   « À facturer » est le seul qui appelle une action du bureau — c'est le
   seul à porter une couleur qui accroche l'œil. */
.badge-a_facturer { background: var(--etat-attention-fond); color: var(--etat-attention); }
.badge-facture { background: var(--etat-ok-fond); color: var(--etat-ok); }
.badge-clos { background: var(--etat-neutre-fond); color: var(--etat-neutre); }

/* ---------- Boutons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--touch);
  padding: 12px 20px;
  border-radius: 10px;
  border: 1px solid transparent;
  font-weight: 600;
  cursor: pointer;
  background: var(--leaf);
  color: #fff;
}
.btn:active { transform: translateY(1px); }
.btn svg { width: 19px; height: 19px; }
.btn-secondaire { background: var(--surface); color: var(--ink); border-color: var(--line); }
.btn-danger     { background: var(--alerte); color: #fff; }
.btn-bloc       { width: 100%; }
.btn:disabled   { opacity: .45; cursor: not-allowed; }

/* Rangée d'actions.
   Dans une PAGE, elle suit simplement le contenu : on la trouve en bas
   quand on a fini de lire, pas flottant par-dessus. Un bandeau qui
   recouvre les dernières lignes d'un devis pendant qu'on le relit devant
   le client donne l'impression qu'il manque quelque chose. */
.actions-bas {
  display: flex;
  gap: 10px;
  margin-top: 18px;
  padding: 12px 0 0;
  border-top: 1px solid var(--line);
}

/* Dans une FENÊTRE en revanche, elle reste accessible au pouce sans avoir
   à faire défiler jusqu'en bas : le contenu y est court et l'action
   attendue. */
.panneau .actions-bas {
  position: sticky;
  bottom: calc(-22px - env(safe-area-inset-bottom, 0px));
  z-index: 3;
  margin: 0 -18px calc(-22px - env(safe-area-inset-bottom, 0px));
  padding: 12px 18px calc(22px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line);
  /* Opaque : le contenu qui défile dessous ne doit pas se lire à travers. */
  background: var(--paper);
}

/* Les boutons d'une PAGE (fiche client, devis, visite, affaire…) sont en
   haut, à côté de « Retour », et restent là pendant qu'on fait défiler : on
   n'a plus à descendre au bas d'une longue fiche pour enregistrer. Le haut
   négatif rattrape le retrait de .contenu (voir .cat-barre). Fond opaque.
   Seuls les gestes destructifs restent au bas de la page, à l'écart. */
.barre-ecran {
  position: sticky;
  top: -20px;
  z-index: 6;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: -20px -16px 14px;
  padding: 12px 16px 10px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.barre-ecran .btn {
  flex: 0 0 auto !important;
  min-height: 44px;
  padding: 8px 14px;
}
.barre-ecran .btn-principal { margin-left: auto; }

/* En bas de page, à l'écart : ce qu'on ne touche pas par mégarde. */
.actions-page-bas {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 26px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* ---------- Formulaires ---------- */
.champ { margin-bottom: 16px; }
.champ label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: 6px; }
.champ input,
.champ select,
.champ textarea {
  width: 100%;
  min-height: var(--touch);
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  /* 16px minimum : en dessous, iOS zoome automatiquement au focus */
  font-size: 16px;
}
.champ textarea { min-height: 100px; resize: vertical; }
.champ input:focus,
.champ select:focus,
.champ textarea:focus {
  outline: none;
  border-color: var(--leaf);
  box-shadow: 0 0 0 3px var(--leaf-pale);
}
/* Une erreur de saisie se lit sous le champ, en rouge, jusqu'à correction. */
.erreur-champ { color: var(--alerte); font-weight: 700; font-size: .92rem; margin-top: 6px; }
.champ input[aria-invalid="true"],
.champ select[aria-invalid="true"],
.champ textarea[aria-invalid="true"] { border: 2px solid var(--alerte); }

/* Le contour du clavier : visible sur tous les boutons, liens et champs (les champs
   ont déjà le leur ci-dessus ; un bouton vert n'en avait aucun de lisible). */
button:focus-visible, a:focus-visible, select:focus-visible, summary:focus-visible,
[role="button"]:focus-visible, [tabindex]:focus-visible {
  outline: 3px solid var(--sky);
  outline-offset: 2px;
}
.champ-double { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.obligatoire { color: var(--alerte); }

/* Cases à cocher — grandes, faciles à toucher */
.case {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  margin-bottom: 10px;
  cursor: pointer;
}
.case input[type="checkbox"] {
  width: 24px;
  height: 24px;
  margin: 2px 0 0;
  flex-shrink: 0;
  accent-color: var(--leaf);
}
.case .libelle { font-weight: 600; }
.case .explication { font-size: .85rem; color: var(--ink-soft); margin-top: 3px; }
.case.requise { border-color: var(--leaf); background: var(--leaf-pale); }

/* ---------- Recherche ---------- */
.recherche { position: relative; margin-bottom: 16px; }
/* Safari dessine un champ « search » à sa façon (pilule, loupe propre) et
   ignore une partie du CSS : on remet l'apparence à zéro, et le champ
   reçoit ici son style complet, identique à celui des autres champs. */
.recherche input {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-height: var(--touch);
  padding: 11px 14px 11px 44px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  font-size: 16px;
}
.recherche input:focus {
  outline: none;
  border-color: var(--leaf);
  box-shadow: 0 0 0 3px var(--leaf-pale);
}
.recherche input::-webkit-search-decoration,
.recherche input::-webkit-search-results-decoration { -webkit-appearance: none; }
.recherche svg {
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  color: var(--ink-soft);
  pointer-events: none;
}

/* ---------- Lignes de devis ---------- */
.ligne-devis {
  display: grid;
  /* Colonne de la poignée en tête : vide sur un devis en lecture seule ou
     d'une seule ligne, elle ne prend alors aucune largeur — voir
     dessinerLignes() dans ecran-devis.js. */
  grid-template-columns: auto 1fr auto;
  gap: 10px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.ligne-devis:last-child { border-bottom: none; }
.ligne-devis .details { font-size: .85rem; color: var(--ink-soft); }
.ligne-devis .montant { font-weight: 700; text-align: right; white-space: nowrap; }

/* Glisser-déposer des lignes : on prend la poignée, pas la ligne. Un clic
   sur la ligne ouvre l'éditeur, et au doigt la page défile toujours
   quand on la parcourt — seule la poignée refuse le défilement. */
.poignee-ligne {
  display: flex; align-items: center; justify-content: center;
  width: 28px; min-height: 36px; margin-left: -4px;
  color: var(--ink-soft); font-size: 1.1rem; letter-spacing: -2px;
  cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none; border-radius: 6px;
}
.poignee-ligne:hover { background: var(--leaf-pale); color: var(--ink); }
.ligne-devis.survole { border-radius: 8px; }

/* Les photos du produit sous la ligne. Touchée, une photo passe de
   « imprimée sur le devis » à « retirée », et inversement. */
.vignettes-rangee { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.vignette-ligne {
  position: relative; width: 48px; height: 48px; padding: 0;
  border: 2px solid var(--leaf); border-radius: 8px; background: #fff;
  overflow: hidden; cursor: pointer;
}
.vignette-ligne img { width: 100%; height: 100%; object-fit: contain; display: block; }
.vignette-ligne.exclue { border-color: var(--line); }
.vignette-ligne.exclue img { opacity: .3; }
.vignette-ligne.exclue::after {
  content: ''; position: absolute; left: -6px; right: -6px; top: 50%;
  border-top: 2px solid var(--alerte, #b3261e); transform: rotate(-45deg);
}
.vignettes-aide { font-size: .78rem; margin-top: 4px; }

/* Fiche produit (A2) : jamais de prix, la fenêtre s'ouvre au clic. */
.btn-fiche {
  font: inherit; font-size: .78rem; margin-left: 8px; padding: 2px 10px;
  border: 1px solid var(--line); border-radius: 999px; background: #fff; cursor: pointer;
}

.totaux { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 14px; }
.totaux .ligne { display: flex; justify-content: space-between; padding: 5px 0; }
.totaux .ttc {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--forest-2);
  border-top: 1px solid var(--line);
  margin-top: 8px;
  padding-top: 12px;
}

/* ---------- Réglage discret ---------- */
/* Volontairement sans libellé explicite : le client qui regarde l'écran
   ne doit pas comprendre de quoi il s'agit. Le pourcentage n'est jamais
   affiché — seul le total évolue. */
.reglage {
  display: none;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  margin-top: 12px;
  background: #f2f4f1;
  border-radius: 10px;
}
.reglage.ouvert { display: flex; }
.reglage input[type="range"] { flex: 1; accent-color: #8fa89a; height: 28px; }
.reglage .valeur {
  font-variant-numeric: tabular-nums;
  font-size: .82rem;
  color: var(--ink-soft);
  min-width: 54px;
  text-align: right;
}

/* Bloc marge — visible uniquement pour les rôles autorisés */
.bloc-marge {
  background: #f2f4f1;
  border-radius: 10px;
  padding: 12px 14px;
  margin-top: 12px;
  font-size: .88rem;
}
.bloc-marge .ligne { display: flex; justify-content: space-between; padding: 3px 0; }

/* ---------- Signature ---------- */
.zone-signature {
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  height: 220px;
  position: relative;
  overflow: hidden;
}
.zone-signature canvas { width: 100%; height: 100%; display: block; }
.zone-signature .consigne {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-soft);
  pointer-events: none;
  font-size: .9rem;
}
.zone-signature.signee .consigne { display: none; }

/* ---------- Photos ---------- */
.grille-photos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.grille-photos .vignette {
  aspect-ratio: 1;
  border-radius: 10px;
  overflow: hidden;
  background: var(--leaf-pale);
  position: relative;
}
.grille-photos img { width: 100%; height: 100%; object-fit: cover; }
.grille-photos .ajouter {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--line);
  color: var(--leaf);
  cursor: pointer;
}
.grille-photos .ajouter svg { width: 30px; height: 30px; }
@media (max-width: 520px) { .grille-photos { grid-template-columns: repeat(2, 1fr); } }

/* ---------- États vides ---------- */
.vide { text-align: center; padding: 56px 24px; color: var(--ink-soft); }
.vide svg { width: 50px; height: 50px; opacity: .35; margin-bottom: 14px; }

/* ---------- Écran de connexion ---------- */
.connexion {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100dvh;
  padding: 32px 24px;
  max-width: 440px;
  margin: 0 auto;
}
.connexion .logo { width: 104px; height: 104px; margin: 0 auto 20px; border-radius: 24px; box-shadow: 0 1px 3px rgba(15,36,48,.15); }
/* Le logo porte déjà le nom : le titre reste pour les lecteurs d'écran, sans doublon visible. */
.connexion h1 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.signature-editeur { margin: 28px 0 0; text-align: center; color: var(--ink-soft); font-size: .8125rem; }
.signature-editeur::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 99px; background: #ff7e11; }
.connexion h1 { text-align: center; }

/* ---------- Panneau modal ---------- */
.modal {
  position: fixed;
  inset: 0;
  background: rgba(22,33,27,.5);
  display: none;
  align-items: flex-end;
  z-index: 50;
}
.modal.ouvert { display: flex; }
/* La croix reste en haut de la fenêtre quand elle défile ; hauteur nulle
   pour ne pas pousser le titre. */
.modal-fermer { position: sticky; top: -8px; height: 0; z-index: 2; text-align: right; }
.modal-fermer button {
  width: 44px; height: 44px; margin: -14px -10px 0 0;
  border: none; background: transparent; color: var(--ink-soft);
  font-size: 1.8rem; line-height: 1; cursor: pointer;
}
.modal .panneau {
  background: var(--paper);
  width: 100%;
  max-height: 92vh;
  overflow-y: auto;
  border-radius: 20px 20px 0 0;
  padding: 22px 18px calc(22px + env(safe-area-inset-bottom, 0));
}
@media (min-width: 720px) {
  .modal { align-items: center; justify-content: center; }
  .modal .panneau { max-width: 620px; border-radius: 20px; }
}

/* Tiroir du simulateur de primes (ecran-devis.js) : le cadre OPX prend
   tout ce qu'il peut, les données et les boutons tiennent dans une colonne.
   Sous 720 px, feuille du bas ordinaire, blocs empilés. */
.sp-entete { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.sp-entete h2 { margin: 0; }
.sp-outils { display: flex; align-items: center; gap: 8px; margin-right: 36px; }
.sp-outils .btn { padding: 6px 12px; min-height: 0; }
.sp-cadre { position: relative; overflow: auto; -webkit-overflow-scrolling: touch; height: 48vh; max-height: 440px; border: 1px solid var(--line, #d8ddd9); border-radius: 10px; background: #fff; }
.sp-cadre iframe { position: absolute; top: 0; left: 0; border: 0; }
.sp-copie { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line, #e3e7e4); overflow-wrap: anywhere; }
.sp-colonne h3 { margin: 18px 0 6px; }
.sp-copie .btn { white-space: nowrap; flex-shrink: 0; }
/* La zone du PDF de simulation : on y glisse le fichier téléchargé. */
.sp-depot { border: 2px dashed transparent; border-radius: var(--radius, 10px); padding: 6px; margin: 0 -8px; transition: border-color .15s, background .15s; }
.sp-depot.survol { border-color: var(--leaf, #2e7d4f); background: rgba(46, 125, 79, .06); }
.sp-pied { padding-top: 12px; }
.sp-etat { margin: 0; font-weight: 600; color: var(--leaf, #2e7d4f); overflow-wrap: anywhere; }
.sp-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.sp-actions .btn { flex: 1 1 150px; }
.sp-actions #sp-fermer { flex-basis: 100%; }
@media (min-width: 720px) {
  .modal .panneau.plein {
    max-width: none; width: 100vw; height: 100vh; max-height: 100vh;
    border-radius: 0; overflow: hidden;
    display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 18px;
    padding: 14px 18px;
  }
  /* La croix, premier enfant du panneau, ne doit pas prendre une case de la grille. */
  .modal .panneau.plein { position: relative; }
  .sp-tiroir > .modal-fermer { position: absolute; top: 14px; right: 18px; height: auto; }
  .sp-tiroir .sp-cadre-zone { display: flex; flex-direction: column; min-height: 0; }
  .sp-tiroir .sp-cadre { flex: 1; height: auto; max-height: none; }
  .sp-tiroir .sp-colonne { display: flex; flex-direction: column; min-height: 0; padding-top: 40px; }
  .sp-tiroir .sp-colonne-defile { flex: 1; overflow-y: auto; padding-right: 4px; }
  .sp-tiroir .sp-colonne h3:first-child { margin-top: 0; }
}

/* ---------- Message flottant ---------- */
.notification {
  position: fixed;
  left: 50%;
  bottom: 84px;
  transform: translateX(-50%);
  /* Un long message s'étale sur la largeur au lieu de s'empiler dans une
     colonne étroite ; un message court garde sa pastille. */
  width: max-content;
  max-width: calc(100vw - 32px);
  background: var(--forest-2);
  color: #fff;
  padding: 12px 20px;
  border-radius: 22px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  text-align: center;
  box-shadow: var(--ombre);
  z-index: 60;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s;
}
/* Visible, le message se referme d'un toucher (voir notifier()). */
.notification.visible { opacity: 1; pointer-events: auto; cursor: pointer; }
/* Pas seulement la couleur : un daltonien doit voir que c'est une erreur. */
.notification.erreur { background: var(--alerte); }
.notification.erreur::before { content: '⚠ '; }

/* ---------- Planning ---------- */
.nav-semaine {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.nav-semaine .btn { padding: 10px 14px; }
.nav-semaine .intitule { flex: 1; text-align: center; }
/* Téléphone : six commandes ne tiennent pas sur 340 px. Elles se resserrent, et
   l'intitulé garde la place qui reste (il retourne à la ligne plutôt que de pousser
   le bouton d'impression hors de l'écran). */
@media (max-width: 480px) {
  .nav-semaine { gap: 6px; }
  .nav-semaine .btn { padding: 10px 11px; min-width: 44px; }
  .nav-semaine .intitule { min-width: 0; flex: 1 1 0; }
}

.jour { margin-bottom: 18px; }
.entete-jour {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  margin-bottom: 8px;
  border-radius: 10px;
  background: #eef1ec;
  font-weight: 600;
  font-size: .92rem;
  cursor: pointer;
  /* Toucher l'en-tête d'un jour crée une intervention à cette date :
     la cible doit rester confortable au doigt. */
  min-height: var(--touch);
}
.jour.aujourdhui .entete-jour { background: var(--leaf-pale); color: var(--forest); }

/* Statuts d'intervention absents de la première série de badges. */
.badge-en_cours { background: var(--etat-info-fond); color: var(--etat-info); }
.badge-termine { background: var(--etat-ok-fond); color: var(--etat-ok); }
.badge-reporte { background: var(--etat-attention-fond); color: var(--etat-attention); }
.badge-annule { background: var(--etat-neutre-fond); color: var(--etat-neutre); }

/* ---------- Capture vidéo et note vocale ---------- */
.apercu-capture {
  width: 100%;
  aspect-ratio: 16 / 9;
  max-width: 100%;
  border-radius: var(--radius);
  background: #000;
  object-fit: cover;
}

/* Substitut visuel pendant une note vocale : sans image à montrer, une
   pastille qui pulse indique que l'enregistrement tourne réellement. */
.niveau-audio {
  height: 120px;
  border-radius: var(--radius);
  background: var(--leaf-pale);
  display: flex;
  align-items: center;
  justify-content: center;
}
.niveau-audio span {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  background: var(--leaf);
  animation: pulse 1.4s ease-in-out infinite;
}

.minuterie {
  text-align: center;
  font-size: 1.35rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--forest-2);
  margin: 14px 0 4px;
}

/* Vignettes des médias non visuels : icône centrée et durée en surimpression. */
.grille-photos .vignette.media-video,
.grille-photos .vignette.media-audio {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--forest);
  cursor: pointer;
}
.grille-photos .vignette.media-audio { background: var(--sky-pale); color: var(--sky); }
.grille-photos .vignette svg { width: 34px; height: 34px; }
.grille-photos .vignette .duree {
  position: absolute;
  right: 6px;
  bottom: 6px;
  padding: 2px 7px;
  border-radius: 20px;
  background: rgba(22, 33, 27, .72);
  color: #fff;
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
}

/* ---------- Suivi de chantier ---------- */
.avancement {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
}
.avancement .jauge {
  flex: 1;
  height: 9px;
  border-radius: 20px;
  background: #e4e9e2;
  overflow: hidden;
}
.avancement .jauge span {
  display: block;
  height: 100%;
  border-radius: 20px;
  background: var(--leaf);
  transition: width .25s ease;
}
.avancement .valeur {
  font-size: .82rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  min-width: 42px;
  text-align: right;
}

/* Le chantier du jour se repère d'un coup d'œil, tablette à bout de bras. */
.chantier-du-jour { border-left: 4px solid var(--leaf); }

/* Liste des travaux : recopiée du devis, donc déjà mise en forme. */
.liste-travaux { white-space: pre-line; margin: 0; }

/* Couleur d'équipe, dans le planning et la liste des équipes. */
.pastille {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: baseline;
}

/* Boutons de pointage : cibles larges, utilisées avec des gants. */
.grille-actions { display: grid; gap: 10px; }
.grille-actions .btn { min-height: 54px; }

/* Compte désactivé : présent mais visiblement hors service. */
.compte-inactif { opacity: .62; }
.compte-inactif .nom { text-decoration: line-through; }

/* Échéance d'entretien dépassée : l'installation sort de conformité. */
.echeance-retard { border-left: 4px solid var(--alerte); }

/* Bandeau des devis à relancer : présent sans être alarmant. */
.bandeau-relance { border-left: 4px solid var(--ember); }

/* ---------- Chiffres du suivi commercial ---------- */
.chiffres {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 14px;
}
.chiffre {
  background: #f2f4f1;
  border-radius: 10px;
  padding: 12px 10px;
  text-align: center;
}
.chiffre .valeur {
  display: block;
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--forest-2);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.chiffre .libelle {
  display: block;
  font-size: .78rem;
  color: var(--ink-soft);
  margin-top: 3px;
}
@media (max-width: 460px) {
  .chiffres { grid-template-columns: 1fr; }
  .chiffre { display: flex; justify-content: space-between; align-items: baseline; text-align: left; }
  .chiffre .libelle { margin-top: 0; }
}

/* ---------- Classement de l'équipe ---------- */
.rang {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.rang:last-child { border-bottom: none; }

.rang .place {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 50%;
  background: #eef1ec;
  color: var(--ink-soft);
  font-size: .82rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* La tête du classement se repère sans avoir à lire les montants. */
.rang:first-child .place { background: var(--leaf); color: #fff; }

.rang .identite { flex: 1; min-width: 0; }
.rang .identite .nom {
  font-weight: 600;
  font-size: .95rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rang .jauge {
  height: 6px;
  border-radius: 20px;
  background: #e4e9e2;
  overflow: hidden;
  margin-top: 6px;
}
.rang .jauge span { display: block; height: 100%; border-radius: 20px; background: var(--leaf-2); }

.rang .montants { text-align: right; white-space: nowrap; }
.rang .montants .ca { font-weight: 700; font-variant-numeric: tabular-nums; }

.rang-moi { background: var(--leaf-pale); border-radius: 10px; padding-left: 8px; padding-right: 8px; }
.rang-moi .jauge { background: #d3e2d8; }

/* ---------- Dossier client ---------- */

/* Étiquettes libres du profil : repères visuels, pas des statuts. */
.etiquettes { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.etiquette {
  background: var(--sky-pale);
  color: var(--sky);
  border-radius: 20px;
  padding: 3px 10px;
  font-size: .78rem;
  font-weight: 600;
}

dl.profil { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; margin: 0; }
dl.profil dt { color: var(--ink-soft); font-size: .88rem; }
dl.profil dd { margin: 0; font-size: .92rem; }

/* Historique : un fil d'événements, du plus récent au plus ancien. */
.evenement {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}
.evenement:last-child { border-bottom: none; }

.evenement .pictogramme {
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: #eef1ec;
  color: var(--forest);
  display: flex;
  align-items: center;
  justify-content: center;
}
.evenement .pictogramme svg { width: 17px; height: 17px; }

.evenement .corps { flex: 1; min-width: 0; }
.evenement .corps .titre { font-weight: 600; font-size: .95rem; }
.evenement .corps .details { font-size: .85rem; color: var(--ink-soft); }

.evenement .cote { text-align: right; white-space: nowrap; }
.evenement .cote .date { font-size: .82rem; color: var(--ink-soft); }

/* Réserve de réception, échéance dépassée, rappel en retard : ce qui
   demande une action se repère sans lire. */
.evenement-alerte .pictogramme { background: var(--ember-pale); color: var(--ember); }

/* Bandeau des rappels du jour, en tête de la vue Clients. */
.bandeau-rappel { border-left: 4px solid var(--sky); }

@media (max-width: 460px) {
  .evenement .cote .date { font-size: .78rem; }
  dl.profil { grid-template-columns: 1fr; gap: 2px; }
  dl.profil dd { margin-bottom: 8px; }
}

/* ---------- « Mot de passe oublié ? » sous la connexion ---------- */
.aide-connexion { margin-top: 16px; text-align: center; }
.aide-connexion summary {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch);
  padding: 0 12px;
  color: var(--leaf);
  font-weight: 600;
  cursor: pointer;
}
.aide-connexion p { margin: 4px 0 0; text-align: left; }

/* ---------- Champ mot de passe avec bouton « Afficher » ---------- */
/* Taper un mot de passe solide en aveugle sur un clavier tactile est une
   source d'erreur permanente : on se trompe, on recommence, et on finit
   par en choisir un plus court. Le bouton reste discret et n'apparaît
   jamais sur un écran montré au client. */
.champ-mdp { position: relative; }

.champ-mdp input {
  /* Place réservée au bouton, pour que le texte ne passe pas dessous. */
  padding-right: 92px;
}

.voir-mdp {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  min-height: 44px;
  padding: 0 12px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--leaf);
  font-size: .88rem;
  font-weight: 600;
  cursor: pointer;
}

.voir-mdp:active { background: var(--leaf-pale); }

/* ---------- Bascule « Mon équipe / Tous » (écran chantiers) ---------- */
/* Visible seulement pour le directeur, qui reçoit tous les chantiers.
   Une tablette de terrain n'a rien à filtrer : le serveur ne lui envoie
   que les siens. */
.onglets-portee {
  display: flex;
  gap: 6px;
  padding: 4px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
}

.onglets-portee button {
  flex: 1;
  min-height: 42px;
  border: none;
  border-radius: 9px;
  background: transparent;
  color: var(--ink-soft);
  font-size: .9rem;
  font-weight: 600;
  cursor: pointer;
}

.onglets-portee button.actif {
  background: var(--leaf-pale);
  color: var(--forest);
}

/* ---------- Bandeau de simulation de vue ---------- */
/* Volontairement voyant, et non discret : on doit savoir en permanence
   qu'on ne regarde pas l'application normale, sans quoi on prend un
   onglet manquant pour une panne. */
.bandeau-simulation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  background: var(--ember-pale);
  color: var(--ember);
  border-bottom: 1px solid var(--ember);
  font-size: .9rem;
  font-weight: 600;
  flex-shrink: 0;
}

.bandeau-simulation button {
  min-height: 36px;
  padding: 0 14px;
  border: 1px solid var(--ember);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ember);
  font-size: .88rem;
  font-weight: 700;
  cursor: pointer;
}

/* ---------- Déplacer une carte du planning ---------- */
/* La carte quitte le flux et suit le doigt. La place qu'elle occupait
   est tenue par un vide de même hauteur : sans lui, la liste se referme
   sous le doigt et les jours remontent au moment même où l'on vise. */
.en-deplacement {
  position: fixed;
  z-index: 70;
  margin: 0;
  pointer-events: none;
  box-shadow: 0 12px 28px rgba(22, 33, 27, .28);
  transform: scale(1.03);
  opacity: .96;
}
.place-tenue {
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  margin-bottom: 8px;
}
/* La cible visée s'éclaire : on lâche rarement au pixel près.
   Générique, parce que deux écrans s'en servent — un jour du planning,
   un bloc de la bibliothèque de modèles. */
.survole {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
  background: var(--leaf-pale);
}
/* L'appui maintenu doit gagner contre le défilement du navigateur.

   LES TROIS SORTES DE PRISE, ET NON LA SEULE PREMIÈRE. Trois écrans
   emploient le glisser-déposer : le planning déplace des `.deplacable`,
   mais il déplace AUSSI les étiquettes en attente de date
   (`[data-a-planifier]`), et la bibliothèque déplace des
   `.modele-carte`. Ces deux dernières restaient en `touch-action: auto` :
   le doigt y réservait le défilement dans toutes les directions et le
   zoom, et sur iPad le geste mourait avant d'exister. C'est précisément
   l'étiquette qu'on n'arrivait pas à poser sur le planning. */
.deplacable,
.modele-carte,
[data-a-planifier] { touch-action: pan-y; }
.deplacable.en-deplacement { touch-action: none; }

/* Ce qui se prend se dit à la main qui passe : le curseur change, et la
   carte ne se surligne plus comme un paragraphe. Tirer sur une carte
   traînait jusque-là une sélection bleue au lieu de la déplacer. */
.deplacable,
.modele-carte,
[data-a-planifier] {
  cursor: grab;
  /* Sans cela, iOS ouvre son menu « Copier / Partager » au bout d'une
     demi-seconde d'appui — et annule le pointeur en l'ouvrant. Notre
     appui maintenu dure quatre cents millisecondes : les deux se
     croisent, et c'est le menu qui gagnait. */
  -webkit-touch-callout: none;
  user-select: none;
  -webkit-user-select: none;
}
.deplacable.en-deplacement,
.modele-carte.en-deplacement { cursor: grabbing; }

/* Pendant le geste, plus rien ne se surligne nulle part : la souris qui
   tire une carte sélectionnait le texte de tout ce qu'elle survolait. */
body.en-glissement {
  user-select: none;
  -webkit-user-select: none;
  cursor: grabbing;
}

/* Un rendez-vous n'est pas un chantier : un liseré suffit à les
   distinguer d'un coup d'œil, sans deux colonnes ni deux écrans. */
.carte.rendez-vous { border-left: 4px solid var(--sky); }

/* L'heure d'abord, en évidence : c'est ce qu'on cherche en premier sur
   un agenda, et elle se perdait en fin de ligne grise. */
.heure-evenement {
  display: inline-block;
  margin-right: 8px;
  padding: 1px 7px;
  border-radius: 6px;
  background: var(--leaf-pale);
  color: var(--forest);
  font-variant-numeric: tabular-nums;
  font-size: .86rem;
}
.carte.rendez-vous .heure-evenement { background: var(--sky-pale); color: var(--sky); }

/* ---------- La bibliothèque de modèles ---------- */
/* Un bloc par catégorie, qui sert aussi de cible de dépôt : son fond
   plus clair dit qu'on peut y lâcher quelque chose. */
.groupe-modeles {
  background: var(--paper);
  border: 1px solid var(--line);
  margin-bottom: 14px;
}
.groupe-modeles .carte {
  background: var(--surface);
  margin-bottom: 8px;
}
.entete-categorie {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
/* La poignée rend les deux gestes non ambigus : la carte déplace le
   modèle, la poignée déplace toute la catégorie. Sans elle, viser
   l'en-tête pour prendre un modèle emporterait le bloc entier. */
.poignee-categorie {
  cursor: grab;
  color: var(--ink-soft);
  font-size: 1.1rem;
  letter-spacing: -2px;
  padding: 6px 4px;
  user-select: none;
}

/* ---------- Ce qui attend une date ---------- */
/* Un liseré ambre, comme le bandeau des relances : c'est la même nature
   d'information — quelque chose qui devrait déjà être fait. */
.bandeau-a-planifier {
  border-left: 4px solid var(--ember);
  background: var(--ember-pale);
}
.bandeau-a-planifier .carte { background: var(--surface); }

/* ---------- Un conflit de planning ---------- */
/* Signalé, jamais bloquant : on envoie parfois sciemment deux équipes au
   même endroit. Une application qui refuse se fait contourner ; une
   application qui prévient se fait lire. */
.conflit {
  color: var(--alerte);
  font-size: .85rem;
  font-weight: 600;
  margin-top: 2px;
}
/* Au mois, un jour sans rien n'est qu'une ligne — mais il reste une
   cible de dépôt : masquer les jours vides interdirait d'y déplacer quoi
   que ce soit. */
.jour.jour-vide { padding-bottom: 4px; opacity: .72; }

/* ---------- Sections repliables de l'écran commercial ---------- */
/* L'écran empilait tout : plusieurs milliers de pixels sur un téléphone.
   Restent ouverts ce sur quoi on agit ; le reste se déplie. */
.titre-repliable {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  cursor: pointer;
  min-height: var(--touch);
  user-select: none;
}
.titre-repliable .chevron { color: var(--ink-soft); font-size: .9rem; }

/* ---------- Raccourcis de saisie d'un contact ---------- */
/* Six champs après un appel de trois minutes, on saute l'étape. */
.raccourcis-contact {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.raccourcis-contact .btn { flex: 1 1 45%; min-height: 44px; }
.raccourcis-contact .choisi {
  background: var(--leaf);
  border-color: var(--leaf);
  color: #fff;
}

/* ---------- Résultat du dimensionnement ---------- */
/* Le chiffre qui part dans la note remise au client : il doit se lire
   d'un coup d'œil, pendant la visite, sur la table de la cuisine. */
.resultat-dimensionnement {
  margin-top: 12px;
  padding: 12px;
  border: 2px solid var(--leaf);
  border-radius: var(--radius);
  background: var(--leaf-pale);
}

/* Les types de projet d'une visite : des cases, plusieurs à la fois. */
.types-projet-choix {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 8px;
}

/* Les données d'une étude : deux ou trois colonnes quand la place le
   permet, une seule sur téléphone. */
.grille-etude {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  column-gap: 12px;
}
.grille-etude .champ { margin-bottom: 10px; }


/* =====================================================================
   LE PLANNING SUR UN ÉCRAN HORIZONTAL
   =====================================================================
   En une colonne, deux jours consécutifs ne tiennent jamais ensemble à
   l'écran : on ne peut pas faire glisser une carte vers une cible qu'on
   ne voit pas. Les jours passent donc en colonnes — mais seulement quand
   il y a de quoi les loger.

   1200 px, et non 900 : sous ce seuil, sept colonnes et la file
   d'attente laissent moins de cent pixels par jour. Un jour de cent
   pixels affiche un mot par ligne et deux barres de défilement ; c'est
   pire que la pile verticale qu'il remplace.
   ===================================================================== */
@media (min-width: 1200px) {
  /* Le planning prend toute la largeur, comme le récapitulatif d'import
     de catalogue : ce sont des tableaux, pas du texte. Les autres écrans
     restent bridés à 900 px — une ligne de deux mille pixels ne se lit
     pas. */
  .contenu-large.pleine-largeur { max-width: none; }

  /* Le nombre de colonnes vient du rendu : six jours d'ordinaire, sept
     quand un dimanche porte quelque chose, sept toujours au mois — un
     calendrier qui saute une colonne ne se lit plus. */
  .grille-jours.portee-semaine,
  .grille-jours.portee-mois {
    display: grid;
    grid-template-columns: repeat(var(--colonnes, 7), minmax(0, 1fr));
    gap: 8px;
    align-items: start;
  }

  /* Chaque colonne garde sa place même vide, et DÉFILE POUR ELLE-MÊME
     avec une hauteur bornée : sans cela, un jeudi chargé étire sa
     colonne sur des milliers de pixels tandis que les autres s'arrêtent
     bien plus haut, et la cible n'est plus sous le doigt à la hauteur où
     l'on tient la carte.

     `overflow: hidden auto` et non `overflow-y: auto` : dès qu'un seul
     axe cesse d'être visible, l'autre devient automatique — d'où les
     barres horizontales apparues sous chaque jour. */
  .grille-jours.portee-semaine .jour,
  .grille-jours.portee-mois .jour {
    margin: 0;
    min-height: 96px;
    max-height: calc(100vh - 330px);
    overflow: hidden auto;
  }

  .grille-jours .entete-jour {
    position: sticky;
    top: 0;
    background: var(--surface);
    z-index: 2;
  }

  /* Dans une colonne étroite, un numéro de devis est un mot de vingt
     caractères : sans coupure, il déborde et fait apparaître une barre
     de défilement par carte. */
  .grille-jours .carte {
    padding: 9px 10px;
    margin-bottom: 7px;
    font-size: .88rem;
    overflow-wrap: anywhere;
  }
  .grille-jours .carte .nom { font-size: .92rem; line-height: 1.3; }
  .grille-jours .carte .entete { flex-direction: column; gap: 4px; align-items: stretch; }
  .grille-jours .discret { font-size: .8rem; }

  .grille-jours.portee-mois .jour { min-height: 120px; }

  /* Une journée seule n'a pas à s'étaler sur toute la largeur d'un
     écran de deux mille pixels : on la garde lisible. */
  .grille-jours.portee-jour { max-width: 760px; }
}

/* Case laissée vide en début de mois, pour que chaque jour tombe dans sa
   colonne. Elle n'existe qu'en disposition en colonnes. */
.case-vide { display: none; }
@media (min-width: 1200px) {
  .grille-jours.portee-mois .case-vide { display: block; }
}

/* La file d'attente ne passe À CÔTÉ des jours que s'il reste de quoi
   loger sept colonnes lisibles. Entre 1200 et 1600 px, elle leur
   volerait deux cent quatre-vingts pixels — soit quarante par jour, et
   des titres qui s'écrivent un mot par ligne. Elle reste alors
   au-dessus, sur toute la largeur. */
@media (min-width: 1600px) {
  .planning-corps {
    display: grid;
    grid-template-columns: minmax(230px, 300px) minmax(0, 1fr);
    gap: 16px;
    align-items: start;
  }

  /* Sous les yeux pendant qu'on répartit : c'est d'elle qu'on tire les
     chantiers à poser. */
  .planning-attente { position: sticky; top: 0; }
}

/* Ce qu'un jour masqué emportait avec lui.

   La colonne s'en va pour de bon — un réglage qui laisse en place ce
   qu'on décoche ne règle rien — mais le travail qui s'y trouvait ne
   doit pas disparaître en silence. */
.bandeau-masque {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
  padding: 10px 14px;
  border-radius: 10px;
  border-left: 4px solid var(--ember);
  background: var(--ember-pale);
  font-size: .9rem;
}
.bandeau-masque span { flex: 1; }
.bandeau-masque button { flex: 0 0 auto; }

/* Le réglage des jours affichés, posé sur l'écran plutôt que dans un
   menu lointain : c'est en regardant sa semaine qu'on s'aperçoit qu'une
   colonne ne sert à rien. */
.jours-affiches {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.jours-affiches .case { margin: 0; }

/* =====================================================================
   LA GRILLE HORAIRE
   =====================================================================
   Une pile de cartes dit ce qu'il y a dans la journée ; elle ne dit pas
   si la journée est pleine. Trois chantiers d'une heure et trois poses
   de six heures font la même pile — et l'un des deux plannings est
   intenable.
   ===================================================================== */

/* Le rail des heures occupe une colonne de plus, à gauche des jours.

   La disposition en grille est déclarée ICI et pas seulement dans la
   règle des écrans larges : celle-là ne vise que la semaine et le mois,
   et la vue du JOUR — qui porte des heures jusque sur un téléphone —
   laissait alors le rail s'empiler AU-DESSUS de la journée, ses
   graduations alignées à droite de l'écran, à cent lieues des cartes
   qu'elles datent. */
.grille-jours.avec-rail {
  display: grid;
  grid-template-columns: 52px repeat(var(--colonnes, 7), minmax(0, 1fr));
  gap: 8px;
  align-items: start;
}

.rail-heures {
  position: relative;
  margin-top: 46px; /* aligné sous les en-têtes de jour */
}
.rail-heures .graduation {
  position: absolute;
  right: 8px;
  transform: translateY(-50%);
  font-size: .76rem;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* La journée devient un axe du temps. Les cartes s'y posent à leur
   heure, hautes de leur durée : les trous se voient. */
.jour.avec-heures { display: flex; flex-direction: column; }
.jour.avec-heures .grille-heures { position: relative; }

.trait-heure {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px solid var(--line);
  pointer-events: none;
}

/* Deux équipes parties à la même heure se rangent côte à côte : sans
   cela elles se recouvrent exactement, et la seconde disparaît — or le
   chevauchement est justement ce qu'on vient vérifier. */
.creneau {
  position: absolute;
  padding-right: 3px;
  overflow: hidden;
}
.creneau .carte {
  height: 100%;
  margin: 0;
  overflow: hidden;
  padding: 6px 8px;
  font-size: .82rem;
}
.creneau .carte .nom { font-size: .84rem; line-height: 1.25; }
.creneau .carte .entete { flex-direction: column; gap: 2px; align-items: stretch; }
.creneau .avancement, .creneau .badge { display: none; }

/* Une carte étroite (deux événements se partagent une colonne de jour) ne coupe
   plus ses mots en deux : l'heure tient sur une ligne, le titre aussi, et le
   détail s'ouvre en touchant la carte. */
.creneau { container-type: inline-size; }
@container (max-width: 130px) {
  .creneau .carte { padding: 4px 6px; }
  .creneau .carte .heure-evenement {
    display: block; margin: 0 0 2px; padding: 0 4px;
    font-size: .8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .creneau .carte .nom { font-size: .8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .creneau .carte .discret { display: none; }
  .creneau .carte .conflit { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* Ce qui n'a pas d'heure — une journée de congés, un salon — ne peut
   pas se poser sur un axe du temps : l'y placer au hasard tromperait. */
.jour.avec-heures .sans-heure {
  padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 6px;
}
.jour.avec-heures .sans-heure .carte { margin-bottom: 6px; padding: 7px 9px; }

/* En vue jour, une seule colonne : les cartes peuvent respirer. */
.grille-jours.portee-jour.avec-rail { grid-template-columns: 52px minmax(0, 1fr); }
.grille-jours.portee-jour .creneau .carte { font-size: .9rem; padding: 8px 10px; }
.grille-jours.portee-jour .creneau .badge { display: inline-block; }

/* Avec des heures, la colonne ne défile plus pour elle-même : c'est la
   grille entière qui tient la hauteur du temps affiché, et une colonne
   qui défilerait seule désaxerait les heures des autres. */
@media (min-width: 1200px) {
  .grille-jours.avec-rail .jour {
    max-height: none;
    overflow: visible;
  }
}

/* =====================================================================
   LA BARRE DU PLANNING
   =====================================================================
   Quatre rangées empilées — la semaine, les portées, le week-end, les
   boutons — mangeaient trois cent soixante-dix pixels avant la première
   heure de la journée, et la grille finissait sous le bord de l'écran.
   Or c'est la grille qu'on est venu voir.

   Sur un téléphone, rien ne change : à une colonne, serrer ne gagne
   rien et rend tout plus petit. La barre ne se replie que là où il y a
   de la largeur à dépenser. */

.barre-planning { display: contents; }

/* Transparent tant que la barre l'est : les filtres et les boutons
   reviennent l'un sous l'autre, comme s'il n'existait pas. */
.reglages-planning { display: contents; }

.actions-planning {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.actions-planning .btn { flex: 1; }

@media (min-width: 1200px) {
  .barre-planning {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 14px;
    margin-bottom: 12px;
  }

  /* Chaque bloc perd sa marge du bas : c'est la barre qui les espace.
     Ceux du groupe de droite aussi, qui ne sont plus ses enfants
     directs. */
  .barre-planning > *,
  .barre-planning .reglages-planning > * { margin-bottom: 0; }

  /* La date et ses flèches n'ont pas besoin de toute la largeur.

     ESSAYÉ ET REPRIS : leur donner leur propre ligne, centrée sur la
     grille des jours. C'était juste au pixel près et laid à l'œil — une
     ligne presque vide au-dessus d'une ligne pleine. L'intitulé reste
     donc parmi les autres commandes. */
  .barre-planning .nav-semaine { flex: 0 0 auto; gap: 8px; }

  /* LA PORTÉE D'ABORD, LA DATE ENSUITE.

     On choisit ce qu'on regarde — jour, semaine, mois — puis on se
     déplace dedans. La barre suit ce même ordre de lecture.

     Par `order` et non dans le HTML : sur un téléphone, la barre se
     défait et les blocs reviennent l'un sous l'autre dans l'ordre du
     document, où la date doit rester en tête. Deux ordres, un seul
     balisage. */
  .barre-planning .onglets-portee { order: -1; }

  /* LES FILTRES ET LES BOUTONS, RANGÉS À DROITE EN UN SEUL BLOC.

     Les filtres disent QUI est affiché : c'est une restriction de ce
     qu'on regarde, pas une commande. Posés au milieu de la barre, ils
     séparaient la date de ce qu'elle surmonte. Contre les boutons, ils
     forment un bloc de réglages à droite et laissent la gauche à la
     seule navigation. */
  .barre-planning .reglages-planning {
    display: flex;
    align-items: center;
    /* Insécable, et qui ne se laisse pas serrer : le groupe tient sur
       la ligne, ou descend ENTIER à la suivante — où la marge
       automatique le ramène à droite. Autorisé à se replier sur
       lui-même, il rallongeait la barre au lieu de se déplacer, et
       coûtait une rangée de plus dès 1920 px. */
    flex-wrap: nowrap;
    gap: 14px;
    margin-left: auto;
    flex: 0 0 auto;
  }

  .barre-planning .champ-equipe,
  .barre-planning .champ-personne { flex: 0 1 220px; }
  .barre-planning .nav-semaine .intitule { min-width: 190px; }
  .barre-planning .nav-semaine .btn { padding: 8px 12px; }

  .barre-planning .onglets-portee { flex: 0 0 auto; padding: 3px; }
  .barre-planning .onglets-portee button {
    flex: 0 0 auto;
    min-height: 34px;
    padding: 0 18px;
  }

  .barre-planning .jours-affiches { flex: 0 0 auto; gap: 12px; }

  .barre-planning .champ-equipe select { min-height: 38px; padding-top: 6px; padding-bottom: 6px; }

  /* C'est le groupe qui se pousse à droite, pas les boutons seuls : le
     regard va d'abord à la portée et à la date, qui disent ce qu'on
     regarde. */
  .barre-planning .actions-planning {
    flex: 0 0 auto;
    flex-wrap: nowrap;
  }
  .barre-planning .actions-planning .btn {
    flex: 0 0 auto;
    padding: 9px 16px;
  }

  /* La grille prend la place ainsi rendue, et la journée entière tient
     sans défilement : la hauteur d'une heure est ajustée à la volée. */
  .grille-jours.avec-rail .jour { margin-bottom: 0; }
}

/* Une journee trop chargee pour la grille retombe en liste. Elle garde
   la hauteur des autres colonnes et defile pour elle-meme : sans cela
   ses cartes descendent le long du rail, et on les lit comme si elles
   etaient posees a ces heures-la. */
@media (min-width: 1200px) {
  .grille-jours.avec-rail .jour.jour-liste {
    max-height: calc(var(--h-heure) * var(--heures-affichees, 12) + 46px);
    overflow-y: auto;
    overflow-x: hidden;
  }
  .grille-jours.avec-rail .jour.jour-liste .carte { font-size: .84rem; }

  /* La file d'attente ne doit pas allonger la page a elle seule : une
     centaine de devis a poser la rendrait interminable. Sa hauteur
     exacte est posee a la volee, en meme temps que celle des heures. */
  .planning-attente {
    overflow-y: auto;
    overflow-x: hidden;
  }
}

.astuce-glisser { margin-top: 14px; }

/* « À planifier » se replie en une languette : les jours retrouvent leur largeur. */
.attente-bascule {
  display: block; width: 100%; margin: 0 0 8px; padding: 8px 10px; min-height: 44px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  color: var(--forest-2); font: inherit; font-weight: 700; font-size: .88rem; text-align: left; cursor: pointer;
}
@media (min-width: 1200px) {
  .planning-corps.attente-repliee { grid-template-columns: 150px minmax(0, 1fr) !important; }
  .planning-corps.attente-repliee .planning-attente > :not(.attente-bascule) { display: none; }
}

/* Avec les heures, l'arbitrage change de sens.
   Sans elles, la file d'attente reste au-dessus des jours en dessous de
   1600 px : elle leur volerait deux cent quatre-vingts pixels de large.
   Avec elles, c'est la HAUTEUR qui manque — une file de trois cents
   devis repousse la première heure de la journée sous le bord de
   l'écran, et le planning redevient un rouleau. Elle passe donc à côté
   dès 1200 px, plus étroite. */
@media (min-width: 1200px) and (max-width: 1599.98px) {
  .planning-corps.avec-heures {
    display: grid;
    grid-template-columns: minmax(190px, 240px) minmax(0, 1fr);
    gap: 14px;
    align-items: start;
  }
  .planning-corps.avec-heures .planning-attente { position: sticky; top: 0; }
  .planning-corps.avec-heures .planning-attente .carte { font-size: .84rem; }
}

/* ---------- Le mois, en heures ---------- */
/* Une pile de semaines, chacune avec son rail : trente colonnes
   horaires côte à côte donneraient des cases de cinquante pixels. On
   descend dans le mois, on ne va jamais sur le côté. */
.grille-jours.portee-mois.avec-rail { margin-bottom: 18px; }

/* Les jours des mois voisins ne sont là que pour que la ligne soit une
   semaine entière : ils se voient, en retrait. */
.grille-jours .jour.hors-mois { opacity: .55; }

/* ---------- Qui va sur le chantier ---------- */
.qui-y-va { color: var(--forest); }

/* Une case à cocher par personne : à cinq ou dix, une liste déroulante
   à sélection multiple se manipule mal au doigt. */
.liste-personnes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}
.liste-personnes .case { margin: 0; }

/* Revenir à aujourd'hui : un point entre les deux flèches. Le bouton
   pleine largeur répétait la portée choisie juste au-dessus. */
.retour-aujourdhui { padding: 8px 10px; }
.retour-aujourdhui svg { width: 18px; height: 18px; display: block; }

/* Les semaines du mois s'empilent dans un seul bloc, qui occupe la
   colonne des jours. */
.semaines-empilees { min-width: 0; }

/* Les équipes se cochent comme les personnes : cocher une équipe coche
   les siens, et l'on ajuste ensuite — un absent qu'on décoche, un
   renfort qu'on ajoute. */
.liste-equipes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}
.liste-equipes .case { margin: 0; }
.liste-equipes .pastille { margin-right: 6px; }

/* ---------- Le choix d'une heure (champ-heure.js) ---------- */
/* Le champ garde l'allure des autres, avec une horloge ; on y tape
   l'heure, ou on la clique dans le panneau : toutes les heures, les
   minutes de 5 en 5, d'un coup. */
.champ input[data-heure] {
  cursor: text;
  padding-right: 36px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23777' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 18px;
}
.porte-choix-heure { position: relative; }
.choix-heure {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 50;
  width: 300px;
  max-width: calc(100vw - 32px);
  padding: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
}
.choix-heure.a-droite { left: auto; right: 0; }
.choix-heure-titre { font-size: .8rem; font-weight: 600; color: var(--ink-soft); margin: 2px 2px 6px; }
.choix-heure-grille { display: grid; gap: 4px; margin-bottom: 8px; }
.choix-heure-grille.heures { grid-template-columns: repeat(6, 1fr); }
.choix-heure-grille.minutes { grid-template-columns: repeat(6, 1fr); margin-bottom: 0; }
.choix-heure button {
  min-height: 34px;
  padding: 0 2px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: .88rem;
  cursor: pointer;
}
.choix-heure button:hover { border-color: var(--leaf); }
.choix-heure button.choisi {
  background: var(--leaf);
  border-color: var(--leaf);
  color: #fff;
  font-weight: 600;
}

/* ---------- Les journées d'un chantier ---------- */
/* L'en-tête : « Journées », leur nombre, le samedi — sur une ligne. */
.journees-tete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.champ .journees-tete > label[for] { margin: 0; }
.champ .journees-tete input[type="number"] {
  width: 72px;
  min-height: 40px;
  padding: 6px 10px;
  text-align: center;
}
.champ .journees-tete .case { margin: 0; }

/* Serrées : trois jours de pose doivent se lire sans faire défiler
   trois écrans. Les cases deviennent des puces, date et heures tiennent
   sur une rangée. */
.journee-carte { padding: 10px 12px; margin-bottom: 8px; }
.journee-carte .champ { margin-bottom: 8px; }
.journee-carte .champ:last-child { margin-bottom: 0; }
.journee-carte .champ > label { margin-bottom: 4px; font-size: .85rem; }
.journee-entete {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}
.journee-entete .j-retirer { margin-left: auto; min-height: 36px; padding: 6px 12px; font-size: .88rem; }
.journee-entete .j-reprendre {
  background: none;
  border: none;
  padding: 0;
  color: var(--leaf);
  font: inherit;
  font-size: .88rem;
  text-decoration: underline;
  cursor: pointer;
}
.journee-horaires {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 8px;
}
.journee-horaires input { padding: 8px 10px; }
@media (max-width: 420px) {
  .journee-horaires { grid-template-columns: 1fr 1fr; }
  .journee-horaires .champ:first-child { grid-column: 1 / -1; }
}
.journee-carte .liste-equipes,
.journee-carte .liste-personnes { gap: 6px; }
/* « .champ label » passerait la case en bloc, la coche au-dessus du nom. */
.journee-carte .champ .case,
.champ .journees-actions .case {
  display: flex;
  margin: 0;
  font-weight: inherit;
  font-size: inherit;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 6px 12px;
  border-radius: 999px;
}
/* Sans quoi la case hérite de « .champ input » : pleine largeur, haute
   comme un champ. */
.journee-carte .champ .case input[type="checkbox"],
.champ .journees-actions .case input[type="checkbox"] {
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  margin: 0;
}
.journee-carte .case .libelle,
.journees-actions .case .libelle { font-weight: 500; }

/* ---------- Une couleur par personne ---------- */
/* Le bandeau d'une carte porte la couleur de ceux qui y vont, divisé en
   autant de tranches verticales que de personnes : la carte reste une —
   une pose à deux n'est pas deux poses — mais on voit d'un coup d'oeil
   qui est de sortie.

   EN HAUT ET SUR TOUTE LA LARGEUR, et non sur le bord gauche : un
   liséré de six pixels coupé en deux fait deux traits de trois, où
   aucune couleur ne se reconnaît. Sur la largeur d'une carte, chaque
   tranche a de quoi se voir. */
/* UNE CLASSE, et non un sélecteur sur l'attribut style.
   `.carte[style*="--bandeau"]` pesait plus lourd que `.en-deplacement` :
   le `position: relative` du bandeau écrasait le `position: fixed` du
   glissement, et la carte saisie filait à mille pixels de là, hors de
   l'écran. On la croyait disparue.

   Le `position: relative` ne vaut d'ailleurs que HORS glissement : en
   vol, c'est `fixed` qui porte le bandeau, et il le porte aussi bien. */
.carte.avec-bandeau { overflow: hidden; }
.carte.avec-bandeau:not(.en-deplacement) { position: relative; }

/* Le bandeau se pose SUR le rembourrage du haut, il ne l'agrandit pas :
   une carte plus haute de quatorze pixels, multipliée par tout ce qu'un
   jour porte, repousse la suite hors de l'écran — et l'on ne peut plus
   viser ce qu'on ne voit pas pour le déplacer. */
.carte.avec-bandeau::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background: var(--bandeau);
}

/* Le nom de chacun porte sa couleur, pour faire le lien avec le
   bandeau : deux teintes voisines se départagent alors par le nom. */
.pastille-personne {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: baseline;
}

/* ---------- Les congés ---------- */
/* Une semaine d'absence pour deux personnes, ce sont dix cartes qui
   poussent les vrais chantiers vers le bas. Les congés quittent donc les
   colonnes pour une étiquette, à gauche, avec la file d'attente : ce
   n'est pas du travail à répartir, c'est une contrainte à connaître. */
.bandeau-conges {
  border-left: 4px solid var(--ink-soft);
  background: var(--paper);
}

.ligne-conges {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 6px 0;
  border-top: 1px solid var(--line);
  font-size: .9rem;
}
.ligne-conges:first-of-type { border-top: none; }
.ligne-conges .nom { font-weight: 600; }
.ligne-conges .discret { margin-left: auto; text-align: right; }

/* Grisé et dit en toutes lettres, plutôt que retiré de la liste : une
   personne absente de la liste laisse croire qu'elle n'existe pas, là où
   une personne barrée dit qu'elle existe mais qu'elle n'est pas là. */
.case.en-conges .libelle { color: var(--ink-soft); }
.mention-conges { color: var(--ember); font-weight: 600; }

/* Matin, après-midi : les deux moitiés d'une journée de travail. */
.raccourcis-conges {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}
.raccourcis-conges .btn { flex: 1; }

/* ---------- Les trois boutons du planning ---------- */
/* Chacun porte la couleur de ce qu'il crée, et c'est la MÊME que sur la
   carte qui en sortira : bleu pour un rendez-vous, comme son liséré ;
   vert pour un chantier, la couleur du métier ; gris pour des congés,
   comme leur étiquette. Trois boutons identiques obligeaient à lire les
   trois libellés à chaque fois. */
.btn-rendez-vous { background: var(--sky); color: #fff; }
.btn-intervention { background: var(--leaf); color: #fff; }
/* Les congés en rouge pâle : ce n'est pas du travail qu'on ajoute, c'est
   du temps qu'on retire. La couleur arrête l'œil sans crier. */
.btn-conges {
  background: #fbe9e7;
  color: var(--alerte);
  border-color: #f0cdc6;
}

/* ---------- Le week-end, sur un téléphone ---------- */
/* Deux cases pleine largeur pour dire « samedi » et « dimanche », c'est
   deux cent quatre-vingts pixels avant d'avoir vu le moindre chantier —
   sur un écran qui en fait huit cents. Elles deviennent deux pastilles,
   allumées quand le jour est affiché. */
@media (max-width: 900px) {
  .jours-affiches {
    gap: 8px;
    margin-bottom: 10px;
  }

  .jours-affiches > .discret { width: 100%; font-size: .78rem; margin-bottom: -2px; }

  .jours-affiches .case {
    min-height: 40px;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    margin: 0;
    border-radius: 999px;
  }

  /* La case native disparaît : la pastille entière fait l'interrupteur,
     et sa couleur dit l'état mieux qu'une coche de trois pixels. */
  .jours-affiches .case input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin: 0;
  }

  .jours-affiches .case:has(input:checked) {
    border-color: var(--leaf);
    background: var(--leaf-pale);
  }
  .jours-affiches .case:has(input:checked) .libelle { color: var(--forest); }
  .jours-affiches .case:not(:has(input:checked)) .libelle { color: var(--ink-soft); }
}

/* ---------- La barre du planning, sur un téléphone ---------- */
/* Trois boutons de création débordaient sur deux lignes : « Congés » se
   retrouvait seul, et la rangée prenait cent vingt pixels au lieu de
   cinquante-six. On rétrécit la LARGEUR — rembourrage, libellé, icône —
   jamais la hauteur : la cible tactile reste de quarante-quatre pixels. */
@media (max-width: 720px) {
  .actions-planning { gap: 6px; }
  .actions-planning .btn {
    padding: 10px 6px;
    min-height: 44px;
    font-size: .82rem;
    gap: 5px;
  }
  .actions-planning .btn svg { width: 16px; height: 16px; }
}

/* Le bouton qui ouvre la file : dans l'ambre de la file elle-même, pour
   qu'on reconnaisse ce qu'il ouvre avant de l'avoir ouvert. */
.bouton-attente {
  background: var(--ember-pale);
  color: var(--ember);
  border-color: var(--ember);
  margin-bottom: 16px;
}

/* ---------------------------------------------------------------------
   LE MOIS EN CASES MINUSCULES
   ---------------------------------------------------------------------
   Au mois, sans les heures — c'est-à-dire sur un téléphone —, on ne
   vient pas lire ce qui se passe le 14 : on cherche un jour où poser
   quelque chose. La case se réduit donc au numéro et à une barre de
   remplissage, et une journée libre ne porte aucune barre : le vide se
   repère plus vite qu'une barre à zéro.
   --------------------------------------------------------------------- */
.case-mois {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 5px;
  min-height: 46px;
  padding: 6px 2px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  cursor: pointer;
}
.case-mois .numero { font-size: .82rem; font-weight: 600; line-height: 1; }

/* La barre occupe toute la largeur utile de la case : deux barres
   voisines se comparent alors sans effort, ce qui est tout l'objet. */
.case-mois .charge {
  width: 78%;
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  overflow: hidden;
  position: relative;
}
.case-mois .charge::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--part, 0%);
  background: var(--leaf);
  border-radius: 2px;
}
/* Pleine, elle change de couleur : au-delà, ce n'est plus « chargé »,
   c'est « n'y mettez rien de plus sans regarder ». */
.case-mois .charge.pleine::after { background: var(--ember); }

.case-mois.aujourdhui { border-color: var(--leaf); border-width: 2px; }
.case-mois.hors-mois { opacity: .45; }

/* Le mois compact a ses colonnes à TOUTES les largeurs : la grille des
   semaines, elle, ne devient une grille qu'au-delà de 1200 px — ce qui
   est juste quand chaque jour porte ses cartes, et faux ici. */
.mois-compact {
  display: grid;
  grid-template-columns: repeat(var(--colonnes, 7), minmax(0, 1fr));
  gap: 5px;
}
.mois-compact .nom-jour {
  text-align: center;
  font-size: .7rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: .04em;
  padding-bottom: 2px;
}
/* Les cases vides du début de mois tiennent leur place : sans elles, le
   premier jour glisse sous le lundi et tout le calendrier ment. */
.mois-compact .case-vide { display: block; }

/* Le mois écrit dans une case de calendrier : sous le numéro, plus petit
   et plus pâle — il situe sans prendre la place du jour. */
.case-mois .numero .mois {
  display: block;
  font-size: .58rem;
  font-weight: 600;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: .03em;
  line-height: 1.1;
}

/* =====================================================================
   TABLEAU DE RELECTURE D'UN IMPORT (import-tableau.js)
   ---------------------------------------------------------------------
   Une ligne par référence, et tout ce que l'IA a complété visible d'un
   seul regard : le prix, la puissance, les caractéristiques, la page
   d'où elles sortent. C'est le dernier écran avant que des prix
   d'achat soient réécrits pour tout le catalogue — il doit se relire,
   pas se parcourir.
   ===================================================================== */
.tab-import-defile {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 12px -14px 0;
  padding: 0 14px;
}
.tab-import { width: 100%; border-collapse: collapse; font-size: .88rem; }
.tab-import th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface);
  text-align: left;
  font-weight: 600;
  white-space: nowrap;
  padding: 8px 6px;
  border-bottom: 2px solid var(--line);
}
.tab-import td {
  padding: 7px 6px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.tab-import tbody tr:hover { background: var(--leaf-pale); }

/* Le liseré d'alerte : même code visuel que partout ailleurs dans
   l'application — bord gauche ambre = « regarde ça avant de valider ». */
.tab-import tr.lg-douteuse td:first-child,
.tab-import tr.lg-web td:first-child {
  box-shadow: inset 4px 0 0 var(--ember);
}
.tab-import tr.lg-manquante { opacity: .72; }

.tab-import .col-case { width: 48px; cursor: pointer; }
.tab-import input[type="checkbox"] {
  width: 22px;
  height: 22px;
  cursor: pointer;
  accent-color: var(--leaf);
}
.tab-import .col-ref  { min-width: 130px; }
.tab-import .col-lib  { min-width: 200px; }
.tab-import .col-num  { text-align: right; white-space: nowrap; min-width: 84px; }
.tab-import .col-carac{ min-width: 240px; }
.tab-import .col-page { width: 100px; white-space: nowrap; }
.tab-import .col-src  { width: 100px; }

.tab-import .ref { font-weight: 600; }
.tab-import-refbloc { display: flex; gap: 8px; align-items: flex-start; }
.tab-import-vignettes { display: flex; flex-direction: column; gap: 4px; flex: none; }
.tab-import-vignette {
  width: 48px; height: 48px; flex: none; object-fit: contain;
  background: #fff; border: 1px solid var(--line); border-radius: 6px;
}
/* Pourquoi la ligne est « à vérifier » : écrit sous la référence, pas
   seulement dans une info-bulle qu'une tablette ne montre jamais. */
.tab-import-motifs { margin-top: 3px; font-size: .74rem; line-height: 1.3; color: var(--ember); min-width: 150px; max-width: 220px; }
.tab-import .etiq {
  display: inline-block;
  margin-left: 4px;
  padding: 1px 6px;
  border-radius: 8px;
  font-size: .7rem;
  font-weight: 600;
}
.tab-import .etiq-neuf { background: var(--leaf-pale); color: var(--forest); }

.tab-import .avant {
  text-decoration: line-through;
  color: var(--ink-soft);
  display: block;
  font-size: .8rem;
}
.tab-import .apres { font-weight: 600; }

/* Champ éditable au fil du tableau : il doit se voir modifiable sans
   dessiner une bordure sur chaque cellule au repos. */
.tab-import input.mini {
  width: 100%;
  border: 1px solid transparent;
  background: transparent;
  padding: 3px 4px;
  border-radius: 4px;
  font: inherit;
  color: inherit;
}
.tab-import input.mini:hover { border-color: var(--line); }
.tab-import input.mini:focus { border-color: var(--leaf); background: var(--surface); outline: none; }
.tab-import input.mini-page { width: 46px; text-align: center; display: inline-block; }
.tab-import input.mini-prix { width: 88px; text-align: right; }

/* Où le prix de vente se situe face au prix public — mêmes couleurs que
   la fiche catalogue. */
.tab-import .ecart { display: block; font-size: .74rem; }
.tab-import .ecart-sous      { color: var(--leaf); }
.tab-import .ecart-proche    { color: var(--ember); }
.tab-import .ecart-au_dessus { color: var(--alerte); }
.tab-import .origine { display: block; font-size: .7rem; color: var(--ink-soft); }

/* Les données pour les aides : une pastille par valeur. Celle que l'IA
   a complétée d'après ses connaissances se distingue au premier coup
   d'œil ; un champ exigé et manquant se voit en rouge. */
.tab-import .col-type select.mini { min-width: 130px; }
.tab-import .tag-donnee {
  display: inline-block;
  margin: 1px 2px 1px 0;
  padding: 1px 6px;
  border-radius: 8px;
  font-size: .74rem;
  background: rgba(47,122,79,.10);
  color: var(--forest);
  white-space: nowrap;
}
.tab-import .tag-donnee-ia { background: var(--ember-pale); color: var(--ember); font-style: italic; }
.tab-import .tag-donnee-ia b { font-style: normal; font-size: .66rem; }
.tab-import .tag-donnee-manque { background: transparent; color: var(--alerte); border: 1px dashed var(--alerte); }

.tab-import tr.tab-import-detail td { background: var(--surface); }

.tab-import-completer {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin: 12px 0 4px;
}
.tab-import-completer .discret { flex: 1; min-width: 200px; font-size: .82rem; }

/* Le journal détaillé du traitement d'un import */
.journal-traitement summary {
  cursor: pointer;
  font-weight: 600;
  min-height: 32px;
  display: flex;
  align-items: center;
}
.journal-lignes {
  margin-top: 8px;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--paper);
  padding: 6px 8px;
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: .76rem;
  line-height: 1.45;
  color: var(--ink);
}
.journal-ligne {
  display: grid;
  grid-template-columns: 4.6em 7.5em 1fr;
  gap: 8px;
  overflow-wrap: anywhere;
}
.journal-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.journal-heure { color: var(--ink-soft); }
.journal-etape { color: var(--forest); font-weight: 600; }
.journal-en-cours { color: var(--sky); }
.journal-en-cours .journal-etape { color: var(--sky); }
@media (max-width: 520px) {
  .journal-ligne { grid-template-columns: 4.6em 1fr; }
  .journal-ligne > :last-child { grid-column: 1 / -1; padding-left: 1em; }
}
.tab-import-champs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 8px 14px;
  margin: 6px 0;
}
.tab-import-champs label { display: flex; flex-direction: column; gap: 2px; font-size: .8rem; }
.tab-import-champs label small { color: var(--ink-soft); font-size: .7rem; }
.tab-import-champs label.champ-ia input { font-style: italic; color: var(--ember); }
.tab-import-champs input.mini { border-color: var(--line); background: var(--surface); }

.tab-import .lien-min {
  border: none;
  background: none;
  color: var(--leaf);
  font-size: .78rem;
  text-decoration: underline;
  padding: 0 2px;
  cursor: pointer;
}

.tab-import .src {
  font-size: .76rem;
  padding: 2px 6px;
  border-radius: 10px;
  display: inline-block;
}
.tab-import .src-catalogue { background: rgba(47,122,79,.12); color: var(--forest); }
.tab-import .src-web       { background: var(--ember-pale); color: var(--ember); }
.tab-import .src-absent    { color: var(--ink-soft); }
/* « IA » : une valeur sans source, tirée des seules connaissances de l'IA. */
.tab-import .src-ia        { background: var(--alerte-pale); color: var(--alerte); font-weight: 600; }
.tab-import a.src          { text-decoration: none; display: inline-block; margin: 1px 0; }
.tab-import a.src:hover    { text-decoration: underline; }
.tab-import-sources { margin: 8px 0 4px; font-size: .82rem; }
.tab-import-sources ul { margin: 4px 0 0; padding-left: 18px; }
.tab-import-sources li { margin: 2px 0; overflow-wrap: anywhere; }
.tab-import-sources .source-ia { color: var(--alerte); }
.tab-import-photos { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0 4px; font-size: .82rem; }
.tab-import-photos img { height: 90px; max-width: 160px; object-fit: contain; background: #fff; border: 1px solid var(--line); border-radius: 6px; }

.tab-import-filtres { display: flex; gap: 6px; flex-wrap: wrap; }
.tab-import-filtres .btn { padding: 4px 10px; font-size: .82rem; }
.tab-import-filtres .btn.actif { background: var(--forest); color: #fff; border-color: var(--forest); }

/* Sous 820 px, la désignation et les caractéristiques passent sous la
   référence : le tableau reste lisible sur une tablette en portrait. */
@media (max-width: 820px) {
  .tab-import .col-lib, .tab-import .col-carac { min-width: 150px; }
  .tab-import { font-size: .82rem; }
}

/* ---------- Catalogue : recherche, catégories, sélection ---------- */
/* La recherche et les pastilles de catégorie restent en haut pendant
   qu'on fait défiler : passer des pompes à chaleur à la plomberie ne
   demande plus de remonter tout le catalogue. Le haut négatif rattrape
   le retrait de .contenu, sans quoi les cartes défileraient visibles
   au-dessus de la barre. */
.cat-barre {
  position: sticky;
  top: -20px;
  z-index: 5;
  margin: -20px -16px 12px;
  padding: 20px 16px 8px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.cat-barre-ligne { display: flex; gap: 8px; align-items: stretch; }
.cat-barre-ligne .recherche { flex: 1; min-width: 0; margin-bottom: 0; }
.cat-bouton-filtres { flex: none; white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
.cat-bouton-filtres svg { width: 16px; height: 16px; flex: none; }
.cat-bouton-filtres[aria-expanded="true"] { background: var(--leaf-pale); color: var(--forest); border-color: var(--leaf); }
.cat-compte { font-weight: 700; }

.cat-filtres { padding-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cat-filtres[hidden] { display: none; }
/* Un menu habillé en pastille : le libellé, puis la valeur choisie. */
.cat-puce-menu { display: inline-flex; align-items: center; gap: 4px; padding-right: 6px; max-width: 100%; }
.cat-puce-menu > span { color: var(--ink-soft); font-weight: 400; }
.cat-puce-menu.actif > span { color: var(--forest); }
.cat-puce-menu select {
  min-height: 0;
  max-width: 180px;
  padding: 0 2px;
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.cat-puce-menu:focus-within { outline: 2px solid var(--leaf); outline-offset: 1px; }
.cat-puce-menu select:focus { outline: none; box-shadow: none; }
.cat-lien {
  border: none;
  background: none;
  padding: 6px 4px;
  color: var(--leaf);
  font: inherit;
  font-size: .85rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.cat-lien[hidden] { display: none; }
.cat-filtres-aide { flex-basis: 100%; margin: 0; font-size: .8rem; color: var(--ink-soft); }
.cat-filtres-aide[hidden] { display: none; }

/* Une seule ligne de pastilles qui défile au doigt : sept catégories
   sur trois lignes mangeraient l'écran qu'elles doivent faire gagner. */
.cat-puces {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-top: 10px;
  scrollbar-width: none;
}
.cat-puces:empty { display: none; }
.cat-puces::-webkit-scrollbar { display: none; }
.cat-puce {
  flex: none;
  min-height: 36px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: .85rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.cat-puce.actif { background: var(--leaf-pale); color: var(--forest); border-color: var(--leaf); }
.cat-puce.vide { color: var(--ink-soft); font-weight: 400; }
.cat-puce-nombre { color: var(--ink-soft); font-weight: 400; margin-left: 2px; }

.cat-puces-sep { flex: none; width: 1px; margin: 4px 2px; background: var(--line); }

/* La portée à gauche, les outils à droite, sur une ligne ; sur un
   téléphone, la portée prend sa ligne et les outils la suivante. */
.cat-entete-liste { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
.cat-portee { margin-bottom: 0; padding: 3px; }
.cat-portee button { flex: 0 0 auto; min-height: 36px; padding: 0 14px; }
.cat-outils { display: flex; gap: 8px; margin-left: auto; }
.cat-outils .btn { min-height: 40px; padding-left: 16px; padding-right: 16px; }
@media (max-width: 600px) {
  .cat-portee { flex: 1 1 100%; }
  .cat-portee button { flex: 1; }
  .cat-outils { flex: 1 1 100%; margin-left: 0; }
  .cat-outils .btn { flex: 1 1 0; min-width: 0; padding-left: 8px; padding-right: 8px; }
}

.cat-groupe { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.cat-groupe .btn-fiche { flex: none; }

/* Le prix se touche pour être corrigé : l'indice le dit, sans bouton
   de plus sur chaque carte. */
.cat-prix { border-radius: 8px; padding: 2px 4px; margin: -2px -4px; }
.cat-prix:active { background: var(--leaf-pale); }
.cat-prix-indice { font-size: .75rem; color: var(--leaf); margin-top: 2px; text-decoration: underline; text-underline-offset: 2px; }

.cat-case {
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: 2px;
  border: 2px solid var(--line);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  color: var(--surface);
  background: var(--surface);
}
.carte.cat-cochee { border-color: var(--leaf); box-shadow: inset 4px 0 0 var(--leaf); }
.carte.cat-cochee .cat-case { background: var(--leaf); border-color: var(--leaf); }

/* La barre d'actions de la sélection, sous le pouce. Le bas négatif
   rattrape le retrait bas de .contenu, comme la barre du haut. */
.cat-actions-groupe {
  position: sticky;
  bottom: -90px;
  z-index: 5;
  margin: 16px -16px 0;
  padding: 10px 16px 12px;
  background: var(--paper);
  border-top: 1px solid var(--line);
  display: grid;
  gap: 8px;
}
.cat-actions-ligne { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cat-actions-compte { font-weight: 600; }

.cat-menu-actions { display: grid; gap: 8px; }
.cat-menu-actions .btn { margin: 0; }

/* ---------- Devis : quantité sur la ligne, sélecteur du catalogue ---------- */
/* Le petit − quantité + sous le montant de la ligne : discret, il ne
   rallonge pas la ligne. Les cibles restent touchables au doigt. */
.montant-col { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.qte-mini {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  overflow: hidden;
}
.qte-mini button {
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  color: var(--ink-soft);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.qte-mini button:hover, .qte-mini button:active { background: var(--leaf-pale); color: var(--forest); }
.qte-mini input {
  width: 34px;
  height: 30px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--ink);
  font-size: 16px;
  text-align: center;
}
.qte-mini input:focus { outline: none; background: var(--leaf-pale); }

/* Le − / + des cartes du sélecteur, déjà discrets dans leur rangée. */
.qte-ligne { display: inline-flex; align-items: center; gap: 4px; }
.qte-ligne button {
  width: 36px;
  height: 36px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-soft);
  font: inherit;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
}
.qte-ligne button:active { background: var(--leaf-pale); color: var(--forest); }

/* La recherche et les catégories du sélecteur restent en haut de la
   fenêtre quand la liste défile. Le retrait à droite laisse la croix de
   la fenêtre libre. */
.cat-barre-modale {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0 -18px 10px;
  padding: 8px 18px 8px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.cat-barre-modale .recherche { margin-bottom: 0; margin-right: 36px; }
.cat-barre-modale .cat-adaptees { margin-top: 8px; padding: 10px 12px; }

/* Une référence déjà au devis : combien, de quoi l'ajuster, et la
   dérogation « nouvelle ligne ». */
.dans-devis { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.badge-dans-devis { background: var(--leaf-pale); color: var(--forest); border-radius: 999px; padding: 4px 10px; font-size: .82rem; font-weight: 600; }
.dans-devis .qte-ligne { margin-left: auto; }
.dans-devis .btn-fiche { margin-left: 0; min-height: 40px; }

/* ---------- Planning : le bouton « Ajouter » et son choix ---------- */
.menu-ajouter { position: relative; flex: 1; }
.menu-ajouter > summary { list-style: none; width: 100%; }
.menu-ajouter > summary::-webkit-details-marker { display: none; }
.menu-ajouter > summary::after { content: ''; width: 8px; height: 8px; margin-left: 6px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg) translateY(-2px); }
.menu-ajouter[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
.menu-ajouter-liste {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; min-width: 240px;
  display: flex; flex-direction: column; gap: 6px; padding: 8px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--ombre);
}
.menu-ajouter-liste .btn { justify-content: flex-start; width: 100%; }
@media (max-width: 720px) { .menu-ajouter-liste { left: 0; right: 0; } }

/* ---------- Devis : en-tête allégé, options repliées, bloc Signature ---------- */
.barre-ecran .menu-ajouter { flex: 0 0 auto; }
.barre-ecran .menu-ajouter > summary { min-height: 44px; padding: 8px 14px; }
.barre-ecran .menu-ajouter-liste { left: auto; right: 0; }

.options-devis { margin-top: 18px; }
.options-devis > summary {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  min-height: 56px; padding: 12px 16px; cursor: pointer; list-style: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  font-weight: 700;
}
.options-devis > summary::-webkit-details-marker { display: none; }
.options-devis > summary::after {
  content: ''; flex: none; width: 9px; height: 9px;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft);
  transform: rotate(45deg) translateY(-2px);
}
.options-devis[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
.options-devis > summary .discret { flex: 1; text-align: right; font-weight: 400; }
.a-renseigner { color: var(--ember); }

.signature-choix { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.signature-choix .btn { flex-direction: column; gap: 2px; min-height: 64px; }
.signature-choix small { font-weight: 400; font-size: .8125rem; opacity: .9; }
.signature-choix .btn:disabled { opacity: .45; cursor: not-allowed; }
.reste-a-faire {
  margin-bottom: 12px; padding: 10px 14px; border-radius: 10px;
  background: var(--ember-pale); border: 1px solid var(--ember); color: var(--ember);
}
.reste-a-faire ul { margin: 6px 0 0 18px; padding: 0; }
@media (max-width: 480px) { .signature-choix { grid-template-columns: 1fr; } }

/* ---------- Le type d'installation d'un devis (PAC air/eau…) ---------- */
.types-installation { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0; }
.types-installation:empty { display: none; }
.type-installation {
  display: inline-block; padding: 2px 10px; border-radius: 6px;
  background: var(--etat-info-fond); color: var(--etat-info);
  font-size: .8125rem; font-weight: 700;
}

/* ---------- Devis : les raccourcis « à traiter » sont des pastilles ---------- */
.a-traiter { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.a-traiter:empty { display: none; }
.a-traiter > div:empty { display: none; }
.pastille-a-traiter {
  min-height: 44px; padding: 8px 16px; border-radius: 999px; border-color: transparent;
  background: var(--etat-attention-fond); color: var(--etat-attention);
  font-size: .9rem; font-weight: 700;
}
/* Les badges se lisent à 13 px : 11,8 était trop petit pour un état. */
.badge { font-size: .8125rem; }

/* ---------- Mon entreprise : le logo ---------- */
.logo-entreprise { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin-top: 10px; }
.logo-entreprise img, .logo-vide {
  width: 96px; height: 96px; border-radius: 16px; border: 1px solid var(--line); background: #fff; flex: none;
}
.logo-vide { display: flex; align-items: center; justify-content: center; color: var(--ink-soft); font-size: .8125rem; text-align: center; }
