/* ============================================================
   BUREAU DE BEAUFORT — ÉCRAN CAMPAGNES (css/campagnes.css)
   ------------------------------------------------------------
   theme.css contient déjà tout le vocabulaire visuel réutilisé ici
   (.fil-etapes/.zone-depot/.sugg-grille/.deux-col/.cadre-table/
   .tuile-template/.liste-activite/.recap-prix/.btn…). Ce fichier
   n'ajoute QUE ce qui manque pour l'assistant en 3 étapes :
   sélection de template cliquable, étapes du fil cliquables une
   fois franchies, cases de sélection de contacts, spinner d'aperçu
   témoin (repris à l'identique du spinner de l'éditeur).
   ============================================================ */

/* ---- Fil d'étapes : les étapes déjà passées deviennent cliquables ---
   ⚠️ 'franchie' ne suffisait pas : elle ne marque que les étapes situées AVANT
   l'étape courante. Revenu à l'étape I depuis la III, les onglets II et III
   perdaient donc leur curseur ET ne répondaient plus (campagnes.js : le clic
   était borné à « n < etat.etape »), alors qu'ils gardaient l'aspect d'onglets
   cliquables — c'est le retour d'Hubert du 17/08/2026. 'atteignable' porte
   désormais l'affordance : toute étape DÉJÀ FRANCHIE, dans les deux sens. */
.fil-etapes .et{cursor:default}
.fil-etapes .et.franchie{color:var(--encre)}
.fil-etapes .et.atteignable{cursor:pointer; color:var(--encre)}
.fil-etapes .et.atteignable:hover{background:var(--papier-2)}
.fil-etapes .et.atteignable:focus-visible{outline:2px solid var(--accent); outline-offset:-2px}

/* ---- Sélection de template (étape I) : réutilise .tuile-template ---- */
#camp-grille-templates .tuile-template.actif{
  border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-voile);
}

/* ---- Zone de dépôt CSV : état "fichier survolé" (drag&drop) --------- */
.zone-depot.survol{border-color:var(--accent); color:var(--accent-fonce); background:var(--accent-voile)}

/* ---- Table de contacts sélectionnables (étape II) ------------------- */
#camp-table-contacts td, #camp-table-contacts th{white-space:normal}
#camp-table-contacts .case-contact{cursor:pointer}

/* ---- Aperçu POLICE VIVANTE (étape III) — rendu côté navigateur (D-069).
   Classes AUTOPORTÉES : plus aucune dépendance à editeur.css. ---------- */

/* Spinner de chargement de l'écriture (autonome, ne dépend plus de .rendu-reel-*) */
.camp-spinner{
  min-height:180px; display:flex; align-items:center; justify-content:center;
  flex-direction:column; gap:12px; color:var(--encre-doux); font-size:.85rem;
}
.camp-roue{
  width:26px; height:26px; border-radius:50%;
  border:2px solid var(--accent); border-top-color:var(--accent-fonce);
  animation:camp-rotation 1s linear infinite;
}
@keyframes camp-rotation{ to{ transform:rotate(360deg); } }
/* Roue inline DANS un bouton d'action (import en cours) : blanche, pour rester
   lisible sur le fond bleu du bouton principal. */
.camp-btn-roue{
  display:inline-block; width:14px; height:14px; vertical-align:-2px; margin-right:8px;
  border:2px solid rgba(255,255,255,.45); border-top-color:#fff; border-radius:50%;
  animation:camp-rotation .8s linear infinite;
}
/* Sur un bouton SECONDAIRE (fond blanc), la roue blanche serait invisible :
   variante encre pour rester lisible. */
.btn.secondaire .camp-btn-roue{ border-color:var(--ligne); border-top-color:var(--accent); }

/* Indice à côté de « Continuer » (étape II) : n'apparaît QUE tant que le bouton
   est grisé (import pas encore fait) -> explique pourquoi on ne peut pas avancer. */
