/* =====================================================================
   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;
  /* Le fond de l'écran : une teinte pâle de la couleur de l'entreprise (--leaf-pale, redéfinie par theme.js). */
  --paper:      #eef3f0;
  --paper:      color-mix(in srgb, var(--leaf-pale) 70%, #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);
  /* Une carte se pose sur le fond par son ombre, sans contour. */
  --ombre-carte: 0 1px 2px rgba(15,36,48,.06), 0 6px 16px rgba(15,36,48,.05);
  /* Un creux : le fond d'une carte dans une carte, d'un réglage, d'un chiffre, d'un bouton appuyé.
     Teinté de la couleur de l'entreprise comme l'écran, pour que rien ne reste gris neutre. */
  --creux:       #eef2f0;
  --creux:       color-mix(in srgb, var(--leaf) 6%, #f3f5f4);
  --creux-fonce: #e4e9e2;
  --creux-fonce: color-mix(in srgb, var(--leaf) 10%, #e6eae8);
  --radius:     12px;
  /* Cible tactile minimale confortable */
  --touch:      52px;

  /* 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: #3c5151;      --etat-info-fond: #edf0f0;
  --etat-info: color-mix(in srgb, var(--forest) 25%, #46555c);
  --etat-info-fond: color-mix(in srgb, var(--leaf) 5%, #edf0f0);
  --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: none;
  background: none;
  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); }

/* Le support a répondu à un signalement : le drapeau s'étend et le dit
   (un simple point passait inaperçu). Même gabarit que la puce de synchro. */
.bouton-signaler.a-reponse {
  width: auto;
  gap: 6px;
  padding: 0 12px 0 10px;
  background: #fdebe1;
  color: #d9480f;
  font-size: .82rem;
  font-weight: 700;
  white-space: nowrap;
  animation: signaler-apparition .4s ease-out;
}
.bouton-signaler.a-reponse svg { width: 17px; height: 17px; }
.signaler-libelle .court { display: none; }
@media (max-width: 420px) {
  .signaler-libelle .long { display: none; }
  .signaler-libelle .court { display: inline; }
}
@keyframes signaler-apparition { 0% { transform: scale(.85); opacity: .4; } 60% { transform: scale(1.06); } 100% { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .bouton-signaler.a-reponse { animation: none; } }

.etat-sync {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: .82rem;
  padding: 6px 12px;
  border: none;
  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; overflow-x: hidden; 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); }
/* Le support attend une précision : liseré de la couleur du drapeau étendu. */
.bandeau-support { border: 1px solid color-mix(in srgb, #d9480f 35%, transparent); padding-right: 0; }
.bandeau-support-fermer {
  width: 44px; height: 44px; flex: none;
  border: none; background: none; color: var(--ink-soft); cursor: pointer;
}
.bandeau-support-fermer svg { width: 17px; height: 17px; }
/* Le délégataire de prime a répondu : liseré vert, la couleur du dossier de prime. */
.bandeau-prime { border: 1px solid color-mix(in srgb, var(--leaf) 35%, transparent); padding-right: 0; }
.dp-nouveau { margin-left: 6px; background: var(--leaf); color: #fff; }

/* 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). */
:root[data-apparence="excelpros"] .barre-nav button { font-size: .8125rem; }

/* Téléphone : les onglets rangés derrière « Plus » ne sont pas dans la barre. */
:root[data-apparence="excelpros"] .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) {
  :root[data-apparence="excelpros"] .app { padding-left: 104px; }
  :root[data-apparence="excelpros"] .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;
  }
  :root[data-apparence="excelpros"] .barre-nav button, :root[data-apparence="excelpros"] .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;
  }
  :root[data-apparence="excelpros"] .barre-nav button[hidden] { display: none; }
  :root[data-apparence="excelpros"] .barre-nav button.nav-plus { display: none; }
  /* L'onglet actif se dit par son fond, sans liseré (comme les cartes depuis le 02/11). */
  :root[data-apparence="excelpros"] .barre-nav button[aria-current="page"] { background: var(--leaf-pale); }
  :root[data-apparence="excelpros"] .barre-nav button:hover { background: var(--leaf-pale); }
  :root[data-apparence="excelpros"] .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, .lien-devis { color: inherit; cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 2px; }

/* ---------- Cartes ---------- */
/* Choix du 03/11 (piste B + C) : une carte est cernée d'un trait net, sans ombre ; la liste est
   un peu plus serrée ; les cibles sont plus hautes (--touch) ; les statuts sont cerclés. */
.carte {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: calc(var(--radius) + 4px);
  padding: 14px 16px;
  margin-bottom: 10px;
  box-shadow: none;
}
.carte-action { cursor: pointer; }
.carte-action:active { transform: scale(.995); }

/* LES CARTES : DES PALIERS, ET L'ÉTAT EN TOUTES LETTRES (02/11).

   - L'écran a la teinte pâle de l'entreprise (--paper), une carte est blanche et sans contour,
     une carte DANS une carte est en creux, une troisième redevient blanche.
   - Une carte qui signale un état porte un ton (ton-erreur, ton-attention, ton-ok, ton-info) :
     une étiquette dit l'état en toutes lettres (« À corriger », « À vérifier », « Fait »,
     « Information » ; data-etat="…" en donne une autre) et un cadre fin de sa couleur la détache.
     Seul ce qui demande d'agir (erreur, attention) prend un fond coloré : un fond pâle de plus
     se confondait avec celui de l'écran.
   - Aucun liseré : un état se dit par un cadre, un fond ou une étiquette.

   :where() garde l'imbrication à la spécificité d'une seule classe : un ton l'emporte toujours. */
.carte :where(.carte) {
  background: var(--creux);
  border-color: transparent;
  border-radius: var(--radius);
  box-shadow: none;
}
.carte :where(.carte) :where(.carte) { background: var(--surface); }

/* Le texte coloré « à la main » dans ces cartes (color:var(--alerte), var(--ember)) prend la
   nuance foncée de l'état : l'orange vif manquait de contraste sur le fond orange. */
.carte.ton-erreur    { --ton: var(--etat-erreur);    --ton-fond: var(--etat-erreur-fond);    --ton-etat: 'À corriger'; --alerte: var(--etat-erreur); }
.carte.ton-attention { --ton: var(--etat-attention); --ton-fond: var(--etat-attention-fond); --ton-etat: 'À vérifier'; --ember: var(--etat-attention); }
.carte.ton-ok        { --ton: var(--etat-ok);        --ton-fond: var(--surface);             --ton-etat: 'Fait'; }
.carte.ton-info      { --ton: var(--etat-info);      --ton-fond: var(--surface);             --ton-etat: 'Information'; }
.carte:is(.ton-erreur, .ton-attention, .ton-ok, .ton-info) {
  background: var(--ton-fond);
  border-color: color-mix(in srgb, var(--ton) 32%, transparent);
}
.carte:is(.ton-erreur, .ton-attention) { box-shadow: none; }
.carte:is(.ton-ok, .ton-info) { border-color: color-mix(in srgb, var(--ton) 22%, transparent); }
.carte:is(.ton-erreur, .ton-attention, .ton-ok, .ton-info)::before {
  content: var(--ton-etat);
  display: block;
  width: fit-content;
  margin-bottom: 8px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--ton);
  color: #fff;
  font-size: .76rem;
  font-weight: 700;
  line-height: 1.5;
}
.carte[data-etat]:is(.ton-erreur, .ton-attention, .ton-ok, .ton-info)::before { content: attr(data-etat); }
.carte:is(.ton-erreur, .ton-attention, .ton-ok, .ton-info) :is(h3, .nom.titre) { color: var(--ton); }
/* Un GROS bloc qui alerte (dossier de prime, mentions, étude) n'a qu'un fond à demi teinté :
   tout rose sur un écran de haut, il écrasait ce qu'il contient. */
.carte.ton-leger:is(.ton-erreur, .ton-attention) { background: color-mix(in srgb, var(--ton-fond) 50%, var(--surface)); }

/* L'en-tête d'un écran (le numéro du devis) : le titre parle, pas de liseré. */
.carte.carte-maitre .nom { font-family: var(--font-titre); font-size: 1.15rem; font-weight: 600; color: var(--forest-2); }
/* Les montants : le bloc qu'on regarde avec le client, en couleur pleine. */
.carte.carte-montants { background: var(--leaf); color: #fff; }
.carte-montants .totaux { border-top: 0; margin-top: 0; padding-top: 0; }
.carte-montants .totaux .ttc { color: #fff; border-top-color: rgba(255,255,255,.35); font-size: 1.6rem; }
.carte-montants .discret { color: rgba(255,255,255,.9); }
.carte-montants :is(.reglage, .bloc-marge) { color: var(--ink); }

/* Le récapitulatif en tête du devis (ecran-devis.js › suivreRecap) : une ligne par alerte,
   son état en toutes lettres ; la toucher descend jusqu'à la carte, sous la barre d'actions. */
.carte:is(.ton-erreur, .ton-attention) { scroll-margin-top: 84px; }
.recap-devis ul { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 6px; }
.recap-devis button {
  width: 100%; min-height: 44px; display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 8px 12px; border: 0; border-radius: 10px; cursor: pointer;
  background: var(--creux); color: var(--ink); font: inherit; font-size: .92rem;
}
.recap-devis button > span:last-child { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.recap-devis button::after {
  content: ''; flex: none; width: 8px; height: 8px;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft); transform: rotate(-45deg);
}
.etiquette-etat {
  flex: none; padding: 2px 10px; border-radius: 999px; background: var(--etat-neutre);
  color: #fff; font-size: .74rem; font-weight: 700; white-space: nowrap;
}
.etiquette-etat.etat-erreur { background: var(--etat-erreur); }
.etiquette-etat.etat-attention { background: var(--etat-attention); }

/* 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);
}

/* APPARENCE DE BASE, dite « classique » (réglage « apparence » de l'entreprise, voir theme.js ›
   appliquerApparence) : depuis le 3/11/2026, c'est l'allure d'origine de Green Vision qui sert de
   base à toutes les entreprises — la police de l'appareil, les titres gras, la barre du bas complète.
   Seule la valeur « excelpros » (:root[data-apparence="excelpros"]) donne les polices ExcelPros,
   la barre latérale de l'ordinateur et « Plus » ; ces règles-là ne s'appliquent qu'à elle. */
:root:not([data-apparence="excelpros"]) {
  --font-texte: system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-titre: var(--font-texte);
}
:root:not([data-apparence="excelpros"]) body { font-variant-numeric: normal; }
:root:not([data-apparence="excelpros"]) h2,
:root:not([data-apparence="excelpros"]) h3,
:root:not([data-apparence="excelpros"]) .barre-haut .titre { font-weight: 700; }
:root:not([data-apparence="excelpros"]) .titre-reglages {
  margin: 22px 0 8px;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* 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 .entete > :first-child { min-width: 0; overflow-wrap: anywhere; }
.carte .entete > .badge { flex: none; }
.carte .nom { font-weight: 700; font-size: 1.02rem; }

/* Une alerte de prix sur un devis : rouge, cerclée, avec un point d'exclamation — plus qu'une
   bordure orange qu'on ne voyait pas (signalement #60). Elle pulse trois fois à son arrivée
   (jamais en boucle, et pas du tout si la personne a demandé moins de mouvement). */
.alerte-tarif {
  --a: var(--alerte);
  --a-pale: var(--alerte-pale);
  background: var(--a-pale);
  border: 2px solid var(--a);
}
.alerte-tarif--ember { --a: var(--ember); --a-pale: var(--ember-pale); }
.alerte-tarif .titre { display: flex; gap: 10px; align-items: flex-start; color: var(--a); }
.alerte-tarif .titre::before {
  content: '!';
  flex: none;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--a);
  color: #fff;
  font-weight: 800;
  font-size: .95rem;
}
.alerte-tarif--pulse { animation: alerte-tarif-pulse 1.1s ease-in-out 3; }
@keyframes alerte-tarif-pulse {
  50% { box-shadow: 0 0 0 6px var(--a-pale), 0 0 0 8px var(--a); }
}
@media (prefers-reduced-motion: reduce) {
  .alerte-tarif--pulse { animation: none; }
}

/* 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); }

/* Le badge qui change le statut d'un devis. Même dessin que le badge, une
   flèche pour dire qu'il se touche, et une zone tactile de 44 px tirée
   par une bordure transparente : le dessin ne grossit pas, le doigt ne
   le rate pas. */
.badge-bouton {
  border: 0;
  font-family: inherit;
  cursor: pointer;
  position: relative;
}
.badge-bouton::after { content: ' ▾'; }
.badge-bouton::before {
  content: '';
  position: absolute;
  inset: -11px -4px;
}
.badge-bouton:focus-visible { outline: 2px solid var(--leaf); outline-offset: 2px; }

/* La fenêtre de choix du statut : chaque ligne est un bouton. */
.choix-statuts { display: grid; gap: 8px; }
.choix-statut {
  width: 100%;
  align-items: center;
  text-align: left;
  font: inherit;
  color: inherit;
  background: var(--surface);
  cursor: pointer;
}
.choix-statut .badge { flex-shrink: 0; min-width: 84px; text-align: center; }
.choix-statut[disabled] { cursor: default; }
.choix-statut[disabled]:not(.requise) { opacity: .55; }
.intertitre-statuts { margin: 18px 0 8px; font-size: .95rem; color: var(--ink-soft); }
.etape-statut { margin-top: 10px; min-height: 44px; }

/* ---------- 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%; }
/* Bouton d'une ligne de liste : assez petit pour se ranger sous un texte, assez grand pour un doigt. */
.btn-petit      { min-height: 36px; padding: 6px 12px; font-size: .88rem; }
/* Action qu'on ne veut pas rater en passant : un lien rouge, loin des boutons principaux. */
.btn-lien-danger { background: none; border: 0; color: var(--alerte); text-decoration: underline; min-height: 36px; padding: 6px 8px; font: inherit; cursor: pointer; }

/* Écran Facturation : trois onglets en haut, un seul sujet à l'écran à la fois. Ils défilent avec la page : des totaux flottants cachaient la liste. */
.fa-onglets { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin: 0 -16px 12px; padding: 8px 16px; background: var(--paper); border-bottom: 1px solid var(--line); }
.fa-onglets:empty { display: none; }
.fa-chiffre { display: flex; flex-direction: column; justify-content: center; gap: 1px; min-height: 52px; padding: 6px 10px; text-align: left; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; color: var(--ink); font: inherit; cursor: pointer; }
.fa-chiffre-titre { font-size: .8rem; color: var(--ink-soft); }
.fa-chiffre-valeur { font-size: 1.05rem; }
.fa-chiffre-nombre { white-space: nowrap; }
.fa-chiffre-unite { font-size: .72rem; font-weight: 600; color: var(--ink-soft); }
.fa-chiffre-ht { font-size: .75rem; color: var(--ink-soft); white-space: nowrap; }
.fa-chiffre-detail { font-size: .78rem; color: var(--ink-soft); }
.fa-chiffre--actif { border-color: var(--leaf); box-shadow: inset 0 -3px 0 var(--leaf); }
.fa-chiffre--actif .fa-chiffre-titre { color: var(--ink); font-weight: 700; }
.fa-chiffre--alerte .fa-chiffre-detail { color: var(--ember); font-weight: 700; }
.fa-onglet[hidden] { display: none; }
/* Ce qui demande une attention, en une bande : pas une carte entière avant la liste. */
.fa-alerte { margin-bottom: 12px; padding: 8px 12px; border: 1px solid color-mix(in srgb, var(--ember) 35%, transparent); border-radius: 10px; background: var(--ember-pale); color: var(--ember); font-size: .88rem; }
.fa-alerte p { margin: 0 0 4px; }
.fa-entete-liste { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 10px; }
.fa-entete-liste h3 { margin: 0; }
.fa-entete-liste .discret { margin: 2px 0 0; }
.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;
  flex-wrap: wrap;
  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;
}
/* minmax(0, …) : une colonne ne s'élargit pas au contenu de son champ (un select aux options longues,
   une date sur iPhone) — sinon la page entière dépasse de l'écran (signalement #54). */
.champ-double { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
.champ-double > .champ { min-width: 0; }
/* Un seul champ visible (l'autre est caché) : il prend toute la largeur au lieu d'une moitié. */
.champ-double:has(> .champ[hidden]) { grid-template-columns: minmax(0, 1fr); }
.champ input, .champ select, .champ textarea { max-width: 100%; }
.champ-double input, .champ-double select { min-width: 0; }
.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 minmax(0, 1fr) auto;
  gap: 10px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.ligne-devis:last-child { border-bottom: none; }
/* Un titre ouvre une section du devis : un bandeau teinté, pas une ligne de plus. */
.ligne-devis-titre {
  margin: 10px -8px 2px;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--leaf-pale);
  border-bottom: none;
  color: var(--forest-2);
  align-items: center;
}
.ligne-devis-titre:first-child { margin-top: 0; }
.ligne-devis:has(+ .ligne-devis-titre) { border-bottom: none; }
.ligne-devis > * { min-width: 0; overflow-wrap: anywhere; }
/* Une ligne à deux enfants (texte, montant) n'a pas de colonne de poignée : deux colonnes, sinon le
   texte prend la colonne « auto » et pousse le montant ou le badge hors de l'écran. */
.ligne-devis:has(> :nth-child(2):last-child) { grid-template-columns: minmax(0, 1fr) auto; }
.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: var(--creux);
  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: var(--creux);
  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; }
/* Clavier ouvert (iPhone : il ne réduit ni la page ni dvh, il recouvre) : la fenêtre se cale
   sur la partie réellement visible, mesurée par ui.js (visualViewport), et EN HAUT de celle-ci.
   Calée en bas, elle raccourcissait à chaque lettre tapée dans une recherche (moins de
   résultats) : son haut descendait et le champ passait sous le clavier. La marge du bas laisse
   la place à la barre « Préremplir / ✓ » qu'iOS fait flotter au-dessus du clavier sans la
   compter dans la partie visible. */
.modal.ouvert.clavier {
  inset: auto;
  left: 0;
  right: 0;
  top: var(--vv-haut, 0);
  height: var(--vv-hauteur, 100%);
  align-items: flex-start;
}
.modal.clavier .panneau { max-height: var(--vv-hauteur, 92dvh); padding-bottom: 76px; }
/* 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;
  max-height: 92dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  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: '⚠ '; }
/* Le bouton d'un message (« Annuler ») : assez grand pour le pouce. */
.notification-action {
  margin-left: 12px;
  min-height: 36px;
  padding: 6px 14px;
  border: 1px solid rgba(255, 255, 255, .6);
  border-radius: 18px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}


/* ---------- 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: var(--surface);
  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: var(--creux-fonce);
  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 : une carte ton-ok étiquetée « Aujourd'hui » (ecran-chantier.js). */

/* 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 : une carte ton-erreur étiquetée « En retard » (ecran-entretien.js). */

/* Bandeau des devis à relancer : présent sans être alarmant. */
.bandeau-relance { border: 1px solid color-mix(in srgb, var(--ember) 35%, transparent); }

/* ---------- Chiffres du suivi commercial ---------- */
.chiffres {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 14px;
}
.chiffre {
  background: var(--creux);
  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;
}

/* Un chiffre qu'on peut toucher pour voir ce qui le compose (signalement #59). La flèche le dit :
   sans elle, une tuile ressemble à un affichage. 44 px de haut au moins pour le doigt. */
.chiffre-bouton {
  position: relative;
  width: 100%;
  min-height: 44px;
  border: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.chiffre-bouton::after {
  content: '›';
  position: absolute;
  top: 6px;
  right: 9px;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--ink-soft);
}
.chiffre-bouton:active { background: var(--creux-fonce); }
.chiffre-bouton:focus-visible, .rang-bouton:focus-visible, .ligne-bouton:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}
@media (max-width: 460px) {
  /* Tuile en ligne : une marge à droite pour la flèche, centrée sur la hauteur de la ligne. */
  .chiffre-bouton { padding-right: 30px; }
  .chiffre-bouton::after { top: 50%; transform: translateY(-50%); right: 12px; }
}

/* Une ligne de classement ou d'origine qui ouvre son détail. */
.rang-bouton {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  min-height: 44px;
}
.rang-bouton:last-child { border-bottom: none; }
.rang-bouton:active { background: var(--creux); }

/* Une ligne de la fenêtre de détail (un devis qu'on peut ouvrir). */
.ligne-bouton {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.ligne-bouton:active { background: var(--creux); }
@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: var(--creux);
  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: var(--creux-fonce);
  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: var(--creux);
  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: 1px solid color-mix(in srgb, var(--sky) 35%, transparent); }

@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: 1px solid color-mix(in srgb, var(--sky) 45%, transparent); background: color-mix(in srgb, var(--sky-pale) 60%, var(--surface)); }

/* 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: 1px solid color-mix(in srgb, var(--ember) 35%, transparent);
  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: 1px solid color-mix(in srgb, var(--ember) 35%, transparent);
  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; }
}

/* La note d'une journée en liste n'a de sens qu'à côté du rail des heures. */
.note-jour-liste { display: none; }

/* 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; }
  /* Fond rayé et mot d'explication : cette colonne ne suit PAS les heures du rail
     (signalement du 28/10 : trois chantiers de 8 h à 17 h « empilés sans correspondre »). */
  .grille-jours.avec-rail .jour.jour-liste {
    background-image: repeating-linear-gradient(135deg, transparent 0 10px, color-mix(in srgb, var(--ink-soft) 8%, transparent) 10px 11px);
    outline: 1px dashed var(--line);
  }
  .grille-jours.avec-rail .jour.jour-liste .note-jour-liste {
    display: block; margin: 4px 6px 6px; font-size: .74rem; line-height: 1.25; color: var(--ink-soft);
  }

  /* 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; }
  /* Dans cette colonne étroite, la pastille passe SOUS le titre : à côté, elle ne lui laissait
     que quatre-vingt-dix pixels, et « condensation » se coupait au milieu (3/11/2026). */
  .planning-corps.avec-heures .planning-attente .carte .entete { flex-direction: column; align-items: flex-start; gap: 4px; }
  .planning-corps.avec-heures .planning-attente .carte .entete > :first-child { overflow-wrap: normal; }
  .planning-corps.avec-heures .planning-attente .carte .nom { line-height: 1.3; }
  /* Le titre « À planifier (8) » garde sa ligne : le bouton « Ajouter » descend s'il le faut. */
  .planning-corps.avec-heures .bandeau-a-planifier > div:first-child { flex-wrap: wrap; }
  .planning-corps.avec-heures .bandeau-a-planifier h3 { white-space: nowrap; }
}

