/* ============================================================
   BUREAU DE BEAUFORT — ÉDITEUR DE TEMPLATE (css/editeur.css)
   ------------------------------------------------------------
   theme.css contient déjà tout le vocabulaire visuel de l'éditeur
   (.editeur/.panneau/.chips/.chip/.recap-prix/.scene-apercu/
   .carte-physique/.texte-manuscrit/.curseur-plume/.bascule…).
   Ce fichier n'ajoute QUE ce qui manque : le champ « Votre message »
   (highlighted textarea), l'aperçu police vivante dans la carte et
   les notes d'aperçu (débordement / variables).
   ============================================================ */

/* Message inline de l'éditeur (état d'enregistrement / info ponctuelle),
   au-dessus du bouton « Enregistrer le template ». */
#msg-rendu-reel{background:var(--papier-2); border-radius:var(--rayon-ctrl); padding:10px 12px; margin-top:0}

/* Spinner + message d'erreur PARTAGÉS avec l'aperçu témoin des campagnes
   (campagnes.js réutilise .rendu-reel-spinner / .roue / .rendu-reel-erreur) —
   conservés ici tant que campagnes.css n'a pas ses propres classes. */
.rendu-reel-spinner{
  display:flex; flex-direction:column; align-items:center; gap:10px;
  color:var(--encre-doux); font-size:.85rem;
}
.rendu-reel-spinner .roue{
  width:26px; height:26px; border-radius:50%;
  border:3px solid var(--ligne); border-top-color:var(--accent);
  animation:tourne .8s linear infinite;
}
@keyframes tourne{to{transform:rotate(360deg)}}
.rendu-reel-erreur{color:var(--cire); font-size:.85rem}

/* ============================================================
   CHAMP « VOTRE MESSAGE » — éditeur de texte CLAIR (highlighted
   textarea). Un textarea TRANSPARENT au-dessus d'un div de
   surlignage : le caret natif se place au clic, le div peint le
   même texte avec les {{variables}} en jetons colorés + littéraux.
   PIÈGE CLÉ : le textarea et le div DOIVENT avoir des métriques
   STRICTEMENT identiques (font, line-height, padding, box-sizing,
   white-space, word-break) sinon le surlignage se décale du texte.
   -> la classe commune ci-dessous fixe ces métriques pour les deux.
   ============================================================ */
.editeur-texte{position:relative}
.editeur-texte .saisie-claire,
.editeur-texte .surlignage{
  /* métriques PARTAGÉES (doivent coïncider au pixel près) */
  font-family:'Inter',sans-serif; font-size:.92rem; line-height:1.55;
  letter-spacing:normal; padding:11px 13px;
  border:1px solid transparent; border-radius:var(--rayon-ctrl);
  box-sizing:border-box; width:100%;
  white-space:pre-wrap; word-break:break-word; overflow-wrap:break-word;
  min-height:170px; max-height:340px;
  /* La gouttière de barre de défilement DOIT être réservée IDENTIQUEMENT sur
     les deux couches : sinon, quand le textarea affiche sa scrollbar (texte
     long > max-height), sa largeur de texte rétrécit alors que le div reste
     pleine largeur -> le retour à la ligne diverge et le surlignage se
     décale du caret (piège classique du highlighted-textarea). */
  scrollbar-gutter:stable;
}
/* Textarea au-dessus : texte transparent, SEUL le caret est visible,
   il reçoit les clics/frappe (caret natif positionnable = clic = curseur). */
.editeur-texte .saisie-claire{
  position:relative; z-index:2; display:block; margin:0; resize:none;
  background:transparent; color:transparent; caret-color:var(--encre);
  border-color:var(--ligne); overflow:auto; -webkit-text-fill-color:transparent;
}
.editeur-texte .saisie-claire:focus{outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-voile)}
/* Div de surlignage dessous : peint le texte + les jetons. Ne reçoit
   aucun clic (le textarea au-dessus s'en charge). overflow:hidden : le
   scroll est recopié depuis le textarea par editeur.js. */
.editeur-texte .surlignage{
  position:absolute; inset:0; z-index:1; margin:0;
  color:var(--encre); background:var(--blanc-carte);
  overflow:hidden; pointer-events:none; user-select:none;
}
/* Jeton {{variable}} : littéral, coloré. Le padding horizontal est ANNULÉ par
   une marge négative égale -> l'avance inline du jeton = celle du texte brut du
   textarea (sinon chaque jeton pousserait le texte suivant de 4px et décalerait
   le caret, cumulativement). box-decoration-break:clone garde le fond correct. */
.editeur-texte .surlignage .jeton{
  color:var(--accent-fonce); background:var(--accent-voile);
  border-radius:5px; padding:0 2px; margin:0 -2px;
  box-decoration-break:clone; -webkit-box-decoration-break:clone;
}
/* Jeton mal formé ({{...}} non-\w, jamais substitué côté serveur) : signalé. */
.editeur-texte .surlignage .jeton.invalide{
  color:var(--cire); background:color-mix(in srgb, var(--cire) 14%, transparent);
}
#note-variables code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.86em;background:var(--papier-2);padding:1px 4px;border-radius:4px}

/* ============================================================
   OPTIONS DE TEXTE (T-079) — alignement + taille du bloc manuscrit.
   Réglages qui se répercutent à l'écran ET sur le tracé (police vivante).
   ============================================================ */
.opt-texte{display:flex;flex-direction:column;gap:12px}
.opt-alignement{display:flex;gap:6px;flex-wrap:wrap}
.opt-alignement button{
  flex:1;min-width:64px;padding:7px 8px;font-size:.8rem;
  border:1px solid var(--ligne);border-radius:var(--rayon-ctrl);
  background:var(--blanc-carte);color:var(--encre-doux);cursor:pointer;
}
.opt-alignement button:hover{border-color:var(--accent);color:var(--accent-fonce)}
.opt-alignement button.actif{border-color:var(--accent);background:var(--accent-voile);color:var(--accent-fonce);font-weight:600}
.opt-taille{display:flex;flex-direction:column;gap:4px}
.opt-taille .lib-sec{font-size:.8rem;color:var(--encre-doux)}
.opt-taille input[type=range]{width:100%;accent-color:var(--accent)}
#opt-taille-val{color:var(--accent-fonce);font-weight:600}

/* ============================================================
   ÉLÉMENTS IMPRIMÉS COMPOSABLES (T-079) — logo + texte imprimé.
   Overlay % sur la carte (couche laser, distincte du manuscrit).
   Le conteneur est pointer-events:none : seuls les éléments captent
   la souris (les clics à vide retombent sur l'aperçu manuscrit).
   ============================================================ */
/* ============================================================
   MODÈLE « DIAPO » (refonte type PowerPoint) : la carte EST la slide.
   Barre d'insertion au-dessus, blocs déplaçables dedans, barre d'outils
   FLOTTANTE au-dessus du bloc sélectionné, guides d'alignement.
   ============================================================ */