.camp-hint-continuer{ display:none; font-size:.82rem; color:var(--encre-doux); font-style:italic }
.camp-continuer-wrap:has(#camp-btn-continuer-2:disabled) .camp-hint-continuer{ display:inline }
/* Variante COMPACTE en ligne (analyse du fichier, cartes d'essai progressives) :
   même roue, posée à côté du texte au lieu d'un grand bloc centré. */
.camp-spinner.enligne{
  min-height:0; flex-direction:row; gap:8px; justify-content:flex-start;
  padding:4px 0;
}
.camp-spinner.enligne .camp-roue{ width:16px; height:16px; flex:0 0 auto; }
.camp-apercu-erreur{
  color:var(--cire, #a23); font-size:.85rem; text-align:center; padding:16px;
  display:flex; flex-direction:column; align-items:center; gap:10px;
}

/* Scène d'aperçu : la carte + l'enveloppe, à leur vrai ratio (posé en JS). */
.camp-apercu-scene{display:flex; flex-wrap:wrap; gap:16px; align-items:flex-start}
.camp-carte-physique{
  position:relative; flex:1 1 260px; min-width:220px; max-width:420px;
  background:#fffdf7; border-radius:6px; overflow:hidden;
  box-shadow:0 6px 20px rgba(20,30,60,.14); border:1px solid var(--ligne, #e6e2d8);
  /* aspect-ratio réel posé inline par le JS (dimsCourantes) */
}
.camp-carte-physique.camp-enveloppe{background:#fbf8ef}
/* z:4 = la PLUME AU-DESSUS du laser (on imprime d'abord, on trace ensuite). */
.camp-rendu-vivant{position:absolute; inset:0; pointer-events:none; z-index:4}
/* overflow:visible = MIROIR du correctif de l'editeur (.rendu-vivant svg,
   editeur.css) : le <svg> a un viewBox cale sur la largeur utile et le
   user-agent le clippe pile dessus (svg:not(:root){overflow:hidden}), ce qui
   tranche net les lettres liees/italiques qui debordent de leur boite
   d'avance. On laisse SEULEMENT le debordement se dessiner : ni le viewBox ni
   les dimensions passees au moteur ne bougent. La vraie limite physique reste
   .camp-carte-physique{overflow:hidden} ci-dessus — SEULE limite, ici comme
   cote editeur (aucun clip-path ajoute nulle part).
   ⚠️ CORRIGE s.143 : ce commentaire affirmait un invariant WYSIWYG qui n'etait
   PAS tenu — l'editeur portait alors un clip-path parasite sur .zone-ecriture
   (inset(0 -6px 0 -6px)) qui coupait, lui, a 6 px horizontal / 0 px vertical,
   pendant que cet ecran-ci ne coupait qu'au bord du carton. Un meme texte se
   coupait donc DIFFEREMMENT sur les deux ecrans. Ce clip-path a ete retire
   (editeur.css, .zone-ecriture) : les deux ecrans coupent maintenant au meme
   endroit — le bord physique du carton, seul et unique, sur les deux vues.

   ⚠️⚠️ clip-path:inset(0 -50% 0 -50%) = LA BORNE DU BAS, RÉTABLIE (s.143),
   STRICTEMENT IDENTIQUE a celle de .rendu-vivant svg (editeur.css) — c'est ce
   qui garantit que les deux ecrans coupent pareil. Le `overflow:hidden` du
   navigateur ne coupait pas QUE les cotes : il tenait AUSSI la hauteur. Le
   moteur plafonne le viewBox (`svgH = min(L.height, hauteurMax)`, vivante.js)
   mais emet quand meme TOUTES les lignes ; sans clip, un texte trop long se
   dessinait par-dessus tout le carton et par-dessus la couche imprimee
   (z-index 4 contre 3). `0` haut/bas = on coupe la ou le navigateur coupait ;
   `-50%` gauche/droite = cotes libres. ⛔ Toute modification ici doit etre
   repercutee a l'identique dans editeur.css, sinon les deux ecrans divergent
   de nouveau — c'est exactement le defaut corrige en s.143. */
.camp-rendu-vivant svg{display:block; width:100%; height:auto; overflow:visible; clip-path:inset(0 -50% 0 -50%)}
/* Couche imprimée (logo / texte imprimé) en overlay % — LECTURE SEULE. */
.camp-couche-imprimee{position:absolute; inset:0; pointer-events:none; z-index:3}
.camp-el-imprime{position:absolute; overflow:hidden; display:flex}
.camp-el-imprime img{width:100%; height:100%; object-fit:contain; display:block}
.camp-el-imprime embed{width:100%; height:100%; border:0; display:block; pointer-events:none}
.camp-el-texte{width:100%; height:100%; overflow:hidden; line-height:1.15; white-space:pre-wrap; word-break:break-word}
/* Recto imprimé = PDF importé (embed authentifié), à la place du manuscrit. */
.camp-recto-pdf{position:absolute; inset:0; background:#fff}
.camp-recto-pdf embed{width:100%; height:100%; display:block}
.camp-apercu-note{font-size:.8rem; color:var(--dore-fonce, #8a6d1c); text-align:center; padding:8px 6px 2px}

/* Navigation entre destinataires (dernier contrôle humain avant impression). */
.camp-apercu-nav{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
.camp-nav-btn{padding:6px 12px; min-width:0}
.camp-sel-dest{flex:1; min-width:160px; max-width:100%}
.camp-dest-libelle{margin-top:8px; font-size:.85rem; color:var(--encre); font-weight:600}
.camp-dest-libelle .camp-badge-nontracable{margin-left:8px}

/* Badge « adresse non traçable » (chiffres absents de la police). */
.camp-badge-nontracable{
  display:inline-block; font-size:.72rem; font-weight:600; padding:2px 8px;
  border-radius:10px; background:#fbe4e4; color:#a23; white-space:nowrap;
}

/* Bandeaux : ALERTE (bloquant, chiffres d'adresse) / AVERTISSEMENT (non bloquant). */
.camp-bandeau-alerte{
  background:#fbe4e4; color:#8a1c1c; border:1px solid #eaa; border-radius:var(--rayon-ctrl);
  padding:12px 14px; font-size:.85rem; line-height:1.4;
}
.camp-bandeau-alerte b{color:#7a1414}
.camp-bandeau-alerte .camp-lien-voir{
  display:inline-block; margin-top:6px; color:#8a1c1c; text-decoration:underline;
  background:none; border:none; cursor:pointer; font:inherit; padding:0;
}
.camp-bandeau-avert{
  background:var(--dore-voile, #f7efdc); color:var(--dore-fonce, #8a6d1c);
  border:1px solid var(--dore, #d9b64e); border-radius:var(--rayon-ctrl);
  padding:10px 14px; font-size:.83rem; line-height:1.4;
}
.camp-tracabilite-liste{margin:6px 0 0 18px; padding:0}
.camp-tracabilite-liste li{margin:2px 0}

/* Gate D-012/D-071 : le bouton « Lancer » désactivé doit SE VOIR (dernier
   contrôle avant impression). theme.css est NO-TOUCH -> style local ciblé. */
#camp-btn-lancer:disabled{opacity:.5; filter:grayscale(.35); cursor:not-allowed}

@media (max-width:1020px){
  .camp-carte-physique{max-width:100%}
}

/* ---- Mapping éditable des variables du template -> colonnes CSV ----- */
#camp-mapping-lignes .camp-map-ligne{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:8px 0; border-bottom:1px solid var(--ligne);
}
#camp-mapping-lignes .camp-map-ligne:last-child{border-bottom:none}
.camp-map-var{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.84rem;
  color:var(--accent-fonce); background:var(--accent-voile);
  border-radius:5px; padding:2px 8px; white-space:nowrap;
}
.camp-map-fleche{color:var(--encre-doux)}
.camp-map-select{flex:1; min-width:180px; width:auto}

/* ---- Confirmation de paiement (DEMO ou LIVE) ------------------------ */
.camp-confirmation{
  background:var(--accent-voile); color:var(--accent-fonce); border-radius:var(--rayon-ctrl);
  padding:14px 16px; font-size:.9rem; font-weight:600; text-align:center;
}

@media (max-width:1020px){
  .camp-apercu-images img{max-height:220px}
}

/* ============================================================
   ÉTAPE 0 — point de départ (2 portes) + PORTE A (fichier -> IA)
   ============================================================ */

/* Les deux grandes cartes de choix. */
.camp-portes{display:grid; grid-template-columns:1fr 1fr; gap:16px}
@media (max-width:760px){ .camp-portes{grid-template-columns:1fr} }
.camp-porte{
  display:flex; flex-direction:column; gap:8px; text-align:left;
  padding:20px; border:1px solid var(--ligne); border-radius:var(--rayon-ctrl, 10px);
  background:var(--papier, #fff); cursor:pointer; transition:border-color .12s, box-shadow .12s, background .12s;
  font:inherit; color:var(--encre);
}
.camp-porte:hover, .camp-porte:focus-visible{
  border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-voile); background:var(--accent-voile);
  outline:none;
}
.camp-porte-ic{font-size:1.7rem; line-height:1}
.camp-porte b{font-size:1rem}
.camp-porte span{font-size:.83rem; color:var(--encre-doux); line-height:1.4}

/* Choix de format (cartes cliquables). */
.camp-pa-formats{display:flex; gap:10px; flex-wrap:wrap}
.camp-pa-format{
  flex:1 1 150px; min-width:140px; text-align:left; padding:12px 14px;
  border:1px solid var(--ligne); border-radius:var(--rayon-ctrl, 10px);
  background:var(--papier, #fff); cursor:pointer; font:inherit; color:var(--encre);
  transition:border-color .12s, box-shadow .12s;
}
.camp-pa-format:hover, .camp-pa-format:focus-visible{border-color:var(--accent); outline:none}
.camp-pa-format.actif{border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-voile)}
.camp-pa-format b{display:block; font-size:.92rem}
.camp-pa-format small{display:block; color:var(--encre-doux); font-size:.78rem; margin-top:2px}

/* Chips de variables insérables (relecture Porte A). */
.camp-pa-chips{display:flex; gap:8px; flex-wrap:wrap}
.camp-pa-chip{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.8rem;
  color:var(--accent-fonce); background:var(--accent-voile); border:1px solid var(--accent-voile);
  border-radius:6px; padding:3px 9px; cursor:pointer;
}
.camp-pa-chip:hover, .camp-pa-chip:focus-visible{border-color:var(--accent); outline:none}

/* Raccourcis d'objectif (optionnels, toggle). Le modèle s'adapte sinon. */
.camp-pa-objectifs{display:flex; gap:8px; flex-wrap:wrap}
.camp-pa-objectif{
  padding:6px 14px; border:1px solid var(--ligne); border-radius:999px;
  background:var(--papier, #fff); cursor:pointer; font:inherit; font-size:.84rem; color:var(--encre);
  transition:border-color .12s, box-shadow .12s, background-color .12s;
}
.camp-pa-objectif:hover, .camp-pa-objectif:focus-visible{border-color:var(--accent); outline:none}
.camp-pa-objectif.actif{
  border-color:var(--accent); background:var(--accent-voile); color:var(--accent-fonce);
  box-shadow:0 0 0 2px var(--accent-voile);
}

/* Questions de clarification (mode assisté). */
.camp-pa-questions{display:flex; flex-direction:column; gap:12px}
.camp-pa-question label{display:block; font-size:.85rem; color:var(--encre); margin-bottom:6px}
.camp-pa-question textarea{width:100%; resize:vertical}
/* Suggestions cliquables d'une question guidée (D-074) : cliquer remplit la
   réponse (éditable) — le textarea reste le champ « autre ». */
.camp-pa-suggestions{display:flex; gap:6px; flex-wrap:wrap; margin-bottom:6px}
.camp-pa-suggestion{
  padding:4px 12px; border:1px solid var(--ligne); border-radius:999px;
  background:var(--papier, #fff); cursor:pointer; font:inherit; font-size:.8rem; color:var(--encre);
  transition:border-color .12s, background-color .12s;
}
.camp-pa-suggestion:hover, .camp-pa-suggestion:focus-visible{border-color:var(--accent); outline:none}
.camp-pa-suggestion.actif{border-color:var(--accent); background:var(--accent-voile); color:var(--accent-fonce)}

/* Dictée vocale : bouton micro ancré en HAUT-droit du champ (le bas-droit peut
   être occupé par un bouton du navigateur/extension -> collision de clic). */
.camp-champ-vocal{position:relative; display:block}
.camp-champ-vocal textarea{padding-right:44px}   /* place pour le micro */
.camp-mic{
  position:absolute; right:8px; top:8px; width:32px; height:32px; border-radius:50%;
  border:1px solid var(--ligne); background:var(--papier, #fff); cursor:pointer;
  display:flex; align-items:center; justify-content:center; font-size:15px; line-height:1;
  color:var(--encre-doux); z-index:3;
  transition:border-color .12s, background-color .12s, color .12s;
}
.camp-mic:hover, .camp-mic:focus-visible{border-color:var(--accent); color:var(--accent-fonce); outline:none}
.camp-mic.actif{
  border-color:var(--cire, #a23); color:#fff; background:var(--cire, #a23);
  animation:camp-mic-pulse 1.1s ease-in-out infinite;
}
@keyframes camp-mic-pulse{
  0%,100%{ box-shadow:0 0 0 0 rgba(170,34,51,.45) }
  50%    { box-shadow:0 0 0 6px rgba(170,34,51,0) }
}
.camp-mic-hint{margin-top:6px; font-size:.78rem; color:var(--encre-doux); font-style:italic; min-height:1em}

/* Historique VISIBLE des ajustements (puces retirables) — ils s'accumulent. */
.camp-consignes{display:flex; flex-wrap:wrap; gap:6px; align-items:center}
.camp-consignes-titre{font-size:.78rem; color:var(--encre-doux); width:100%}
.camp-consigne-chip{
  display:inline-flex; align-items:center; gap:6px; padding:3px 6px 3px 10px;
  border:1px solid var(--ligne); border-radius:999px; background:var(--accent-voile);
  color:var(--encre); font-size:.8rem;
}
.camp-consigne-x{
  border:none; background:transparent; cursor:pointer; color:var(--encre-doux);
  font-size:.75rem; line-height:1; padding:2px;
}
.camp-consigne-x:hover, .camp-consigne-x:focus-visible{color:var(--cire, #a23); outline:none}

/* Section « Reprendre une campagne » (écran de choix). */
.camp-reprise{margin-bottom:18px}
.camp-reprise h4{font-size:.85rem; margin:0 0 8px; color:var(--encre)}
.camp-reprise-liste{display:flex; flex-direction:column; gap:8px}
.camp-reprise-item{
  display:flex; align-items:center; gap:10px; padding:10px 12px;
  border:1px solid var(--ligne); border-radius:var(--rayon-ctrl, 10px); background:var(--papier, #fff);
}
.camp-reprise-item .infos{flex:1 1 auto; min-width:0}
.camp-reprise-item .nom{font-weight:600; color:var(--encre)}
.camp-reprise-item .meta{font-size:.78rem; color:var(--encre-doux)}
.camp-reprise-item .btn{white-space:nowrap}
.camp-reprise-suppr{
  border:1px solid var(--ligne); background:var(--papier, #fff); cursor:pointer;
  border-radius:8px; width:34px; height:34px; font-size:15px; color:var(--encre-doux);
}
.camp-reprise-suppr:hover, .camp-reprise-suppr:focus-visible{border-color:var(--cire, #a23); color:var(--cire, #a23); outline:none}

/* Personnalisation PAR DESTINATAIRE (étape III, D-074). */
.camp-perso-barre{
  height:8px; border-radius:999px; background:var(--accent-voile); overflow:hidden;
}
.camp-perso-barre-fill{
  height:100%; width:0; border-radius:999px; background:var(--accent);
  transition:width .4s ease;
}
.camp-perso-badge{
  display:inline-block; font-size:.78rem; padding:2px 10px; border-radius:10px;
  background:var(--accent-voile); color:var(--accent-fonce); white-space:nowrap;
}
.camp-perso-badge.echec{background:var(--dore-voile, #f7efdc); color:var(--dore-fonce, #8a6d1c)}
.camp-perso-badge.edite{background:var(--papier-2, #f6f4ee); color:var(--encre)}

/* Bulle d'analyse IA du fichier (chat de cadrage Porte A, D-074). */
.camp-pa-bulle-ia{
  background:var(--accent-voile); border:1px solid var(--ligne);
  border-radius:12px 12px 12px 4px; padding:10px 14px;
  font-size:.85rem; line-height:1.5; color:var(--encre);
}

/* Champ libre « Autre… » des questions à choix multiples (masqué tant que la
   chip Autre n'est pas sélectionnée). */
.camp-mcq-autre{width:100%; margin-top:6px; resize:vertical}
.camp-mcq-btn-autre{border-style:dashed}

/* Édition DIRECTE dans le rendu (pattern éditeur T-EDIT s.104) : textarea
   transparent superposé à la zone d'écriture de la carte — le texte tapé est
   rendu en police vivante en dessous (jamais de doublon machine). */
.camp-saisie-carte{
  position:absolute; z-index:3; margin:0; padding:0; border:none; outline:none;
  background:transparent; color:transparent; caret-color:var(--encre, #1a2740);
  -webkit-text-fill-color:transparent; resize:none; overflow:hidden;
  font:400 14px/1.5 'Inter', system-ui, sans-serif;
  white-space:pre-wrap; word-break:break-word; cursor:text;
}
.camp-saisie-carte:focus{outline:none}
/* Le caret NATIF vise la police d'écran invisible ci-dessus (14px Inter), pas
   les glyphes tracés : il « écrivait 3-4 lettres en avant ». Dès que le moteur
   peut fournir la géométrie réelle (classe posée au focus), on le masque et on
   dessine le nôtre à la position EXACTE du glyphe (BDB_VIVANTE.rectCaret). */
.camp-saisie-carte.caret-custom{caret-color:transparent}
/* ★ Le surlignage NATIF doit rester totalement invisible : un <textarea> en
   color:transparent redevient VISIBLE sélectionné (le style de sélection du
   navigateur repeint le texte machine par-dessus l'écriture — constaté dans
   l'éditeur, D-089). La sélection est redessinée par .camp-sel-vivante. */
.camp-saisie-carte::selection{background:transparent;color:transparent}
.camp-saisie-carte::-moz-selection{background:transparent;color:transparent}
.camp-caret-vivant{
  position:absolute; width:2px; background:var(--accent); z-index:5; display:none;
  pointer-events:none; border-radius:1px; animation:camp-caret-clignote 1.05s step-end infinite;
}
@keyframes camp-caret-clignote{0%,100%{opacity:1}50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.camp-caret-vivant{animation:none}}
/* Bandes de sélection à la géométrie du moteur, SOUS l'encre (z 2 < rendu 4). */
.camp-sel-vivante{position:absolute; inset:0; pointer-events:none; z-index:2}
.camp-sel-vivante .sel-bande{
  position:absolute; background:var(--accent); opacity:.24; border-radius:2px;
}
@media (prefers-contrast:more){.camp-sel-vivante .sel-bande{opacity:.38}}
.camp-carte-physique.edition{box-shadow:0 0 0 2px var(--accent-voile), 0 0 0 3px var(--accent)}

/* Cartes d'ESSAI (échantillon avant le lot complet). */
.camp-perso-essai-liste{display:flex; flex-direction:column; gap:8px}
.camp-essai-carte{
  background:var(--papier-2, #f6f4ee); border:1px solid var(--ligne);
  border-radius:var(--rayon-ctrl, 10px); padding:10px 12px; font-size:.83rem; line-height:1.5;
}
.camp-essai-carte b{display:block; font-size:.8rem; margin-bottom:4px}
.camp-essai-carte p{margin:0; white-space:pre-wrap}
.camp-essai-carte.echec{border-color:var(--dore-fonce, #8a6d1c)}

/* ---- Récap d'import : colonnes reconnues + adresse (Lot 1, porte B et A) --- */
.camp-recap-colonnes{
  background:var(--papier-2, #f6f4ee); border:1px solid var(--ligne);
  border-radius:var(--rayon-ctrl, 10px); padding:12px 14px; font-size:.84rem; line-height:1.5;
}
.camp-recap-colonnes b{color:var(--encre)}
.camp-recap-chips{display:flex; gap:6px; flex-wrap:wrap; margin-top:6px}
.camp-recap-chip{
  display:inline-block; font-size:.78rem; padding:2px 9px; border-radius:10px;
  background:var(--accent-voile); color:var(--accent-fonce); white-space:nowrap;
}
.camp-recap-chip.adresse{background:var(--dore-voile, #f7efdc); color:var(--dore-fonce, #8a6d1c)}
.camp-recap-note{margin-top:8px; color:var(--encre-doux); font-size:.8rem}
.camp-recap-apercu{margin-top:8px; overflow-x:auto; overflow-y:auto; max-height:240px}
.camp-recap-apercu table{border-collapse:collapse; font-size:.78rem; min-width:100%}
.camp-recap-apercu th, .camp-recap-apercu td{
  border:1px solid var(--ligne); padding:3px 8px; text-align:left; white-space:nowrap;
  color:var(--encre-doux);
}
/* en-tête collant : les noms de colonnes restent visibles en défilant vers le bas */
.camp-recap-apercu th{color:var(--encre); background:var(--papier, #fff); position:sticky; top:0; z-index:1}

/* Aperçu du fichier DANS l'éditeur de mapping (retour Hubert) : titre + colonne
   surlignée quand elle est reliée à une variable {{...}}. */
.camp-map-apercu-titre{font-size:.8rem; color:var(--encre-doux); margin-bottom:6px}
.camp-recap-apercu td.col-mappee{background:var(--accent-voile); color:var(--encre)}
.camp-recap-apercu th.col-mappee{background:var(--accent-voile); color:var(--accent-fonce)}

/* ---- Détail du tarif (étape III, .recap-prix) — retour Hubert : le tarif
   de campagne doit détailler ses postes comme le devis (CATALOGUE_PRIX.html
   §03 « Détail du prix »). Rempli par rendreDetailPrix() (campagnes.js).
   Sur le bloc .recap-prix (fond encre foncé, texte blanc) : lignes plus
   petites que .ligne (le tarif « / carte » reste le repère principal), une
   par poste actif, jamais une ligne à 0.
   Contraste (retour oeil, mesure) : .5 d'opacité ne donnait que 4,59:1 (à
   peine au-dessus du minimum AA) contre 7,71:1 pour .ligne juste au-dessus
   -- même opacité que .ligne (.72) pour rester ≥ 7:1, quitte à perdre un
   peu de la hiérarchie visuelle voulue (un détail nettement plus lisible
   vaut mieux qu'un détail à peine lisible). ---------------------------- */
.recap-detail{margin-top:2px}
.recap-detail:empty{margin-top:0}
.recap-detail .recap-detail-ligne{
  font-size:.76rem; padding:2px 0; color:rgba(255,255,255,.72);
}
/* ⛔ UN MONTANT NE SE COUPE JAMAIS EN DEUX (vu a l'ecran le 13/08/2026, sur un
   libelle de poste un peu long) : la colonne du recap fait ~340 px, et sans
   `nowrap` le flex laissait « + 2,00 » sur une ligne et « € » toute seule a la
   suivante. C'est un prix : il se lit d'un bloc, quel que soit le libelle. */
.recap-detail .recap-detail-ligne > span:last-child{white-space:nowrap; padding-left:10px}

/* ---- Détail du tarif, TOUJOURS DÉROULÉ (Hubert, 13/08/2026) ---------------
   C'était un <details> replié par défaut, pour éviter que ses ~46 px poussent
   « Lancer la campagne » hors champ à 1366×673. La place est reprise autrement
   (le bouton est remonté juste sous la box), et un prix qui demande un clic
   pour être lu est un prix que le client ne lit pas. Titre simple, non
   cliquable : plus rien à ouvrir, donc plus rien qui ressemble à un bouton.
   Masqué en entier par campagnes.js quand il n'y a aucun poste. ---------- */
.recap-detail-bloc{margin-top:6px}
.recap-detail-bloc > .recap-detail{margin-top:2px}
.recap-detail-titre{
  font-size:.68rem; letter-spacing:.1em; text-transform:uppercase;
  color:rgba(255,255,255,.45); font-weight:600; padding:2px 0;
}

/* ---- ★ AFFRANCHISSEMENT — la ligne cliquable, SOUS la box du prix ----------
   Hubert, 13/08/2026 : « on a l'impression que c'est doublé compté avec ce
   format là ». Elle vivait DANS le bloc .recap-prix, trois lignes sous le poste
   « Affranchissement — envoi direct + 2,00 € » du détail : deux montants
   identiques dans le même cadre se lisent comme deux dépenses. Sortie de la
   box, elle redevient ce qu'elle est — une COMMANDE qui agit sur le total —
   pendant que le détail reste le seul endroit qui chiffre les postes.
   Fond CLAIR désormais (elle est dans .carte-ui, plus sur l'encre).
   Toute la ligne est cliquable (<label for>) et non la seule case : sur
   1366×673 une case de 14 px est une cible minuscule.
   Cochée, elle prend la couleur d'accent : à cet endroit l'état « c'est activé »
   doit se voir d'un coup d'oeil, sans relire le libellé. ------------------- */
.recap-option{
  display:flex; align-items:flex-start; gap:10px; cursor:pointer;
  margin-top:10px; padding:10px 12px; border-radius:12px;
  font-size:.82rem; line-height:1.35; color:var(--encre-doux);
  border:1px solid var(--ligne); background:var(--blanc-carte);
  -webkit-tap-highlight-color:transparent; user-select:none;
  transition:background .12s ease, border-color .12s ease;
}
.recap-option:hover{border-color:var(--accent); background:var(--papier)}
.recap-option:has(input:checked){
  border-color:var(--accent); background:var(--accent-voile, var(--papier));
}
.recap-option input[type=checkbox]{
  flex:none; margin:1px 0 0; width:15px; height:15px; cursor:pointer;
  accent-color:var(--accent);
}
/* Titre et prix sur la MÊME ligne, description dessous : le libellé courait
   sinon sous le montant sur trois lignes, et l'oeil ne savait plus quel prix
   allait avec quel mot (vu à l'écran le 13/08/2026). */
.recap-option .recap-option-txt{flex:1 1 auto; min-width:0}
.recap-option .recap-option-txt b{color:var(--encre); font-weight:600; display:block}
.recap-option .recap-option-sous{display:block; margin-top:1px; font-size:.78rem}
/* Le montant ne se coupe JAMAIS en deux (même règle que les lignes du détail). */
.recap-option .recap-option-prix{flex:none; white-space:nowrap; padding-left:6px; font-weight:600}
.recap-option:has(input:checked) .recap-option-prix{color:var(--accent-fonce, var(--encre))}
/* Campagne lancée : le tarif est figé, la ligne n'est plus une commande. */
.recap-option:has(input:disabled){cursor:default; opacity:.55}
.recap-option:has(input:disabled):hover{border-color:var(--ligne); background:var(--blanc-carte)}
.recap-option input[type=checkbox]:disabled{cursor:default}


/* ---- ★ SAISIE À LA MAIN d'un destinataire (Hubert, 08/08/2026) ------------
   3e voie de l'étape II, à côté de l'import de fichier et du carnet, pour les
   ENVOIS UNITAIRES (« certaine personne utiliserons peut être pour des envoies
   unitaire »). Replié par défaut : l'écran d'Hubert fait 1366×673 et le bouton
   d'action de l'étape est déjà juste sous le pli — 6 champs dépliés en
   permanence l'enfonceraient. Le repli natif <details> ne coûte qu'un clic et
   ne dépend d'aucun JS. */
.camp-manuel{margin-top:16px}
.camp-manuel > summary{cursor:pointer; list-style:none; display:flex; align-items:baseline;
  gap:8px; flex-wrap:wrap}
.camp-manuel > summary::-webkit-details-marker{display:none}
.camp-manuel > summary::before{content:'▸'; font-size:.7rem; color:var(--encre-doux)}
.camp-manuel[open] > summary::before{content:'▾'}
.camp-manuel-sous{font-weight:400; font-size:.8rem; color:var(--encre-doux); letter-spacing:0}
.camp-manuel-corps{margin-top:14px}
/* 2 colonnes : tient a 1366 px, retombe a 1 colonne sur petit ecran. */
.camp-manuel-grille{display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:10px 14px}
.camp-manuel-grille .camp-man-large{grid-column:1 / -1}
.camp-manuel-grille .champ{display:flex; flex-direction:column; gap:4px; margin:0}
.camp-manuel-grille input{width:100%}
.camp-man-opt{font-weight:400; color:var(--encre-doux); text-transform:none; letter-spacing:0}
.camp-manuel-actions{display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin-top:14px}
.camp-manuel-aide{margin:0}
.camp-man-ok{margin-top:10px; color:var(--vert, #2f7a4f); font-weight:600}
@media (max-width:720px){
  .camp-manuel-grille{grid-template-columns:1fr}
}

/* ===== « Modifier mon template » depuis l'étape III (18/08/2026) =====
   Demande d'Hubert : ouvrir l'éditeur sur le template de la campagne, et y
   revenir à l'enregistrement.

   ⚠️ L'aide n'est pas décorative. À quelques centimètres vivent « Enregistrer
   ce texte » et « ↻ Régénérer cette carte », qui ne touchent QUE la carte
   affichée ; celui-ci touche TOUS les destinataires. Sans la phrase, les trois
   boutons se ressemblent et la confusion est garantie. */
.camp-modif-tpl{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  margin-top:12px; padding-top:12px; border-top:1px solid var(--ligne);
}
.camp-modif-tpl-aide{font-size:.78rem; color:var(--encre-doux); flex:1; min-width:180px}
.camp-modif-tpl .btn[disabled]{opacity:.55; cursor:default}

/* ---- CONCORDANCE (25/08/2026) : ce qui cloche entre la carte et sa fiche ---
   Deux niveaux, et ils ne disent pas la même chose :
     .grave     = la carte s'adresse a quelqu'un d'autre, ou cite un autre
                  destinataire du lot -> a corriger avant de lancer ;
     .attention = a verifier (un prenom qui n'est celui de personne, une
                  adresse recopiee dans le texte).
   Rien ne s'affiche quand tout concorde : c'est le cas normal, et un bandeau
   permanent finirait par ne plus etre lu. */
.camp-concordance{
  margin:0 0 6px; padding:9px 11px; border-radius:var(--rayon-ctrl);
  font-size:.83rem; line-height:1.35;
}
.camp-concordance.grave{
  background:rgba(162,51,51,.08); color:var(--cire, #a23);
  border:1px solid rgba(162,51,51,.25); font-weight:600;
}
.camp-concordance.attention{
  background:var(--accent-voile); color:var(--encre-doux);
  border:1px solid var(--ligne);
}