/* Le nom du jour en tête de colonne : en long partout, en court dans les colonnes étroites
   d'un ordinateur de 1280 px où « Mercredi 4 nov » passait sur deux lignes (ecran-planning.js › nomJourEntete). */
.entete-jour > span:first-child { white-space: nowrap; }
.jour-nom-court { display: none; }
@media (min-width: 1200px) and (max-width: 1599.98px) {
  .grille-jours.avec-rail .jour-nom-long { display: none; }
  .grille-jours.avec-rail .jour-nom-court { display: inline; }
  .grille-jours.avec-rail .entete-jour { padding-left: 8px; padding-right: 8px; }
}

/* ---------- 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"],
.journee-carte .champ .case input[type="radio"],
.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: 1px solid color-mix(in srgb, var(--ink-soft) 35%, transparent);
  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;
}
/* « photo au catalogue », « photo écartée » : à la place d'une vignette,
   dans sa largeur — sans déborder sur la référence. */
.tab-import-vignette-texte {
  display: block; width: 48px; font-size: .68rem; line-height: 1.2;
  color: var(--ink-soft); text-align: center; white-space: normal;
}
/* 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; }
.tab-import input.mini-prix.apres { font-weight: 600; }
/* La catégorie, sous la désignation : corrigée sans ajouter de colonne (#90). */
.tab-import select.mini-categorie {
  margin-top: 3px; max-width: 100%; font-size: .8rem; color: var(--ink-soft);
  border: 1px solid transparent; background: transparent; border-radius: 4px; padding: 2px 2px;
}
.tab-import select.mini-categorie:hover, .tab-import select.mini-categorie:focus { border-color: var(--line); }