/* ---- RUBAN (façon PowerPoint) : commandes groupées, toujours visibles ---- */
.ruban{
  /* ★ Le ruban COLLE à la zone d'édition (demande d'Hubert, 30/07/2026 :
     « un peu d'espace entre la barre d'outils et le haut de la zone
     d'édition ... pour que ça colle »). Mesuré au banc de gestes : 14 px
     de vide pur ici + 8 px sous la barre de vue. Rendus à la feuille — la
     boîte de référence de l'échelle est globale et figée, les 3 formats
     grandissent donc du MÊME ratio (invariant D-088 préservé). */
  display:flex;align-items:stretch;gap:0;flex-wrap:wrap;margin:0 0 2px;
  background:var(--blanc-carte);border:1px solid var(--ligne);border-radius:var(--rayon);
  padding:6px 4px 3px;box-shadow:var(--ombre);
}
.ruban .grp{display:flex;flex-direction:column;gap:3px;padding:0 10px;border-right:1px solid var(--ligne)}
.ruban .grp:last-child{border-right:0}
.ruban .grp-corps{display:flex;align-items:center;gap:3px;flex-wrap:wrap;min-height:30px}
.ruban .grp-nom{font-size:.63rem;letter-spacing:.05em;text-transform:uppercase;color:var(--encre-doux);text-align:center}
.ruban .rb-col{display:flex;flex-direction:column;gap:4px}
.ruban .rb-ligne{display:flex;align-items:center;gap:4px}
.ruban button{
  font:inherit;font-size:.85rem;line-height:1;cursor:pointer;min-width:28px;height:28px;
  padding:0 7px;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--encre);
  display:inline-flex;align-items:center;justify-content:center;
}
/* Icônes fines (alignement, organiser) — même gabarit que le ruban PowerPoint. */
.ruban .ic-al{width:16px;height:16px;fill:currentColor;display:block}
/* Boutons d'INSERTION : icône AU-DESSUS, libellé DESSOUS (format PPT). */
.ruban .ins-btn{
  flex-direction:column;gap:3px;height:auto;min-width:60px;padding:5px 8px 4px;
  font-size:.7rem;color:var(--encre);white-space:nowrap;
}
.ruban .ins-btn .ic-ins{width:26px;height:26px;display:block;color:var(--accent-fonce)}
.ruban .ins-btn:hover .ic-ins{color:var(--accent)}
.ruban .ins-btn span{line-height:1.1}
.ruban .tb-sep{width:1px;height:18px;background:var(--ligne);margin:0 3px;display:inline-block}
.ruban button:hover:not(:disabled){background:var(--papier-2);border-color:var(--ligne)}
.ruban button.actif{background:var(--accent-voile);border-color:var(--accent);color:var(--accent-fonce)}
.ruban button:disabled{opacity:.32;cursor:default}
.ruban button sup,.ruban button sub{font-size:.6em}
.ruban select,.ruban input[type=number]{
  font:inherit;font-size:.82rem;height:28px;padding:0 6px;border:1px solid var(--ligne);
  border-radius:6px;background:var(--blanc-carte);color:var(--encre);width:auto;
}
.ruban select{max-width:150px}
.ruban select:disabled,.ruban input:disabled{opacity:.4}
.ruban input[type=number]{width:52px}
.ruban input[type=color]{width:28px;height:28px;padding:0;border:1px solid var(--ligne);border-radius:6px;background:none;cursor:pointer}
/* Icônes d'alignement : on rend les 4 boutons distincts visuellement. */
#rb-aligns button[data-al="gauche"]{text-align:left}
#rb-aligns button[data-al="centre"]{text-align:center}
#rb-aligns button[data-al="droite"]{text-align:right}
#rb-aligns button[data-al="justifie"]{letter-spacing:-1px}

/* ---- Grille 3 colonnes SANS SCROLL : réglages | diapo | prix ---- */
/* ============================================================
   MISE EN PAGE PLEIN ÉCRAN (façon PowerPoint) : ruban en haut (centré),
   réglages à gauche (largeur fixe), PRIX calé à droite, et la VISIONNEUSE
   au centre qui prend tout l'espace restant, sur tous les écrans.
   ============================================================ */
/* ★ L'éditeur est un ATELIER plein écran : on lève le gabarit de lecture
   (.page{max-width:1240px}) qui laissait ~470 px inutilisés à droite, et on
   réduit les marges pour que la visionneuse et le panneau prix aillent
   jusqu'aux bords. */
#page-editeur.page.actif{
  /* ⚠️ Le repli `calc(100vh - 104px)` ne sert QUE le temps que le JS pose
     --h-editeur. Il valait 104 px alors que le chrome au-dessus en occupe ~140
     (entête + lien retour + paddings) : mesuré le 09/08, corrigé ici.
     ⚠️ min-height : 520 px était un PIÈGE à 673 px de haut. 673 − 140 − 14 = 519,
     donc le plancher l'emportait d'1 px et rendait toute la page défilante.
     Ramené à 460 : il redevient ce qu'il doit être, un garde-fou pour les très
     petits écrans, jamais une contrainte à la taille de travail d'Hubert. */
  display:flex;flex-direction:column;height:var(--h-editeur, calc(100vh - 132px));min-height:460px;
  max-width:none;width:100%;padding:6px 10px 12px;
}

/* ---- ★ ENTÊTE COMPACTE, DANS L'ÉDITEUR SEULEMENT (09/08/2026) ----------
   Hubert : « tu as pas mal de place au-dessus, Éditeur de template, Retour aux
   templates, je pense qu'on peut les réduire un peu et monter le ruban plus
   haut pour laisser plus de place en dessous pour l'aperçu et les colonnes ».
   ⚠️ `.entete` est un nœud UNIQUE, partagé par TOUS les écrans (dashboard,
   templates, campagnes…), et `theme.css` est NO-TOUCH (PROPRIETE_FICHIERS.md).
   On ne touche donc ni au fichier ni aux autres écrans : on surcharge ici, en
   ne ciblant QUE le cas où l'éditeur est la page active. `:has()` est supporté
   par le Chrome/Edge de ce poste ; si un jour il ne l'était pas, l'entête
   reprendrait simplement sa taille d'origine — aucune casse.
   Ce que ça rend : ~37 px, qui retombent AUTOMATIQUEMENT dans l'aperçu et les
   colonnes (la chaîne sous le ruban est élastique : --h-editeur -> flex:1). */