/* 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); }
/* Prix d'achat ramené à la pièce depuis une facture (#89) : dit, et pas seulement en info-bulle. */
.tab-import .origine-calcul { white-space: normal; max-width: 140px; margin-left: auto; color: var(--forest); }

/* 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; }

/* La barre de recherche et de filtres des listes (barre-filtres.js) : les
   mêmes pastilles et menus que le catalogue, sans sa barre collée en haut. */
.filtres-barre { margin-bottom: 14px; }
/* Le tri, sur la ligne de la recherche : un bouton aussi haut que le champ,
   sans libellé — l'icône et la valeur choisie suffisent — et une liste à nous
   plutôt que celle du navigateur, qu'on ne peut pas habiller. */
.filtres-tri-zone { position: relative; flex: none; display: flex; }
.filtres-tri {
  align-self: stretch; display: inline-flex; align-items: center; gap: 6px;
  padding: 0 10px 0 12px; max-width: 100%;
}
.filtres-tri svg { width: 16px; height: 16px; flex: none; color: var(--ink-soft); }
.filtres-tri svg:last-child { width: 14px; height: 14px; }
.filtres-tri-valeur { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.filtres-tri[aria-expanded="true"] { background: var(--leaf-pale); color: var(--forest); border-color: var(--leaf); }
.filtres-tri[aria-expanded="true"] svg:last-child { transform: rotate(180deg); }

.filtres-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
  min-width: 220px; padding: 6px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(15, 36, 48, .16), var(--ombre);
}
.filtres-menu[hidden] { display: none; }
.filtres-menu-titre {
  padding: 6px 10px 4px; font-size: .75rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-soft);
}
.filtres-menu-option {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 44px; padding: 8px 10px;
  border: none; border-radius: 8px; background: transparent;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.filtres-menu-option svg { width: 18px; height: 18px; flex: none; color: var(--leaf); visibility: hidden; }
.filtres-menu-option:hover, .filtres-menu-option:focus-visible { background: var(--paper); outline: none; }
.filtres-menu-option:focus-visible { box-shadow: inset 0 0 0 2px var(--leaf); }
.filtres-menu-option.actif { background: var(--leaf-pale); color: var(--forest); font-weight: 700; }
.filtres-menu-option.actif svg { visibility: visible; }

/* Sur un petit téléphone, « Filtres » ne garde que son icône et son compteur,
   pour que la recherche, le bouton et le tri tiennent sur une ligne. */
@media (max-width: 480px) {
  .filtres-barre .filtres-texte { display: none; }
  .filtres-tri { padding: 0 8px; gap: 4px; }
  .filtres-tri-valeur { max-width: 70px; }
}
@media (max-width: 360px) {
  .filtres-tri-valeur { max-width: 56px; }
}

/* 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); box-shadow: inset 0 0 0 1px 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: 0 0 0 1px 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); }

/* L'emplacement sous la ligne : une pastille à toucher, et dans la fenêtre,
   des propositions à cocher (emplacements-devis.js). */
.emplacement-ligne {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 6px; min-height: 32px;
  padding: 3px 12px; border: 1px dashed var(--line); border-radius: 999px;
  background: transparent; color: var(--ink-soft); font: inherit; font-size: .82rem; cursor: pointer;
  max-width: 100%; text-align: left;
}
.emplacement-ligne.rempli { border-style: solid; background: var(--sky-pale); color: var(--sky); font-weight: 600; }
.emplacement-ligne:disabled { cursor: default; }
.choix-emplacement { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.choix-emplacement button {
  min-height: 36px; padding: 4px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink); font: inherit; font-size: .88rem; cursor: pointer;
}
.choix-emplacement button[aria-pressed="true"] { background: var(--sky-pale); border-color: var(--sky); color: var(--sky); font-weight: 600; }

/* 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; }

/* Les marques sous les catégories : une ligne qui défile, plus discrète que
   celle des catégories (contour seul, plus petite, coins moins ronds : un
   autre niveau), séparée d'un filet et annoncée par son libellé. */
.cat-marques { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); align-items: center; }
.cat-rangee-libelle {
  flex: none;
  padding-right: 4px;
  color: var(--ink-soft);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.cat-marques .cat-puce { min-height: 32px; padding: 4px 11px; border-radius: 6px; background: transparent; font-size: .8rem; }
.cat-marques .cat-puce.actif { background: var(--leaf-pale); }
.cat-marques .cat-puce.actif.actif-defaut { background: transparent; }
/* « Sans marque » : une invitation à compléter plus qu'une marque. */
.cat-puce-sans { font-style: italic; border-style: dashed; }

/* « Toutes », choisie par défaut : un contour, pas de fond — le fond
   coloré signale un filtre réellement posé. */
.cat-puce.actif.actif-defaut { background: var(--surface); box-shadow: none; }

/* Sur un grand écran, les pastilles passent à la ligne au lieu de défiler :
   plus de catégorie coupée au bord. La tablette garde une seule ligne. */
@media (min-width: 900px) {
  .cat-puces { flex-wrap: wrap; overflow-x: visible; row-gap: 6px; }
}

/* Le titre d'une marque dans une liste rangée par marque. Dans le
   sélecteur du devis, il se colle sous la barre de recherche (sa hauteur
   est mesurée en JavaScript dans --barre-h) : on sait dans quelle marque
   on se trouve en faisant défiler. */
.groupe-marque {
  position: sticky;
  top: var(--barre-h, 0px);
  z-index: 1;
  margin: 12px 0 6px;
  padding: 6px 2px;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  font-size: .95rem;
  font-weight: 700;
}
.groupe-marque .cat-puce-nombre {
  display: inline-block;
  min-width: 1.6em;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--line);
  color: var(--ink-soft);
  font-size: .75rem;
  font-weight: 600;
  text-align: center;
}
/* Sur l'écran Catalogue, la barre ne colle pas : le titre défile avec la liste. */
.groupe-marque-liste { position: static; }

/* 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; }
/* Sur téléphone la barre passe à la ligne : « Plus » peut se trouver à gauche d'une rangée, et une
   liste de 240 px ancrée à son bouton sortait de l'écran. Elle s'ancre à la barre entière, sur toute
   sa largeur, sous la dernière rangée (signalement du 28/10 : « problème d'affichage des boutons »). */
@media (max-width: 720px) {
  .barre-ecran .menu-ajouter { position: static; }
  .barre-ecran .menu-ajouter-liste { left: 0; right: 0; min-width: 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); }

/* ---------- Mes signalements : un suivi repliable, pas un formulaire ---------- */
.signalement-suivi {
  margin-top: 10px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.signalement-suivi > summary {
  display: flex; align-items: center; gap: 12px;
  min-height: 44px; padding: 10px 12px; cursor: pointer; list-style: none;
}
.signalement-suivi > summary::-webkit-details-marker { display: none; }
.signalement-suivi > 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);
}
.signalement-suivi[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
.signalement-suivi-titre { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
/* Une étiquette, pas la pastille ronde des équipes (.pastille, 10 px) : le texte en débordait. */
.signalement-non-lu {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  background: #fdebe1; color: #d9480f; font-size: .75rem; font-weight: 700; white-space: nowrap;
}
.signalement-suivi-statut { font-size: .85rem; font-weight: 600; color: var(--ink-soft); }
.signalement-suivi-statut.resolu { color: var(--accent, #2f6f4e); }
.signalement-suivi-resume { flex-basis: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.signalement-suivi[open] .signalement-suivi-resume { display: none; }
.signalement-suivi-corps { padding: 0 12px 12px; }

.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; }
/* Les bandeaux se remplissent après l'affichage : l'espace sous « Nouveau
   devis » ne s'ouvre que lorsqu'il y en a un, sinon il s'ajouterait à celui
   de dessous. */
.a-traiter:has(> :not(:empty)) { margin-top: 12px; }
.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; }
/* Statuts cerclés (choix du 03/11) : la couleur de l'état en trait et en texte, fond clair. */
.badge:not(.badge-dans-devis) { background: transparent; border: 1.5px solid currentColor; padding: 3px 10px; }

/* Ce qui attend, en tête de l'écran Devis (app.js › dessinerAFacturer). */
/* N tuiles se partagent la largeur, sur téléphone aussi (la règle des chiffres de la fiche client les empile). */
.a-traiter > div:has(> .chiffres-attente) { flex: 1 1 100%; }
.chiffres-attente { margin: 0; width: 100%; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.chiffres-attente .chiffre { background: var(--surface); border: 1px solid var(--line); text-align: left; padding: 12px 14px; }
.chiffres-attente .chiffre.attend .valeur { color: var(--etat-attention); }
.chiffres-attente .chiffre.attend { border-color: color-mix(in srgb, var(--etat-attention) 40%, transparent); }
.chiffres-attente .chiffre-bouton::after { top: 10px; }
@media (max-width: 599.98px) { .chiffres-attente { gap: 6px; } .chiffres-attente .chiffre { display: block; padding: 10px; } .chiffres-attente .libelle { font-size: .72rem; } }

/* Les pastilles des onglets : combien attendent (app.js › rafraichirPastillesNav). */
.barre-nav button { position: relative; }
.nav-pastille {
  position: absolute; top: 5px; left: 50%; margin-left: 6px;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: var(--etat-attention); color: #fff;
  font-style: normal; font-size: .68rem; font-weight: 700; line-height: 18px; text-align: center;
  box-shadow: 0 0 0 2px var(--surface);
}
@media (max-width: 599.98px) {
  html:root .app .barre-nav button .nav-pastille { position: static; margin-left: auto; box-shadow: none; }
  .bouton-menu { position: relative; }
  .bouton-menu.avec-pastille::after {
    content: ''; position: absolute; top: 9px; right: 9px; width: 9px; height: 9px; border-radius: 50%;
    background: var(--etat-attention); box-shadow: 0 0 0 2px var(--surface);
  }
}

/* ---------- 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; }

/* Liste des devis : la ligne la plus chère dit ce que vend le devis. Une ligne, coupée au besoin. */
.ligne-principale {
  font-size: .9rem; color: var(--ink-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}

/* Bandeau « sans heure » : une ligne par événement, le détail est dans l'infobulle.
   Le bandeau vide des jours sans événement garde la grille alignée sur ses voisines. */
.jour.avec-heures .sans-heure .carte { padding: 5px 8px; }
.jour.avec-heures .sans-heure .carte .discret { display: none; }
.jour.avec-heures .sans-heure .carte .nom { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jour.avec-heures .sans-heure .carte .entete { align-items: center; }
.jour.avec-heures .sans-heure.vide { padding: 0; }

/* L'infobulle des événements du planning. */
.infobulle-planning {
  position: fixed; z-index: 1000; max-width: 300px; padding: 10px 12px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(20, 40, 30, .18);
  font-size: .84rem; line-height: 1.4; pointer-events: none;
}
.infobulle-planning[hidden] { display: none; }
.infobulle-planning .ib-titre { font-weight: 700; font-size: .92rem; margin-bottom: 2px; }
.infobulle-planning .ib-heure { color: var(--leaf); font-weight: 600; margin-bottom: 4px; }

/* Le planning avec heures s'ajuste à la hauteur de l'écran : la marge de fin de
   liste (90 px) n'y sert à rien et laissait une bande vide sous la grille. */
.contenu:has(.grille-jours.avec-rail) { padding-bottom: 12px; }

/* =====================================================================
   TÉLÉPHONE — reprises de la revue d'interface du 2026-10-13
   =====================================================================
   Regroupées ici plutôt que dispersées dans le fichier : tout ce qui ne vaut
   que sur un petit écran se lit et se retire au même endroit. */

/* L'APPARENCE DE BASE (celle d'origine de Green Vision) : barre complète dès 600 px, et sous cette largeur
   les mêmes quatre onglets + « Plus » que l'apparence « excelpros » (app.js › construireBarreNav). */
@media (max-width: 599.98px) {
  :root:not([data-apparence="excelpros"]) .barre-nav button.nav-secondaire { display: none; }
  :root:not([data-apparence="excelpros"]) .barre-nav button { font-size: .8125rem; }
}
@media (min-width: 600px) {
  :root:not([data-apparence="excelpros"]) .barre-nav button.nav-plus { display: none; }
}

@media (max-width: 719.98px) {
  /* Clavier ouvert dans un champ : la barre du bas et le rappel du devis lui laissent la place
     (sur un téléphone de 844 px, le clavier en prend 300, il ne resterait que le champ). */
  .app:has(.contenu :is(input, textarea, select):focus) .barre-nav,
  .app:has(.contenu :is(input, textarea, select):focus) .bandeau-reprise { display: none; }

  /* Cibles tactiles : 44 px au moins. */
  .bouton-signaler { width: 44px; height: 44px; }
  .btn-fiche { min-height: 36px; padding: 6px 12px; }
  .sp-outils .btn { min-height: 40px; }

  /* Plancher de texte à 13 px pour ce qui se lit debout, au soleil. */
  .badge, .tarif-age, .chiffre .libelle, .etiquette, .vignettes-aide,
  .signalement-non-lu, .evenement .cote .date { font-size: .8125rem; }
}

@media (max-width: 479.98px) {
  /* La barre d'actions d'un écran passe à la ligne : une ligne qui défile cachait les boutons
     de droite (signalement du 14/10 : « les boutons vont sur la droite, cachés sur téléphone »). */
  .barre-ecran { flex-wrap: wrap; }
  .barre-ecran .btn-principal { margin-left: 0; }
  /* Sur téléphone la barre prend 2 ou 3 rangées (170 px sur 812) : collée en haut, elle cachait la
     pastille du statut et recevait le toucher à sa place (signalement #55). Elle défile avec la page.
     Le top de la barre collante (-20px) ne vaut plus : en relative il la remontait de 20 px sous le
     bord de .contenu, et la première rangée de boutons était coupée (signalement OVH #13). */
  .barre-ecran { position: relative; top: 0; }
}

@media (max-width: 419.98px) {
  /* « À jour » ne garde que son point ; le texte reste pour les lecteurs d'écran, et revient
     de lui-même quand la synchronisation a un problème. */
  .etat-sync[data-etat="ok"] #texte-sync {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  .etat-sync[data-etat="ok"] { padding: 12px; }
}

/* =====================================================================
   TÉLÉPHONE — le menu des onglets en tiroir (menu-tel.js)
   =====================================================================
   Sous 600 px, plus de barre en bas : le bouton ☰, à droite de l'en-tête, ouvre
   la même <nav> en tiroir. Tous les onglets y sont listés (plus de « Plus »).
   La tablette (600 px et plus) garde la barre du bas, l'ordinateur son rail. */
.bouton-menu, .menu-entete, .voile-menu { display: none; }

@media (max-width: 599.98px) {
  .barre-haut { gap: 8px; padding-top: calc(6px + env(safe-area-inset-top, 0)); padding-bottom: 6px; }
  .barre-haut .logo { height: 32px; width: 32px; font-size: .85rem; }

  .bouton-menu {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; margin-right: -8px; flex-shrink: 0;
    border: none; background: none; border-radius: 999px; color: var(--forest-2); cursor: pointer;
  }
  .bouton-menu svg { width: 24px; height: 24px; }
  .bouton-menu:active { background: var(--leaf-pale); }
  .app.menu-ouvert .bouton-menu { background: var(--leaf-pale); color: var(--forest); }

  /* Le contenu retrouve toute la hauteur : la marge de 90 px passait sous la barre du bas. */
  .contenu { padding-bottom: 24px; }

  .voile-menu {
    display: block; position: fixed; inset: 0; z-index: 40;
    background: rgba(15, 36, 48, .45);
    animation: voile-menu-entree .22s ease-out;
  }
  .voile-menu[hidden] { display: none; }

  /* La barre du bas devient le tiroir, posé hors écran à droite. */
  .app .barre-nav {
    position: fixed; top: 0; right: 0; bottom: 0; left: auto; z-index: 41;
    width: min(84vw, 320px);
    flex-direction: column; gap: 2px;
    padding: 0 8px calc(12px + env(safe-area-inset-bottom, 0));
    border-top: none; border-left: 1px solid var(--line);
    box-shadow: -12px 0 32px rgba(15, 36, 48, .18);
    overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
    transform: translateX(100%); visibility: hidden;
    transition: transform .22s ease-out, visibility 0s linear .22s;
  }
  .app.menu-ouvert .barre-nav {
    transform: none; visibility: visible;
    transition: transform .22s ease-out, visibility 0s;
  }

  .menu-entete {
    display: flex; align-items: center; gap: 12px; flex-shrink: 0;
    margin: 0 -8px 8px;
    padding: calc(16px + env(safe-area-inset-top, 0)) 16px 16px;
    background: var(--forest); color: #fff;
  }
  .menu-entete .monogramme {
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
    width: 44px; height: 44px; border-radius: 12px;
    background: rgba(255, 255, 255, .16); color: #fff;
    font-weight: 700; font-size: 1rem; letter-spacing: .02em;
  }
  .menu-entete .monogramme.avec-logo {
    background-color: #fff; background-size: cover; background-position: center; color: transparent;
  }
  .menu-nom {
    font-family: var(--font-titre); font-weight: 600; font-size: 1.05rem; line-height: 1.25;
    overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }

  /* Les onglets : icône puis nom, à gauche, comme dans une application. */
  html:root .app .barre-nav button {
    flex: 0 0 auto; width: 100%; min-width: 0; min-height: 52px;
    flex-direction: row; justify-content: flex-start; gap: 14px;
    padding: 0 14px; border: none; border-left: 3px solid transparent; border-radius: 12px;
    font-size: 1rem; text-align: left;
  }
  html:root .app .barre-nav button svg { width: 22px; height: 22px; flex-shrink: 0; }
  html:root .app .barre-nav button.nav-secondaire { display: flex; }
  html:root .app .barre-nav button.nav-plus,
  html:root .app .barre-nav button[hidden] { display: none; }
  html:root .app .barre-nav button[aria-current="page"] {
    background: var(--leaf-pale); color: var(--forest);
  }
  html:root .app .barre-nav button:active { background: var(--leaf-pale); }

  /* Réglages en pied de tiroir, séparé des écrans de travail. */
  html:root .app .barre-nav button[data-vue="reglages"] {
    margin-top: auto; position: relative;
  }
  html:root .app .barre-nav button[data-vue="reglages"]::before {
    content: ""; position: absolute; left: 0; right: 0; top: -7px; height: 1px; background: var(--line);
  }
}

@keyframes voile-menu-entree { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .voile-menu { animation: none; }
  .app .barre-nav, .app.menu-ouvert .barre-nav { transition: none; }
}

/* =====================================================================
   TÉLÉPHONE — le planning
   =====================================================================
   Six rangées de commandes mangeaient 40 % de l'écran avant la première heure.
   Elles tiennent ici en trois : navigation, portée, filtre + file « À planifier ».
   « Ajouter » devient un bouton rond flottant, sous le pouce. */
@media (max-width: 599.98px) {
  .barre-planning {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; margin-bottom: 12px;
  }
  .barre-planning .nav-semaine,
  .barre-planning .onglets-portee,
  .barre-planning .champ-equipe { grid-column: 1 / -1; margin: 0; }
  .barre-planning .champ-personne { grid-column: 1; margin: 0; }
  .barre-planning .bouton-attente { grid-column: 2; }
  .barre-planning .champ-equipe { margin: 0; }

  /* Navigation : des icônes sans cadre ; le titre prend la place qui reste. */
  .barre-planning .nav-semaine { gap: 2px; }
  .barre-planning .nav-semaine .btn {
    width: 40px; min-width: 40px; min-height: 44px; padding: 0;
    background: transparent; border-color: transparent; color: var(--forest-2);
  }
  .barre-planning .nav-semaine .btn:active { background: var(--leaf-pale); }
  .barre-planning .nav-semaine .btn svg { width: 22px; height: 22px; }
  #pl-precedent { order: 1; }
  .barre-planning .nav-semaine .intitule { order: 2; }
  #pl-suivant { order: 3; }
  #pl-aujourdhui { order: 4; }
  #pl-reglages { order: 5; }
  #pl-imprimer { order: 6; }
  .nav-semaine .intitule strong { display: block; font-size: 1.02rem; line-height: 1.2; }
  .nav-semaine .intitule .discret { font-size: .8125rem; line-height: 1.3; }

  /* Jour / Semaine / Mois : une piste, et l'onglet actif posé dessus. */
  .barre-planning .onglets-portee { padding: 3px; gap: 2px; background: var(--line); border: none; border-radius: 10px; }
  .barre-planning .onglets-portee button { min-height: 38px; border-radius: 8px; }
  .barre-planning .onglets-portee button.actif {
    background: var(--surface); color: var(--forest); box-shadow: 0 1px 3px rgba(15, 36, 48, .18);
  }

  /* Filtre : plus bas, avec une silhouette pour dire « qui ». */
  .barre-planning .champ select { min-height: 40px; padding: 8px 12px; font-size: .95rem; }
  .barre-planning .champ-personne select {
    padding-left: 38px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b73' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: 12px center; background-size: 18px;
  }

  /* La file : une pastille à côté du filtre, avec son nombre. */
  .barre-planning .bouton-attente {
    width: auto; min-height: 40px; margin: 0; padding: 0 12px; gap: 8px;
    border-radius: 10px; font-size: .9rem; white-space: nowrap;
  }
  .bouton-attente .compte {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px;
    background: var(--ember); color: #fff; font-size: .8125rem; font-weight: 700;
  }

  /* « Ajouter » : bouton rond flottant, liste ouverte au-dessus. */
  .actions-planning { display: contents; }
  .actions-planning .menu-ajouter {
    position: fixed; z-index: 30; flex: none;
    right: calc(16px + env(safe-area-inset-right, 0)); bottom: calc(16px + env(safe-area-inset-bottom, 0));
  }
  .app:has(.bandeau-reprise:not([hidden])) .actions-planning .menu-ajouter { bottom: calc(66px + env(safe-area-inset-bottom, 0)); }
  .actions-planning .menu-ajouter > summary {
    display: flex; align-items: center; justify-content: center; cursor: pointer;
    width: 56px; height: 56px; min-height: 56px; padding: 0; border-radius: 50%;
    background: var(--forest); color: #fff; border-color: transparent;
    box-shadow: 0 6px 16px rgba(15, 36, 48, .3);
  }
  .actions-planning .menu-ajouter > summary svg { width: 26px; height: 26px; }
  .actions-planning .menu-ajouter > summary::after { display: none; }
  .actions-planning .menu-ajouter[open] > summary svg { transform: rotate(45deg); }
  .libelle-ajouter {
    position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  .actions-planning .menu-ajouter-liste {
    top: auto; bottom: calc(100% + 12px); left: auto; right: 0;
    width: min(260px, calc(100vw - 32px)); padding: 6px;
  }
  .actions-planning .menu-ajouter-liste .btn { min-height: 52px; font-size: 1rem; border-color: transparent; }

  /* Le jour : en-tête plus fin, badge à sa taille. */
  .grille-jours .entete-jour { min-height: 38px; padding: 4px 12px; margin-bottom: 6px; font-size: .88rem; }
  .grille-jours.portee-jour .creneau .badge { align-self: flex-start; }

  /* Les listes (semaine, mois) passent sous le bouton flottant sans que la dernière carte s'y cache. */
  .contenu:has(.barre-planning) { padding-bottom: 84px; }
}

/* ---------------------------------------------------------------------
   Factures des sous-traitants (ecran-st-factures.js)
   Le sens des couleurs est celui du reste de l'application : vert = dans la
   tolérance ou fait, ambre = à voir, rouge = dépassé ou retenu.
   --------------------------------------------------------------------- */
.stf-total { margin: 0 0 10px; font-size: 1.05rem; }
.stf-retard { color: var(--alerte); font-weight: 700; }

.stf-ligne { display: flex; flex-direction: column; gap: 4px; width: 100%; margin-bottom: 8px; padding: 12px 14px; text-align: left; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); color: var(--ink); font: inherit; cursor: pointer; min-height: var(--touch); }
.stf-ligne:active { background: var(--paper); }
.stf-ligne-tete { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.stf-ligne-pied { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.stf-ligne-pied:empty { display: none; }

.stf-puce { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: .8rem; font-weight: 700; }
.stf-puce--ok { background: var(--etat-ok-fond); color: var(--etat-ok); }
.stf-puce--inferieur { background: var(--etat-attention-fond); color: var(--etat-attention); }
.stf-puce--depasse { background: var(--etat-erreur-fond); color: var(--etat-erreur); }
.stf-puce--inconnu { background: var(--etat-neutre-fond); color: var(--etat-neutre); }

.stf-sous-titre { margin: -6px 0 6px; color: var(--ink-soft); }
.stf-phrase { margin: 0 0 12px; }

/* La frise : cinq étapes, la courante en relief. Le sens ne repose pas sur la couleur seule :
   l'étape faite porte un point plein, la courante un anneau, celles à venir un point creux. */
.stf-frise { display: flex; margin: 0 0 16px; padding: 0; list-style: none; }
.stf-etape { position: relative; flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; text-align: center; font-size: .74rem; color: var(--ink-soft); }
.stf-etape::before { content: ''; position: absolute; top: 8px; left: -50%; width: 100%; height: 2px; background: var(--line); z-index: 0; }
.stf-etape:first-child::before { display: none; }
.stf-etape-point { position: relative; z-index: 1; width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--line); background: var(--surface); box-sizing: border-box; }
.stf-etape-nom { overflow-wrap: anywhere; }
.stf-etape--fait { color: var(--ink); }
.stf-etape--fait::before { background: var(--leaf); }
.stf-etape--fait .stf-etape-point { background: var(--leaf); border-color: var(--leaf); }
.stf-etape--courante { color: var(--ink); font-weight: 700; }
.stf-etape--courante::before { background: var(--leaf); }
.stf-etape--courante .stf-etape-point { border-color: var(--leaf); box-shadow: 0 0 0 4px var(--leaf-pale); }
.stf-etape--alerte .stf-etape-point { border-color: var(--alerte); background: var(--alerte-pale); box-shadow: 0 0 0 4px var(--alerte-pale); }
.stf-etape--alerte .stf-etape-nom { color: var(--alerte); }

/* La confrontation : facture et estimation côte à côte quand la place le permet. */
.stf-conf { display: grid; grid-template-columns: 1fr; gap: 12px; margin-bottom: 14px; }
@media (min-width: 640px) { .stf-conf { grid-template-columns: 1fr 1fr; } }
.stf-conf-col { padding: 12px 14px; background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); }
.stf-conf-col h3, .stf-titre-section { margin: 0 0 8px; font-size: 1rem; }
.stf-titre-section { margin-top: 16px; }
.stf-lignes { margin: 0 0 10px; }
.stf-lignes > div { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; }
.stf-lignes dt { color: var(--ink-soft); min-width: 0; overflow-wrap: anywhere; }
.stf-lignes dd { margin: 0; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stf-lignes-total { margin-top: 4px; padding-top: 6px !important; border-top: 1px solid var(--line); font-weight: 700; }
.stf-lignes-total dt { color: var(--ink); }

/* La jauge : une échelle de 80 % à 120 % de l'estimation, la bande est la tolérance. */
.stf-jauge { margin: 0 0 6px; padding: 12px 14px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--surface); }
.stf-jauge-piste { position: relative; height: 14px; border-radius: 7px; background: var(--etat-neutre-fond); }
.stf-jauge-bande { position: absolute; top: 0; bottom: 0; background: var(--etat-ok-fond); border-radius: 7px; outline: 1px solid var(--etat-ok); }
.stf-jauge-repere { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--ink-soft); }
.stf-jauge-curseur { position: absolute; top: -5px; width: 8px; height: 24px; margin-left: -4px; border-radius: 4px; background: var(--etat-ok); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--etat-ok); transition: left .25s ease; }
.stf-jauge--depasse { border-color: var(--etat-erreur); background: var(--etat-erreur-fond); }
.stf-jauge--depasse .stf-jauge-curseur { background: var(--etat-erreur); box-shadow: 0 0 0 1px var(--etat-erreur); }
.stf-jauge--inferieur { border-color: var(--etat-attention); background: var(--etat-attention-fond); }
.stf-jauge--inferieur .stf-jauge-curseur { background: var(--etat-attention); box-shadow: 0 0 0 1px var(--etat-attention); }
.stf-jauge-echelle { display: flex; justify-content: space-between; margin-top: 4px; font-size: .72rem; color: var(--ink-soft); }
.stf-jauge-texte { margin: 6px 0 0; }
.stf-jauge-vide { margin: 0 0 6px; padding: 10px 14px; border: 1px dashed var(--line); border-radius: var(--radius); }
@media (prefers-reduced-motion: reduce) { .stf-jauge-curseur { transition: none; } }

.stf-controles { margin: 0; padding: 0; list-style: none; }
.stf-controle { display: flex; align-items: flex-start; gap: 10px; padding: 6px 0; border-top: 1px solid var(--line); }
.stf-controle:first-child { border-top: 0; }
.stf-controle > span:last-child { display: flex; flex-direction: column; min-width: 0; }
.stf-controle-icone { flex: none; width: 22px; height: 22px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: .85rem; }
.stf-controle--ok .stf-controle-icone { background: var(--etat-ok-fond); color: var(--etat-ok); }
.stf-controle--attention .stf-controle-icone { background: var(--etat-attention-fond); color: var(--etat-attention); }
.stf-controle--bloquant .stf-controle-icone { background: var(--etat-erreur-fond); color: var(--etat-erreur); }

.stf-motif { margin: 12px 0 0; padding: 8px 12px; border: 1px solid color-mix(in srgb, var(--ember) 35%, transparent); border-radius: 10px; background: var(--ember-pale); }
.stf-retenue, .stf-retenue-bloc { padding: 8px 12px; border: 1px solid color-mix(in srgb, var(--alerte) 35%, transparent); border-radius: 10px; background: var(--alerte-pale); color: var(--alerte); }
.stf-retenue-bloc { margin-bottom: 12px; }
.stf-retenue-bloc ul { margin: 6px 0; padding-left: 18px; }
.stf-actions { align-items: center; }

.stf-journal { margin: 0; padding: 0; list-style: none; }
.stf-journal li { display: flex; flex-direction: column; gap: 1px; padding: 8px 0; border-top: 1px solid var(--line); }
.stf-journal li:first-child { border-top: 0; }

/* Les titres de section : une icône dans un rond de la couleur de la section. */
.stz-titre { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; font-size: 1.05rem; }
.stz-icone { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 10px; }
.stz-icone svg { width: 18px; height: 18px; }
.stz-icone--afaire { background: var(--etat-attention-fond); color: var(--etat-attention); }
.stz-icone--attente { background: var(--etat-info-fond); color: var(--etat-info); }
.stz-icone--historique { background: var(--leaf-pale); color: var(--leaf); }

/* La pastille d'initiales d'un sous-traitant : sa couleur le suit d'un écran à l'autre. */
.pastille-initiales { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; font-family: var(--font-titre); font-size: .82rem; font-weight: 700; letter-spacing: .02em; }
.pastille-initiales--0 { background: var(--leaf-pale); color: var(--forest); }
.pastille-initiales--1 { background: var(--sky-pale); color: #1d5670; }
.pastille-initiales--2 { background: var(--ember-pale); color: #8a3f15; }
.pastille-initiales--3 { background: #efe7f6; color: #5d3480; }
.pastille-initiales--4 { background: #fbf0cf; color: #6b4e00; }
.pastille-initiales--5 { background: #dff1ee; color: #1b6158; }
.stz-nom { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.stz-nom strong { min-width: 0; overflow-wrap: anywhere; }

/* Les lignes (factures, relevés, fiches) : une bande à gauche dit l'état, le montant ressort. */
.stf-ligne, .pst-carte { border: 1px solid color-mix(in srgb, var(--line) 35%, transparent); }
.stf-ligne--deposee, .stf-ligne--en_controle, .stf-ligne--releve-brouillon { border-color: color-mix(in srgb, var(--etat-attention) 55%, transparent); }
.stf-ligne--a_corriger, .stf-ligne--releve-ecart, .stf-ligne--retard { border-color: color-mix(in srgb, var(--etat-erreur) 55%, transparent); }
.stf-ligne--validee, .stf-ligne--programmee, .stf-ligne--releve-envoye, .stf-ligne--releve-facture_recue { border-color: color-mix(in srgb, var(--etat-info) 55%, transparent); }
.stf-ligne--payee, .stf-ligne--releve-paye { border-color: color-mix(in srgb, var(--etat-ok) 55%, transparent); }
.stf-ligne--refusee { border-color: color-mix(in srgb, var(--etat-neutre) 55%, transparent); }
.stf-ligne-tete:has(.pastille-initiales) ~ span { padding-left: 46px; }
.stf-ligne-montant { flex: none; font-family: var(--font-titre); font-size: 1.05rem; font-weight: 700; white-space: nowrap; }
.stf-ligne-montant small { font-family: var(--font-texte); font-size: .74rem; font-weight: 400; color: var(--ink-soft); }
.stf-total--payer { padding: 10px 14px; border-radius: var(--radius); background: var(--etat-info-fond); color: var(--etat-info); }
.stf-total--payer strong { font-family: var(--font-titre); font-size: 1.2rem; }

/* Le suivi (ecran-sous-traitance.js) et les fiches (ecran-sous-traitants.js). */
.stz-bloc { padding: 10px 0; border-top: 1px solid var(--line); }

/* La barre du haut : le sous-traitant choisi et le dépôt d'une facture. */
.stz-barre { display: flex; gap: 8px; align-items: stretch; margin: 10px 0 16px; }
.stz-barre .champ { flex: 1; min-width: 0; margin: 0; }
.stz-barre .btn { flex: none; white-space: nowrap; }
@media (max-width: 480px) {
  /* Sur téléphone la barre passe à la ligne et rien ne dépasse de l'écran (signalement 87). */
  .stz-barre { flex-wrap: wrap; }
  .stz-barre .champ { flex: 1 1 100%; }
  .stz-barre .btn { flex: 1 1 100%; white-space: normal; }
  .stz-action .stz-action-phrase { padding-left: 0; }
  .stf-ligne-montant, .stf-lignes dd { white-space: normal; overflow-wrap: anywhere; }
}
.stz-compte { display: inline-flex; align-items: center; justify-content: center; min-width: 24px; height: 24px; padding: 0 8px; border-radius: 12px; background: var(--etat-attention-fond); color: var(--etat-attention); font-size: .82rem; font-weight: 700; }

/* Une carte d'action : qui, combien, pourquoi, et le bouton qui dit quoi faire. */
.stz-action { margin-bottom: 10px; padding: 14px; background: var(--surface); border: 1px solid var(--line); border: 1px solid color-mix(in srgb, var(--etat-neutre) 35%, transparent); border-radius: var(--radius); box-shadow: var(--ombre); }
.stz-action--alerte { border-color: color-mix(in srgb, var(--etat-erreur) 55%, transparent); }
.stz-action--attention { border-color: color-mix(in srgb, var(--etat-attention) 55%, transparent); }
.stz-action--info { border-color: color-mix(in srgb, var(--etat-info) 55%, transparent); }
/* Comme les cartes d'alerte (.carte.ton-*) : l'urgence en toutes lettres, un fond pâle pour ce qui presse. */
.stz-action--alerte { background: color-mix(in srgb, var(--etat-erreur-fond) 55%, var(--surface)); box-shadow: none; }
.stz-action--attention { background: color-mix(in srgb, var(--etat-attention-fond) 55%, var(--surface)); box-shadow: none; }
.stz-action:is(.stz-action--alerte, .stz-action--attention)::before {
  content: 'Urgent'; display: block; width: fit-content; margin-bottom: 8px; padding: 2px 10px; border-radius: 999px;
  background: var(--etat-erreur); color: #fff; font-size: .76rem; font-weight: 700; line-height: 1.5;
}
.stz-action--attention::before { content: 'À faire'; background: var(--etat-attention); }
.stz-action-tete { display: flex; align-items: center; gap: 10px; }
.stz-action-nom { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.stz-action-phrase { margin: 8px 0 0; padding-left: 46px; }
.stz-action--alerte .stz-action-phrase { color: var(--etat-erreur); font-weight: 600; }
.stz-action-boutons { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.stz-action-boutons .btn { flex: 1 1 150px; }

/* Ce qu'on doit en tout : le total d'abord, puis son détail en trois lignes. */
.stz-estimation { margin: 0 0 16px; padding: 14px; border: 1px solid color-mix(in srgb, var(--etat-info) 30%, transparent); border-radius: var(--radius); background: var(--etat-info-fond); }
.stz-estimation h3 { margin: 0 0 4px; font-size: .95rem; color: var(--etat-info); }
.stz-estimation-total { margin: 0 0 8px; }
.stz-estimation-total strong { font-family: var(--font-titre); font-size: 1.6rem; color: var(--etat-info); }
.stz-estimation-total small { color: var(--ink-soft); }
.stz-estimation .stf-lignes { margin: 0; }
.stz-estimation .discret { margin: 8px 0 0; }

/* Rien à faire : une seule ligne, rassurante. */
.stz-rien { display: flex; align-items: center; gap: 12px; padding: 14px; border-radius: var(--radius); background: var(--etat-ok-fond); color: var(--etat-ok); }
.stz-rien p { margin: 2px 0 0; }
.stz-rien-icone { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: var(--etat-ok); color: #fff; font-weight: 700; }

/* « En attente » et « Historique » : repliés, le titre dit ce qu'ils contiennent. */
.stz-pli-titre { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stz-pli-titre .stz-titre { margin: 0; }
.stz-pli-titre .discret { padding-left: 42px; font-size: .86rem; }
.stz-sous-titre { margin: 16px 0 8px; font-size: .82rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-soft); }
.stz-note { margin: 12px 0 0; padding: 8px 12px; border: 1px solid color-mix(in srgb, var(--etat-info) 35%, transparent); border-radius: 10px; background: var(--etat-info-fond); color: var(--ink); }

/* Une carte repliable : le titre fait office de bouton, une flèche dit qu'elle s'ouvre. */
details.carte-pliable > summary { display: flex; justify-content: space-between; align-items: center; gap: 12px; min-height: var(--touch); cursor: pointer; list-style: none; }
details.carte-pliable > summary::-webkit-details-marker { display: none; }
details.carte-pliable > summary h3 { margin: 0; }
details.carte-pliable > 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); transition: transform .15s ease;
}
details.carte-pliable[open] > summary { margin-bottom: 8px; }
details.carte-pliable[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
@media (prefers-reduced-motion: reduce) { details.carte-pliable > summary::after { transition: none; } }

.stf-choix { align-items: flex-start; padding: 8px 0; border-top: 1px solid var(--line); }
.stf-choix > span { display: flex; flex-direction: column; }

/* ---------------------------------------------------------------------
   Les disponibilités d'un sous-traitant (disponibilites-grille.js) : sur sa
   page, et dans la fenêtre de la direction. Vert = disponible, rouge =
   indisponible, gris = pas renseigné ; chaque case montre le matin en haut
   et l'après-midi en bas.
   --------------------------------------------------------------------- */
.dsp-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 12px; }
.dsp-titre { flex: 1; text-align: center; font-family: var(--font-titre); font-size: 1.05rem; }
.dsp-pas { flex: none; width: 44px; height: 44px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--ink); font-size: 1.5rem; line-height: 1; cursor: pointer; }
.dsp-retour { display: block; margin: -4px auto 12px; }
.dsp-grille { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; margin-bottom: 10px; }
.dsp-tete { padding: 4px 0; text-align: center; font-size: .78rem; font-weight: 700; color: var(--ink-soft); }
.dsp-jour { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: space-between; gap: 3px; min-height: 52px; padding: 5px 4px 4px; background: var(--surface); border: 1px solid var(--line); border-radius: 10px; color: var(--ink); font: inherit; font-variant-numeric: tabular-nums; cursor: pointer; }
.dsp-jour:disabled { opacity: .45; cursor: default; }
.dsp-jour--hors { background: var(--paper, var(--surface)); opacity: .55; }
.dsp-jour--auj { border-color: var(--etat-info); font-weight: 700; color: var(--etat-info); }
/* Un doigt qui part à l'horizontale peint la plage ; à la verticale, la page défile (le navigateur
   annule alors le geste : rien n'est peint). */
.dsp-grille--peinture { touch-action: pan-y; user-select: none; -webkit-user-select: none; }
/* Le temps de l'enregistrement (ou s'il a échoué) : un liseré pointillé. */
.dsp-jour--modifie { border: 2px dashed var(--etat-info); padding: 4px 3px 3px; }
.dsp-jour--apercu { background: var(--etat-info-fond); box-shadow: inset 0 0 0 2px var(--etat-info); }
.dsp-jour:focus-visible { outline: 3px solid var(--etat-info); outline-offset: 1px; }
.dsp-moities { display: flex; flex-direction: column; gap: 2px; width: 100%; }
.dsp-m { display: block; height: 7px; border-radius: 3px; background: var(--etat-neutre-fond); border: 1px solid transparent; box-sizing: border-box; }
.dsp-m--disponible { background: var(--etat-ok); }
/* Des hachures : rouge et vert ne se distinguent pas pour tout le monde. */
.dsp-m--indisponible { background: repeating-linear-gradient(135deg, var(--etat-erreur) 0 3px, color-mix(in srgb, var(--etat-erreur) 55%, var(--surface)) 3px 5px); }
.dsp-m--rien { background: var(--etat-neutre-fond); border-color: var(--line); }
.dsp-planifie { position: absolute; top: 3px; right: 3px; width: 7px; height: 7px; border-radius: 50%; background: var(--etat-info); }
.dsp-planifie--legende { position: static; display: inline-block; }
.dsp-legende { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0 0 14px; padding: 0; list-style: none; font-size: .85rem; color: var(--ink-soft); }
.dsp-legende li { display: flex; align-items: center; gap: 6px; }
.dsp-legende .dsp-m { width: 16px; }
.dsp-choix { display: flex; gap: 4px; margin: 0 0 10px; padding: 4px; border-radius: 12px; background: var(--etat-neutre-fond); }
.dsp-bouton { flex: 1; min-height: 44px; border: 0; border-radius: 9px; background: transparent; color: var(--ink-soft); font: inherit; font-weight: 600; cursor: pointer; }
.dsp-bouton[aria-checked="true"] { background: var(--surface); color: var(--forest, var(--ink)); box-shadow: var(--ombre, 0 1px 3px rgba(0,0,0,.15)); }
.dsp-bouton--disponible[aria-checked="true"] { color: var(--etat-ok); }
.dsp-bouton--indisponible[aria-checked="true"] { color: var(--etat-erreur); }
.dsp-bouton:focus-visible { outline: 3px solid var(--etat-info); outline-offset: -3px; }
/* Le pinceau : ce qu'on pose, choisi AVANT de toucher les jours. */
.dsp-pinceau { margin: 0 0 14px; }
.dsp-pinceau-entete { margin: 0 0 6px; font-family: var(--font-titre); font-size: 1rem; font-weight: 600; color: var(--ink); }
/* Les étapes 1 à 4 : une pastille numérotée devant chaque titre, le contenu aligné sous le titre. */
.dsp-etapes { counter-reset: dsp-etape; margin: 0; padding: 0; list-style: none; }
.dsp-etapes > li { counter-increment: dsp-etape; position: relative; padding-left: 32px; margin: 0 0 10px; }
.dsp-etapes > li::before { content: counter(dsp-etape); position: absolute; left: 0; top: -1px; width: 22px; height: 22px; border-radius: 50%; background: var(--etat-info-fond); color: var(--etat-info); font-size: .8rem; font-weight: 700; line-height: 22px; text-align: center; }
.dsp-etapes .dsp-choix { margin-bottom: 4px; }
/* Sur un téléphone, les boutons reprennent toute la largeur : « Pas renseigné » tient sur une ligne. */
@media (max-width: 480px) { .dsp-etapes .dsp-choix { margin-left: -32px; } }
.dsp-pinceau-titre { margin: 0 0 4px; font-size: .9rem; font-weight: 700; color: var(--ink); }
.dsp-precision { margin: 0; font-size: .85rem; color: var(--ink-soft); }
.dsp-aide { margin: 0 0 2px; }
.dsp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
/* « Enregistrement… », « Enregistré. » ou l'échec, au-dessus du calendrier : une hauteur fixe, pour que la
   grille ne bouge pas sous le doigt quand le texte change. */
.dsp-etat { display: flex; align-items: center; gap: 8px; min-height: 1.6em; margin: 0 0 6px; font-size: .88rem; font-weight: 600; color: var(--ink-soft); }
.dsp-etat[data-statut="fait"] { color: var(--etat-ok); }
.dsp-etat[data-statut="erreur"] { color: var(--etat-erreur); }

/* « Qui est disponible cette semaine » (disponibilites-tableau.js) : une ligne par sous-traitant, une case
   par jour. Sur un téléphone, le tableau défile de côté plutôt que d'écraser les cases sous 44 px. */
.dspt-defile { overflow-x: auto; margin: 0 0 12px; -webkit-overflow-scrolling: touch; }
.dspt { display: grid; gap: 4px; min-width: 460px; }
.dspt-ligne { display: grid; grid-template-columns: minmax(110px, 1.6fr) repeat(7, minmax(44px, 1fr)); gap: 4px; align-items: stretch; width: 100%; padding: 4px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); color: var(--ink); font: inherit; text-align: left; cursor: pointer; }
.dspt-ligne:hover { border-color: var(--etat-info); }
.dspt-ligne:focus-visible { outline: 3px solid var(--etat-info); outline-offset: 1px; }
.dspt-ligne--tete { border-color: transparent; background: transparent; cursor: default; }
.dspt-nom { display: flex; flex-direction: column; justify-content: center; min-width: 0; padding: 0 4px; font-weight: 600; overflow-wrap: anywhere; }
.dspt-ligne--tete .dspt-nom { font-size: .78rem; font-weight: 700; color: var(--ink-soft); }
.dspt-manque { font-size: .78rem; font-weight: 600; color: var(--etat-neutre); }
.dspt-tete { text-align: center; font-size: .78rem; font-weight: 700; line-height: 1.25; color: var(--ink-soft); }
.dspt-tete--auj { color: var(--etat-info); }
.dspt-case { position: relative; display: flex; align-items: flex-end; min-height: 44px; padding: 14px 4px 5px; border-radius: 8px; background: var(--paper, var(--etat-neutre-fond)); }
.dspt-case--passe { opacity: .45; }

/* L'avertissement sous la liste du sous-traitant, dans le formulaire d'une intervention : gris quand il n'a
   rien dit, rouge quand il a dit qu'il n'est pas disponible. Vide, il ne prend aucune place. */
.mention-dispo { margin: 6px 0 0; font-size: .88rem; font-weight: 600; color: var(--ink-soft); }
.mention-dispo:empty { display: none; }
.mention-dispo--non { color: var(--etat-erreur, #a3221b); }

/* ---------------------------------------------------------------------
   Libre, pris, absent : l'état de chacun au moment de planifier (formulaire d'une intervention), et les
   jours colorés quand on filtre sur une personne ou un sous-traitant (planning).
   Les couleurs sont celles de toute l'application : vert = libre, ambre = déjà pris, rouge = congés ou
   indisponible, gris = pas renseigné. La couleur ne va jamais seule : un mot la suit (ou, pour « libre »,
   est lu à voix haute), et le titre de la pastille le redit.
   --------------------------------------------------------------------- */
.case input[type="radio"] { width: 24px; height: 24px; margin: 2px 0 0; flex-shrink: 0; accent-color: var(--leaf); }

.etat-dispo { display: inline-flex; align-items: center; gap: 5px; margin-left: 8px; font-size: .8rem; font-weight: 600; white-space: nowrap; vertical-align: baseline; }
.etat-dispo i { flex: none; width: 10px; height: 10px; border-radius: 50%; background: currentColor; }
.etat-dispo--libre { color: var(--etat-ok); }
.etat-dispo--pris { color: var(--etat-attention); }
.etat-dispo--absent, .etat-dispo--indisponible { color: var(--etat-erreur); }
.etat-dispo--inconnu { color: var(--etat-neutre); }
.etat-dispo--inconnu i { background: transparent; border: 2px solid currentColor; box-sizing: border-box; }
/* « libre » est la règle : six mots identiques à la suite noieraient les exceptions. */
.etat-dispo--libre .etat-mot, .dispo-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.legende-dispo { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 0 0 10px; font-size: .82rem; }
.legende-dispo .etat-dispo { margin-left: 0; }

/* Le jour, coloré par ce que dit le sous-traitant (ou par les congés d'une personne) : une barre sous l'en-tête.
   Un pseudo-élément, et non un fond : `.grille-jours .entete-jour` pose un `background` qui effacerait un dégradé. */
.dispo-disponible, .dispo-indisponible, .dispo-partiel, .dispo-rien { position: relative; }
.dispo-disponible::after, .dispo-indisponible::after, .dispo-partiel::after, .dispo-rien::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 4px; border-radius: 0 0 10px 10px; pointer-events: none; }
.dispo-disponible::after { background: var(--etat-ok); }
.dispo-indisponible::after { background: var(--etat-erreur); }
.dispo-partiel::after { background: linear-gradient(90deg, var(--etat-ok) 50%, var(--etat-erreur) 50%); }
.dispo-rien::after { background: repeating-linear-gradient(90deg, var(--etat-neutre) 0 6px, transparent 6px 12px); }
.case-mois.dispo-disponible::after, .case-mois.dispo-indisponible::after, .case-mois.dispo-partiel::after, .case-mois.dispo-rien::after { height: 6px; }

/* ---------- Le dossier de prime d'un devis (dossier-prime.js) ---------- */
.badge-dp-a_preparer, .badge-dp-abandonne { background: var(--etat-neutre-fond); color: var(--etat-neutre); }
.badge-dp-envoye, .badge-dp-solde_envoye { background: var(--etat-info-fond); color: var(--etat-info); }
.badge-dp-complement, .badge-dp-solde_complement { background: var(--etat-attention-fond); color: var(--etat-attention); }
.badge-dp-accepte, .badge-dp-prime_versee { background: var(--etat-ok-fond); color: var(--etat-ok); }
.badge-dp-refuse { background: var(--etat-erreur-fond); color: var(--etat-erreur); }
.dp-entete { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.dp-entete h3 { margin: 0; }
.dp-pieces { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.dp-piece { padding: 10px 12px; border: 1px solid var(--bordure, #d9ded9); border-radius: 10px; background: var(--surface); }
.dp-piece.dp-ok { border-color: color-mix(in srgb, var(--etat-ok) 35%, transparent); }
.dp-piece.dp-manque { border-color: color-mix(in srgb, var(--etat-attention) 40%, transparent); }
.dp-piece:is(.dp-ok, .dp-manque) .dp-piece-titre::after {
  content: 'Fait'; margin-left: auto; padding: 1px 9px; border-radius: 999px;
  background: var(--etat-ok); color: #fff; font-size: .72rem; font-weight: 700;
}
.dp-piece.dp-manque .dp-piece-titre::after { content: 'À déposer'; background: var(--etat-attention); }
.dp-piece-titre { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-weight: 600; }
.dp-etiquette { font-size: .72rem; font-weight: 700; padding: 1px 8px; border-radius: 999px; background: var(--etat-neutre-fond); color: var(--etat-neutre); }
.dp-fichiers { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; font-size: .875rem; }
.dp-fichiers li { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.dp-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.dp-actions .btn { padding: 6px 12px; font-size: .875rem; }
.dp-obstacles { margin: 12px 0 0; padding: 10px 14px; border-radius: 10px; background: var(--ember-pale); border: 1px solid var(--ember); color: var(--ember); }
.dp-obstacles ul { margin: 4px 0 0 18px; padding: 0; }
.dp-suivi { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--bordure, #d9ded9); }
.dp-historique { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 6px; font-size: .875rem; }
.dp-historique .quand { color: var(--gris, #5f6b63); }
.dp-mails { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.dp-mail { padding: 10px 12px; border: 1px solid var(--bordure, #d9ded9); border-radius: 10px; font-size: .9rem; }
.dp-mail-recu { border: 1px solid color-mix(in srgb, var(--leaf) 35%, transparent); }
.dp-mail-envoye { border: 1px solid color-mix(in srgb, var(--gris, #5f6b63) 35%, transparent); background: var(--etat-neutre-fond); }
.dp-mail .quand { color: var(--gris, #5f6b63); font-size: .8rem; }
.dp-mail-texte { margin-top: 4px; white-space: pre-wrap; overflow-wrap: anywhere; }
.dp-citation { margin: 6px 0 0; padding: 8px 10px; border-radius: 8px; background: var(--etat-neutre-fond); font: inherit; font-size: .85rem; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 220px; overflow: auto; }
.badge-dp-liste { margin-left: 6px; font-size: .68rem; padding: 2px 8px; }

/* Le viseur photo de l'application (media.js, capturerPhoto) : plein écran, par-dessus les fenêtres. */
.viseur-photo { position: fixed; inset: 0; z-index: 90; display: flex; flex-direction: column; background: #000; }
.viseur-video { flex: 1; min-height: 0; width: 100%; object-fit: contain; background: #000; }
.viseur-barre { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px calc(14px + env(safe-area-inset-bottom, 0)); background: rgba(0, 0, 0, .7); }
.viseur-barre .btn { min-width: 104px; }
.viseur-declencheur { flex: none; width: 68px; height: 68px; border-radius: 50%; border: 4px solid #fff; background: rgba(255, 255, 255, .25); cursor: pointer; }
.viseur-declencheur:active { background: #fff; }

/* ---------- Le tableau de bord de la Direction (ecran-tableau-de-bord.js) ----------
   Maquette D du 03/11 : les tuiles « À faire » (l'état en toutes lettres, un fond coloré
   seulement pour ce qui presse), le bloc des ventes en couleur pleine, deux graphiques,
   les indicateurs, les chantiers. Aucun liseré. */
.tdb { display: flex; flex-direction: column; gap: 16px; }
.tdb-entete { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.tdb-titre { margin: 0; font-size: 1.1rem; color: var(--forest-2); }
.tdb-maj { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tdb-maj .btn { min-height: 40px; padding: 0 14px; }
.tdb-erreur { color: var(--etat-attention); }

.tdb-tuiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.tdb-tuile {
  display: flex; flex-direction: column; gap: 4px; min-height: 150px; padding: 16px;
  border: 1px solid transparent; border-radius: calc(var(--radius) + 4px);
  background: var(--surface); box-shadow: var(--ombre-carte);
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.tdb-tuile:active { background: var(--creux); }
.tdb-tuile.tdb-urgent    { background: var(--etat-erreur-fond);    border-color: color-mix(in srgb, var(--etat-erreur) 30%, transparent);    box-shadow: none; }
.tdb-tuile.tdb-attention { background: var(--etat-attention-fond); border-color: color-mix(in srgb, var(--etat-attention) 30%, transparent); box-shadow: none; }
.tdb-etiquette {
  align-self: flex-start; margin-bottom: 6px; padding: 2px 10px; border-radius: 999px;
  background: var(--etat-info); color: #fff; font-size: .76rem; font-weight: 700; line-height: 1.5;
}
.tdb-urgent .tdb-etiquette    { background: var(--etat-erreur); }
.tdb-attention .tdb-etiquette { background: var(--etat-attention); }
.tdb-nombre { font-family: var(--font-titre); font-size: 2rem; font-weight: 700; line-height: 1.1; color: var(--forest-2); font-variant-numeric: tabular-nums; }
.tdb-urgent .tdb-nombre    { color: var(--etat-erreur); }
.tdb-attention .tdb-nombre { color: var(--etat-attention); }
.tdb-libelle { font-weight: 700; }

.tdb-detail-entete { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.tdb-detail-entete h3 { margin: 0; }
.tdb-lignes { display: flex; flex-direction: column; }
.tdb-ligne, .tdb-jour {
  display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px; padding: 10px 0;
  border: 0; border-top: 1px solid var(--line); background: none; font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.tdb-ligne:first-child, .tdb-jour:first-of-type { border-top: 0; }
.tdb-ligne-quoi, .tdb-jour-quoi { flex: 1; min-width: 0; }
.tdb-jour-date { width: 92px; flex: none; font-weight: 700; color: var(--forest-2); }
.tdb-equipe { font-size: .85rem; color: var(--ink-soft); background: var(--creux); padding: 3px 10px; border-radius: 999px; white-space: nowrap; }

.tdb-ventes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; background: var(--leaf); color: #fff; }
.tdb-ventes h3 { color: #fff; opacity: .92; font-size: .95rem; margin: 0; }
.tdb-grand { font-family: var(--font-titre); font-size: 2.6rem; font-weight: 700; line-height: 1.1; margin: 6px 0 2px; font-variant-numeric: tabular-nums; }
.tdb-sous { display: block; opacity: .92; font-size: .9rem; }
.tdb-jauge { height: 12px; border-radius: 999px; background: rgba(255,255,255,.25); overflow: hidden; margin: 12px 0 6px; }
.tdb-jauge span { display: block; height: 100%; background: #fff; border-radius: 999px; }
/* Deux colonnes : le signé (ce qui est vendu) et le facturé (ce qui part en facture). */
.tdb-ventes-col { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.tdb-ventes-ecarts { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 4px; }
.tdb-ventes-ecarts > div { flex: 1 1 140px; background: rgba(255,255,255,.13); border-radius: 12px; padding: 8px 12px; font-size: .85rem; }
.tdb-ventes-ecarts b { display: block; font-size: 1.15rem; }
.tdb-sous b { font-weight: 700; }

.tdb-graphes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.tdb-graphe h3 { margin: 0 0 2px; }
.tdb-graphe .discret { margin: 0 0 8px; }
.tdb-canevas { position: relative; height: 230px; }
.tdb-legende { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 8px; font-size: .85rem; color: var(--ink-soft); }
.tdb-legende i { display: inline-block; width: 14px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: middle; }
.tdb-legende .tdb-marge-trait { height: 0; border-top: 3px solid var(--forest-2); }
.tdb-note { margin: 6px 0 0; font-size: .8rem; }
.tdb-cumul-marge { display: block; font-size: .9rem; margin-top: 2px; }
.tdb-cumul { margin: 4px 0 8px; font-size: 1.05rem; }
.tdb-cumul b { font-size: 1.4rem; color: var(--forest-2); margin-right: 8px; }
.tdb-bon { color: var(--etat-ok); font-weight: 700; }
.tdb-mauvais { color: var(--etat-erreur); font-weight: 700; }

.tdb-indicateurs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
.tdb-indicateur {
  display: flex; flex-direction: column; gap: 3px; padding: 16px;
  border-radius: calc(var(--radius) + 4px); background: var(--surface); box-shadow: var(--ombre-carte);
}
.tdb-ind-libelle { font-size: .9rem; font-weight: 700; color: var(--ink-soft); }
.tdb-ind-valeur { font-family: var(--font-titre); font-size: 1.8rem; font-weight: 700; line-height: 1.1; color: var(--forest-2); font-variant-numeric: tabular-nums; }
.tdb-ind-evolution { font-size: .9rem; font-weight: 700; }

@media (max-width: 900px) {
  .tdb-graphes, .tdb-ventes { grid-template-columns: 1fr; }
  .tdb-indicateurs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .tdb-tuiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .tdb-tuile { min-height: 136px; padding: 12px; }
  .tdb-nombre { font-size: 1.6rem; }
  .tdb-grand { font-size: 2.2rem; }
  .tdb-jour-date { width: 62px; font-size: .9rem; }
  .tdb-equipe { display: none; }
  .tdb-canevas { height: 200px; }
}

/* Réception d'une pose : « Suivre l'entretien ? » (signalement OVH 15). Des choix
   de la taille d'un doigt ; la périodicité se place sous « Oui ». */
.rc-suivi h3 { margin-bottom: 4px; }
.rc-suivi-choix { display: flex; align-items: center; gap: 10px; min-height: 44px; font-weight: 500; cursor: pointer; }
.rc-suivi-choix input { width: 22px; height: 22px; accent-color: var(--leaf); flex: none; }
.rc-suivi select { margin: 0 0 6px 32px; width: calc(100% - 32px); }