main:has(#page-editeur.page.actif) .entete{padding-top:9px;padding-bottom:9px}
main:has(#page-editeur.page.actif) .entete h1{font-size:1.16rem;line-height:1.3}
main:has(#page-editeur.page.actif) .entete .badge-compte{padding-top:4px;padding-bottom:4px}
/* Le lien « ← Retour aux templates » : plus discret, moins haut. */
#page-editeur.page.actif > .btn.discret{padding:2px 8px;font-size:.82rem}
.editeur.editeur-ppt{
  display:grid;   /* explicite : ne dépend plus de theme.css */
  grid-template-columns:248px minmax(0,1fr) 250px;gap:16px;align-items:stretch;
  flex:1;min-height:0;
}
@media (max-width:1450px){.editeur.editeur-ppt{grid-template-columns:226px minmax(0,1fr) 228px;gap:12px}}
@media (max-width:1180px){
  #page-editeur.page.actif{height:auto;display:block}
  .editeur.editeur-ppt{grid-template-columns:1fr;gap:16px}
}
/* Ruban : contenu CENTRÉ tant qu'il tient — mais JAMAIS de débordement à gauche.
   ⛔ `justify-content:center` + `overflow-x:auto` est un piège : dès que le
   contenu dépasse, il déborde des DEUX côtés et le défilement ne peut pas aller
   en négatif -> la partie gauche devient définitivement inatteignable, sans même
   une barre de défilement pour le signaler. C'est le « je ne peux même plus
   accéder à tout à gauche » d'Hubert (08/08/2026). Le correctif d'alors n'avait
   fait que réduire la largeur du contenu : le MÉCANISME restait armé, et la
   largeur du ruban dépend de DONNÉES (les noms des écritures, 13 polices) — un
   nom plus long, un zoom, et le défaut revenait.
   Les marges automatiques centrent quand il y a de la place et valent 0 quand il
   n'y en a plus : le contenu se colle alors à gauche et tout reste atteignable. */
.ruban{justify-content:flex-start;flex-wrap:nowrap;overflow-x:auto;position:sticky;top:0;z-index:16}
.ruban > :first-child{margin-left:auto}
.ruban > :last-child{margin-right:auto}
/* Le groupe « Fichier » reste DANS le flux centré : un margin-left:auto le
   collait à droite et cassait le centrage demandé sur les grands écrans.
   (Le `margin-right:auto` posé ci-dessus sur le DERNIER enfant ne fait pas ça :
   combiné au `margin-left:auto` du PREMIER, les deux se partagent l'espace
   libre — c'est ce qui centre. Mesuré : 20 px de chaque côté à 1100 px.) */
.ruban .grp-fin{border-right:0}
@media (max-width:1340px){.ruban{gap:0}.ruban .grp{padding:0 6px}}
.ins-btn-primaire{color:var(--accent-fonce)}
.ins-btn-primaire .ic-ins{color:var(--accent)}
/* Colonnes : chacune défile pour elle-même, jamais la page entière. */
.editeur-ppt .panneau{min-height:0;overflow:auto;padding-right:4px}
/* ★ `top:0` OBLIGATOIRE. theme.css:134 (NO-TOUCH) pose
   `.scene-apercu{position:sticky;top:92px}` ; en passant en `relative` sans
   neutraliser `top`, ces 92 px deviennent un DÉCALAGE SEC et permanent.
   Mesuré au banc : colonne centrale à 318,8 px alors que la colonne de
   gauche démarre à 226,8 px — c'est l'essentiel de « l'espace entre la
   barre d'outils et le haut de la zone d'édition » signalé par Hubert
   (les marges de 14 et 8 px ne pesaient que 22 px sur 136). */
.editeur-ppt .scene-apercu{position:relative;top:0;display:flex;flex-direction:column;min-height:0}
.editeur-ppt .colonne-prix{
  /* ★ .colonne-prix est un <aside> : theme.css lui impose width:244px, height:100vh,
     position:sticky et une bordure de barre latérale. C'est ce qui forçait la
     grille à déborder (scroll vertical) et dépassait la colonne (scroll
     horizontal). On neutralise TOUT ici. */
  position:static;width:auto;height:auto;min-height:0;max-height:100%;
  overflow:auto;border-right:0;flex-shrink:1;top:auto;
}
.editeur-ppt .panneau{gap:13px}
.editeur-ppt .panneau .champ label.lib{margin-bottom:5px}
/* La planche défile quand le format dépasse (A4 au zoom 100 %), comme la zone
   de travail de PowerPoint. */
/* LA VISIONNEUSE : prend toute la hauteur restante de la colonne centrale
   (comme la zone de diapo de PowerPoint) et défile si le format dépasse. */
.editeur-ppt .planche{
  padding:22px;min-height:0;flex:1;max-height:none;overflow:auto;
  /* « exactement comme ppt » : la feuille est centrée dans les DEUX sens.
     `safe` = si elle est plus grande que le cadre (zoom manuel), on repart du
     haut au lieu de couper le sommet, ce que ferait un `center` sec. */
  align-items:safe center;justify-content:safe center;
}
/* MISE EN PAGE EMPILÉE (< 1180 px) : la visionneuse est plafonnée à 70vh.
   ⚠ LIMITE CONNUE ET MESURÉE : dans cette mise en page seulement, la hauteur de
   la visionneuse dépend encore de son contenu, donc l'échelle physique dérive
   entre formats (rapports 1,169 / 1,985 au lieu de 1,4832 / 1,4293) et l'A6
   s'affiche à 98 % au lieu de 100 %. Tentative de correction faite et MESURÉE
   (`height:70vh` fixe) : elle règle la dérive mais sort 4 commandes de zoom du
   cadre à 1024 px — pire. Le produit est un outil B2B de bureau : on préserve
   les tailles réellement utilisées (≥ 1280 px, toutes exactes) plutôt que de
   les dégrader pour cette mise en page de secours. */
@media (max-width:1180px){.editeur-ppt .planche{max-height:70vh}}
/* Écran court : la marge de la visionneuse (22 px en haut ET en bas) coûtait
   44 px sur les 249 px disponibles en 1280×800 — 18 % de la place, pour du
   vide. On la réduit là où chaque pixel compte. */
@media (max-height:860px){.editeur-ppt .planche{padding:10px}}
/* Écran COURT : le ruban prend 90 px, soit ~20 % de tout l'espace utile en
   1280×625. On le compacte (icônes plus petites, noms de groupes retirés) :
   les commandes restent toutes là, la feuille récupère la place. */
@media (max-height:780px){
  .ruban .grp-nom{display:none}
  .ruban .grp{gap:0}
  .ruban .ins-btn .ic-ins{width:20px;height:20px}
  .ruban .grp-corps{min-height:26px}
}
/* ★ LE RUBAN RESTE SUR UN RANG. Mesuré à 1280×705 : ses groupes se repliaient
   sur deux rangs (`.grp-corps{flex-wrap:wrap}`) -> ruban à 107 px au lieu de 71,
   soit 36 px volés à la feuille sur un écran déjà court. La barre elle-même
   défile déjà horizontalement si besoin (`.ruban{overflow-x:auto}`). */
/* ⚠ PAS de préfixe `.editeur-ppt` : le ruban est un FRÈRE de `.editeur-ppt`
   (app.html:186 vs 261), pas un descendant — la règle préfixée ne s'appliquait
   à rien (ruban resté à 107 px en 1280, mesuré). */
.ruban .grp-corps{flex-wrap:nowrap}
/* ⚠ MAIS PAS SOUS 1280 px : mesuré, le ruban n'y tient pas sur un rang et
   débordait de 43 px (1024) à 53 px (1180) — une quinzaine de commandes
   devenaient INATTEIGNABLES à la souris, sans barre de défilement visible.
   Sous ce seuil, le repli sur deux rangs est le bon comportement : il coûte
   36 px de hauteur mais garde tout accessible. */
@media (max-width:1279px){.ruban .grp-corps{flex-wrap:wrap}}
/* ÉCRAN À LA FOIS ÉTROIT ET COURT (portable 1280×720 typique) : on serre le
   ruban juste assez pour qu'il tienne sur UN rang — sinon il en prend deux et
   vole 36 px à la feuille, qui n'occupe plus que 26 % de la hauteur. Toutes les
   commandes restent présentes ; c'est leur habillage qui maigrit. */
@media (min-width:1181px) and (max-width:1299px) and (max-height:820px){
  .ruban .grp-corps{flex-wrap:nowrap}
  .ruban .grp{padding:0 4px}
  .ruban .ins-btn{padding:2px 5px}
  .ruban .ins-btn .ic-ins{width:18px;height:18px}
  .ruban .ins-btn span{font-size:.64rem}
  .ruban button{font-size:.68rem}
}
/* MISE EN PAGE EMPILÉE (< 1180 px) : là, c'est la PAGE qui défile. Un ruban
   collant à `top:0` viendrait alors se ranger DERRIÈRE l'entête, elle-même
   collante et translucide (`theme.css .entete{sticky; top:0; z-index:20}` contre
   `z-index:16` pour le ruban) : on verrait les deux se superposer. Dans cette
   mise en page, le ruban défile donc avec le contenu.
   (Ce n'est pas ce qui rendait des commandes « inatteignables » au banc :
   c'était le banc qui mesurait après avoir fait défiler la page.) */
@media (max-width:1180px){.ruban{position:static}}
/* ★ LA VISIONNEUSE D'ABORD, sur les écrans étroits aussi.
   Mesuré à 1366×768 : la barre de vue passait sur DEUX lignes (39 -> 77 px) et
   les bandeaux sous la planche prenaient 39 px chacun -> 54 à 61 % de la
   colonne centrale mangés par autre chose que la feuille, réduite à 19 % de la
   hauteur de fenêtre. On compacte ce qui entoure la visionneuse. */
.editeur-ppt .barre-vue{flex-wrap:nowrap;overflow-x:auto;gap:8px;margin-bottom:2px;align-items:center}
.editeur-ppt .barre-vue::-webkit-scrollbar{height:0}
/* ⚠ RÉGRESSION MESURÉE PUIS CORRIGÉE : avec `nowrap` seul, le libellé
   « Aperçu · votre texte tel quel » était COMPRIMÉ à 40 px de large et cassait
   sur 5 lignes -> la barre montait à 97 px (pire que les 77 px d'origine).
   On lui interdit de se comprimer, et sous 1340 px on le retire : c'est une
   redite, la visionneuse se suffit à elle-même. */
.editeur-ppt .barre-vue > *{flex:none}
/* ⚠ Le libellé est le SEUL élément qui a le droit de rétrécir, et il le fait
   avec une ellipse (jamais sur plusieurs lignes). Rendre TOUTE la barre rigide
   la faisait déborder de 37 à 62 px entre 1341 et ~1400 px de large : le bouton
   « Ajuster » sortait du cadre à 1366 px — la largeur de portable la plus
   courante — sans aucune barre de défilement pour le rattraper. */
.editeur-ppt .apercu-libelle{
  flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
@media (max-width:1340px){.editeur-ppt .apercu-libelle{display:none}}
.editeur-ppt #alerte-deborde,
.editeur-ppt .note-rendu,
.editeur-ppt .scene-apercu > .alerte-min{margin-top:6px;font-size:.72rem;line-height:1.2}
/* ★ La planche est un conteneur FLEX : sans ceci la carte est COMPRIMÉE pour
   tenir dedans (un A4 se retrouvait à la largeur d'un A6) et l'échelle entre
   formats était fausse. flex:none => la carte garde sa taille physique et la
   planche défile. */
.editeur-ppt .carte-physique{flex:none;max-width:none}
/* Barre de zoom (bas de barre de vue) */
.zoom-barre{display:inline-flex;align-items:center;gap:6px;margin-left:auto}
.zoom-barre button{
  font:inherit;font-size:.9rem;line-height:1;cursor:pointer;width:24px;height:24px;
  border:1px solid var(--ligne);border-radius:6px;background:var(--blanc-carte);color:var(--encre);
  display:inline-flex;align-items:center;justify-content:center;
}
.zoom-barre button:hover{border-color:var(--accent);color:var(--accent-fonce)}
.zoom-barre input[type=range]{width:96px;accent-color:var(--accent)}
.zoom-barre #zoom-val{font-size:.76rem;color:var(--encre-doux);min-width:42px;text-align:right;font-variant-numeric:tabular-nums}

/* ---- Colonne PRIX (live, ligne par ligne) ---- */
.colonne-prix{position:sticky;top:92px;display:flex;flex-direction:column;gap:12px}
.prix-carte{background:var(--encre);color:#fff;border-radius:var(--rayon);padding:16px 18px}
.prix-titre{font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;color:rgba(255,255,255,.55);margin-bottom:11px}
.prix-lignes{display:flex;flex-direction:column;gap:6px;font-size:.82rem}
.prix-lignes .pl{display:flex;justify-content:space-between;gap:10px;color:rgba(255,255,255,.78)}
.prix-lignes .pl b{color:#fff;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap}
.prix-lignes .pl.base{color:rgba(255,255,255,.92)}
.prix-lignes .pl.remise,.prix-lignes .pl.remise b{color:#7fd6a9}
.prix-lignes .pl.vide{color:rgba(255,255,255,.4);font-style:italic}
.prix-tot{border-top:1px solid rgba(255,255,255,.18);margin-top:12px;padding-top:11px;display:flex;flex-direction:column;gap:8px}
.prix-tot .prix-l{display:flex;justify-content:space-between;align-items:center;gap:10px;font-size:.84rem;color:rgba(255,255,255,.7)}
.prix-tot .prix-l b{color:#fff;font-size:1.1rem;font-variant-numeric:tabular-nums}
.prix-tot .prix-l.qte label{color:rgba(255,255,255,.7);margin:0}
.prix-tot .prix-l.qte input{
  width:78px;padding:5px 8px;font-size:.86rem;text-align:right;background:rgba(255,255,255,.1);
  border:1px solid rgba(255,255,255,.22);color:#fff;border-radius:8px;
}
.prix-tot .prix-l.grand{border-top:1px solid rgba(255,255,255,.14);padding-top:9px}
.prix-tot .prix-l.grand b{font-family:'Fraunces',serif;font-size:1.5rem;font-weight:600}
.colonne-prix .alerte-min{color:#ffb4a2;margin:8px 0 0;font-size:.78rem}

/* ---- SAISIE DIRECTE sur la carte (le message se tape dans la diapo) ---- */
.saisie-carte{
  position:absolute;inset:0;width:100%;height:100%;margin:0;padding:0;border:0;outline:none;
  background:transparent;color:transparent;caret-color:var(--accent);resize:none;overflow:hidden;
  font-family:'Inter',sans-serif;font-size:12px;line-height:1.5;z-index:5;
  min-height:0;border-radius:0;
  /* Inerte par défaut : 1 clic sélectionne le BLOC (comme PowerPoint) ;
     la saisie s'active au 2e clic / double-clic (classe .edite). */
  pointer-events:none;
}
/* En saisie : le champ passe AU-DESSUS de la couche des blocs (z-index 3),
   sinon le cadre du bloc capte le clic et on ne peut jamais taper. */
.saisie-carte.edite{pointer-events:auto;z-index:30;background:rgba(255,255,255,.28);box-shadow:inset 0 0 0 2px var(--accent)}
/* ...et le cadre du bloc d'écriture cesse de capter la souris pendant la saisie. */
.el-composable.el-manuscrit.en-saisie{pointer-events:none}
.el-composable.el-manuscrit.en-saisie .el-poignee{pointer-events:auto}
/* Placeholder d'écriture (pas encore matérialisé) : visible au survol seulement. */
.el-composable.el-virtuel{border-color:transparent;background:transparent}
.el-composable.el-virtuel:hover{border-color:var(--accent-voile);border-style:dashed}
.el-composable.el-virtuel .el-badge{opacity:0;transition:opacity .12s}
.el-composable.el-virtuel:hover .el-badge{opacity:1}
.saisie-carte::placeholder{color:var(--encre-doux);opacity:.55}
/* Le caret NATIF est masqué : il tomberait au mauvais endroit (les lettres du
   textarea n'ont rien à voir avec les glyphes tracés). On dessine le nôtre à la
   position EXACTE du glyphe (BDB_VIVANTE.rectCaret). */
.saisie-carte.edite{caret-color:transparent}
/* Accolades des variables : la banque de la plume ne les contient pas, on les
   pose en typo d'écran à la position EXACTE (repère, jamais du texte). */
.reperes-var{position:absolute;inset:0;pointer-events:none;z-index:6}
.marque-var{
  position:absolute;color:var(--accent);opacity:.75;font-family:'Inter',sans-serif;
  font-weight:600;line-height:1;white-space:pre;
}
.caret-vivant{
  position:absolute;width:2px;background:var(--accent);z-index:31;display:none;
  pointer-events:none;border-radius:1px;animation:caret-clignote 1.05s step-end infinite;
}
/* ---- SURLIGNAGE DE SÉLECTION (D-089) ----------------------------------
   Le texte visible est un SVG de glyphes : le surlignage natif du textarea
   transparent est peint aux métriques d'une police d'écran, donc à côté des
   traits. On dessine nos propres bandes, à la géométrie du moteur, et SOUS
   l'encre (z-index 0 dans une zone-ecriture où le SVG suit) pour ne jamais
   masquer l'écriture. */
/* ★ Le surlignage NATIF doit rester totalement invisible. Un <textarea> en
   color:transparent redevient VISIBLE dès qu'on le sélectionne : le style
   de sélection du navigateur repeint le texte (constaté en capture — le
   texte machine s'affichait en bleu foncé PAR-DESSUS l'écriture). On neutralise
   donc la sélection native ; la nôtre est dessinée ci-dessous. */
.saisie-carte::selection{background:transparent;color:transparent}
.saisie-carte::-moz-selection{background:transparent;color:transparent}
.selection-vivante{position:absolute;inset:0;pointer-events:none;z-index:0}
.selection-vivante .sel-bande{
  position:absolute;background:var(--accent);opacity:.24;border-radius:2px;
}
@media (prefers-contrast:more){.selection-vivante .sel-bande{opacity:.38}}
@keyframes caret-clignote{0%,100%{opacity:1}50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.caret-vivant{animation:none}}
.saisie-carte:focus{box-shadow:none;border:0}

.barre-insert{
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:0 0 10px;padding:7px 9px;
  background:var(--blanc-carte);border:1px solid var(--ligne);border-radius:var(--rayon-ctrl);
}
.barre-insert .ins-lib{font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--encre-doux);margin-right:2px}
.ins-btn{
  display:inline-flex;align-items:center;gap:6px;font:inherit;font-size:.84rem;cursor:pointer;
  padding:6px 11px;border:1px solid var(--ligne);border-radius:999px;
  background:var(--papier-2);color:var(--encre);
}
.ins-btn:hover{border-color:var(--accent);color:var(--accent-fonce);background:var(--accent-voile)}
.ins-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.ins-btn .ins-ic{font-size:.95rem;line-height:1}
.barre-insert .ins-sep{flex:1}
.barre-insert .ins-astuce{font-size:.74rem;color:var(--encre-doux)}
.planche{position:relative}

/* Barre d'outils flottante (contextuelle au bloc sélectionné). */
.el-toolbar{
  position:absolute;z-index:20;display:flex;align-items:center;gap:4px;flex-wrap:nowrap;
  white-space:nowrap;max-width:none;
  padding:4px 6px;background:var(--blanc-carte);border:1px solid var(--ligne);
  border-radius:10px;box-shadow:0 6px 22px rgba(38,48,63,.18);
}
.el-toolbar[hidden]{display:none}
/* La barre flottante ne doit JAMAIS créer de défilement dans la visionneuse
   (22 px mesurés en 1280×625 dès qu'un bloc était sélectionné) : elle est
   posée en overlay, hors du flux de défilement de la planche. */
.editeur-ppt .el-toolbar{position:absolute;z-index:22}
/* AVIS EN SURIMPRESSION (enregistrement, erreurs).
   ⚠ TROIS PIEGES DEJA PAYES, tous mesures :
   1. En FLUX dans `.planche` (conteneur flex) il devenait un item et chassait
      la feuille de son centre, definitivement (marges 96/96 -> 10/183).
   2. En `position:absolute` DANS `.planche` (overflow:auto) il defile avec la
      carte : au zoom il partait jusqu'a 1362 px hors du cadre -> invisible,
      alors que c'est son unique raison d'etre.
   3. `transform` declare DEUX FOIS dans la meme regle : la seconde ecrasait le
      `translate(-50%)`, donc l'avis cache restait colle a left:50% et depassait
      de 243 px -> 362 px de defilement horizontal PERMANENT des qu'un message
      un peu long s'affichait (le repli « Erreur inconnue » suffisait).
   -> Il vit donc dans `.scene-apercu` (qui ne defile pas), hors flux, avec UNE
   SEULE declaration `transform`, et un texte qui passe a la ligne au lieu de
   deborder du fond. La transition ne porte que sur l'opacite : pas de
   glissement horizontal parasite. */
.avis-visionneuse{
  position:absolute;left:50%;top:46px;transform:translateX(-50%);
  z-index:30;max-width:min(88%,520px);width:max-content;
  padding:6px 14px;border-radius:8px;background:var(--encre,#26303F);color:#fff;
  font-size:.78rem;line-height:1.25;text-align:center;
  white-space:normal;overflow-wrap:anywhere;
  box-shadow:0 6px 16px -8px rgba(0,0,0,.45);
  opacity:0;visibility:hidden;transition:opacity .16s;pointer-events:none;
}
.avis-visionneuse.visible{opacity:1;visibility:visible}
.el-toolbar .tb-titre{font-size:.72rem;color:var(--encre-doux);padding:0 4px;white-space:nowrap}
.el-toolbar .tb-sep{width:1px;height:18px;background:var(--ligne);margin:0 2px}
.el-toolbar button{
  font:inherit;font-size:.8rem;cursor:pointer;padding:4px 8px;border-radius:7px;
  border:1px solid transparent;background:transparent;color:var(--encre);line-height:1.2;
}
.el-toolbar button:hover{background:var(--papier-2);border-color:var(--ligne)}
.el-toolbar button.actif{background:var(--accent-voile);border-color:var(--accent);color:var(--accent-fonce);font-weight:600}
.el-toolbar button.danger{color:var(--cire)}
.el-toolbar select,.el-toolbar input[type=number]{
  font:inherit;font-size:.8rem;padding:3px 5px;border:1px solid var(--ligne);
  border-radius:7px;background:var(--blanc-carte);color:var(--encre);
}
.el-toolbar select{max-width:150px}
.el-toolbar input[type=number]{width:52px}
.el-toolbar input[type=color]{width:26px;height:24px;padding:0;border:1px solid var(--ligne);border-radius:6px;background:none;cursor:pointer}

/* Guides d'alignement (magnétisme). */
.guides{position:absolute;inset:0;pointer-events:none;z-index:12}
.guides .guide{position:absolute;background:var(--accent);opacity:.9}
.guides .guide.v{width:1px;top:0;bottom:0}
.guides .guide.h{height:1px;left:0;right:0}

.couche-elements{position:absolute;inset:0;pointer-events:none;z-index:3}
/* Le BLOC ne coupe rien lui-même (ses poignées doivent dépasser) : c'est
   .el-contenu, à l'intérieur, qui masque ce qui sort du cadre — c'est lui la
   « fenêtre » du rognage. */
.el-composable{
  position:absolute;pointer-events:auto;cursor:move;box-sizing:border-box;
  border:1px dashed transparent;overflow:visible;
}
.el-contenu{position:absolute;inset:0;overflow:hidden}
.el-composable:hover{border-color:var(--accent-voile)}
.el-composable.actif{border-color:var(--accent);border-style:solid}
/* Bloc d'ÉCRITURE manuscrite : cadre transparent (le tracé vit au-dessus,
   dans .zone-ecriture) — on ne voit le cadre qu'au survol/sélection. */
.el-composable.el-manuscrit{background:transparent}
.el-composable.el-manuscrit.actif{border-style:dashed;border-color:var(--accent)}
/* Débordement : le bloc d'écriture le montre TOUT DE SUITE (cadre rouge). */
.el-composable.el-manuscrit.deborde{border-color:var(--cire);border-style:solid;background:rgba(190,60,50,.06)}
.el-composable.el-manuscrit.deborde .el-badge{background:var(--cire)}
/* PDF affiché comme une page (la visionneuse est masquée par le fragment #toolbar=0). */
.el-pdf-page{width:100%;height:100%;border:0;display:block;pointer-events:none}
/* Mode ROGNER : l'image déborde volontairement de son cadre. */
.el-composable.en-crop{outline:2px dashed var(--accent);outline-offset:2px}
/* Étiquette « ✍ Écriture » : repère DISCRET. Elle n'apparaît qu'au survol ou
   quand le bloc est sélectionné, et disparaît pendant la frappe — sinon elle
   recouvre en permanence le haut de la carte (logo, première ligne). */
.el-composable.el-manuscrit .el-badge{
  position:absolute;left:0;top:-17px;font-size:.65rem;line-height:1;padding:2px 6px;
  background:var(--accent);color:#fff;border-radius:5px 5px 0 0;white-space:nowrap;
  opacity:0;transition:opacity .12s;pointer-events:none;
}
/* Uniquement au SURVOL : en simple sélection il restait affiché par-dessus le
   logo et la première ligne (constat Hubert). Jamais pendant la frappe. */
.el-composable.el-manuscrit:hover .el-badge{opacity:1}
.el-composable.el-manuscrit.en-saisie .el-badge,
.el-composable.el-manuscrit.en-saisie:hover .el-badge{opacity:0}
/* ★ NI EN SÉLECTION. Constat d'Hubert re-signalé : « quand je clique sur la
   zone de texte manuscrite, j'ai toujours le truc "Écriture" avec le logo qui
   reste ». Un clic sélectionne SANS entrer en saisie -> la règle ci-dessus ne
   mordait pas et le survol laissait l'étiquette à opacity 1 (mesuré). Dès que
   la zone est sélectionnée, l'étiquette disparaît : on sait déjà où on est. */
.el-composable.el-manuscrit.actif .el-badge,
.el-composable.el-manuscrit.actif:hover .el-badge{opacity:0}
/* Édition de texte EN PLACE (double-clic), façon PowerPoint. */
.el-texte-contenu[contenteditable="true"]{outline:2px solid var(--accent);cursor:text;pointer-events:auto;background:rgba(255,255,255,.6)}
.el-composable img{width:100%;height:100%;object-fit:contain;display:block;pointer-events:none;user-select:none}
/* PDF « fond de page » composable : l'embed remplit l'élément et ne capte
   jamais la souris (sinon il volerait le drag/resize). */
.el-composable embed{width:100%;height:100%;border:0;display:block;pointer-events:none}
.el-texte-contenu{width:100%;height:100%;overflow:hidden;line-height:1.25;pointer-events:none;word-break:break-word}
/* Poignée de redimensionnement (coin bas-droite), visible sur l'élément actif. */
/* 8 poignées façon PowerPoint : 4 angles + 4 milieux.
   ZONE D'ACCROCHE GÉNÉREUSE (22 px) avec une pastille visible plus petite au
   centre : attraper un angle ne demande plus de viser au pixel près. */
.el-poignee{
  position:absolute;width:22px;height:22px;border:0;background:transparent;
  pointer-events:auto;display:none;z-index:14;touch-action:none;
}
.el-poignee::after{
  content:"";position:absolute;left:50%;top:50%;width:11px;height:11px;
  transform:translate(-50%,-50%);border-radius:50%;
  background:#fff;border:1.5px solid var(--accent);box-shadow:0 1px 3px rgba(20,30,60,.28);
}
.el-poignee:hover::after{background:var(--accent-voile);transform:translate(-50%,-50%) scale(1.15)}
.el-composable.actif .el-poignee{display:block}
.el-poignee.p-nw{left:-11px;top:-11px;cursor:nwse-resize}
.el-poignee.p-n {left:calc(50% - 11px);top:-11px;cursor:ns-resize}
.el-poignee.p-ne{right:-11px;top:-11px;cursor:nesw-resize}
.el-poignee.p-e {right:-11px;top:calc(50% - 11px);cursor:ew-resize}
.el-poignee.p-se{right:-11px;bottom:-11px;cursor:nwse-resize}
.el-poignee.p-s {left:calc(50% - 11px);bottom:-11px;cursor:ns-resize}
.el-poignee.p-sw{left:-11px;bottom:-11px;cursor:nesw-resize}
.el-poignee.p-w {left:-11px;top:calc(50% - 11px);cursor:ew-resize}
/* Le bloc SÉLECTIONNÉ (et ses poignées) passe au-dessus des autres. */
.el-composable.actif{z-index:10}

/* ---- Mode ROGNER : poignées noires façon PowerPoint (équerres + barres) ---- */
.el-composable.en-crop .el-poignee::after{
  background:#1b1b1b;border:0;border-radius:0;box-shadow:0 0 0 1px rgba(255,255,255,.85);
}
.el-composable.en-crop .el-poignee.p-nw::after,
.el-composable.en-crop .el-poignee.p-ne::after,
.el-composable.en-crop .el-poignee.p-sw::after,
.el-composable.en-crop .el-poignee.p-se::after{width:15px;height:15px}
.el-composable.en-crop .el-poignee.p-n::after,
.el-composable.en-crop .el-poignee.p-s::after{width:22px;height:5px}
.el-composable.en-crop .el-poignee.p-e::after,
.el-composable.en-crop .el-poignee.p-w::after{width:5px;height:22px}
/* Pendant le rognage, la partie coupée reste visible en fantôme (repère PPT). */
.el-crop-ghost{position:absolute;pointer-events:none;opacity:.28;z-index:1}
.el-crop-ghost img{width:100%;height:100%;object-fit:contain;display:block}
/* Le placeholder virtuel n'a pas de poignées (rien à redimensionner encore). */
.el-composable.el-virtuel .el-poignee{display:none}

/* Actions + panneau d'édition de l'élément sélectionné (dans le panneau). */
.el-actions{display:flex;gap:8px;flex-wrap:wrap}
.el-editeur{margin-top:10px;padding:10px;border:1px solid var(--ligne);border-radius:var(--rayon-ctrl);background:var(--papier-2)}
.el-ed-titre{font-size:.82rem;font-weight:600;color:var(--encre);margin-bottom:2px}
.el-ed-ligne{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px}
.el-ed-texte{width:100%;font-size:.85rem;padding:6px 8px;border:1px solid var(--ligne);border-radius:var(--rayon-ctrl);resize:vertical;box-sizing:border-box;font-family:'Inter',sans-serif}
.el-ed-num{font-size:.8rem;color:var(--encre-doux);display:flex;align-items:center;gap:4px}
.el-ed-num input{width:56px}
.el-editeur select{width:auto;flex:1;min-width:120px}
.el-ed-suppr{color:var(--cire);border-color:var(--cire)}

/* ============================================================
   IMPORT RECTO PDF (prestation « imprimée », T-079) — la carte devient
   une zone d'import (non éditable), qui COUVRE la carte (z au-dessus des
   couches manuscrite + éléments). L'enveloppe reste éditable.
   ============================================================ */
.import-recto{position:absolute;inset:0;z-index:4;background:var(--blanc-carte);display:flex;flex-direction:column}
.import-recto-corps{flex:1;display:flex;flex-direction:column;min-height:0}
.import-recto-apercu{flex:1;width:100%;border:0;min-height:0}
.import-recto-barre{padding:6px;text-align:center;border-top:1px solid var(--ligne)}
.import-recto-vide{
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;
  color:var(--encre-doux);border:2px dashed var(--ligne);border-radius:8px;margin:10px;text-align:center;padding:16px;
}
.import-recto-txt{font-size:.9rem}

/* ============================================================
   ZONE D'ÉCRITURE DANS LA CARTE = APERÇU PASSIF (plus de saisie).
   Positionnée en px par editeur.js depuis les marges physiques mm
   (ui.json > editeur.zone_mm) ; sert à dimensionner le rendu.
   ============================================================ */
/* z-index:4 = la PLUME AU-DESSUS du laser (réalité physique : on imprime
   d'abord, le robot trace ensuite PAR-DESSUS). pointer-events:none -> les
   clics traversent quand même vers les éléments composables (z:3).
   overflow:visible, SANS clip-path : la BOÎTE (donc largeurPx/hauteurMaxPx
   passés au moteur vivante — la mise en page, le retour à la ligne) NE BOUGE
   PAS ; on laisse simplement le débordement naturel du tracé (italiques/
   liées de la police vivante, dont le glyphe déborde un peu de sa boîte
   d'avance, horizontalement ET verticalement) se DESSINER sans le couper net.
   ⚠️ CORRECTIF s.142/143, faux au 1er jet — dit tel quel pour ne pas se
   retromper : un `clip-path:inset(0 -6px 0 -6px)` avait été posé ICI en
   pensant qu'il élargissait le masque « en sécurité additionnelle » par-
   dessus le vrai correctif (overflow:visible sur .rendu-vivant svg,
   ci-dessous). C'est FAUX : un clip-path s'applique à l'élément ET à tout
   son sous-arbre, donc dès que le <svg> enfant cesse de couper (grâce à son
   overflow:visible), c'est CE clip-path qui devient la limite qui coupe
   réellement — plafonnée à 6 px horizontal et 0 px vertical, alors que la
   mesure réelle (`TODO.md`, ronde 1, point 9) donne 23 à 62 px de tracé
   encore tranché selon le corps. Un clip-path large et fixe n'aurait pas
   couvert tous les corps de texte ; il est donc RETIRÉ, purement. Le
   débordement se dessine désormais jusqu'au bord du carton, exactement comme
   le fait déjà #rendu-adresse-vivant (vue Enveloppe, jamais entourée de
   .zone-ecriture) et comme le fait l'aperçu de campagne (camp-rendu-vivant,
   campagnes.css) : la SEULE limite physique, sur les 3 vues, est
   .carte-physique{overflow:hidden} (web/css/theme.css) — elle ne bouge pas. */
.zone-ecriture{position:absolute;overflow:visible;pointer-events:none;z-index:4}

/* Conteneur du rendu SVG vivante : plein cadre de la zone ; le SVG suit
   la largeur (viewBox 1:1 avec les px calculés -> width:100%). */
.rendu-vivant{position:absolute;inset:0}

/* ---- COMPOSITION du bloc adresse (vue Enveloppe, 10/08/2026) --------------
   Hubert : « je veux que ce soit clicable et avoir la possibilité d'ajouter une
   variable à un endroit voulu ». Ce champ porte le MODÈLE, pas le résultat.
   ⚠️ Il est VISIBLE et en police d'interface, à la différence de #zone-texte
   (transparent, superposé à l'écriture de la carte). Ce n'est pas une
   incohérence : on y écrit « {{civilite}} », et l'écriture vivante ne trace PAS
   les accolades (mesuré le 10/08 : « {{prenom}} » sort « prenom »). Le rendu à
   la plume reste juste au-dessous, avec un destinataire d'exemple — le client
   voit donc ce qu'il compose ET ce que ça donnera.
   Position/taille posées par editeur.js (mêmes coordonnées que le bloc adresse). */
.zone-adresse{
  position:absolute;z-index:6;
  font-family:ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace;
  font-size:12px;line-height:1.5;color:var(--encre);
  background:rgba(255,255,255,.92);
  border:1px dashed var(--accent);border-radius:6px;
  padding:6px 8px;resize:none;overflow:hidden;
  /* ⚠️ min-height:0 et box-sizing : theme.css habille TOUS les textarea et leur
     impose une hauteur minimale. Mesuré le 10/08 sur le site EN LIGNE : le champ
     sortait à 171 px pour 4 lignes (au lieu des ~100 calculés), et son bas
     tombait à 674 px sur un écran de 673 — donc SOUS LE PLI, invisible sans
     défiler. La hauteur posée en JS ne peut gagner que si ces deux lignes
     neutralisent le minimum hérité et incluent bordure + padding. */
  min-height:0;box-sizing:border-box;
}
.zone-adresse:focus{outline:2px solid var(--accent);outline-offset:1px;background:#fff}
/* Le bloc adresse rendu à la plume invite au clic (c'est la surface qu'Hubert
   a essayé de cliquer). Le curseur le dit avant même l'essai. */
#rendu-adresse-vivant{cursor:text}
/* overflow:visible = LE VRAI correctif de bord (le seul autre, sur
   .zone-ecriture, a été retiré — voir son commentaire ci-dessus, s.143).
   Le <svg> a un viewBox="0 0 largeurPx svgH" en unités = px (1:1) ; par
   défaut le user-agent le clippe pile sur ce viewBox (svg:not(:root){
   overflow:hidden} de la feuille de style navigateur), ce qui tranche net
   toute lettre liée/italique dont le tracé déborde la boîte d'avance
   nominale. On ne change ni le viewBox ni largeurPx/hauteurMaxPx passés au
   moteur (la mise en page — retour à la ligne, échelle — NE BOUGE PAS,
   cf. invariants de cadrage) : on laisse seulement le débordement se
   DESSINER, sans le déplacer ni le redimensionner. La vraie limite physique
   reste .carte-physique{overflow:hidden} (le bord réel de la carte/de
   l'enveloppe) : elle ne bouge pas. S'applique aussi à #rendu-adresse-vivant
   (vue Enveloppe, même classe .rendu-vivant) : avant ce correctif son SVG
   n'avait AUCUN traitement de marge.

   ⚠️⚠️ clip-path:inset(0 -50% 0 -50%) = LA BORNE DU BAS, RÉTABLIE (s.143).
   `overflow:hidden` du navigateur ne coupait pas QUE les côtés : il tenait
   AUSSI la hauteur. Le moteur plafonne bien le viewBox
   (`svgH = min(L.height, hauteurMax)`, vivante.js) MAIS il émet quand même
   TOUTES les lignes, y compris celles qui dépassent — c'est le clip du
   navigateur, et lui seul, qui les cachait. En posant `overflow:visible` on
   a donc libéré les DEUX axes : un texte trop long ne s'arrêtait plus en bas
   de la zone, il continuait à se dessiner par-dessus la carte et par-dessus
   la couche imprimée (.zone-ecriture est en z-index:4, AU-DESSUS du z:3).
   Défaut trouvé par l'agent `controle`, jamais vu par personne d'autre.
   inset(top right bottom left) : `0` en haut/bas = on coupe exactement là où
   le navigateur coupait, donc AUCUN changement de comportement vertical ;
   `-50%` à gauche/droite = la moitié de la largeur de débordement de chaque
   côté, très au-delà des 23-62 px mesurés, donc les côtés restent libres.
   ⛔ Ne PAS mettre une valeur verticale négative « pour les jambages » : elle
   rouvrirait le débordement vertical qu'on vient de refermer. Les jambages de
   la dernière ligne se comportent exactement comme avant cette session. */
.rendu-vivant svg{display:block;width:100%;height:auto;overflow:visible;clip-path:inset(0 -50% 0 -50%)}

/* #rendu-texte vit DANS la zone (aperçu) : borné par la zone. */
.zone-ecriture .texte-manuscrit{max-height:100%;height:100%;overflow:hidden}

/* Libellé statique « Aperçu · données d'exemple » (remplace l'ancien toggle). */
.apercu-libelle{font-size:.78rem;color:var(--encre-doux)}

/* Barre des variables (sous le champ message, dans le panneau) + notes. */
#boutons-vars{margin-top:12px}
/* Variable présente dans le texte : liseré appuyé (repère « utilisée »). */
.vars button.utilisee{border-color:var(--accent)}
/* Variable CUSTOM (tapée/créée par le client, hors des 4 standards). */
.vars button.custom{background:color-mix(in srgb, var(--dore) 16%, transparent);color:var(--dore)}
.vars button.custom:hover{border-color:var(--dore)}
/* Bouton « + variable » : neutre, discret. */
.vars button.ajout-var{background:transparent;color:var(--encre-doux);border:1px dashed var(--ligne)}
.vars button.ajout-var:hover{border-color:var(--accent);color:var(--accent-fonce)}
/* Formulaire inline « + variable » (remplace la barre le temps de saisir). */
.ajout-var-form{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.ajout-var-form input{width:auto;flex:1;min-width:150px;font-size:.82rem;padding:6px 10px}
.ajout-var-form .btn.discret{padding:5px 10px}
.ajout-var-err{font-size:.76rem;color:var(--cire)}
.indication.centre{text-align:center}
/* .alerte-min de theme.css est calibrée pour le récap SOMBRE (rose pâle) :
   sous la planche claire on repasse sur la couleur cire, lisible. */
#alerte-deborde{color:var(--cire);text-align:center}

/* ============================================================
   ★ RÉGLAGES DE L'ÉCRITURE — champs du ruban + panneaux flottants
   (09/08/2026, remplace les 5 curseurs du 08/08 qui débordaient le ruban)
   ============================================================ */

/* --- Champs compacts DANS le ruban (façon Word) ---
   Ils héritent de `.ruban input[type=number]` (28 px de haut, 52 de large) :
   on ne redéfinit RIEN de ces métriques, sinon le ruban se met à respirer
   différemment d'un groupe à l'autre. */
.ruban .rbp-ic{width:14px;height:14px;fill:var(--encre-doux);flex:0 0 auto;opacity:.75}
/* Un réglage non neutre se voit sans lire le chiffre (même code visuel que
   les boutons `.actif` du ruban). */
.ruban input.rbp-actif{border-color:var(--accent);color:var(--accent-fonce);font-weight:600}

/* --- Panneaux flottants ---
   ⚠️ position:FIXED, et le nœud vit HORS du ruban (frère, dans #page-editeur) :
   `.ruban` porte overflow-x:auto, un enfant absolu y serait rogné et défilerait
   avec lui. Les coordonnées sont posées en JS depuis le rect du bouton.
   z-index 40 : au-dessus du ruban (16) et de l'entête (20), sous la modale (100). */
.rbp-panneau{
  /* 340 px : mesuré pour que les 4 niveaux tiennent sur UNE ligne (à 300 px,
     « Marquée » passait à la ligne et le panneau paraissait bâclé). */
  position:fixed; z-index:40; width:340px; padding:12px 14px 10px;
  background:var(--blanc-carte); border:1px solid var(--ligne); border-radius:10px;
  box-shadow:0 10px 30px rgba(38,48,63,.20);
}
.rbp-panneau[hidden]{display:none}
.rbp-panneau-etroit{width:250px}
.rbp-titre{
  font-size:.66rem; letter-spacing:.06em; text-transform:uppercase;
  color:var(--encre-doux); margin-bottom:9px;
}
/* Liste de niveaux : réutilise .chips/.chip de theme.css, juste resserrés. */
.rbp-chips{gap:5px; margin-bottom:11px}
.rbp-chips .chip{padding:4px 10px; font-size:.76rem}
/* Les 4 axes : libellé à largeur fixe -> les pistes s'alignent verticalement. */
.rbp-axes{display:flex; flex-direction:column; gap:7px}
.rbp-axe{display:flex; align-items:center; gap:8px; cursor:pointer}
.rbp-axe-lib{font-size:.76rem; color:var(--encre-doux); flex:0 0 92px}
.rbp-axe input[type=range]{flex:1; min-width:0; margin:0; accent-color:var(--accent); cursor:pointer}
/* Chiffres tabulaires : la largeur ne saute pas pendant qu'on glisse. */
.rbp-axe-val{
  font-size:.74rem; color:var(--encre); flex:0 0 34px; text-align:right;
  font-variant-numeric:tabular-nums;
}
.rbp-aide{
  margin:10px 0 0; font-size:.7rem; line-height:1.35; color:var(--encre-doux);
  border-top:1px solid var(--ligne); padding-top:8px;
}

/* --- Ruban : palier compact élargi à 1400 px ---
   La recette existait déjà (padding des groupes réduit, icônes 18 px) mais était
   bornée à max-width:1299px, donc INACTIVE sur un 1366. Or à 1366×673 le ruban
   n'a plus que 15 px de marge (mesuré) : l'ajout du bouton Interligne dans
   « Paragraphe » le ferait déborder de ~17 px. Élargir le palier libère ~72 px
   sur les 6 groupes — sans écrire une seule règle neuve. */
@media (min-width:1181px) and (max-width:1400px) and (max-height:820px){
  .ruban .grp{padding:0 4px}
  .ruban .ins-btn{padding:2px 5px}
  .ruban .ins-btn .ic-ins{width:18px;height:18px}
  .ruban .ins-btn span{font-size:.64rem}
  .ruban button{font-size:.68rem}
}

/* Un PRIX ne se coupe JAMAIS (oeil du 18/08 : « 5,10 » puis « € » seul à la
   ligne, dans le plus gros caractère de l'écran, colonne de 228 px à
   1366×673 — le défaut de la s.154 réapparu dans la colonne de l'éditeur).
   Même doctrine que l'écran campagne : le montant est insécable. */
#recap-total, #recap-unitaire { white-space: nowrap; }
