/* =========================================================================
   Feuille de style commune a toutes les pages de 3DMaking.

   Contient UNIQUEMENT ce qui etait strictement identique sur les 9 pages :
   les variables de design (couleurs, rayons, ombres) et le menu lateral.
   Le reste du style vit encore dans le <style> de chaque page, qui est
   charge APRES ce fichier : une page peut donc toujours redefinir une
   valeur pour elle seule (c est le cas de --ring sur moderation.html).

   POUR CHANGER LE THEME DU SITE : modifier les variables ci-dessous, une
   seule fois, plutot que les 9 fichiers HTML.
   ========================================================================= */

:root{
  /* Le site est TOUJOURS sombre, aucune variante claire — sans ceci, les
     widgets natifs du navigateur (liste deroulante d'un <select> ouverte,
     entre autres) se rendent avec le theme clair par defaut du systeme
     d'exploitation, ignorant les couleurs `background`/`color` posees sur
     <option> : fond blanc illisible, surlignage au survol pris depuis la
     couleur d'accentuation Windows plutot que la charte du site. Corrige
     le 2026-08-18 (signale par l'utilisateur sur .topbar-printer-select,
     mais touchait tout <select> du site de la meme facon). */
  color-scheme: dark;

  /* Fonds, du plus sombre au plus clair. Teinte verte tres legere, calee
     sur le vert de marque (avant : gris violace, herite du template
     d'origine et sans rapport avec la charte). */
  --ink: #0A0D0B;
  --surface: #121814;
  --surface-2: #19221C;
  --surface-3: #212D25;

  /* Bordures */
  --line: rgba(255,255,255,0.08);
  --line-strong: rgba(255,255,255,0.14);

  /* Accent — vert de la marque. Actions et interactions UNIQUEMENT,
     jamais les alertes (voir --danger). */
  --accent: #175E24;
  --accent-soft: rgba(23,94,36,0.18);
  /* Meme famille de vert, mais assez clair pour rester lisible SUR un fond
     sombre. --accent (2,28:1 sur le panneau du menu) passe sous le minimum
     de 3:1 recommande pour un element graphique : reserve-le aux fonds
     pleins et aux bordures, pas aux petits traits comme les icones. */
  --accent-bright: #22c55e;

  /* Accent secondaire — dore, associe aux Polys dans toute l'interface */
  --accent-2: #f2b134;
  --accent-2-soft: rgba(242,177,52,0.16);

  /* Etats */
  --danger: #FF6467;
  --danger-soft: rgba(255,100,103,0.14);
  --warning: #f59e0b;
  --warning-soft: rgba(245,158,11,0.14);
  --ok: #34d399;
  --ok-soft: rgba(52,211,153,0.14);

  /* Texte, du plus lisible au plus discret */
  --text: #F8FAFC;
  --text-2: #9EB3A6;
  --text-3: #586B5F;

  /* Formes et effets. Angles carres partout (charte 2026-08-16, refonte
     visuelle) — avant : 10px/8px, arrondis. */
  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 1px 3px rgba(0,0,0,0.5), 0 12px 32px -8px rgba(0,0,0,0.75);
  --ring: 0 0 0 3px rgba(34,197,94,0.35);

  /* Couleurs de role (badges) */
  --role-admin: #FF6467;
  --role-admin-soft: rgba(255,100,103,0.14);
  --role-mod: #1F7A3F;
  --role-mod-soft: rgba(31,122,63,0.16);
  --role-printer: #175E24;
  --role-printer-soft: rgba(23,94,36,0.16);
  /* Bêta-testeur : le jaune de la boutique de Polys (--accent-2 #f2b134),
     demandé explicitement. Écrit en dur plutôt qu'en var(--accent-2) pour que
     le soft correspondant existe sous la même forme que les 3 autres rôles. */
  --role-beta: #F2B134;
  --role-beta-soft: rgba(242,177,52,0.16);
  --mod: #1F7A3F;
  --mod-soft: rgba(31,122,63,0.16);

  /* =====================================================================
     JETONS AJOUTES LE 2026-09-21 — chantier du theme clair
     =====================================================================
     104 couleurs de cette feuille etaient ecrites EN DUR, donc hors de
     portee de tout theme. Chaque jeton ci-dessous vaut EXACTEMENT le
     litteral qu il remplace : le rendu n a pas change d un pixel, et
     c est verifie (styles calcules compares avant / apres).

     Un theme clair se declinera ici, et NULLE PART ailleurs.

     ⚠️ LES JETONS EN -rgb PORTENT UN CANAL, PAS UNE COULEUR : ils
     s ecrivent rgba(var(--x), .12). C est la seule facon sans build de
     garder plusieurs alphas d une meme couleur — la feuille en compte 9
     pour les ombres et 7 pour les voiles. Les mettre en couleur pleine
     obligerait a un jeton par alpha.
     ⚠️ AUCUNE VALEUR PROCHE N A ETE FONDUE : .03 et .035 restent deux
     alphas distincts. Les unifier changerait le rendu, et serait une
     refonte, pas une refactorisation.
     ===================================================================== */

  /* Canaux — pour les variantes d alpha */
  --voile-rgb: 255,255,255;   /* voiles clairs posés SUR le fond sombre (survols, séparateurs) */
  --ombre-rgb: 0,0,0;   /* ombres portées */
  --accent-rgb: 23,94,36;   /* vert de marque (= --accent) */
  --accent-bright-rgb: 34,197,94;   /* vert clair lisible sur fond sombre (= --accent-bright) */
  --accent-2-rgb: 242,177,52;   /* doré des Polys (= --accent-2) */
  --danger-rgb: 255,100,103;   /* rouge d’alerte (= --danger) */
  --danger-vif-rgb: 239,68,68;   /* rouge vif des jauges et pastilles */
  --ok-rgb: 52,211,153;   /* vert de validation (= --ok) */
  --warning-rgb: 245,158,11;   /* ambre d’avertissement (= --warning) */
  --info-rgb: 124,184,255;   /* bleu d’information */
  --danger-doux-rgb: 224,85,85;   /* rouge adouci (bordures de consentement) */
  /* 🔴 CE CANAL S APPELAIT --danger-clair-rgb, ET C ETAIT UN PIEGE QUE J AI
     POSE MOI-MEME le 21/09 : --danger-clair vaut #f87171 (248,113,113), pas
     255,80,80. Deux couleurs differentes sous un meme nom, dans un fichier dont
     tout l objet est de decliner un theme : qui aurait redefini l une aurait
     cru changer l autre. Le canal porte desormais le nom de son SEUL usage. */
  --hachures-rgb: 255,80,80;   /* rouge des hachures « non imprimable » (motif repete, sans rapport avec --danger-clair) */
  --voile-modale-rgb: 8,9,12;   /* voile derrière une fenêtre modale */
  --voile-menu-rgb: 10,13,11;   /* fond du panneau du menu latéral */
  --voile-palette-rgb: 4,7,5;   /* voile derrière la palette de commandes */
  --voile-apercu-rgb: 8,10,14;   /* fond des incrustations de l’aperçu 3D */
  --vert-clair-rgb: 74,222,128;   /* canal du vert clair */
  --voile-pied-rgb: 8,11,9;   /* fond du pied de page (present sur 25 pages) */
  --ardoise-rgb: 15,23,42;   /* bleu ardoise des panneaux d’aide des outils */
  --brique-rgb: 224,71,44;   /* rouge brique des pastilles « non imprimable » */
  /* Le vert du bouton « Exporter » des 5 pages ancrées. ⚠️ Ce N’EST PAS
     --accent-bright (34,197,94) : c’est un vert plus saturé, propre a ce
     bouton, et les fondre ferait changer d’aspect le geste qui fait payer. */
  --vert-export-rgb: 0,174,66;
  --vert-export: #00AE42;
  --vert-export-fonce: #008F36;
  --vert-export-vif: #02C64D;
  --vert-export-vif-fonce: #00A33D;
  /* L’orange de la pastille « 3DMaking » de la barre du haut des outils. */
  --orange-vif: #FF6B00;
  --orange-fonce: #EA580C;
  --emeraude-rgb: 16,185,129;   /* emeraude des cartes de prereglage actives */
  --ardoise-nuit-rgb: 17,24,39;   /* ardoise tres sombre des incrustations de l apercu */
  --cyan-rgb: 6,182,212;   /* cyan du survol d une carte de prereglage */
  --surface-rgb: 18,24,20;   /* canal de la surface des cartes (= --surface) */
  --accent-fonce-rgb: 31,122,63;   /* canal de l accent au survol (= --accent-fonce) */
  --danger-clair-rgb: 248,113,113;   /* canal du rouge clair d alerte (= --danger-clair #f87171). ATTENTION : ce nom designait AVANT le rouge des hachures (255,80,80), qui est une AUTRE couleur — renomme --hachures-rgb */

  /* Couleurs pleines, nommees par leur ROLE et pas par leur valeur */
  --sur-accent: #fff;   /* ce qui est posé SUR un fond d’accent plein : le texte d’un bouton, et aussi la pastille d’un interrupteur allumé — même rôle, donc même jeton */
  --accent-fonce: #1f7a31;   /* accent au survol (plus sombre que --accent) */
  --sur-accent-bright: #08210f;   /* texte très sombre sur le vert clair */
  --sur-vert-badge: #06200f;   /* texte du badge « modèle 3DMaking » */
  --sur-vert-pastille: #06210F;   /* texte d’une pastille sur fond vert clair */
  --sur-vert-lien: #06110A;   /* texte du lien « Aller au contenu » */
  --danger-fonce: #e5484b;   /* rouge au survol d’un bouton d’alerte */
  --danger-vif: #ef4444;   /* rouge vif des jauges et pastilles */
  --danger-clair: #f87171;   /* rouge clair d’un texte d’alerte */
  --danger-doux: #e05555;   /* rouge adouci d’une bordure */
  --danger-pale: #f0a0a0;   /* rouge pâle d’un message d’échec */
  --danger-texte: #e0605f;   /* rouge d’un libellé « vide » */
  --danger-fond: #2a1718;   /* fond d’un avis d’erreur */
  --orange: #f97316;   /* orange des jauges */
  --jaune: #eab308;   /* jaune des jauges */
  --warning-doux: #e0a355;   /* ambre d’une source incertaine */
  --warning-pale: #e8c07a;   /* ambre pâle d’une note */
  --info: #7cb8ff;   /* bleu d’information */
  --media-fond: #0c0e11;   /* fond d’une vignette ou d’un aperçu d’image */
  --vignette-haut: #2c3d31;   /* haut du dégradé d’une vignette de création */
  --vignette-bas: #151d17;   /* bas du dégradé d’une vignette de création */
  --avis-fond: #18261c;   /* fond d’un avis discret */
  --texte-bouton-doux: #c9d3cc;   /* texte d’un bouton discret de l’aperçu */
  --vert-clair: #4ade80;   /* vert clair des pastilles et points vivants (plus clair que --accent-bright) */
  --vert-pale: #86efac;   /* vert tres pale, lisible sur un fond vert sombre */
  --text-vif: #fff;   /* texte le plus lisible, au survol d un fond sombre. A NE PAS CONFONDRE avec --sur-accent : celui-ci reste blanc en theme clair, celui-la devra devenir sombre */
  --apercu-fond-centre: #19261e;   /* centre du degrade derriere l apercu 3D de l accueil */
  --apercu-fond-bord: #0c120e;   /* bord du degrade derriere l apercu 3D de l accueil */
  --sur-vert-onglet: #06210d;   /* texte sur le vert vif d un onglet actif (applications, nouveautes) */
  --sur-media: #fff;   /* texte pose sur une image ou une incrustation sombre : il reste clair quel que soit le theme, contrairement a --text-vif */
  --verdict-echec: #E4572E;   /* rouge orange du verdict d echec d un export (Mon compte) */
  --viewport-fond: #080B11;   /* fond de la zone d apercu 3D des outils */
  --sur-media-doux: #c9d3cc;   /* libelle discret pose sur une incrustation sombre de l apercu (reste clair quel que soit le theme, comme --sur-media) */
  --warning-texte: #fbbf24;   /* texte d un encart d avertissement ambre */
  --danger-texte-clair: #fca5a5;   /* texte d un encart de blocage rouge */
  --fond-code: #fff;   /* fond OBLIGATOIREMENT blanc derriere un QR code : un code ne se lit pas sur fond colore, donc ce blanc ne suit PAS le theme */
  /* 🔴 QUATRE VERTS D EXPORT QUASI JUMEAUX, trouves en sortant les couleurs du
     dur, et c est une VRAIE derive de recopie entre pages : 1 a 4 unites sur 255
     d ecart, invisibles a l oeil.
       --vert-export-fonce      #008F36  11 pages
       --vert-export-fonce-bis  #008F35  vase et QR
       --vert-export-vif        #02C64D  les 5 pages ancrees
       --vert-export-vif-bis    #02c44c  la lightbox seule
     ⚠️ LES FONDRE CHANGERAIT LE RENDU : c est une decision de l exploitant, pas
     un effet de bord d un chantier de jetons. On les nomme donc tous, ce qui rend
     l anomalie VISIBLE au lieu de la laisser enfouie dans huit pages. */
  --vert-export-fonce-bis: #008F35;   /* fin du degrade du bouton Exporter du QR. ATTENTION : 1/255 de moins que --vert-export-fonce (#008F36) des 5 pages ancrees, tres probablement une coquille d origine. Les fondre CHANGERAIT le rendu : c est une decision de l exploitant, pas un effet de bord de ce chantier */
  --sur-accent-sombre: #0c1210;   /* texte SOMBRE pose sur un fond d accent plein (maillot). A ne pas confondre avec --sur-accent-bright (#08210f), qui est un autre sombre pour le vert clair */
  --viewport-fond-haut: #0d1116;   /* haut du degrade de fond de l apercu 3D du maillot */
  --viewport-fond-bas: #090c10;   /* bas du degrade de fond de l apercu 3D du maillot */
  --viewport-fond-profond: #060911;   /* fond de l apercu 3D du vase, plus sombre que --viewport-fond */
  --viewport-fond-halo: #131a15;   /* centre du halo de fond de l apercu de la lightbox */
  --viewport-fond-mauve: #100c11;   /* fond de l apercu du createur de statue, legerement mauve */
  --stepper-enfonce: #b8371f;   /* fond d un bouton de pas-a-pas pendant l appui (maillot) */
  --magenta: #c026d3;   /* magenta de la couleur de peinture active du createur de statue */
  --vert-export-vif-bis: #02c44c;   /* survol du bouton Exporter de la lightbox, debut. 2 unites de moins que --vert-export-vif : voir le commentaire des quatre verts jumeaux */
  --vert-export-vif-fonce-bis: #009d3b;   /* survol du bouton Exporter de la lightbox, fin */
  --argent: #c7c7c7;   /* argent de la 2e place du classement */
  --bronze: #c98a4b;   /* bronze de la 3e place du classement */
  --interrupteur-pastille: #e9efea;   /* pastille d un interrupteur ON/OFF au repos (panneau) */
  --sur-vert-interrupteur: #0b2a16;   /* texte ON pose sur le vert d un interrupteur allume */
  --papier-fond: #FFFFFF;   /* LA FEUILLE d un devis (.quote-paper et @media print). ⚠ NE SUIT PAS LE THEME : un devis s imprime sur du papier blanc, en theme clair comme en sombre */
  --papier-encre: #1A1A1A;   /* encre du corps d un devis, sur la feuille */
  --papier-encre-forte: #0F172A;   /* encre des titres d un devis */
  --papier-encre-noire: #000000;   /* encre a l impression (@media print) */
  --papier-texte-doux: #4A5568;   /* texte secondaire d un devis */
  --papier-texte-gris: #64748B;   /* texte gris d un devis */
  --papier-texte-gris-clair: #718096;   /* texte gris clair d un devis */
  --papier-texte-pale: #94A3B8;   /* mention pale d un devis */
  --papier-trait: #E2E8F0;   /* filet de separation sur la feuille */
  --papier-trait-pointille: #CBD5E1;   /* filet pointille sur la feuille */
  --papier-fond-doux: #F1F5F9;   /* bande legerement grisee sur la feuille */
  --papier-fond-tres-doux: #F8FAFC;   /* bande a peine grisee sur la feuille */
  --papier-fond-neutre: #FAFAFA;   /* fond neutre d un encart sur la feuille */
  --accent-fonce-bis: #1e7430;   /* accent au survol des createurs de devis. 1 unite de moins que --accent-fonce (#1f7a31) : meme derive de recopie que les quatre verts d export */
  --survol-dore: rgba(var(--accent-2-rgb), 0.25);   /* survol d un bouton dore. ⚠ En clair il devient PLEIN : un lavis a 25 % sur du blanc ne porte plus de texte blanc (mesure : 1,24:1) */
  /* 🔴 UNE OMBRE QUI SERT DE TEINTE DE SURFACE, et c est la seule du site :
     la bande de la vitrine de l accueil etait `rgba(var(--ombre-rgb), 0.25)`.
     Sur fond sombre elle creuse a peine ; sur du BLANC elle donne un gris 191,
     et son libelle tombe a 4,15:1 (mesure au RENDU, pas au calcul).
     ⚠ Mon instrument de contraste ne pouvait PAS le voir : le `color` est sur
     le libelle, le `background` sur la bande — deux regles differentes. C est
     le banc navigateur qui l a trouve. */
  --bande-vitrine: rgba(var(--ombre-rgb), 0.25);
}
/* =====================================================================
   THEME CLAIR — chantier n°2, 2026-09-21
   Il ne REDEFINIT que ce qui doit changer ; le reste est herite du :root
   ci-dessus. Un jeton absent d ici est un jeton dont l identite ne depend
   pas du theme.

   TROIS FAMILLES, et la deuxieme est celle qu on oublie :
   A. CE QUI BASCULE : fonds, textes, filets, voiles, ombres.
   B. CE QUI NE BASCULE PAS, et il a fallu le NOMMER pour ca pendant la
      sortie du dur : --sur-accent (blanc sur un bouton vert), --sur-media
      (texte sur une incrustation qui reste sombre), --fond-code (un QR ne
      se lit pas sur fond colore), --papier-* (un devis s imprime sur du
      blanc), --viewport-fond* (un rendu 3D se regarde sur du sombre).
      Sans ces noms, le theme aurait inverse le fond d un QR code et casse
      le SCAN, sans rien lever.
   C. 🔴 CE QU IL FAUT REACCORDER, le vrai travail : une bonne moitie des
      couleurs du site ont ete choisies pour BRILLER sur du noir. Mesure :
      --ok (#34d399) donne 8,0:1 sur --surface sombre et 1,9:1 sur du
      blanc. Pas a recopier : a refaire, plus foncees, meme teinte.

   ⚠️ EN CASCADE, mesure et pas devinee : --accent-bright a DEUX roles,
   couleur de TEXTE sur fond sombre et FOND d un texte sombre
   (--sur-accent-bright). L assombrir casse le second : les deux basculent
   donc ENSEMBLE (fond vert fonce, texte blanc). C est exactement ce que
   scripts/verifier-contraste-theme.js attrape si on l oublie.
   ⚠️ AUCUN prefers-color-scheme : le clair ne s applique que si on le
   demande (data-theme="clair"). Le defaut du site reste le sombre.
   ===================================================================== */
[data-theme="clair"] {
  /* --- A. Les fonds et le papier de l'ecran --------------------------- */
  --ink: #F4F7F4;                      /* fond de page, tres legerement verdi */
  --surface: #FFFFFF;                  /* les cartes */
  --surface-2: #F1F5F1;                /* une carte dans une carte */
  --surface-3: #E9EFEA;                /* bouton discret, piste d'interrupteur */
  --surface-rgb: 255,255,255;
  --media-fond: #EDF1ED;
  --avis-fond: #E9F5EB;
  --danger-fond: #FDECEC;
  --vignette-haut: #DDE8DF;
  --vignette-bas: #F0F4F0;
  --voile-menu-rgb: 246,248,246;       /* le panneau du menu lateral */
  --voile-pied-rgb: 236,241,236;       /* le pied de page */

  /* --- A. Les textes ------------------------------------------------- */
  --text: #10180F;
  --text-2: #46584B;
  --text-3: #6B7C70;
  --text-vif: #000000;                 /* son commentaire l'annoncait deja */
  --texte-bouton-doux: #3A4740;

  /* --- A. Filets, voiles, ombres -------------------------------------
     ⚠️ --voile-rgb passe au NOIR : c'est lui qui fabrique tous les
     survols et les separateurs des pages (`rgba(var(--voile-rgb), .06)`).
     Reste en blanc, il rendrait chaque survol INVISIBLE sur du clair —
     une centaine de reglages d'un coup, et aucune erreur nulle part. */
  --voile-rgb: 0,0,0;
  --line: rgba(0,0,0,0.10);
  --line-strong: rgba(0,0,0,0.18);
  --shadow: 0 1px 2px rgba(0,0,0,0.06), 0 10px 26px -10px rgba(0,0,0,0.16);
  --interrupteur-pastille: #8B9A8F;    /* la pastille au repos doit se voir SUR une piste claire */

  /* --- C. Les verts reaccordes --------------------------------------- */
  --accent-bright: #115C28;
  --vert-clair: #115C28;
  --vert-pale: #0D4A1F;
  --ok: #02614A;
  /* Les textes poses SUR ces verts basculent avec eux : un vert fonce
     veut du blanc, pas un vert-noir. */
  --sur-accent-bright: #FFFFFF;
  --sur-vert-badge: #FFFFFF;
  --sur-vert-pastille: #FFFFFF;
  --sur-vert-lien: #FFFFFF;
  --sur-vert-onglet: #FFFFFF;
  --sur-vert-interrupteur: #FFFFFF;

  /* --- C. Les rouges reaccordes -------------------------------------- */
  --danger: #C0262A;
  --danger-fonce: #9E1F22;
  --danger-vif: #C62828;
  --danger-clair: #C0362F;
  --danger-doux: #B4322F;
  --danger-pale: #A8534E;
  --danger-texte: #B42318;
  --danger-texte-clair: #B42318;
  --verdict-echec: #B33A15;

  /* --- C. Les ambres et le dore -------------------------------------
     ⚠️ --accent-2 (le dore des Polys) est une couleur d'IDENTITE, et
     pourtant il se fonce : a #f2b134 il donne 1,9:1 sur du blanc, donc un
     montant en Polys ILLISIBLE. Le canal --accent-2-rgb, lui, ne bouge
     pas : les lavis dores restent dores, et la piece de monnaie dessinee
     en SVG garde sa couleur. On fonce le TEXTE, pas la marque. */
  --accent-2: #8A6008;
  --warning: #8A5300;
  --warning-doux: #8A5A1A;
  --warning-pale: #7A5A28;
  --warning-texte: #8A5300;
  --jaune: #8A6D00;
  --orange: #B34700;

  /* --- C. Les bleus, les metaux ------------------------------------- */
  --info: #1B5FA8;
  --argent: #757575;
  --bronze: #8A5A28;

  /* --- C. Les pastilles de role -------------------------------------- */
  --role-admin: #C0262A;
  --role-beta: #8A6008;
  /* Les lavis : plus PALES en clair. Un lavis a 18 % d une couleur sombre sur du
     blanc donne un fond moyen qui mange le contraste de son propre texte
     (mesure : 3,49:1). Sur fond sombre le meme lavis eclaircit a peine — ce
     n est pas la meme operation. */
  --accent-soft: rgba(23,94,36,0.10);
  --ok-soft: rgba(52,211,153,0.12);
  --survol-dore: #8A6008;
  /* ⚠ L ardoise est une SURFACE, donc elle bascule : c est le fond d un toast
     sur 2 pages et celui des incrustations de l apercu 3D sur 8 autres. Les
     ilots ci-dessous la gardent SOMBRE ; partout ailleurs elle s eclaircit,
     sinon un toast au texte fonce se poserait sur de l ardoise nuit. */
  --ardoise-rgb: 236,240,245;
  --bande-vitrine: rgba(var(--ombre-rgb), 0.05);
}
/* =====================================================================
   LES ILOTS QUI RESTENT SOMBRES, MEME EN THEME CLAIR
   🔑 Une propriete personnalisee CASCADE : redeclarer --text sur le
   conteneur suffit a rendre sa valeur sombre a tout ce qu il contient,
   quel que soit le nom du jeton que la regle emploie.
   ⚠️ C est ce que des retouches regle par regle auraient manque : les
   15 outils remappent --text-main: var(--text) et --text-muted:
   var(--text-2) dans leur propre :root. Corriger var(--text) a la main
   dans .pv-spec-v aurait laisse tomber les .pv-spec-v{color:var(--text-main)}
   des autres outils — meme regle, autre nom, aucune erreur. J ai commence
   par ces retouches, et je les ai annulees pour ce bloc.

   POURQUOI CES CONTENEURS : leur FOND ne suit pas le theme, et c est
   voulu — la toile 3D se regarde sur du sombre (--viewport-fond), un
   bouton de galerie se pose sur la PHOTO d une creation.
   ⚠️ --voile-rgb en fait partie, et l oublier serait invisible : il
   fabrique les survols (rgba(var(--voile-rgb), .06)). Passe au noir par
   le theme clair, un survol pose sur une toile SOMBRE disparait. Mesure
   avant ce bloc : le survol du selecteur de vue du maillot tombait a
   1,09:1.
   ⚠️ .toast n y est PAS : son fond n est pas le meme selon la page
   (ardoise sombre sur 2 pages, --surface-2 ailleurs). En faire un ilot
   posait un texte clair sur un fond clair — mesure a 1,05:1.
   ⚠️ CHAQUE SELECTEUR A ETE VERIFIE PRESENT dans les pages (15 fichiers
   pour #pvSpecs) : une regle ecrite pour un selecteur inexistant serait
   lisible et parfaitement inerte — piege paye deux fois sur ce projet.
   ===================================================================== */
[data-theme="clair"] .viewport-container,
[data-theme="clair"] .pv-mode-btn,
[data-theme="clair"] #pvSpecs,
[data-theme="clair"] .pv-tete,
[data-theme="clair"] .pv-mode-bar,
[data-theme="clair"] #pvModeBar,
[data-theme="clair"] #previewModeBar,
[data-theme="clair"] #explodeBar,
[data-theme="clair"] .viewport-badge,
[data-theme="clair"] .view-controls,
[data-theme="clair"] .vs-rail,
[data-theme="clair"] .vs-panel,
[data-theme="clair"] .hud-note-led,
[data-theme="clair"] #loadingTag,
[data-theme="clair"] .gal-like-btn,
[data-theme="clair"] .gal-fav-btn {
  --text: #F8FAFC;
  --text-2: #9EB3A6;
  --text-3: #586B5F;
  --text-main: #F8FAFC;
  --text-muted: #9EB3A6;
  --ardoise-rgb: 15,23,42;
  --voile-rgb: 255,255,255;
  --line: rgba(255,255,255,0.08);
  --line-strong: rgba(255,255,255,0.14);
}



/* --- Reset et menu lateral (identiques sur toutes les pages) --- */
*{box-sizing:border-box;}
#sideNavHandle{
    width:46px; flex:0 0 auto;
    display:flex; align-items:center; justify-content:center;
    background:linear-gradient(180deg, rgba(var(--voile-rgb), .035), rgba(var(--voile-rgb), 0)), var(--surface);
    border-right:1px solid var(--line);
    color:var(--text-2); cursor:pointer; transition:color .15s ease;
  }
#sideNavHandle svg{width:18px; height:18px;}
#sideNavPanel{
    width:0; overflow:hidden; flex:0 0 auto;
    background:var(--surface); border-right:1px solid var(--line); box-shadow:var(--shadow);
    transition:width .22s ease; display:flex; flex-direction:column; padding-top:18px;
  }
/* ⚠️ OUVERTURE AU CLIC UNIQUEMENT (2026-09-05, demande utilisateur : « au
   survol ça gêne certains utilisateurs »). Il y avait AUSSI `#sideNav:hover`
   ici — le menu se dépliait dès qu'on frôlait le bord gauche de l'écran, y
   compris en visant autre chose. La classe `.nav-open` est posée par le petit
   bloc JS présent dans chacune des 24 pages qui portent le menu (poignée =
   bascule, clic à l'extérieur ou Échap = fermeture) ; elle existait déjà pour
   le tactile, qui n'a pas de :hover, et devient désormais le SEUL chemin.
   ⚠️ Ne pas remettre `:hover` sur cette règle. Le survol reste sur la POIGNÉE
   seule (couleur d'accent, plus bas) : c'est l'indice qu'elle est cliquable,
   et il n'ouvre rien. */
  #sideNav.nav-open #sideNavPanel{width:min(276px, 86vw);}
#sideNavPanel .side-title{
    padding:0 20px 14px; margin-bottom:6px; border-bottom:1px solid var(--line);
    font-size:10.5px; letter-spacing:.14em; text-transform:uppercase;
    font-weight:800; color:var(--text-3); white-space:nowrap;
  }
/* Logo a la place du mot "Menu" (2026-08-20, demande utilisateur : le texte
   "Menu" faisait pauvre en tete du panneau). SVG vectoriel (logo-3DMaking-dark.svg)
   donc net a n'importe quelle taille — jamais de pixelisation.
   Agrandi le meme jour (largeur pleine du panneau plutot qu'une petite
   hauteur fixe) pour occuper quasiment tout l'espace disponible de l'en-tete,
   comme demande — width:100% s'adapte automatiquement a min(276px, 86vw). */
#sideNavPanel .side-title-logo{width:100%; height:auto; display:block;}
/* Entrees du menu : pastilles arrondies detachees des bords du panneau.
   La marge horizontale (8px) + le padding (12px) redonnent le meme retrait
   de texte qu'avant (20px), pour ne pas decaler l'alignement du menu. */
.side-link{
    display:flex; align-items:center; gap:12px;
    padding:11px 12px; margin:2px 8px; border-radius:var(--radius-sm);
    color:var(--text); text-decoration:none; font-size:13px; font-weight:650; white-space:nowrap;
    border-left:2px solid transparent; transition:background .15s ease, color .15s ease, border-color .15s ease;
  }
.side-group-toggle{display:flex; align-items:center; gap:12px; padding:11px 12px; margin:2px 8px; border-radius:var(--radius-sm); width:auto; background:none; border:none; border-left:2px solid transparent; text-align:left; font-family:inherit; color:var(--text); font-size:13px; font-weight:650; white-space:nowrap; cursor:pointer; transition:background .15s ease, color .15s ease, border-color .15s ease;}
.side-group-toggle:hover{background:var(--accent-soft); color:var(--accent); border-left-color:var(--accent);}
.side-group-toggle .side-chevron{margin-left:auto; width:14px; height:14px; opacity:.7; transition:transform .2s ease;}
.side-group-toggle.open .side-chevron{transform:rotate(90deg);}
.side-submenu{max-height:0; overflow:hidden; transition:max-height .22s ease;}
.side-submenu.open{max-height:160px;}
/* Meme retrait visuel qu'avant pour les sous-entrees : 12px de padding de
   base + 24px d'indentation. */
.side-sublink{padding-left:36px;}

/* Bascule du thème clair (assets/theme-clair.js), posée en fin de menu.
   ⚠️ Elle reprend `.side-link` EXPRÈS : même allure, mêmes marges, et elle
   suivra une refonte du menu sans qu'on y revienne. Ces trois lignes ne
   règlent que ce qu'un <button> ne tient pas d'un <a> : la largeur, le
   remplissage par défaut du navigateur et le curseur.
   🔑 Une marge AU-DESSUS, parce que ce n'est pas une destination : la séparer
   des liens évite qu'on la clique en croyant naviguer. */
.theme-bascule{
    width:auto; background:none; border:none; border-left:2px solid transparent;
    text-align:left; font-family:inherit; cursor:pointer; margin-top:10px;
  }

/* Icones du menu, toujours vertes (voir --accent-bright : --accent serait
   trop sombre sur ce panneau pour rester lisible). Elles sont dessinees en
   stroke="currentColor", d'ou la simple propriete color. */
.side-link .side-icon, .side-group-toggle .side-icon{
    flex:0 0 auto; width:16px; height:16px; opacity:1;
    color:var(--accent-bright);
  }
.side-badge{margin-left:auto; font-size:9px; font-weight:800; letter-spacing:.04em; text-transform:uppercase; color:var(--text-3); background:var(--accent-soft); border-radius:var(--radius-sm); padding:2px 6px; white-space:nowrap;}

/* --- Cartes ------------------------------------------------------------ */
.card{
  position:relative; width:100%;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius); box-shadow:var(--shadow);
  transition:border-color .15s ease, box-shadow .15s ease;
}
.card::before{
  content:''; position:absolute; inset:0 0 auto 0; height:1px;
  border-radius:var(--radius) var(--radius) 0 0;
  background:linear-gradient(90deg, transparent, rgba(var(--voile-rgb), .14) 20%, rgba(var(--voile-rgb), .14) 80%, transparent);
  pointer-events:none;
}
.card-title{
  margin:0; padding:13px 18px; border-bottom:1px solid var(--line);
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  font-weight:750; color:var(--text-2);
  display:flex; align-items:center; gap:9px;
}
.card-title::before{
  content:''; width:7px; height:7px; border-radius:var(--radius-sm);
  flex:0 0 auto; background:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);
}
.card-body{padding:18px;}
.card-compact .card-title, .card.compact .card-title{padding:9px 14px; font-size:10px;}
.card-compact .card-body, .card-body.compact{padding:13px 14px;}

/* --- Boutons, aide et messages d'erreur -------------------------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  padding:9px 18px; background:var(--surface-3); color:var(--text);
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);
  font-weight:650; font-size:13px; font-family:inherit; cursor:pointer;
  text-decoration:none;
  transition:background .14s ease, border-color .14s ease, color .14s ease, box-shadow .14s ease;
}
.btn:hover{border-color:var(--ok); color:var(--ok); background:var(--accent-soft);}
.btn-primary{
  background:var(--accent); border-color:transparent; color:var(--sur-accent);
  font-weight:700; box-shadow:0 2px 8px rgba(var(--accent-rgb), 0.4);
}
.btn-primary:hover{
  background:var(--accent-fonce); border-color:transparent; color:var(--sur-accent);
  box-shadow:0 4px 14px rgba(var(--accent-bright-rgb), 0.35);
}
.btn-row{display:flex; gap:10px; flex-wrap:wrap; align-items:center;}
.hint{font-size:11.5px; color:var(--text-3); margin-top:7px; line-height:1.45;}
/* display:none est FONCTIONNEL : le JS passe a block pour reveler l'erreur.
   Le design system proposait display:flex — applique tel quel, tous les
   messages d'erreur du site seraient affiches en permanence. */
.error-text{
  color:var(--danger); font-size:11.5px; font-weight:600;
  margin-top:6px; display:none;
}

/* --- Pastille de Polys, son infobulle, et badges de role ----------------- */
.balance-pill{
  margin-left:auto; font-size:11.5px; font-weight:750; letter-spacing:.02em;
  color:var(--accent-2); background:rgba(var(--accent-2-rgb), 0.12);
  border:1px solid rgba(var(--accent-2-rgb), 0.35); border-radius:var(--radius-sm);
  padding:5px 13px; white-space:nowrap; text-transform:none;
  position:relative; display:inline-flex; align-items:center; gap:6px;
  text-decoration:none; cursor:pointer;
  transition:background .15s ease, border-color .15s ease;
}
.balance-pill:hover{background:rgba(var(--accent-2-rgb), 0.22); border-color:rgba(var(--accent-2-rgb), 0.6);}
/* La pastille n'est plus cliquable (elle ne mène plus vers boutique.html) :
   on neutralise le curseur et l'effet :hover hérités de .balance-pill,
   sans toucher à l'apparence de base (voir #balancePill/#balancePillTopbar,
   qui n'ont plus de href). */
#balancePill, #balancePillTopbar{cursor:default;}
#balancePill:hover, #balancePillTopbar:hover{background:rgba(var(--accent-2-rgb), 0.12); border-color:rgba(var(--accent-2-rgb), 0.35);}
.balance-pill-tooltip{
  position:absolute; left:50%; top:calc(100% + 8px); transform:translate(-50%, 4px);
  background:var(--surface-3); border:1px solid var(--line-strong); color:var(--text);
  font-size:11px; font-weight:650; padding:6px 11px; border-radius:var(--radius-sm);
  white-space:nowrap; box-shadow:var(--shadow);
  text-transform:none; letter-spacing:normal;
  opacity:0; pointer-events:none;
  transition:opacity .15s ease, transform .15s ease; z-index:50;
}
.balance-pill:hover .balance-pill-tooltip{opacity:1; transform:translate(-50%, 0);}
/* Pastille imprimante Bambu Lab choisie (compte.html) — même forme que
   .balance-pill, variante verte pour la distinguer du solde de Polys
   (orange). Cachée par défaut (style="display:none" inline) : affichée en
   JS seulement une fois qu'on sait si l'utilisateur est connecté. */
.printer-pill{
  color:var(--ok); background:var(--ok-soft);
  border-color:rgba(var(--ok-rgb), 0.35);
}
.printer-pill:hover{background:rgba(var(--ok-rgb), 0.22); border-color:rgba(var(--ok-rgb), 0.6);}

/* Sélecteur de matériau dans la barre du haut (topbar) — juste à gauche de
   .printer-pill sur les outils qui exportent en 3MF. <select> natif restylé
   (fond sombre au lieu du blanc par défaut du navigateur) plutôt qu'un
   composant maison : garde le comportement clavier/accessibilité natif.
   Caché par défaut (style="display:none" inline), affiché en JS une fois le
   compte chargé — même logique que .printer-pill.

   TOUT `!important` ci-dessous, dans .topbar-material-select ET
   .topbar-printer-select (base + :hover + :focus/:focus-visible) — décision
   ferme prise le 2026-08-18 après 2 fuites réelles causées par le CSS
   générique `select`/`select:focus`/`input:focus` que chacune des 6 pages
   outil définit pour SES PROPRES champs, avec des valeurs différentes d'une
   page à l'autre (héritées de gabarits copiés/collés séparément, jamais
   unifiés). Une règle de classe gagne déjà en spécificité sur les
   propriétés qu'elle déclare, mais (1) c'est facile d'en oublier une par
   erreur (vécu deux fois), et (2) rien n'empêche une page ACTUELLE ou
   FUTURE de déclarer un jour une règle encore plus spécifique. `!important`
   ici rend ces 2 composants réellement immunisés, sans condition : toute
   nouvelle page qui inclut ce balisage (voir le HTML identique déjà
   utilisé sur les 6 outils) affiche ce style, point final, quel que soit
   le CSS local qu'elle définit par ailleurs pour ses propres `<select>`.
   Si ce composant doit un jour changer d'apparence, modifier UNIQUEMENT
   ici — ne jamais recréer un style concurrent dans le <style> d'une page.

   Fond NEUTRE partout (base/hover/focus), pas de teinte verte — décision du
   2026-08-18 après un essai de fond vert permanent : Chrome/Windows ne
   respecte pas fiablement `background` posé sur chaque `<option>`
   individuellement et a tendance à peindre TOUT le popup natif ouvert en
   reprenant la couleur de fond du `<select>` lui-même (même mécanisme déjà
   rencontré sur `border-color`) — un fond vert donnait donc une liste
   déroulante entièrement lavée de vert, pas juste la ligne sélectionnée.
   La distinction imprimante/matériau reste visible via le texte et l'icône
   (verts pour l'imprimante), jamais via le fond. */
.topbar-material-select{
  appearance:none !important; -webkit-appearance:none !important; -moz-appearance:none !important;
  font-size:11.5px !important; font-weight:750 !important; letter-spacing:.02em !important; font-family:inherit !important;
  color:var(--ok) !important; background:var(--surface-2) !important;
  border:1px solid var(--line-strong) !important; border-radius:var(--radius-sm) !important;
  padding:5px 26px 5px 13px !important; white-space:nowrap !important; cursor:pointer !important;
  max-width:170px !important; text-overflow:ellipsis !important;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
  background-repeat:no-repeat !important; background-position:right 6px center !important; background-size:14px !important;
  transition:background-color .15s ease, border-color .15s ease;
}
.topbar-material-select:hover{background:var(--surface-3) !important;}
.topbar-material-select:focus,
.topbar-material-select:focus-visible{outline:none !important; box-shadow:none !important; border-color:var(--line-strong) !important; background:var(--surface-2) !important;}
.topbar-material-select option{background:var(--surface-2); color:var(--text);}

/* Sélecteur d'imprimante dans la topbar — même composant que
   .topbar-material-select (voir les 2 commentaires `!important`/fond neutre
   ci-dessus, valables ici aussi), distinct uniquement par le texte/l'icône
   verts. Remplace l'ancien lien <a class="printer-pill"> vers compte.html :
   on change l'imprimante sans quitter l'outil, compte.html reste utilisable
   pour le même réglage. */
.topbar-printer-select{
  appearance:none !important; -webkit-appearance:none !important; -moz-appearance:none !important;
  font-size:11.5px !important; font-weight:750 !important; letter-spacing:.02em !important; font-family:inherit !important;
  color:var(--ok) !important; background:var(--surface-2) !important;
  border:1px solid var(--line-strong) !important; border-radius:var(--radius-sm) !important;
  padding:5px 26px 5px 13px !important; white-space:nowrap !important; cursor:pointer !important;
  max-width:170px !important; text-overflow:ellipsis !important;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2334d399' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") !important;
  background-repeat:no-repeat !important; background-position:right 6px center !important; background-size:14px !important;
  transition:background-color .15s ease, border-color .15s ease;
}
.topbar-printer-select:hover{background:var(--surface-3) !important;}
.topbar-printer-select:focus,
.topbar-printer-select:focus-visible{outline:none !important; box-shadow:none !important; border-color:var(--line-strong) !important; background:var(--surface-2) !important;}
.topbar-printer-select option{background:var(--surface-2); color:var(--text);}

.role-badge{
  display:inline-flex; align-items:center; gap:6px; padding:4px 12px;
  border-radius:999px; font-size:11px; font-weight:800;
  letter-spacing:.03em; text-transform:uppercase;
}
.role-badge.admin{color:var(--role-admin); background:var(--role-admin-soft);}
.role-badge.moderator{color:var(--role-mod); background:var(--role-mod-soft);}
.role-badge.printer{color:var(--role-printer); background:var(--role-printer-soft);}
.role-badge.beta{color:var(--role-beta); background:var(--role-beta-soft);}

/* --- Socle commun des modales ------------------------------------------ */
.modal-overlay{
  display:none; position:fixed; inset:0; z-index:300;
  background:rgba(var(--voile-modale-rgb), 0.78);
  -webkit-backdrop-filter:blur(5px); backdrop-filter:blur(5px);
  align-items:center; justify-content:center; padding:16px;
}
.modal-overlay.open{display:flex;}
.modal{
  position:relative; width:360px; max-width:100%; padding:24px;
  background:var(--surface); border:1px solid var(--line-strong);
  border-radius:var(--radius); box-shadow:0 24px 60px -12px rgba(var(--ombre-rgb), .9);
}
.modal-title{
  margin:0 0 16px; font-size:15px; font-weight:750; color:var(--text);
  display:flex; align-items:center; gap:8px;
}
.modal-close{
  position:absolute; top:12px; right:12px; width:28px; height:28px;
  background:transparent; border:none; color:var(--text-3);
  font-size:16px; cursor:pointer; border-radius:var(--radius-sm);
  display:flex; align-items:center; justify-content:center;
  transition:background .12s ease, color .12s ease;
}
.modal-close:hover{background:var(--surface-2); color:var(--text);}
.modal-switch{margin-top:12px; font-size:11.5px; color:var(--text-3); text-align:center;}
.modal-switch a{color:var(--accent-2); cursor:pointer; text-decoration:none;}
.modal-switch a:hover{text-decoration:underline;}

/* --- Menu lateral : etats (uniformises sur --accent) -------------------- */
#sideNav{position:fixed; inset:0 auto 0 0; z-index:100; display:flex; align-items:stretch;}
#sideNav:hover #sideNavHandle, #sideNav.nav-open #sideNavHandle{color:var(--accent);}
.side-link:hover{background:var(--accent-soft); color:var(--accent); border-left-color:var(--accent);}
.side-link.active{background:var(--accent-soft); color:var(--accent); border-left-color:var(--accent);}
/* width:auto et non 100% : avec la marge horizontale des pastilles, un
   bouton a 100% deborderait de 16px et son coin arrondi droit serait rogne
   par l'overflow:hidden du panneau. */
button.side-link{background:none; border:none; border-left:2px solid transparent; width:auto; text-align:left; font-family:inherit; cursor:pointer;}
.side-link.disabled{opacity:.45; cursor:not-allowed; pointer-events:none;}

/* =========================================================================
   MENU COMPTE (avatar + carte de profil au survol) — 2026-08-20
   =========================================================================
   Balisage et logique produits par assets/account-menu.js (module partagé,
   window.AccountMenu). Ce bloc CSS est la SOURCE UNIQUE du style de ce
   composant : ne jamais recréer un style concurrent dans le <style> d'une
   page — meme regle que .topbar-printer-select/.topbar-material-select plus
   haut, et pour la meme raison (chaque page a ses propres regles generiques
   heritees de gabarits copies/colles, une propriete non declaree ici peut
   fuir vers elles).

   Ce composant a d'abord ete duplique a la main sur 11 pages : 4 collisions
   de declaration reelles (ROLE_LABEL, AVATAR_URL_PREFIX, isSafeAvatarUrl,
   AVATAR_PRESETS charge deux fois) en sont sorties avant mutualisation.
   ========================================================================= */
.account-menu{position:relative; flex:0 0 auto; opacity:0; transition:opacity .15s ease;}
/* Revele seulement une fois l'etat connecte/deconnecte reellement connu
   (AccountMenu.setReady()) : sans ca le bouton vert "Mon compte" clignote une
   fraction de seconde avant de devenir l'avatar, a chaque chargement de page
   — signale par l'utilisateur le 2026-08-20 ("ca fait pas pro"). opacity et
   non display:none : la place reste reservee, aucun saut de mise en page. */
.account-menu.ready{opacity:1;}
.account-menu-trigger{padding:8px 16px; font-size:12.5px;}
.account-menu-trigger .account-avatar{display:none;}
/* Deconnecte : bouton rouge "Se connecter" (jamais vert, ca laissait croire
   a une session active — signale par l'utilisateur le 2026-08-24). Des que
   .is-user est ajoutee (AccountMenu.update()), les regles ci-dessous plus
   bas reprennent la main et retablissent le vert + l'avatar. */
.account-menu:not(.is-user) .account-menu-trigger{
  background:var(--danger); box-shadow:0 2px 8px rgba(var(--danger-rgb), 0.35);
}
.account-menu:not(.is-user) .account-menu-trigger:hover{
  background:var(--danger-fonce); border-color:transparent; color:var(--sur-accent);
  box-shadow:0 4px 14px rgba(var(--danger-rgb), 0.4);
}
.account-menu.is-user .account-menu-trigger{
  width:40px; height:40px; padding:0; border-radius:50%; overflow:hidden;
  background:var(--surface-3); box-shadow:none; border:2px solid var(--accent);
  cursor:pointer;
}
.account-menu.is-user .account-menu-trigger:hover{border-color:var(--ok); background:var(--surface-3);}
.account-menu.is-user .account-menu-trigger .account-menu-label{display:none;}
.account-menu.is-user .account-menu-trigger .account-avatar{display:flex;}
.account-avatar{
  width:100%; height:100%; border-radius:50%; overflow:hidden;
  align-items:center; justify-content:center;
  background:var(--surface-3); color:var(--text-2);
  font-size:15px; font-weight:800;
}
.account-avatar img{width:100%; height:100%; object-fit:cover;}
.account-avatar svg{width:100%; height:100%; display:block;}

/* La carte reste collee SANS ESPACE sous l'avatar (top:100%, pas de
   margin-top) : c'est elle qui doit rester continument survolee pendant que
   la souris descend depuis le rond. L'ecart visuel vient de son padding-top
   — un padding fait partie de la zone survolable, contrairement a une marge
   qui creerait un vrai trou. Bug vecu et corrige le 2026-08-20 : avec une
   marge, la carte se refermait avant d'etre atteignable. */
.account-hover-card{
  display:none; position:absolute; top:100%; right:0; padding-top:10px;
  width:286px; z-index:80;
  opacity:0; transform:translateY(-6px); pointer-events:none;
  transition:opacity .16s ease, transform .16s ease;
}
.account-hover-card-inner{
  padding:18px 16px 16px;
  background:var(--surface-3); border:1px solid var(--line-strong);
  border-radius:var(--radius); box-shadow:var(--shadow);
}
.account-menu.is-user .account-hover-card{display:block;}
.account-menu.is-user:hover .account-hover-card,
.account-menu.is-user:focus-within .account-hover-card{
  opacity:1; transform:translateY(0); pointer-events:auto;
}

/* --- En-tete : avatar + pseudo en gros, centres (demande explicite) ------ */
.am-head{display:flex; flex-direction:column; align-items:center; text-align:center; gap:9px;}
.am-avatar-lg{width:64px; height:64px; flex:0 0 auto; font-size:24px;}
.am-pseudo{
  font-weight:800; font-size:16px; color:var(--text); max-width:100%;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.am-badges{display:flex; align-items:center; justify-content:center; gap:7px; flex-wrap:wrap;}
.am-level{
  display:inline-flex; align-items:center; gap:8px;
  padding:5px 13px; border-radius:999px;
  font-size:12px; font-weight:700; letter-spacing:.01em;
  border:1px solid rgba(var(--accent-2-rgb), .4); color:var(--accent-2); background:var(--accent-2-soft);
}
/* .am-card : scope générique du "corps de carte" produit par
   account-menu.js (cardBodyMarkup) — partagé par les 3 endroits où ce corps
   de carte est monté : la "Fiche personnelle" (carte de survol du menu
   compte, .account-hover-card-inner.am-card ; ET la carte statique de
   compte.html, #personalCardMount .am-card, voir AccountMenu.mountPersonalCard)
   ET la "Fiche publique" (.maker-card-modal .am-card de galerie.html, voir
   AccountMenu.buildPublicCard). Renommé depuis ".account-hover-card
   .role-badge" pour que le role-badge reste caché par défaut partout. */
.am-card .role-badge{display:none;}
.am-card .role-badge.visible{display:inline-flex;}

/* --- Niveau / XP : seuils et remises partages, voir levelFor() dans
       account-menu.js (source unique, plus de copie dans compte.html) ---- */
.am-discount{margin-top:12px; font-size:11.5px; color:var(--text-3); text-align:center;}
.am-discount strong{color:var(--text-2);}
.am-xp-track{
  margin-top:10px; height:8px; border-radius:999px;
  background:var(--surface-2); border:1px solid var(--line-strong); overflow:hidden;
}
.am-xp-fill{height:100%; background:var(--accent); border-radius:999px; transition:width .3s ease; width:0%;}
.am-xp-meta{display:flex; justify-content:space-between; gap:10px; font-size:11px; color:var(--text-3); margin-top:6px;}

/* --- Description (bio) — même rendu que l'ancien ".maker-bio" propre à
       galerie.html, remonté ici pour être partagé par les deux contextes. */
.am-bio{
  margin:12px 0 0; font-size:12.5px; color:var(--text-2); line-height:1.5;
  text-align:center; white-space:pre-wrap; word-break:break-word;
}
.am-bio:empty::before{content:'—'; color:var(--text-3);}

.am-stats{margin-top:14px; padding-top:12px; border-top:1px solid var(--line);}
.am-stat-row{display:flex; justify-content:space-between; align-items:center; gap:10px; font-size:12.5px; margin-bottom:8px;}
.am-stat-row:last-child{margin-bottom:0;}
.am-stat-row .am-stat-label{color:var(--text-2);}
.am-stat-row .am-stat-value{font-weight:750; font-variant-numeric:tabular-nums; display:inline-flex; align-items:center; gap:5px;}

/* --- Selecteurs imprimante + materiau ------------------------------------
   Memes composants que ceux de la barre du haut des 6 outils
   (.topbar-printer-select/.topbar-material-select, style ci-dessus reutilise
   tel quel). Seule difference assumee : ils occupent toute la largeur de la
   carte au lieu des 170px max de la topbar — d'ou ces 2 surcharges, posees
   ICI et pas dans une page, comme l'exige la regle de ces composants. */
.am-selects{margin-top:14px; display:flex; flex-direction:column; gap:8px;}
.account-hover-card .topbar-printer-select,
.account-hover-card .topbar-material-select{
  max-width:none !important; width:100% !important;
}

.account-hover-actions{display:flex; align-items:center; gap:10px; margin-top:14px;}
/* Juste la piece, volontairement sans cadre ni fond rond (contrairement a
   l'avatar) : une simple icone cliquable, pas un bouton. */
.poly-plans-icon-btn{
  display:flex; align-items:center; justify-content:center; flex:0 0 auto;
  width:26px; height:26px; padding:0; background:none; border:none; cursor:pointer;
  filter:brightness(1); transition:filter .15s ease, transform .1s ease;
}
.poly-plans-icon-btn svg{width:100%; height:100%;}
.poly-plans-icon-btn:hover{filter:brightness(1.25);}
.poly-plans-icon-btn:active{transform:scale(0.92);}
.account-hover-manage{flex:1; padding:9px 14px; font-size:12.5px; text-align:center;}
/* Bouton "Se déconnecter" tout en bas de la fiche personnelle (carte de
   survol uniquement) — séparé du reste par un filet, discret par défaut,
   rouge seulement au survol (jamais un bouton plein comme "Gérer mon
   compte", pour ne pas rivaliser visuellement avec l'action principale). */
.account-hover-signout{
  display:block; width:100%; margin-top:10px; padding-top:10px;
  border:none; border-top:1px solid var(--line);
  background:none; color:var(--text-3); font-size:12px; font-weight:600;
  text-align:center; cursor:pointer; transition:color .14s ease;
}
.account-hover-signout:hover{color:var(--danger);}

/* =========================================================================
   BARRE DU HAUT DES OUTILS 3D — 2026-08-20
   =========================================================================
   Demande explicite : les 6 outils doivent avoir EXACTEMENT la meme barre que
   la page d'accueil (pleine largeur, collante, logo a gauche, menu compte a
   droite), mais garnie de LEURS boutons (materiau, imprimante, Exporter,
   etat d'imprimabilite) au lieu des liens marketing.

   Selecteur `#topbar.app-topbar-tool` (id + classe) et non `.app-topbar-tool`
   seul : chacune des 6 pages definit deja sa propre regle `#topbar{...}` en
   carte (fond, bordure, radius, ombre) dans son <style> local, qui gagnerait
   sur une simple classe. Ces pages ne sont donc PAS a modifier cote CSS —
   il suffit d'ajouter la classe au <header id="topbar">.

   Le decalage negatif annule le padding de #app (16px, et 62px a gauche pour
   la poignee du menu lateral) pour que la barre touche les bords de la
   fenetre, comme sur l'accueil ou #page n'a aucun padding.
   ========================================================================= */
#topbar.app-topbar-tool{
  position:sticky; top:0; z-index:60;
  display:flex; align-items:center; gap:14px; flex-wrap:wrap; flex:0 0 auto;
  background:rgba(var(--voile-menu-rgb), 0.97);
  border:none; border-bottom:1px solid var(--line);
  border-radius:0; box-shadow:none;
  padding:4px 16px 4px 62px;
  margin:-16px -16px 0 -62px;
  width:calc(100% + 78px);
}
#topbar.app-topbar-tool .app-brand{display:flex; align-items:center; flex:0 0 auto; text-decoration:none;}
#topbar.app-topbar-tool .app-brand img{height:40px; width:auto; display:block;}
/* Pousse le menu compte tout a droite, comme sur l'accueil. .balance-pill a
   deja `margin-left:auto` : on le neutralise ici pour que ce soit le menu
   compte — et lui seul — qui soit repousse a droite, les autres commandes
   restant groupees a gauche a la suite du titre. */
#topbar.app-topbar-tool .balance-pill{margin-left:auto;}
#topbar.app-topbar-tool .account-menu{margin-left:0;}
/* ⚠️ La règle téléphone de cette barre a déménagé tout en bas de ce fichier,
   dans « LE STUDIO MOBILE » (2026-09-20). Elle y est réécrite en entier — une
   seule ligne, qui défile au doigt — et la laisser ici en ferait deux qui se
   contredisent à 860 px. Ne pas la réintroduire : c'est le bloc du bas qui
   décide de tout ce qui se passe sous 860 px. */

/* =========================================================================
   POPUP "PLANS POLYS" (achat de Polys via de vrais boutons PayPal) — 2026-08-20
   =========================================================================
   Utilisée par index.html + les 6 outils 3D (boutique.html a son propre
   .pack-card, plus large, pas concerné par ce bloc). Mutualisée ici après
   une régression réelle : lors du passage du menu compte à
   assets/account-menu.js le 2026-08-20, ce bloc CSS — jusque-là dupliqué par
   page comme le menu compte — a été supprimé PAR ERREUR sur les 6 outils (le
   script de nettoyage capturait tout entre le commentaire "Menu compte" et la
   première accolade fermante de .account-hover-balance, sans savoir que ce
   bloc-ci vivait entre les deux). La popup restait fonctionnelle (le clic
   PayPal marchait) mais s'affichait sans aucun style — passée inaperçue car
   jamais retestée visuellement après ce refactor précis. Centralisée ici pour
   que ça ne puisse plus se reproduire : plus aucune page ne porte sa propre
   copie de ce bloc. */
.poly-plans-sub{color:var(--text-3); font-size:12px; margin:-10px 0 16px;}
.poly-plan-row{
  display:flex; flex-direction:column; gap:10px;
  padding:12px 14px; margin-bottom:10px;
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:var(--radius-sm); color:var(--text);
}
.poly-plan-row:last-child{margin-bottom:0;}
.poly-plan-row.featured{border-color:rgba(var(--accent-2-rgb), .45);}
.poly-plan-info{display:flex; align-items:center; justify-content:space-between; gap:12px;}
.poly-plan-name{font-size:12.5px; font-weight:750; display:flex; align-items:center; gap:6px; flex-wrap:wrap;}
.poly-plan-badge{
  font-size:9.5px; font-weight:750; padding:2px 7px; border-radius:999px;
  color:var(--accent-2); background:var(--ink); border:1px solid rgba(var(--accent-2-rgb), .5);
  text-transform:uppercase; letter-spacing:.03em;
}
.poly-plan-points{font-size:11.5px; color:var(--text-2); margin-top:3px; display:flex; align-items:center; gap:4px;}
.poly-plan-price{text-align:right; flex:0 0 auto;}
.poly-plan-price .now{font-size:15px; font-weight:800; display:block;}
.poly-plan-price .before{display:block; font-size:10.5px; color:var(--text-3); text-decoration:line-through;}
.poly-plan-pp{min-height:36px;}
.poly-plans-loading, .poly-plans-error{padding:16px 0; text-align:center; color:var(--text-3); font-size:12.5px;}

/* Mention légale de renoncement au droit de rétractation (art. L. 221-28 13°
   Code de la consommation), affichée UNE FOIS au-dessus de la liste des
   plans — avant tout clic sur un bouton PayPal, quel que soit le plan choisi.
   Même contenu que #withdrawalNotice sur boutique.html. */
.poly-plans-legal{
  font-size:10.5px; line-height:1.5; color:var(--text-3);
  padding:9px 12px; margin:0 0 14px;
  background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-sm);
}

/* ---------------------------------------------------------------------------
   ZONE DE DÉPÔT D'IMAGE — produite par le type de champ `fichier` de
   assets/params-schema.js (2026-09-06, emporte-pièce à partir d'une image).
   Le style vit ICI, en un seul exemplaire, et jamais dans le <style> d'une
   page : c'est la règle déjà posée pour le menu compte et les sélecteurs de
   la barre du haut — un composant partagé n'a qu'une seule feuille de style,
   sinon chaque nouvelle page en redéfinit une variante qui dérive.
   --------------------------------------------------------------------------- */
.params-dropzone{
  border:1.5px dashed var(--line-strong); border-radius:var(--radius-sm);
  padding:14px 12px; text-align:center; cursor:pointer;
  font-size:11.5px; line-height:1.45; color:var(--text-3);
  background:var(--surface-2);
  transition:border-color .15s ease, color .15s ease, background .15s ease;
}
.params-dropzone:hover, .params-dropzone.is-survol{
  border-color:var(--accent-bright); color:var(--text-2);
}
.params-dropzone-statut{
  font-size:10.5px; line-height:1.45; color:var(--text-3); margin-top:6px;
}
.params-dropzone-statut:empty{display:none;}
.params-dropzone-statut.ok{color:var(--accent-bright);}
.params-dropzone-statut.ko{color:var(--danger);}

/* ---------------------------------------------------------------------------
   BASCULE À DEUX POSITIONS — produite par le type de champ `bascule` de
   assets/params-schema.js (2026-09-07, « Position de l'épingle » du
   marque-verre). Une case à cocher restait le bon réglage sur le fond, mais
   « Épingle à gauche du prénom » obligeait à déduire l'autre position ; deux
   boutons la nomment.
   Le vert actif reprend `.pv-mode-btn.active` de assets/plate-preview.js
   (fond vert vif, texte très sombre) : c'est déjà la façon dont ce site
   montre « la position choisie » dans la barre d'aperçu.
   --------------------------------------------------------------------------- */
.params-bascule{
  display:flex; gap:4px; padding:3px;
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:var(--radius-sm);
}
.params-bascule-opt{
  flex:1; background:transparent; border:none;
  color:var(--text-3); font-family:inherit; font-size:11.5px; font-weight:700;
  /* Le meme rayon que la barre, et non un rayon reduit : certaines pages
     mettent deliberement --radius-sm a 0 (le marque-verre est tout en coins
     carres), et un `calc(... - 2px)` y devient NEGATIF, donc invalide, donc
     ignore en silence. */
  padding:7px 4px; border-radius:var(--radius-sm);
  cursor:pointer; transition:background .15s ease, color .15s ease;
}
.params-bascule-opt:hover{ background:rgba(var(--voile-rgb), .06); color:var(--text-2); }
.params-bascule-opt.is-actif{
  background:var(--accent-bright); color:var(--sur-accent-bright);
  box-shadow:0 2px 8px rgba(var(--accent-bright-rgb), .22);
}
/* `:focus-visible` et non `:focus` : la bascule s'utilise surtout à la souris,
   et un anneau après chaque clic serait du bruit. Au clavier il reste. */
.params-bascule-opt:focus-visible{
  outline:2px solid var(--accent-bright); outline-offset:2px;
}

/* Avertissement de droits en pied de menu d'export. Écrit d'abord en local
   dans le créateur de maillot (2026-08-20) ; posé ici quand l'emporte-pièce
   a pu partir d'une image importée (2026-09-06), pour ne pas en faire une
   3ᵉ copie. La règle locale du maillot reste en place et gagne (même
   sélecteur, feuille de page chargée après) — valeurs identiques. */
.dropdown-menu .rights-hint{
  padding:8px 15px; font-size:10px; line-height:1.4; color:var(--text-3);
  border-top:1px solid var(--line); cursor:default;
}

/* --- Sauvegardes de réglages « Mes créations » (assets/design-saves.js) ----
   Un seul jeu de styles pour les 13 outils. ⚠️ Aucun rayon dérivé par
   soustraction ici : `calc(var(--radius-sm) - 2px)` devient négatif — donc
   invalide, donc ignoré en silence — sur une page qui met ses rayons à 0,
   ce qui est le cas de tout le site depuis la charte du 16/08. On réutilise
   les variables telles quelles. */
.ds-modal-large{width:760px;}
.ds-champ{
  width:100%; margin-top:4px; padding:9px 11px;
  background:var(--surface-2); color:var(--text);
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);
  font-family:inherit; font-size:13px;
}
.ds-champ:focus{outline:none; border-color:var(--accent-bright); box-shadow:var(--ring);}
.ds-quota{margin-top:7px; font-size:11px; color:var(--text-3);}
.ds-erreur{margin-top:7px; font-size:11.5px; color:var(--danger); line-height:1.45;}
.ds-avertissement{
  margin-top:10px; padding:8px 10px; font-size:11.5px; line-height:1.45;
  color:var(--warning); background:var(--warning-soft);
  border:1px solid rgba(var(--warning-rgb), 0.35); border-radius:var(--radius-sm);
}

.ds-grille{
  display:grid; gap:12px;
  grid-template-columns:repeat(auto-fill, minmax(168px, 1fr));
  max-height:min(60vh, 520px); overflow-y:auto; padding-right:4px;
}
.ds-carte{
  display:flex; flex-direction:column; gap:6px; padding:9px;
  background:var(--surface-2); border:1px solid var(--line);
  border-radius:var(--radius-sm); cursor:pointer;
  transition:border-color .12s ease, background .12s ease;
}
.ds-carte:hover{border-color:var(--accent-bright); background:var(--accent-soft);}
.ds-vignette{
  aspect-ratio:4/3; background:var(--media-fond); border:1px solid var(--line);
  border-radius:var(--radius-sm); overflow:hidden;
  display:flex; align-items:center; justify-content:center;
}
.ds-vignette img{width:100%; height:100%; object-fit:contain; display:block;}
.ds-vignette-vide::after{content:'—'; color:var(--text-3); font-size:18px;}
.ds-nom{
  font-size:12.5px; font-weight:650; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.ds-date{font-size:10.5px; color:var(--text-3);}
/* ⚠️ `wrap` OBLIGATOIRE : une création partagée porte CINQ boutons (Charger,
   Renommer, Dupliquer, Partager, Supprimer) dans une carte de 132 à 220 px —
   sans retour à la ligne ils débordaient de la carte (signalé le 17/09). */
.ds-actions{display:flex; flex-wrap:wrap; gap:6px; margin-top:2px;}
.ds-mini{flex:1 1 auto; min-width:0; font-size:10.5px; padding:5px 8px; white-space:nowrap;}
.ds-suppr:hover{border-color:var(--danger); color:var(--danger); background:var(--danger-soft);}
/* Partage par lien privé (2026-09-10) */
.ds-partage-nom{margin:-4px 0 8px; font-size:12px; font-weight:650; color:var(--text-2); overflow-wrap:anywhere;}
.ds-lien-ligne{display:flex; gap:8px; margin-top:12px; align-items:stretch;}
.ds-lien-ligne .ds-champ{flex:1; min-width:0; font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:11.5px;}
.ds-lien-ligne .btn{white-space:nowrap;}
.ds-liens-titre{margin-top:18px; font-size:10.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--text-3);}
.ds-liens{display:flex; flex-direction:column; gap:6px; margin-top:8px; max-height:190px; overflow-y:auto;}
.ds-lien-actif{display:flex; align-items:center; justify-content:space-between; gap:10px; padding:7px 10px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-2);}
.ds-lien-actif .ds-mini{flex:0 0 auto;}
@media (max-width:560px){ .ds-lien-ligne{flex-direction:column;} }

/* ---- Santé du site (panneau admin, 2026-09-10) ---- */
.sante-resume{display:inline-block; padding:6px 12px; border-radius:999px; font-size:12.5px; font-weight:800; margin-bottom:8px;}
.sante-ok{background:var(--accent-soft); color:var(--accent-bright);}
.sante-ko{background:var(--danger-soft); color:var(--danger);}
.sante-serveur{margin:0 0 10px;}
.sante-table td{vertical-align:top;}
.sante-etat-ok{color:var(--accent-bright); font-weight:700;}
.sante-etat-ko{color:var(--danger); font-weight:800;}
.sante-ligne-ko td{background:var(--danger-soft);}
.sante-ms{font-variant-numeric:tabular-nums; white-space:nowrap; color:var(--text-2);}
.sante-message{max-width:420px; overflow-wrap:anywhere;}
.sante-source{max-width:220px; overflow-wrap:anywhere; font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:11px; color:var(--text-2);}
.photos-mod-actions{display:flex; gap:6px; flex-wrap:wrap;}

/* ---- Photos d'impression (compte → Feedback, 2026-09-10) ---- */
.pi-zone{flex-basis:100%; display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:6px;}
.pi-ajouter, .pi-retirer{font-size:11.5px; padding:5px 10px;}
.pi-statut{font-size:11.5px; font-weight:700; padding:4px 10px; border-radius:999px; background:var(--surface-3); color:var(--text-2);}
.pi-statut.pi-publie{background:var(--accent-soft); color:var(--accent-bright);}
.pi-statut.pi-refuse{background:var(--danger-soft); color:var(--danger);}
.pi-fichier{margin-top:12px; font-size:12px; color:var(--text-2); max-width:100%;}
.pi-apercu{display:block; max-width:100%; max-height:260px; margin-top:12px; border-radius:var(--radius-sm); border:1px solid var(--line); object-fit:contain; background:var(--surface-2);}
.pi-accord{margin-top:14px;}

/* ---- Cartes cadeaux (compte, 2026-09-10) ---- */
.cc-intro{margin-top:0;}
.cc-offrable{margin:10px 0 8px; font-size:13px; font-weight:650; color:var(--text);}
.cc-ligne{display:flex; gap:8px; align-items:stretch; flex-wrap:wrap;}
.cc-ligne .ds-champ{flex:1; min-width:150px;}
.cc-code{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing:.08em;}
.cc-resultat{margin-top:14px; padding:12px; border:1px solid var(--accent-bright); border-radius:var(--radius-sm); background:var(--accent-soft);}
.cc-resultat-titre{font-size:12.5px; font-weight:700; margin-bottom:8px;}
.cc-code-affiche{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight:700; letter-spacing:.06em; overflow-wrap:anywhere;}
.cc-code-grand{font-size:18px; color:var(--accent-bright); flex:1; align-self:center;}
.cc-liste{display:flex; flex-direction:column; gap:6px;}
.cc-carte{display:grid; grid-template-columns:minmax(0,1.4fr) auto minmax(0,1.6fr) auto; gap:12px; align-items:center; padding:8px 12px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-2); font-size:12.5px;}
.cc-carte .ds-mini{flex:0 0 auto;}
.cc-points{font-weight:800; font-variant-numeric:tabular-nums; white-space:nowrap;}
.cc-etat{color:var(--text-2);}
.cc-utilisee{opacity:.62;}
.ds-erreur.cc-ok{color:var(--accent-bright);}
@media (max-width:640px){ .cc-carte{grid-template-columns:1fr auto;} }
.boutique-cadeau{margin:16px 0 0; font-size:13px; color:var(--text-2); text-align:center;}
.boutique-cadeau a{color:var(--accent-bright);}

/* ---- Galerie : modèles officiels et photos de la communauté (2026-09-10) ---- */
.gc-section{width:100%; max-width:1080px; margin:0 0 26px;}
#galleryImpressions.gc-section{margin:34px 0 10px;}
.gc-entete{margin-bottom:12px;}
.gc-titre{margin:0; font-size:18px; font-weight:800; letter-spacing:-.01em;}
.gc-sous-titre{margin:4px 0 0; font-size:12.5px; color:var(--text-2);}
.gc-bande{display:grid; grid-auto-flow:column; grid-auto-columns:minmax(190px, 220px); gap:12px; overflow-x:auto; padding-bottom:6px; scroll-snap-type:x proximity;}
.gc-modele{display:flex; flex-direction:column; border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden; background:var(--surface-2); text-decoration:none; color:var(--text); scroll-snap-align:start; transition:border-color .15s ease, transform .15s ease;}
.gc-modele:hover, .gc-modele:focus-visible{border-color:var(--accent-bright); transform:translateY(-2px);}
.gc-modele img{width:100%; aspect-ratio:1/1; object-fit:cover; display:block; background:var(--surface-3);}
.gc-modele-pied{display:flex; justify-content:space-between; align-items:center; gap:8px; padding:8px 10px; font-size:11.5px;}
.gc-outil{font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
.gc-action{color:var(--accent-bright); font-weight:700; white-space:nowrap;}
.gc-photos{display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:14px;}
.gc-photo{margin:0; border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden; background:var(--surface-2);}
.gc-photo img{width:100%; aspect-ratio:4/3; object-fit:cover; display:block;}
.gc-photo figcaption{display:flex; justify-content:space-between; gap:8px; padding:7px 10px; font-size:11.5px; color:var(--text-2);}
.gc-date{white-space:nowrap;}
/* Rendu de la pièce à côté de sa photo (2026-09-25, migration 122). */
.gc-duo{display:grid; grid-template-columns:1fr 1fr; gap:1px; background:var(--line);}
.gc-duo figure{margin:0; position:relative; background:var(--surface-2);}
.gc-photo .gc-duo img{aspect-ratio:2/3;}   /* deux moitiés = la hauteur d’une photo en 4:3 */
.gc-duo figcaption{position:absolute; left:6px; top:6px; padding:2px 7px; border-radius:999px; font-size:10.5px; font-weight:700; background:var(--surface); color:var(--text-2); border:1px solid var(--line);}
.gc-note{margin:12px 0 0; font-size:12px; color:var(--text-3);}
.gal-badge-officiel{position:absolute; top:8px; left:8px; z-index:1; padding:3px 8px; border-radius:999px; background:var(--accent-bright); color:var(--sur-vert-badge); font-size:10.5px; font-weight:800; letter-spacing:.02em; pointer-events:none;}
@media (prefers-reduced-motion: reduce){ .gc-modele{transition:none;} .gc-modele:hover{transform:none;} }

/* ---- Impression à la demande (outils, compte, admin — 2026-09-11) ---- */
.ds-modal-impression .modal{width:560px; max-width:94vw;}
.ds-imp-intro{margin-top:0;}
.ds-imp-apercu{display:block; max-width:100%; max-height:180px; margin:10px auto 4px; border-radius:var(--radius-sm); border:1px solid var(--line); background:var(--media-fond);}
.ds-imp-form{display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:10px 12px; margin-top:10px;}
.ds-imp-form .ds-imp-droits, .ds-imp-form .hint, .ds-imp-form label:nth-of-type(3), .ds-imp-form label:nth-of-type(7){grid-column:1/-1;}
.ds-imp-champ{display:flex; flex-direction:column; gap:4px; font-size:11.5px; font-weight:650; color:var(--text-2); min-width:0;}
.ds-imp-champ .ds-champ{width:100%;}
.ds-imp-ok{margin-top:14px; padding:12px 14px; border-radius:var(--radius-sm); background:var(--accent-soft); font-size:13px; line-height:1.5;}
.ds-imp-ok a{color:var(--accent-bright);}
@media (max-width:560px){ .ds-imp-form{grid-template-columns:1fr;} }
.cmd-liste{display:flex; flex-direction:column; gap:10px;}
.cmd-ligne, .cmd-admin-tete{display:flex; gap:14px; align-items:flex-start;}
.cmd-ligne{padding:12px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-2);}
.cmd-admin{padding:14px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-2); display:flex; flex-direction:column; gap:12px;}
.cmd-vignette{width:112px; height:84px; object-fit:contain; border-radius:6px; border:1px solid var(--line); background:var(--media-fond); flex:0 0 auto;}
.cmd-info{display:flex; flex-direction:column; gap:5px; min-width:0; flex:1; align-items:flex-start;}
.cmd-titre{font-weight:800; font-size:13.5px;}
.cmd-date, .cmd-adresse{font-size:12px; color:var(--text-2); overflow-wrap:anywhere;}
.cmd-statut{display:inline-block; padding:3px 10px; border-radius:999px; font-size:11.5px; font-weight:700; background:var(--surface-3); color:var(--text-2);}
.cmd-statut.cmd-action{background:var(--accent-2-soft); color:var(--accent-2);}
.cmd-statut.cmd-ok{background:var(--accent-soft); color:var(--accent-bright);}
.cmd-statut.cmd-fin{opacity:.7;}
.cmd-prix{font-size:13px; font-weight:700; font-variant-numeric:tabular-nums;}
.cmd-note{font-size:12.5px; color:var(--text-2); white-space:pre-wrap; overflow-wrap:anywhere;}
.cmd-actions{display:flex; gap:8px; flex-wrap:wrap; margin-top:4px;}
.cmd-admin-form{display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:10px 12px;}
.cmd-admin-large{grid-column:1/-1;}
.cmd-etat{font-size:12.5px; color:var(--text-2);}
.cmd-prevenir{display:inline-flex; align-items:center; gap:6px; font-size:12.5px; color:var(--text-2); cursor:pointer;}
.cmd-payer{margin-top:6px; padding:12px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-3); display:flex; flex-direction:column; gap:10px; width:100%; max-width:420px;}
.cmd-payer-titre{font-weight:800; font-size:13.5px;}
.cmd-consent{display:flex; gap:8px; align-items:flex-start; font-size:12px; line-height:1.5; color:var(--text-2); cursor:pointer;}
.cmd-consent input{margin-top:2px; flex:0 0 auto;}
.cmd-estim{display:flex; flex-direction:column; gap:8px; padding:10px 12px; border:1px dashed var(--line); border-radius:var(--radius-sm);}
.cmd-estim-sortie{display:flex; flex-direction:column; gap:3px; font-size:12.5px; color:var(--text-2); font-variant-numeric:tabular-nums;}
.cmd-estim-sortie strong{color:var(--text); font-size:14px;}
.cpt-section{display:flex; flex-direction:column; gap:10px; padding:14px; margin-top:12px; border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface-2);}
.cpt-titre{font-weight:800; font-size:14px;}
.cpt-chiffres{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px;}
.cpt-chiffre{display:flex; flex-direction:column; gap:2px; padding:10px 12px; border-radius:var(--radius-sm); background:var(--surface-3);}
.cpt-chiffre span{font-size:12px; color:var(--text-2);}
.cpt-chiffre strong{font-size:18px; font-variant-numeric:tabular-nums;}
.cpt-jauge-bloc{display:flex; flex-direction:column; gap:4px;}
.cpt-jauge-tete{display:flex; justify-content:space-between; gap:10px; flex-wrap:wrap; font-size:12.5px; color:var(--text-2);}
.cpt-jauge-tete strong{color:var(--text); font-variant-numeric:tabular-nums;}
.cpt-jauge{height:8px; border-radius:999px; background:var(--surface-3); overflow:hidden;}
.cpt-jauge-rempli{height:100%; border-radius:999px;}
.cpt-vert{background:var(--accent-bright);} .cpt-jaune{background:var(--jaune);} .cpt-orange{background:var(--orange);} .cpt-rouge{background:var(--danger-vif);}
.cpt-alerte{font-size:13px; font-weight:600; padding:8px 12px; border-radius:var(--radius-sm); background:var(--surface-3);}
.cpt-tableau{overflow-x:auto;}
.cpt-tableau table{width:100%; border-collapse:collapse; font-size:12.5px; font-variant-numeric:tabular-nums;}
.cpt-tableau th, .cpt-tableau td{padding:6px 8px; border-bottom:1px solid var(--line); text-align:left; vertical-align:top;}
.cpt-tableau th{color:var(--text-2); font-weight:700;}
.cpt-montant{width:90px !important; display:inline-block; margin-right:6px;}
@media (max-width:760px){ .cpt-chiffres{grid-template-columns:1fr;} }

/* ===== COMPTABILITÉ, refonte C1-C7 (2026-09-15) ============================
   ⚠️ Chaque classe qui pose un `display` a sa garde `[hidden]` : sans elle,
   l'attribut `hidden` (règle du navigateur) serait battu et le volet, le
   formulaire ou les réglages resteraient affichés (piège payé le 09/09). */
.cpt2-pile{display:flex; flex-direction:column; gap:16px;}
.cpt2-barre-haut{margin-bottom:14px;}
.cpt2-carte{background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:18px 20px; display:flex; flex-direction:column; gap:14px; min-width:0;}
.cpt2-declaration{border-color:color-mix(in srgb, var(--accent-2) 30%, var(--line));}
.cpt2-tete{display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex-wrap:wrap;}
.cpt2-tete h3{margin:2px 0 0; font-size:17px; font-weight:800; letter-spacing:-.01em;}
.cpt2-surtitre{font-size:10.5px; font-weight:750; letter-spacing:.11em; text-transform:uppercase; color:var(--text-3);}
.cpt2-actions{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.cpt2-corps{display:flex; flex-direction:column; gap:12px;}
.cpt2-ligne{display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
.cpt2-entre{justify-content:space-between;}
.cpt2-tuiles{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:12px;}
.cpt2-tuile{display:flex; flex-direction:column; gap:3px; padding:13px 15px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); min-width:0;}
.cpt2-tuile strong{font-size:22px; font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.01em;}
.cpt2-tuile-orange{border-color:color-mix(in srgb, var(--accent-2) 40%, var(--line));}
.cpt2-tuile-orange strong{color:var(--accent-2);}
/* Rapprochement PayPal (18/09) : le compteur « À regarder » rouge ou vert. */
.cpt2-tuile-rouge{border-color:color-mix(in srgb, var(--danger) 45%, var(--line));}
.cpt2-tuile-rouge strong{color:var(--danger);}
.cpt2-tuile-vert{border-color:color-mix(in srgb, var(--accent-bright) 40%, var(--line));}
.cpt2-tuile-vert strong{color:var(--accent-bright);}
.cpt2-lbl{font-size:10.5px; font-weight:750; letter-spacing:.09em; text-transform:uppercase; color:var(--text-3);}
.cpt2-detail{font-size:12px; color:var(--text-2);}
.cpt2-grand{font-size:26px; font-weight:800; font-variant-numeric:tabular-nums;}
.cpt2-num{font-family:'JetBrains Mono', ui-monospace, Consolas, monospace; font-variant-numeric:tabular-nums; font-size:12.5px;}
.cpt2-vert{color:var(--accent-bright);} .cpt2-orange{color:var(--accent-2);} .cpt2-rouge{color:var(--danger);}
.cpt2-puce{display:inline-flex; align-items:center; gap:5px; font-size:11.5px; font-weight:700; padding:3px 9px; border-radius:999px; background:var(--surface-3); color:var(--text-2); white-space:nowrap; vertical-align:middle;}
.cpt2-puce-vert{background:var(--ok-soft); color:var(--accent-bright);}
.cpt2-puce-orange{background:var(--accent-2-soft); color:var(--accent-2);}
.cpt2-puce-rouge{background:var(--danger-soft); color:var(--danger);}
.cpt2-puce-bleu{background:rgba(var(--info-rgb), .13); color:var(--info);}
.cpt2-reglages{display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; padding:12px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px dashed var(--line-strong);}
.cpt2-reglages[hidden], .cpt2-form[hidden], .cpt2-volet[hidden], .cpt2-saisie[hidden], .cpt-tableau[hidden], .cpt2-graphe[hidden]{display:none !important;}
.cpt2-champ{display:flex; flex-direction:column; gap:4px; min-width:130px;}
/* Le panneau pose width:100% sur tous les input/select : dans une ligne d'actions, ils reprennent leur taille. */
.cpt2-ligne .ds-champ, .cpt2-actions .ds-champ{width:auto;}
.cpt2-champ .ds-champ{width:100%;}
.cpt2-legende{display:flex; gap:14px; flex-wrap:wrap; font-size:12px; color:var(--text-2); align-items:center;}
.cpt2-legende i{display:inline-block; width:10px; height:10px; border-radius:3px; margin-right:6px; vertical-align:-1px;}
.cpt2-graphe svg{display:block; max-width:100%; height:auto;}
.cpt2-deux{display:grid; grid-template-columns:1fr 1fr; gap:14px;}
.cpt2-deux-inegal{grid-template-columns:1.35fr 1fr;}
.cpt2-bloc{display:flex; flex-direction:column; gap:12px; padding:14px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); min-width:0;}
.cpt2-bloc h4{margin:0; font-size:14px; font-weight:800;}
.cpt2-jauge{display:flex; flex-direction:column; gap:6px;}
.cpt2-jauge-tete{display:flex; justify-content:space-between; gap:10px; flex-wrap:wrap; font-size:12.5px; color:var(--text-2);}
.cpt2-jauge-tete strong{color:var(--text); font-variant-numeric:tabular-nums;}
.cpt2-barre{position:relative; height:8px; border-radius:999px; background:var(--surface-3);}
.cpt2-barre i{position:absolute; top:0; bottom:0; left:0; border-radius:999px; min-width:3px;}
.cpt2-rempli-vert{background:var(--accent-bright);} .cpt2-rempli-orange{background:var(--orange);} .cpt2-rempli-rouge{background:var(--danger-vif);}
.cpt2-barre em{position:absolute; top:-4px; width:2px; height:16px; border-radius:2px; background:var(--accent-2); transform:translateX(-1px);}
.cpt2-calendrier{display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); gap:8px;}
.cpt2-mois{display:flex; flex-direction:column; gap:6px; padding:10px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); min-width:0;}
.cpt2-courant{border-color:color-mix(in srgb, var(--accent-bright) 50%, var(--line));}
.cpt2-futur{opacity:.5;}
.cpt2-mois-nom{font:inherit; font-weight:750; font-size:13px; text-align:left; background:none; border:none; color:var(--text); padding:0; cursor:pointer;}
.cpt2-mois-nom:hover{color:var(--accent-bright);}
.cpt2-coches{display:flex; gap:5px; flex-wrap:wrap;}
.cpt2-coche{font:inherit; font-size:10.5px; font-weight:700; padding:2px 8px; border-radius:999px; border:1px dashed var(--line-strong); background:none; color:var(--text-3); cursor:pointer;}
.cpt2-coche.fait{border-style:solid; border-color:transparent; background:var(--ok-soft); color:var(--accent-bright);}
.cpt2-coche:disabled{cursor:default;}
.cpt2-export{padding-top:12px; border-top:1px solid var(--line);}
.cpt2-filtre{font:inherit; font-size:12px; font-weight:700; padding:5px 11px; border-radius:999px; border:1px solid var(--line); background:var(--surface-2); color:var(--text-2); cursor:pointer; white-space:nowrap;}
.cpt2-filtre.actif{background:var(--ok-soft); border-color:transparent; color:var(--accent-bright);}
.cpt2-recherche{margin-left:auto; width:min(260px, 100%) !important;}
.cpt2-factures{display:grid; grid-template-columns:minmax(0,1fr); gap:14px; align-items:start;}
.cpt2-factures.avec-volet{grid-template-columns:minmax(0,1fr) 340px;}
.cpt2-ligne-facture{cursor:pointer;}
.cpt2-ligne-facture:hover td{background:rgba(var(--voile-rgb), .03);}
.cpt2-ligne-facture.choisie td{background:var(--accent-soft);}
.cpt2-ligne-facture:focus-visible{outline:2px solid var(--accent-bright); outline-offset:-2px;}
.cpt2-volet{position:sticky; top:16px; display:flex; flex-direction:column; gap:10px; padding:14px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line-strong); box-shadow:-18px 0 40px -28px rgba(var(--ombre-rgb), .8);}
.cpt2-volet-tete{justify-content:space-between;}
.cpt2-lignes{display:flex; flex-direction:column; gap:4px; padding:8px 0; border-top:1px solid var(--line); border-bottom:1px solid var(--line);}
.cpt2-consequence{font-size:12.5px; line-height:1.45; padding:9px 11px; border-radius:var(--radius-sm); background:var(--surface-3);}
.cpt2-saisie{width:120px !important;}
.cpt2-fermer{padding:3px 8px !important; font-size:12px !important; background:none !important; border-color:transparent !important; color:var(--text-3) !important;}
.cpt2-fermer:hover{color:var(--danger) !important;}
.btn-danger-doux{background:var(--danger-soft) !important; border-color:transparent !important; color:var(--danger) !important; margin-left:auto;}
.btn-danger-doux:disabled{opacity:.5;}
.cpt2-form{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px,1fr)); gap:10px; align-items:end; padding:12px; border-radius:var(--radius-sm); background:var(--surface-3);}
.cpt2-total{padding-top:8px; border-top:1px solid var(--line-strong); font-size:15px;}
/* Échéancier des obligations (2026-09-18, migration 116) */
.cpt2-obligs{display:flex; flex-direction:column;}
.cpt2-oblig{display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:6px 12px; align-items:start; padding:10px 0; border-top:1px solid var(--line);}
.cpt2-oblig:first-child{border-top:0;}
.cpt2-oblig-texte{display:flex; flex-direction:column; gap:2px; min-width:0;}
.cpt2-oblig-texte strong{font-size:13.5px;}
.cpt2-oblig.faite .cpt2-oblig-texte strong{text-decoration:line-through; color:var(--text-3);}
.cpt2-oblig-actions{display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end;}
.cpt2-corps details > summary{cursor:pointer; padding:6px 0;}
@media (max-width:640px){ .cpt2-oblig{grid-template-columns:1fr;} .cpt2-oblig-actions{justify-content:flex-start;} }
.cpt2-table-marges td, .cpt2-table-marges th{white-space:nowrap;}
@media (max-width:1100px){ .cpt2-calendrier{grid-template-columns:repeat(4, minmax(0,1fr));} .cpt2-factures.avec-volet{grid-template-columns:1fr;} }
@media (max-width:760px){ .cpt2-tuiles, .cpt2-deux, .cpt2-deux-inegal{grid-template-columns:1fr;} .cpt2-calendrier{grid-template-columns:repeat(2, minmax(0,1fr));} .cpt2-recherche{margin-left:0;} }

/* --- Cloche de notifications (assets/notifications.js, migration 94) --------
   Posée dans .topbar-actions, ENTRE la pastille de Polys et le menu compte.
   Le panneau se cale sur la droite : sur mobile il occupe la largeur utile
   moins deux gouttières, jamais plus, pour ne pas déborder de l'écran. */
.notif{position:relative; display:flex; align-items:center;}
.notif-bouton{position:relative; display:grid; place-items:center; width:36px; height:36px; padding:0;
  border:1px solid var(--line); border-radius:999px; background:var(--surface-2); color:var(--text-2); cursor:pointer;
  transition:color .15s ease, border-color .15s ease;}
.notif-bouton:hover{color:var(--accent-bright); border-color:var(--accent-bright);}
.notif-bouton.notif-actif{color:var(--text);}
.notif-pastille{position:absolute; top:-3px; right:-3px; min-width:17px; height:17px; padding:0 4px;
  border-radius:999px; background:var(--accent-bright); color:var(--sur-vert-pastille); font-size:10.5px; font-weight:800;
  line-height:17px; text-align:center; font-variant-numeric:tabular-nums;}
.notif-panneau{position:absolute; top:calc(100% + 10px); right:0; width:min(360px, calc(100vw - 32px));
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm);
  box-shadow:0 18px 40px -20px rgba(var(--ombre-rgb), .55); z-index:60; overflow:hidden;}
.notif-tete{display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:11px 14px; border-bottom:1px solid var(--line); font-size:14px;}
.notif-tout-lu{font:inherit; font-size:12px; font-weight:600; color:var(--accent-bright);
  background:none; border:none; padding:0; cursor:pointer;}
.notif-tout-lu:disabled{color:var(--text-3); cursor:default;}
.notif-liste{max-height:min(60vh, 420px); overflow-y:auto;}
.notif-item{display:flex; gap:11px; padding:11px 14px; border-bottom:1px solid var(--line);
  color:var(--text); text-decoration:none;}
.notif-item:last-child{border-bottom:none;}
a.notif-item:hover{background:var(--surface-2);}
.notif-item-neuve{background:color-mix(in srgb, var(--accent-bright) 8%, transparent);}
.notif-item-ico{flex:0 0 auto; font-size:15px; line-height:1.35;}
.notif-item-corps{display:flex; flex-direction:column; gap:2px; min-width:0;}
.notif-item-titre{font-size:13.5px; font-weight:700; line-height:1.35;}
.notif-item-texte{font-size:12.5px; color:var(--text-2); line-height:1.45;}
.notif-item-date{font-size:11.5px; color:var(--text-3);}
.notif-vide{padding:22px 16px; text-align:center; font-size:13px; color:var(--text-2);}
.notif-pied{padding:10px 14px; border-top:1px solid var(--line); text-align:center;}
.notif-pied a{font-size:12.5px; font-weight:600; color:var(--accent-bright); text-decoration:none;}
.notif-pied a:hover{text-decoration:underline;}
@media (max-width:520px){ .notif-panneau{position:fixed; top:60px; right:12px; left:12px; width:auto;} }

/* --- Tableau de bord admin (assets/admin-tableau-bord.js, migration 94) ----- */
/* Mode vacances (01/10) : bandeau posé par assets/app-stages.js en tête de page. */
#bandeau-vacances{position:relative; z-index:60; display:flex; align-items:center; justify-content:center; gap:10px;
  padding:9px 44px 9px 16px; font-size:13px; line-height:1.4; text-align:center;
  background:var(--warning-soft); color:var(--text); border-bottom:1px solid var(--warning);}
#bandeau-vacances button{position:absolute; right:10px; top:50%; transform:translateY(-50%); width:28px; height:28px;
  border:none; border-radius:7px; background:transparent; color:var(--text-2); font-size:14px; cursor:pointer;}
#bandeau-vacances button:hover{background:var(--surface-2); color:var(--text);}
/* Téléphone (vu le 01/10) : les outils y sont en plein écran FIXE, un bandeau en tête
   chevauchait l'en-tête et l'aperçu. Bulle flottante en bas, au-dessus de « Exporter ». */
@media (max-width:620px){
  #bandeau-vacances{position:fixed; z-index:1200; left:10px; right:10px; bottom:84px; padding:10px 40px 10px 12px;
    font-size:12px; text-align:left; border:1px solid var(--warning); border-radius:12px;
    background:var(--surface); box-shadow:var(--shadow);}
}
.tb-entete{display:flex; align-items:center; gap:14px; margin-bottom:16px;}
.tb-logo{height:34px; width:auto; flex:0 0 auto;}
.tb-eyebrow{font-size:11.5px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--accent-bright);}
.tb-titre{margin:0; font-size:22px; font-weight:800; letter-spacing:-.01em;}
.tb-entete .btn{margin-left:auto;}
.tb-entete > div{min-width:0;}
/* 30/09 (vu sur iPhone) : logo + date + titre + « Rafraîchir » ne tenaient pas sur
   une ligne ; le bouton sortait de la carte. Au téléphone, le logo (déjà en haut de
   page) s'efface et le bouton se fait discret. */
@media (max-width:560px){
  .tb-logo{display:none;}
  .tb-titre{font-size:19px;}
  .tb-entete .btn{padding:7px 12px; font-size:12px; flex:0 0 auto;}
}
/* 30/09 (vu sur iPhone) : la galerie du panneau et de la modération, en tableau à
   6 colonnes, ne tenait pas dans l'écran (pseudo et outil coupés, 3 boutons
   empilés par ligne). Au téléphone, chaque partage devient une fiche : vignette à
   gauche, pseudo / outil / date · statut à droite, boutons côte à côte dessous.
   Mêmes éléments, même ordre : les écouteurs (closest('tr')) restent valables. */
@media (max-width:700px){
  #galleryModCard table, #galleryModCard tbody{display:block; overflow:visible;}
  #galleryModCard thead{display:none;}
  #galleryModBody tr[data-id]{display:grid; grid-template-columns:52px auto minmax(0, 1fr); column-gap:12px; row-gap:2px; align-items:center; padding:12px 0; border-bottom:1px solid var(--line);}
  #galleryModBody tr[data-id] > td{display:block; padding:0; border:none; min-width:0; overflow-wrap:anywhere;}
  #galleryModBody tr[data-id] > td:nth-child(1){grid-column:1; grid-row:1 / span 3; align-self:start;}
  #galleryModBody tr[data-id] > td:nth-child(2){grid-column:2 / -1; grid-row:1; font-weight:700;}
  #galleryModBody tr[data-id] > td:nth-child(3){grid-column:2 / -1; grid-row:2; font-size:13px; color:var(--text-2);}
  #galleryModBody tr[data-id] > td:nth-child(4){grid-column:2; grid-row:3; font-size:13px; color:var(--text-2);}
  #galleryModBody tr[data-id] > td:nth-child(5){grid-column:3; grid-row:3; font-size:13px; color:var(--text-2);}
  #galleryModBody tr[data-id] > td:nth-child(5)::before{content:"· ";}
  #galleryModBody tr[data-id] > td:nth-child(6){grid-column:1 / -1; grid-row:4; display:flex; flex-wrap:wrap; gap:6px; margin-top:8px;}
  #galleryModBody tr[data-id] > td:nth-child(6) .btn{flex:1 1 auto; margin:0; padding:8px 10px; font-size:13px; min-height:38px; white-space:nowrap;}
}
.tb-grille{display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:14px; align-items:start;}
.tb-colonne{display:flex; flex-direction:column; gap:14px; min-width:0;}
.tb-tuiles{display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:12px; margin-bottom:14px;}
.tb-tuile{display:flex; flex-direction:column; gap:3px; padding:13px 15px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line);}
.tb-tuile-lbl{font-size:10.5px; font-weight:750; letter-spacing:.1em; text-transform:uppercase; color:var(--text-3);}
.tb-tuile strong{font-size:22px; font-weight:800; letter-spacing:-.01em; font-variant-numeric:tabular-nums;}
.tb-tuile-orange strong{color:var(--accent-2);}
.tb-tuile-rouge strong{color:var(--danger);}
.tb-tuile-vert strong{color:var(--accent-bright);}
@media (max-width:700px){ .tb-tuiles{grid-template-columns:1fr 1fr;} }
/* La boîte « À traiter » (refonte A6) : la couleur du point dit l'urgence. */
.tb-atraiter{border-color:color-mix(in srgb, var(--accent-2) 35%, var(--line));}
.tb-inbox{display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:10px; align-items:center; padding:9px 0; border-top:1px solid var(--line);}
.tb-inbox:first-child{border-top:none;}
.tb-inbox-point{width:9px; height:9px; border-radius:50%; background:var(--text-3); box-shadow:0 0 0 3px rgba(var(--voile-rgb), .05);}
.tb-inbox-rouge .tb-inbox-point{background:var(--danger); box-shadow:0 0 0 3px var(--danger-soft);}
.tb-inbox-orange .tb-inbox-point{background:var(--accent-2); box-shadow:0 0 0 3px var(--accent-2-soft);}
.tb-inbox-texte{display:flex; flex-direction:column; gap:1px; min-width:0;}
.tb-inbox-texte strong{font-size:13px; font-weight:700; overflow-wrap:anywhere;}
.tb-inbox-texte span{font-size:12px; color:var(--text-2); overflow-wrap:anywhere;}
.tb-inbox-btn{padding:5px 10px !important; font-size:11.5px !important; white-space:nowrap;}
/* Cartes « en direct » adoptées de l'ancienne colonne de gauche (refonte A2). */
.tb-direct .user-list-empty{padding:4px 0;}
@media (max-width:700px){ .tb-grille{grid-template-columns:1fr;} }
/* Cartes de validation posées dans une demi-colonne du tableau de bord : on
   resserre leurs tableaux pour que les boutons d'action restent visibles sans
   défilement horizontal (boutons empilés, cellules moins espacées). */
.tb-colonne .cost-table{font-size:12.5px;}
.tb-colonne .cost-table th, .tb-colonne .cost-table td{padding-right:6px;}
.tb-colonne .gal-mod-thumb{width:40px; height:40px;}
.tb-colonne .photos-mod-actions{flex-direction:column; align-items:stretch; gap:4px;}
.tb-colonne .photos-mod-actions .btn, .tb-colonne #tdModBody .btn{padding:4px 8px; font-size:11.5px;}
/* La colonne « Statut » des photos redit le filtre, et les boutons proposés disent
   déjà où en est la photo : dans la demi-colonne, elle cède sa place aux actions. */
.tb-colonne #photosModCard th:nth-child(5), .tb-colonne #photosModBody td:nth-child(5){display:none;}
.tb-carte{background:var(--surface-2); border:1px solid var(--line); border-radius:var(--radius-sm); padding:16px 18px;}
.tb-carte-tete{display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:12px;}
.tb-carte-tete h3{margin:0; font-size:14px; font-weight:800; letter-spacing:.01em;}
.tb-carte-orange{border-color:color-mix(in srgb, var(--warning) 45%, var(--line));}
.tb-carte-rouge{border-color:color-mix(in srgb, var(--danger-vif) 50%, var(--line));}
.tb-lien{font:inherit; font-size:12px; font-weight:600; color:var(--accent-bright); background:none; border:none; padding:0; cursor:pointer;}
.tb-lien:hover{text-decoration:underline;}
.tb-chiffres{display:grid; grid-template-columns:repeat(auto-fit, minmax(96px,1fr)); gap:10px; margin-bottom:12px;}
.tb-chiffre{display:flex; flex-direction:column; gap:2px; padding:9px 11px; border-radius:var(--radius-sm); background:var(--surface-3);}
.tb-chiffre strong{font-size:17px; font-variant-numeric:tabular-nums;}
.tb-chiffre span{font-size:11.5px; color:var(--text-2); line-height:1.3;}
.tb-liste{display:flex; flex-direction:column;}
.tb-item{display:grid; grid-template-columns:auto 1fr auto; gap:10px; align-items:baseline; padding:7px 0; border-top:1px solid var(--line); font-size:13px;}
.tb-item:first-child{border-top:none;}
.tb-item-quoi{font-size:11px; font-weight:800; text-transform:uppercase; letter-spacing:.05em; color:var(--accent-bright);}
.tb-item-nom{color:var(--text); min-width:0; overflow-wrap:anywhere;}
.tb-item-date{font-size:11.5px; color:var(--text-3); white-space:nowrap; font-variant-numeric:tabular-nums;}
.tb-vide{font-size:13px; color:var(--text-2); padding:6px 0;}
.tb-alerte{font-size:12.5px; font-weight:600; padding:8px 11px; border-radius:var(--radius-sm); background:var(--surface-3); margin-bottom:10px;}
.tb-jauge-bloc{display:flex; flex-direction:column; gap:4px; margin-top:9px;}
.tb-jauge-tete{display:flex; justify-content:space-between; gap:10px; font-size:12px; color:var(--text-2);}
.tb-jauge-tete strong{color:var(--text); font-variant-numeric:tabular-nums;}
.tb-jauge{height:7px; border-radius:999px; background:var(--surface-3); overflow:hidden;}
.tb-jauge-rempli{height:100%; border-radius:999px; background:var(--accent-bright);}
.tb-orange-fond{background:var(--warning);} .tb-rouge-fond{background:var(--danger-vif);}
.tb-rouge{color:var(--danger-clair);}
.tb-pastille{width:12px; height:12px; border-radius:50%; border:1px solid var(--line); background:var(--surface-3); display:inline-block;}
.tb-ligne-sante{font-size:12.5px; color:var(--text-2); margin-bottom:8px;}

.tb-inters{display:flex; flex-direction:column; gap:4px;}
.tb-inter{display:flex; align-items:center; gap:11px; padding:7px 0; border-top:1px solid var(--line); cursor:pointer;}
.tb-inter:first-child{border-top:none;}
.tb-inter-texte{display:flex; flex-direction:column; gap:1px; min-width:0;}
.tb-inter-nom{font-size:13px; font-weight:600;}
.tb-inter-etat{font-size:11.5px; color:var(--text-3);}
.tb-inter-etat.tb-vert{color:var(--accent-bright);}
.tb-raccourcis{display:grid; grid-template-columns:repeat(auto-fit, minmax(130px,1fr)); gap:8px;}
.tb-raccourci{display:flex; align-items:center; gap:8px; padding:9px 11px; border:1px solid var(--line);
  border-radius:var(--radius-sm); background:var(--surface-3); color:var(--text); font-size:12.5px;
  font-weight:600; text-decoration:none; font-family:inherit; cursor:pointer; text-align:left;}
.tb-raccourci:hover{border-color:var(--accent-bright); color:var(--accent-bright);}
.tb-raccourci-ico{font-size:15px;}

/* --- WebGL indisponible (assets/webgl-secours.js) --------------------------- */
.webgl-secours{position:fixed; left:50%; transform:translateX(-50%); bottom:18px; z-index:9999;
  width:min(520px, calc(100vw - 24px)); display:flex; flex-direction:column; gap:8px;
  padding:14px 16px; border-radius:var(--radius-sm); border:1px solid var(--line);
  background:var(--surface); box-shadow:0 18px 40px -18px rgba(var(--ombre-rgb), .7);}
.webgl-secours strong{font-size:14px;}
.webgl-secours-texte{font-size:13px; color:var(--text-2); line-height:1.5;}
.webgl-secours-actions{display:flex; gap:8px; flex-wrap:wrap;}

/* --- Stock de bobines (assets/admin-stock.js) ------------------------------- */
.stock-ligne{padding:11px 0; border-top:1px solid var(--line);}
.stock-ligne:first-child{border-top:none;}
.stock-archivee{opacity:.55;}
.stock-tete{display:flex; align-items:center; gap:9px; font-size:13.5px; margin-bottom:6px;}
.stock-pastille{width:14px; height:14px; border-radius:50%; border:1px solid var(--line); background:var(--surface-3); flex:0 0 auto;}
.stock-reste{margin-left:auto; font-size:12.5px; color:var(--text-2); font-variant-numeric:tabular-nums;}
.stock-bas-texte{color:var(--danger-clair); font-weight:700;}
.stock-jauge{height:7px; border-radius:999px; background:var(--surface-3); overflow:hidden; margin-bottom:8px;}
.stock-jauge-rempli{height:100%; border-radius:999px; background:var(--accent-bright);}
.stock-jauge-rempli.stock-bas{background:var(--danger-vif);}
.stock-form{display:grid; grid-template-columns:repeat(auto-fit, minmax(120px,1fr)); gap:10px; margin-top:14px;}
.stock-champ{display:flex; flex-direction:column; gap:4px;}
.stock-champ .ds-champ{width:100%;}
.stock-course-ligne{display:flex; align-items:center; gap:9px; flex-wrap:wrap; padding:8px 0; border-top:1px solid var(--line); font-size:13px;}
.stock-course-ligne:first-of-type{border-top:none;}
.stock-course-ligne a.btn{margin-left:auto; white-space:nowrap;}

/* Centre de notifications (compte.html) : mêmes lignes, sans le cadre flottant. */
.notif-centre{display:flex; flex-direction:column; border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden;}
.notif-centre .notif-item{border-bottom:1px solid var(--line);}
.notif-centre-vide{padding:26px 16px; text-align:center; color:var(--text-2); font-size:13.5px;}
.notif-reglages{display:flex; flex-wrap:wrap; gap:10px 18px; margin-top:14px;}
.notif-reglages label{display:flex; align-items:center; gap:7px; font-size:13px; color:var(--text-2);}
.cmd-consent.cmd-consent-alerte{color:var(--accent-2); font-weight:600;}
.cmd-bandeau{padding:10px 14px; margin-bottom:12px; border-radius:var(--radius-sm); background:var(--accent-soft); color:var(--accent-bright); font-size:13px; font-weight:600;}
@media (max-width:760px){ .cmd-admin-form{grid-template-columns:1fr 1fr;} .cmd-ligne, .cmd-admin-tete{flex-direction:column;} }

@media (max-width:600px){
  .ds-modal-large{width:100%;}
  .ds-grille{grid-template-columns:repeat(auto-fill, minmax(132px, 1fr));}
}

/* Boutons « Sauvegarder » / « Mes créations » de la barre du haut. Leur
   APPARENCE est recopiée en ligne depuis #resetModelBtn au montage (voir
   assets/design-saves.js) — les outils ne l'habillent pas tous pareil, le QR
   code le fait par sélecteur d'id sans aucune classe. Ne reste ici que le
   survol, qu'un style calculé ne transporte pas. */
.ds-topbar-btn:hover:not(:disabled){
  border-color:var(--accent-bright) !important;
  color:var(--accent-bright) !important;
}
.ds-topbar-btn:disabled{opacity:.55; cursor:default;}

/* ===== Étalonnage du TD (Chromaplaque) ====================================
   Posé ici et jamais dans le <style> d'une page : même règle que le menu
   compte, les sélecteurs de la barre du haut et le module de sauvegardes.
   Préfixe `cp-et-` pour qu'aucune de ces règles ne puisse en toucher d'autres.
   ========================================================================== */
.cp-et-modal{width:520px; max-width:100%; max-height:88vh; overflow:auto;}
.cp-et-titre{margin:18px 0 6px; font-size:12.5px; font-weight:750; color:var(--text);
  border-top:1px solid var(--line); padding-top:12px;}
.cp-et-boutons{display:flex; gap:8px; margin-top:10px; flex-wrap:wrap;}
.cp-et-boutons .secondary-btn{flex:1 1 180px;}
.cp-et-fichier{display:block; width:100%; margin-top:10px; font-size:11.5px; color:var(--text-3);}
/* `touch-action:none` est OBLIGATOIRE : sans lui, un glissement au doigt fait
   défiler la page au lieu de tracer le cadre, et l'étalonnage devient
   inutilisable sur téléphone — or c'est justement l'appareil qui prend la photo. */
.cp-et-toile{display:block; width:100%; margin-top:10px; border:1px solid var(--line-strong);
  border-radius:var(--radius-sm); background:var(--surface-1); cursor:crosshair;
  touch-action:none;}
.cp-et-sortie{margin-top:8px; min-height:34px;}
.cp-et-td{margin:0; font-size:16px; font-weight:800; color:var(--accent-bright);}
.cp-et-echec{margin:6px 0 0; font-size:11.5px; line-height:1.45; color:var(--danger-pale);}
.cp-et-marches{display:flex; gap:3px; margin-top:10px;}
.cp-et-marche{flex:1; height:16px; border-radius:2px; border:1px solid var(--line);}
.cp-et-select{flex:0 0 140px; padding:8px 10px; font-size:12px;
  background:var(--surface-2); color:var(--text);
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);}
.cp-et-rappel{margin:10px 0 0; padding:8px 10px; font-size:11.5px; line-height:1.45;
  color:var(--text-2); background:var(--surface-1);
  border-left:3px solid var(--line-strong); border-radius:var(--radius-sm);}

/* ===== Sélecteur de filaments (Chromaplaque) =============================== */
.cp-fi-modal{width:560px; max-width:100%; max-height:88vh; display:flex; flex-direction:column;}
.cp-fi-barre{display:flex; gap:8px; margin-top:10px;}
.cp-fi-recherche{flex:1; padding:8px 10px; font-size:12px;
  background:var(--surface-2); color:var(--text);
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);}
.cp-fi-liste{margin-top:10px; overflow:auto; max-height:46vh; min-height:120px;
  border:1px solid var(--line); border-radius:var(--radius-sm); padding:4px;}
.cp-fi-ligne{display:flex; align-items:center; gap:10px; width:100%; text-align:left;
  padding:7px 8px; background:transparent; border:none; border-radius:var(--radius-sm);
  cursor:pointer; color:var(--text);}
.cp-fi-ligne:hover{background:var(--surface-2);}
/* Contour clair sur la pastille : sans lui, un filament noir devient invisible
   sur le fond sombre du site, et un blanc se confond avec les cartes. */
.cp-fi-pastille{flex:0 0 22px; height:22px; border-radius:50%;
  border:1px solid var(--line-strong); box-shadow:inset 0 0 0 1px rgba(var(--voile-rgb), .14);}
.cp-fi-texte{display:flex; flex-direction:column; min-width:0;}
.cp-fi-nom{font-size:12px; font-weight:650; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.cp-fi-meta{font-size:10.5px; color:var(--text-3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
/* D'ou vient le TD affiche. Une valeur PUBLIEE par le fabricant et une
   estimation a un facteur 2 pres ne doivent pas se lire pareil : sans cette
   distinction, la seconde passe pour la premiere. La sobre est l'estimation
   (le cas courant), la verte est la valeur sure (le cas rare). */
.cp-fi-src{align-self:flex-start; margin:2px 0 1px; padding:1px 6px;
  font-size:10px; font-weight:650; letter-spacing:.2px; border-radius:999px;
  color:var(--text-3); background:var(--surface-2);
  border:1px solid var(--line); white-space:nowrap;}
.cp-fi-src-sur{color:var(--accent-bright); border-color:var(--accent-bright);
  background:transparent;}
/* Deux sources independantes qui s'accordent : c'est ce qu'on a de plus sur. */
.cp-fi-src-double{border-width:2px;}
/* Deux sources qui se CONTREDISENT. On ne tranche pas en silence : la ligne le
   dit, et l'ambre se lit comme une reserve sans crier a l'erreur — il n'y a pas
   de faute, juste une incertitude qu'il faut voir. */
.cp-fi-src-doute{color:var(--warning-doux); border-color:var(--warning-doux); background:transparent;}
.cp-fi-credits{font-size:10px; opacity:.75; margin-top:6px;}
.cp-fi-credits a{color:inherit; text-decoration:underline;}

/* ===== Onglets de la barre laterale d'un outil =============================
   ⚠️ CES REGLES SONT LA COPIE EXACTE de celles que 9 outils portent DEJA dans
   leur propre bloc <style> (marque-verre, QR, lightbox, vase...). Elles sont
   posees ici pour que le 10e outil a onglets (Chromaplaque) n'en fasse pas une
   10e copie -- meme raison que .cp-fi-*, le type `bascule` de params-schema et
   la zone de depot de fichier : le style d'un mecanisme PARTAGE vit dans
   style.css, jamais dans le <style> d'une page.

   ⚠️⚠️ ET ELLES NE CHANGENT RIEN AUX 9 AUTRES PAGES : leur bloc <style> est
   place APRES ce fichier dans le document, donc a specificite egale ce sont
   leurs regles qui gagnent. Verifie a l'ecran avant de committer. Ne pas
   « nettoyer » les copies inline sans refaire cette verification page par page.

   ℹ️ .sidebar-content n'est PAS repris ici : chaque outil le definit deja, et
   le redefinir risquerait de bouger une mise en page eprouvee. */
.tabs-nav{display:flex; padding:8px 12px; gap:6px;
  background-color:var(--surface-2); border-bottom:1px solid var(--line);}
.tab-btn{flex:1; background:transparent; border:none; color:var(--text-muted);
  font-size:.75rem; font-weight:700; padding:8px 4px;
  border-radius:var(--radius-sm); cursor:pointer; transition:all .2s;
  display:flex; align-items:center; justify-content:center; gap:6px;}
.tab-btn:hover{color:var(--text-main); background:rgba(var(--voile-rgb), .05);}
.tab-btn.active{background:var(--surface-1); color:var(--accent);
  box-shadow:0 2px 8px rgba(var(--ombre-rgb), .3); border:1px solid var(--border);}
.tab-pane{display:none; flex-direction:column; gap:14px;}
.tab-pane.active{display:flex;}

/* ===== La pile en image (Chromaplaque) =====================================
   La bande se lit COMME LA PIECE : le bas touche le plateau, le haut est ce
   qu'on voit. C'est ce sens-la qui rend la regle d'ordre evidente. */
.cp-pile{display:flex; gap:10px; align-items:stretch; margin:10px 0 4px;}
.cp-pile-bande{flex:0 0 34px; display:flex; flex-direction:column;
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);
  overflow:hidden; background:var(--surface-1);}
.cp-pile-seg{width:100%; flex:0 0 auto;
  /* Un lisere clair entre les bandes : deux filaments voisins de teintes
     proches seraient indiscernables sans lui, et c'est justement le cas ou on
     a besoin de les distinguer. */
  box-shadow:inset 0 -1px 0 rgba(var(--voile-rgb), .22);}
/* Une bande VIDE (le filament ne s'imprime jamais) doit se voir : c'est
   exactement la configuration qui pose probleme. Hachures, pas une couleur. */
.cp-pile-seg-vide{background-image:repeating-linear-gradient(45deg,
  rgba(var(--hachures-rgb), .85) 0 4px, rgba(var(--ombre-rgb), .35) 4px 8px) !important;}
.cp-pile-lignes{flex:1; display:flex; flex-direction:column; justify-content:flex-start;
  gap:3px; min-width:0;}
.cp-pile-li{display:flex; align-items:center; gap:7px; font-size:11px;
  color:var(--text-2); min-width:0;}
.cp-pile-li-past{flex:0 0 12px; height:12px; border-radius:3px;
  border:1px solid var(--line-strong);}
.cp-pile-li-nom{flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.cp-pile-li-ep{flex:0 0 auto; font-variant-numeric:tabular-nums; color:var(--text-3);}
.cp-pile-li-vide{color:var(--danger-texte); font-weight:650;}

/* Un filament = UN bloc, au lieu de trois reglages eparpilles. */
.cp-fil-bloc{border:1px solid var(--line); border-radius:var(--radius-sm);
  padding:8px 10px 4px; margin-top:8px; background:var(--surface-1);}
.cp-fil-tete{display:flex; align-items:center; gap:8px; margin-bottom:6px;}
.cp-fil-past{flex:0 0 16px; height:16px; border-radius:4px;
  border:1px solid var(--line-strong); box-shadow:inset 0 0 0 1px rgba(var(--voile-rgb), .14);}
.cp-fil-nom{flex:1; font-size:11.5px; font-weight:650; color:var(--text);}
.cp-fil-ep{font-size:11px; color:var(--text-3); font-variant-numeric:tabular-nums;}
.cp-fil-ep-vide{color:var(--danger-texte); font-weight:650;}
/* Le TD est replie : c'est le seul reglage de physicien de la page, et il a
   desormais une valeur publiee ou estimee dans la plupart des cas. Replier
   n'est PAS retirer -- le champ reste dans le DOM, donc dans les sauvegardes. */
.cp-fil-avance{margin:2px 0 6px;}
.cp-fil-avance>summary{cursor:pointer; font-size:10.5px; color:var(--text-3);
  list-style:none; padding:3px 0;}
.cp-fil-avance>summary::-webkit-details-marker{display:none;}
.cp-fil-avance>summary::before{content:'▸ '; }
.cp-fil-avance[open]>summary::before{content:'▾ ';}
.cp-fil-avance>summary:hover{color:var(--text-2);}
/* Le millimetre a cote du numero de couche : une couche est un compte
   abstrait, et son sens CHANGE des qu'on touche la hauteur de couche. */
.cp-fil-mm{color:var(--text-3); font-weight:400; font-variant-numeric:tabular-nums;}
/* Valeur en euros d'un prix en Polys — assets/prix-polys.js (2026-09-10) */
.prix-euros{opacity:.72; font-weight:500; font-size:.92em; white-space:nowrap;}
/* Avis « modèle de la galerie chargé » — assets/design-saves.js (2026-09-10) */
.ds-avis{position:fixed; left:50%; bottom:24px; transform:translateX(-50%); z-index:10000;
  max-width:min(560px, 92vw); padding:12px 16px; font-size:13px; line-height:1.45;
  color:var(--text); background:var(--surface-3); border:1px solid var(--accent-bright);
  border-radius:var(--radius-sm); box-shadow:0 12px 32px -8px rgba(var(--ombre-rgb), .7);
  transition:opacity .35s ease;}
.ds-avis-fin{opacity:0;}
@media (prefers-reduced-motion: reduce){ .ds-avis{transition:none;} }
/* CGV : encadré de la garantie légale de conformité (2026-09-10) */
.legal-encadre{margin:0 0 10px; padding:12px 14px; border:1px solid var(--line-strong);
  border-radius:var(--radius-sm); background:var(--surface-2);}
.legal-encadre p:last-child{margin-bottom:0;}
/* Consentement avant paiement — voir assets/consentement-achat.js (2026-09-10) */
.consent-achat{display:flex; gap:9px; align-items:flex-start; margin:0 0 12px; padding:10px 12px;
  font-size:11.5px; line-height:1.5; color:var(--text-2); cursor:pointer;
  background:var(--surface-2); border:1px solid var(--line-strong); border-radius:var(--radius-sm);}
.consent-achat input{flex:0 0 auto; margin:2px 0 0; width:15px; height:15px; accent-color:var(--accent-bright);}
.consent-achat a{color:var(--accent-2);}
.consent-achat-apres{margin:18px 0 0;}
.consent-achat-alerte{border-color:var(--danger-doux); box-shadow:0 0 0 2px rgba(var(--danger-doux-rgb), .25);}
/* Chromaplaque — liste des changements de filament (2026-09-10) */
.cp-chg-liste{margin:6px 0 10px 18px; padding:0; font-size:12.5px; line-height:1.55; color:var(--text-2);}
.cp-chg-liste li{margin-bottom:5px; font-variant-numeric:tabular-nums;}
.cp-chg-past{display:inline-block; width:11px; height:11px; margin-right:7px; vertical-align:-1px;
  border:1px solid var(--line-strong); border-radius:3px;}
.cp-chg-vide{color:var(--warning-doux);}

/* ===== Bibliotheque de filaments (Chromaplaque) ============================ */
.cp-bi-modal{width:560px; max-width:100%; max-height:88vh; display:flex; flex-direction:column;}
.cp-bi-barre{display:flex; align-items:center; gap:8px; margin-top:10px;}
.cp-bi-liste{margin-top:10px; overflow:auto; max-height:52vh; min-height:110px;
  border:1px solid var(--line); border-radius:var(--radius-sm); padding:4px;}
.cp-bi-ligne{display:flex; align-items:center; gap:10px; padding:7px 8px;
  border-radius:var(--radius-sm);}
.cp-bi-ligne:hover{background:var(--surface-2);}
.cp-bi-ligne .cp-fi-texte{flex:1;}
/* 🔴 `width:auto` N'EST PAS DÉCORATIF ICI, ET IL EST `!important` POUR UNE
   RAISON MESURÉE (2026-09-22). Les pages d'outil déclarent dans leur propre
   `<style>` : `.secondary-btn{ width: 100% }`. À spécificité ÉGALE (0,1,0 de
   chaque côté) c'est la PAGE qui gagne, parce qu'elle est chargée après cette
   feuille — piège déjà payé sur `.gf-case input` de la galerie. Résultat vu à
   l'écran : dans la bibliothèque de filaments, le bouton « Appliquer » prenait
   TOUTE la largeur de la ligne et le NOM DU FILAMENT était écrasé à zéro
   (`.cp-fi-texte` a `min-width:0`, donc il disparaît sans rien déborder). On ne
   voyait plus qu'une pastille de couleur et un bouton : impossible de savoir
   quelle référence on applique.
   ⚠️ `flex:0 0 auto` ne suffisait pas : il fixe la base sur la largeur
   AUTOMATIQUE, et cette largeur valait 100 % du conteneur. */
.cp-bi-mini{flex:0 0 auto; width:auto !important;
  padding:5px 10px !important; font-size:11px !important;}
/* Et le nom se lit D'UN COUP D'ŒIL : c'est l'information qu'on vient chercher
   dans cette liste, la ligne de détails (TD, source, R²) vient après. */
.cp-bi-ligne .cp-fi-nom{font-size:13px;}
.cp-bi-suppr{flex:0 0 auto; width:28px; height:28px; background:transparent;
  border:1px solid var(--line); border-radius:var(--radius-sm);
  color:var(--text-3); cursor:pointer; font-size:13px;}
.cp-bi-suppr:hover{color:var(--danger-pale); border-color:var(--danger-pale);}
.cp-bi-alerte{font-size:10.5px; color:var(--warning-pale); margin-top:2px;}
/* Bibliothèque reliée au catalogue, sélecteurs de la pile (2026-09-14).
   ⚠️ Chaque bloc qui porte un display a sa règle [hidden] : display:flex écrase
   l'attribut hidden (piège payé sur le panneau admin et l'aperçu 2D). */
.cp-bi-actions{display:flex; flex-wrap:wrap; gap:8px; margin-top:10px;}
.cp-bi-form{display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:10px; padding:10px;
  border:1px solid var(--line); border-radius:var(--radius-sm);}
.cp-bi-form[hidden]{display:none !important;}
.cp-bi-form input[type="text"]{flex:1 1 200px; min-width:0;}
.cp-bi-form input[type="color"]{width:40px; height:32px; padding:0; border:none; background:transparent; cursor:pointer;}
.cp-bi-info{font-size:11px; color:var(--text-3); margin:8px 0 0; line-height:1.45;}
.cp-bi-partage{align-self:flex-start; margin-top:3px; padding:1px 8px; font-size:10.5px; background:transparent;
  color:var(--text-2); border:1px solid var(--line); border-radius:999px; cursor:pointer;}
.cp-bi-partage:hover{color:var(--text); border-color:var(--line-strong);}
.cp-couleur-masquee{display:none !important;}
.cp-fil-biblio{display:flex; flex-direction:column; gap:4px; margin:4px 0 8px;}
.cp-fil-biblio-lab{font-size:11px; color:var(--text-3); font-weight:600;}
.cp-fil-biblio-select{width:100%; padding:7px 9px; font-size:12.5px;}
.cp-fil-biblio-note{font-size:10.5px; color:var(--text-3); line-height:1.4;}
.cp-et-partage{display:flex; align-items:center; gap:6px; font-size:11.5px; color:var(--text-2); margin-top:6px;}
.cp-et-partage[hidden]{display:none !important;}
.cp-et-enregistrer{display:flex; gap:8px; margin-top:10px;}
.cp-et-enregistrer input{flex:1; padding:8px 10px; font-size:12px;
  background:var(--surface-2); color:var(--text);
  border:1px solid var(--line-strong); border-radius:var(--radius-sm);}

/* --- Auto-test connecté du panneau admin (2026-09-12) ---------------------- */
.autotest{display:flex; flex-direction:column; gap:2px; font-size:13px;}
.autotest-famille{margin:12px 0 4px; font-size:11px; letter-spacing:.12em; text-transform:uppercase;
  font-weight:800; color:var(--text-3);}
.autotest-ligne{display:grid; grid-template-columns:18px minmax(0,1fr) minmax(0,auto); gap:10px;
  align-items:baseline; padding:6px 8px; border-radius:var(--radius-sm); background:var(--surface-2);}
.autotest-etat{font-weight:800; color:var(--accent-bright); text-align:center;}
.autotest-ligne.autotest-ko .autotest-etat{color:var(--danger);}
.autotest-nom{min-width:0;}
.autotest-detail{min-width:0; color:var(--text-3); font-size:12px; text-align:right;
  overflow-wrap:anywhere;}
.autotest-bilan{padding:9px 12px; border-radius:var(--radius-sm); font-weight:800; margin-bottom:6px;}
.autotest-bilan.autotest-vert{background:rgba(var(--accent-bright-rgb), .12); color:var(--accent-bright);}
.autotest-bilan.autotest-rouge{background:rgba(var(--danger-vif-rgb), .12); color:var(--danger);}
@media (max-width:620px){
  .autotest-ligne{grid-template-columns:18px minmax(0,1fr);}
  .autotest-detail{grid-column:2; text-align:left;}
}

/* ===== FICHE D'UN COMPTE (refonte A4, 2026-09-15) ========================== */
#ficheCompteMount{display:flex; flex-direction:column; gap:16px;}
.fc-ligne{display:flex; align-items:center; gap:10px; flex-wrap:wrap;}
.fc-entre{justify-content:space-between;}
.fc-titre{margin:0; font-size:15px; font-weight:800;}
.fc-fort{font-weight:700;}
.fc-recherche{width:min(320px, 100%) !important;}
.fc-detail{display:flex; flex-direction:column; gap:14px; padding-top:6px;}
.fc-ident{display:flex; align-items:center; gap:14px; padding:14px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line);}
.fc-avatar{width:48px; height:48px; flex:0 0 48px; border-radius:50%; display:grid; place-items:center; background:var(--accent); color:var(--sur-accent); font-size:20px; font-weight:800; box-shadow:0 0 0 3px var(--accent-soft);}
.fc-ident-texte{display:flex; flex-direction:column; gap:3px; min-width:0;}
.fc-pseudo{margin:0; font-size:19px; font-weight:800; letter-spacing:-.01em;}
.fc-tuiles{display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:10px;}
.fc-gestes{display:flex; gap:8px; flex-wrap:wrap;}
.fc-gestes .btn[aria-expanded="true"]{border-color:var(--accent-bright); color:var(--accent-bright);}
.fc-panneau{display:flex; flex-direction:column; gap:8px; padding:12px; border-radius:var(--radius-sm); background:var(--surface-3); border:1px solid var(--line-strong);}
.fc-panneau[hidden]{display:none !important;}
.fc-form{display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap;}
.fc-form .cpt2-champ{min-width:180px;}
.fc-onglets{padding-top:4px;}
.fc-annuaire{max-height:420px; overflow-y:auto;}
.fc-annuaire th{position:sticky; top:0; background:var(--surface);}
/* Créations du compte, en lecture seule (25/09) : une vignette par sauvegarde. */
.fc-creations{display:grid; grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); gap:10px; padding:4px 0 8px;}
.fc-creation{display:flex; flex-direction:column; gap:4px; padding:8px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); min-width:0;}
.fc-creation img{display:block; width:100%; aspect-ratio:4/3; object-fit:contain; border-radius:6px; background:var(--surface-3);}
.fc-creation-vide{display:grid; place-items:center; aspect-ratio:4/3; border-radius:6px; background:var(--surface-3); font-size:12px; opacity:.7;}
.fc-creation-nom{font-weight:700; font-size:13px; overflow-wrap:anywhere;}
@media (max-width:760px){ .fc-tuiles{grid-template-columns:1fr 1fr;} }

/* ===== RECHERCHE Ctrl+K (refonte A3) ====================================== */
.pal-voile{position:fixed; inset:0; z-index:9000; background:rgba(var(--voile-palette-rgb), .62); display:flex; justify-content:center; align-items:flex-start; padding:12vh 16px 16px;}
.pal-voile[hidden]{display:none !important;}
.pal-boite{width:min(620px, 100%); background:var(--surface); border:1px solid var(--line-strong); border-radius:14px; box-shadow:0 30px 80px -20px rgba(var(--ombre-rgb), .85); overflow:hidden; display:flex; flex-direction:column; max-height:72vh;}
.pal-champ{display:flex; align-items:center; gap:10px; padding:13px 16px; border-bottom:1px solid var(--line);}
.pal-champ svg{width:18px; height:18px; color:var(--text-3); flex:0 0 18px;}
.pal-champ input{flex:1 1 auto; width:auto !important; background:none !important; border:none !important; box-shadow:none !important; padding:4px 0 !important; font-size:16px !important; color:var(--text);}
.pal-champ kbd, .pal-pied kbd{font:600 10.5px 'JetBrains Mono', ui-monospace, monospace; padding:1px 6px; border-radius:5px; border:1px solid var(--line-strong); color:var(--text-2);}
.pal-resultats{overflow-y:auto; padding:6px 8px 8px;}
.pal-groupe{font-size:10px; font-weight:750; letter-spacing:.11em; text-transform:uppercase; color:var(--text-3); padding:10px 10px 4px;}
.pal-item{display:flex; align-items:center; gap:10px; width:100%; padding:8px 10px; border-radius:8px; border:none; background:none; color:var(--text); font:inherit; font-size:13.5px; text-align:left; cursor:pointer;}
.pal-item.actif, .pal-item:hover{background:var(--accent-soft);}
.pal-ico{width:26px; height:26px; flex:0 0 26px; border-radius:7px; display:grid; place-items:center; background:var(--surface-3); font-size:13px;}
.pal-texte{flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.pal-texte small{color:var(--text-3); margin-left:6px;}
.pal-indice{font-size:11.5px; color:var(--text-3); white-space:nowrap;}
.pal-vide{padding:18px 12px; color:var(--text-3); font-size:13px;}
.pal-pied{display:flex; gap:14px; padding:8px 14px; border-top:1px solid var(--line); font-size:11.5px; color:var(--text-3);}

/* ===== COMMANDES EN COLONNES (refonte A5) ================================== */
.cmd-pipeline{display:grid; grid-template-columns:repeat(5, minmax(0,1fr)); gap:10px; margin:4px 0 14px;}
.cmd-pipeline[hidden]{display:none !important;}
.cmd-col{display:flex; flex-direction:column; gap:8px; padding:10px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); min-width:0;}
.cmd-col.attente{opacity:.78;}
.cmd-col-tete{display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:12px; font-weight:750;}
.cmd-col-nb{font-size:11px; font-weight:800; padding:1px 8px; border-radius:999px; background:var(--surface-3); color:var(--text-2);}
.cmd-col-nb.chaud{background:var(--accent-2-soft); color:var(--accent-2);}
.cmd-mini{display:flex; flex-direction:column; gap:6px; padding:9px; border-radius:8px; background:var(--surface); border:1px solid var(--line); font:inherit; color:var(--text); text-align:left; cursor:pointer;}
.cmd-mini:hover{border-color:var(--accent-bright);}
.cmd-mini-tete{display:flex; gap:8px; align-items:center; min-width:0;}
.cmd-mini-tete img{width:32px; height:32px; flex:0 0 32px; object-fit:cover; border-radius:6px; background:var(--surface-3);}
.cmd-mini-titre{font-size:12.5px; font-weight:750;}
.cmd-mini-sous{font-size:11.5px; color:var(--text-3); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.cmd-mini-action{font-size:11.5px; font-weight:750; color:var(--accent-bright);}
.cmd-mini-action.retard{color:var(--danger);}
.cmd-col-vide{font-size:12px; color:var(--text-3); padding:4px 2px;}
.cmd-admin.cmd-flash{box-shadow:0 0 0 2px var(--accent-bright); transition:box-shadow .6s ease;}
@media (max-width:1100px){ .cmd-pipeline{grid-template-columns:repeat(3, minmax(0,1fr));} }
@media (max-width:700px){ .cmd-pipeline{grid-template-columns:1fr;} }

/* ===== SESSION D'IMPRESSION (2026-09-15) =================================== */
.cmd-session{display:flex; flex-direction:column; gap:10px; padding:12px 14px; margin:4px 0 14px;
  border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--accent-bright);}
.cmd-session[hidden]{display:none !important;}
.cmd-session-tete{display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;}
.cmd-session-tete h4{margin:0; font-size:14px; font-weight:800;}
.cmd-session-lignes{display:flex; flex-direction:column; gap:2px; max-height:230px; overflow-y:auto;}
.cmd-session-ligne{display:flex; align-items:center; gap:9px; padding:4px 6px; border-radius:6px; font-size:12.5px; cursor:pointer;}
.cmd-session-ligne:hover{background:var(--surface-3);}
.cmd-session-txt{flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--text-2);}
.cmd-session-txt strong{color:var(--text);}
.cmd-session-manque{color:var(--danger); font-weight:700;}
.cmd-session-juste{color:var(--warning); font-weight:700; font-size:12.5px;}
.cmd-session-voir{flex:0 0 auto; background:none; border:none; padding:2px 6px; font:inherit; font-size:11.5px;
  color:var(--accent-bright); cursor:pointer; text-decoration:underline;}
.cmd-session-totaux{font-size:13px; font-weight:750; font-variant-numeric:tabular-nums;}
.cmd-session-verdict{display:flex; flex-direction:column; gap:3px; font-size:12.5px;}
.cmd-session-barre{flex-wrap:wrap; align-items:center; gap:8px;}

/* ===== MON COMPTE, refonte M1-M5 (2026-09-15) ============================== */
.ce-espace{display:grid; grid-template-columns:minmax(0,340px) minmax(0,1fr); gap:16px; align-items:start; padding:16px;}
.ce-colonne{display:flex; flex-direction:column; gap:16px; min-width:0;}
.ce-bloc-haut{padding:16px 16px 0;}
.ce-bloc-haut:empty{display:none;}
#pane-creations{padding:16px;}
.ce-grand{font-size:28px; font-weight:800; letter-spacing:-.01em; font-variant-numeric:tabular-nums;}
.ce-liste{display:flex; flex-direction:column;}
.ce-action{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:10px 0; border-top:1px solid var(--line);}
.ce-action:first-child{border-top:none;}
.ce-reprendre{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:10px;}
.ce-reprise{display:flex; align-items:center; gap:10px; padding:10px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line); color:var(--text); text-decoration:none; min-width:0;}
.ce-reprise:hover{border-color:var(--accent-bright);}
.ce-reprise strong{display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px;}
/* Sans min-width:0, le bloc de texte d'un élément flex ne se rétrécit pas : le nom débordait de la carte. */
.ce-reprise > div{min-width:0; flex:1 1 auto;}
.ce-vign{display:grid; place-items:center; aspect-ratio:4/3; width:100%; max-width:100%; overflow:hidden; background:radial-gradient(circle at 50% 40%, var(--vignette-haut), var(--vignette-bas)); font-size:30px;}
.ce-vign img{width:100%; height:100%; object-fit:cover; display:block;}
.ce-vign-petite{width:44px; height:44px; flex:0 0 44px; aspect-ratio:auto; border-radius:8px; font-size:20px;}
/* Frise de commande (M1, M4) */
.ce-frise{display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); margin:8px 0 4px;}
.ce-frise div{position:relative; display:flex; flex-direction:column; align-items:center; gap:5px; font-size:11px; color:var(--text-3); text-align:center;}
.ce-frise div::before{content:''; position:absolute; top:7px; left:-50%; right:50%; height:2px; background:var(--surface-3);}
.ce-frise div:first-child::before{display:none;}
.ce-frise div.fait::before, .ce-frise div.ici::before{background:var(--accent-bright);}
.ce-frise i{position:relative; z-index:1; width:12px; height:12px; border-radius:50%; background:var(--surface-3); border:2px solid var(--surface);}
.ce-frise .fait i{background:var(--accent-bright);}
.ce-frise .ici i{background:var(--surface); border-color:var(--accent-bright); box-shadow:0 0 0 3px var(--accent-soft);}
.ce-frise .fait, .ce-frise .ici{color:var(--text);}
.ce-main{font-size:12.5px; color:var(--text-2); margin-top:2px;}
.ce-main-vous{color:var(--accent-2); font-weight:700;}
/* Portefeuille (M2) */
.ce-deux-inegal{grid-template-columns:minmax(0,.9fr) minmax(0,1.4fr);}
.ce-repartition{display:flex; height:10px; border-radius:999px; overflow:hidden; background:var(--surface-3);}
.ce-repartition i{display:block; height:100%;}
.ce-achetes, .ce-point-achetes{background:var(--accent-bright);}
.ce-offerts, .ce-point-offerts{background:var(--accent-2);}
.ce-point-depenses{background:var(--text-3);}
.ce-point{display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:7px; vertical-align:1px;}
/* Mes créations (M3) */
.ce-creations{display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr)); gap:12px;}
.ce-crea{position:relative; display:flex; flex-direction:column; border-radius:var(--radius-sm); overflow:visible; background:var(--surface-2); border:1px solid var(--line);}
.ce-crea > .ce-vign{border-radius:var(--radius-sm) var(--radius-sm) 0 0; aspect-ratio:16/10; max-height:170px;}
.ce-crea-bas{display:flex; flex-direction:column; gap:6px; padding:10px 11px 11px; min-width:0;}
.ce-crea-bas strong{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.ce-plus{padding:6px 10px !important;}
.ce-menu{position:absolute; right:10px; bottom:48px; z-index:5; display:flex; flex-direction:column; min-width:170px; padding:6px; border-radius:9px; background:var(--surface-3); border:1px solid var(--line-strong); box-shadow:0 16px 36px -14px rgba(var(--ombre-rgb), .85);}
.ce-menu[hidden]{display:none !important;}
.ce-menu-item{font:inherit; font-size:12.5px; text-align:left; padding:7px 10px; border-radius:6px; border:none; background:none; color:var(--text); cursor:pointer;}
.ce-menu-item:hover, .ce-menu-item:focus-visible{background:var(--accent-soft); outline:none;}
.ce-danger{color:var(--danger);}
/* Niveaux (M5) */
.ce-marches{display:grid; grid-template-columns:repeat(11, minmax(0,1fr)); gap:6px; align-items:end;}
.ce-marche{display:flex; flex-direction:column; align-items:center; gap:4px; font-size:11px; color:var(--text-3);}
.ce-marche b{display:block; width:100%; border-radius:6px 6px 2px 2px; background:var(--surface-3);}
.ce-marche.fait b{background:var(--accent-bright);}
.ce-marche.ici b{background:linear-gradient(to top, var(--accent-bright) 60%, var(--surface-3) 60%); box-shadow:0 0 0 2px var(--accent-soft);}
.ce-marche.ici{color:var(--text); font-weight:700;}
.ce-marche-remise{font-size:10px;}
/* Avis */
.ce-avis{position:fixed; left:50%; bottom:18px; transform:translateX(-50%); z-index:10000; max-width:calc(100vw - 32px); padding:10px 14px; border-radius:10px; background:var(--avis-fond); border:1px solid rgba(var(--accent-bright-rgb), .35); color:var(--text); font-size:13px; box-shadow:0 18px 40px -16px rgba(var(--ombre-rgb), .85);}
.ce-avis-erreur{background:var(--danger-fond); border-color:rgba(var(--danger-rgb), .45);}
@media (max-width:900px){ .ce-espace{grid-template-columns:1fr;} .ce-reprendre{grid-template-columns:1fr;} .ce-deux-inegal{grid-template-columns:1fr;} }
@media (max-width:560px){ .ce-marches{gap:3px;} .ce-marche-remise{display:none;} .ce-frise{font-size:10px;} }

/* Bandeau du re-téléchargement verrouillé (migration 106). Posé au-dessus de
   l'aperçu par design-saves.js : il doit se voir sans discussion, parce qu'il
   explique pourquoi tous les réglages sont grisés. */
.ds-reexport{display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px;
  margin:0 0 14px; padding:12px 15px; border-radius:10px;
  border:1px solid color-mix(in srgb, var(--accent-bright) 45%, var(--line));
  background:color-mix(in srgb, var(--accent-bright) 10%, var(--surface));
  color:var(--text); font-size:13.5px;}
.ds-reexport strong{flex:0 0 auto; color:var(--accent-bright); font-size:14px;}
.ds-reexport span{flex:1 1 260px; color:var(--text-2); line-height:1.5;}
.ds-reexport .btn{flex:0 0 auto;}
@media (max-width:560px){ .ds-reexport{gap:8px;} .ds-reexport span{flex:1 1 100%;} }

/* ===== MODÉRER PAR LOT (2026-09-16) ======================================== */
.lot-barre{display:flex; align-items:center; gap:12px; flex-wrap:wrap; margin:8px 0 10px;
  padding:8px 12px; border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--accent-bright);}
.lot-barre[hidden]{display:none !important;}
.lot-tout{display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:700; cursor:pointer;}
.lot-compte{font-size:12.5px; color:var(--text-2); font-variant-numeric:tabular-nums;}
.fc-note{display:flex; flex-direction:column; gap:6px; padding:12px 14px; margin:12px 0;
  border-radius:var(--radius-sm); background:var(--surface-2); border:1px dashed var(--line);}
.fc-note label{font-size:12px; font-weight:750; color:var(--text-2);}

/* ===== COÛT DE LA MATIÈRE SOUS L'APERÇU (2026-09-16) ======================= */
/* ⚠️ pointer-events:auto OBLIGATOIRE : #pvSpecs est en pointer-events:none pour
   laisser tourner la vue 3D derrière lui. Sans ce rappel, le sélecteur de
   matériau serait visible et INCLIQUABLE — le défaut exact de « Estimer poids
   et temps », trouvé en production et pas au banc. */
.cout-matiere{display:flex; flex-direction:column; gap:5px; margin:8px 0 0; padding:8px 9px;
  border-radius:8px; background:var(--surface-2); border:1px solid var(--line); pointer-events:auto;}
.cout-matiere[hidden]{display:none !important;}
.cm-tete{display:flex; align-items:baseline; justify-content:space-between; gap:10px; font-size:12px; color:var(--text-3); font-weight:700;}
.cm-tete strong{font-size:15px; color:var(--accent-bright); font-variant-numeric:tabular-nums;}
.cm-select{width:100%; font:inherit; font-size:11.5px; padding:3px 6px; border-radius:6px;
  background:var(--surface); color:var(--text); border:1px solid var(--line);}
.cm-prix{display:flex; align-items:center; gap:6px; font-size:11px; color:var(--text-3);}
.cm-prix > span{white-space:nowrap;}
/* ⚠️ `.cm-prix input.cm-prix-champ` et pas `.cm-prix-champ` seul (2026-09-19) :
   six outils déclarent `input[type="text"]{width:100%}`, plus spécifique qu'une
   classe seule — la case du prix s'étirait alors sur toute la ligne et
   poussait « €/kg » HORS de l'encart (capture de l'utilisateur, lightbox). */
.cm-prix input.cm-prix-champ{width:64px; min-width:0; flex:0 0 64px; font:inherit; font-size:11.5px; padding:3px 6px;
  border-radius:6px; background:var(--surface); color:var(--text); border:1px solid var(--line);
  text-align:right; font-variant-numeric:tabular-nums;}
.cm-prix input.cm-prix-champ:focus{outline:none; border-color:var(--accent-bright);}
.cm-prix input.cm-prix-champ.cm-prix-faux{border-color:var(--danger);}
.cm-prix-unite{font-weight:700;}
.cm-detail{font-size:11px; color:var(--text-3); font-variant-numeric:tabular-nums;}
.cm-note{font-size:10.5px; color:var(--text-3); line-height:1.4;}

/* ===== QUOTA PLEIN : PROPOSER PLUTÔT QUE REFUSER (2026-09-16) ============== */
.ds-remplacer{display:flex; flex-direction:column; gap:7px; margin-top:12px; padding:11px 13px;
  border-radius:var(--radius-sm); background:var(--warning-soft); border:1px solid var(--warning); font-size:12.5px;}
.ds-remplacer[hidden]{display:none !important;}
.ds-remplacer-quoi{padding:7px 9px; border-radius:6px; background:var(--surface); font-size:12.5px;}
.ds-remplacer button{align-self:flex-start;}

/* ===== PAGE « NOUVEAUTÉS » (2026-09-16) ==================================== */
.nouv-liste{display:flex; flex-direction:column; gap:0; margin-top:14px;}
.nouv{display:grid; grid-template-columns:160px minmax(0,1fr); gap:18px; padding:18px 0; border-top:1px solid var(--line);}
.nouv:first-child{border-top:none;}
.nouv-date{font-size:12.5px; font-weight:750; color:var(--text-3); padding-top:3px;}
.nouv-corps h2{margin:0 0 6px; font-size:17px; font-weight:800;}
.nouv-corps p{margin:0; font-size:14px; line-height:1.6; color:var(--text-2);}
@media (max-width:700px){ .nouv{grid-template-columns:1fr; gap:4px;} }

/* ===== TROIS EXEMPLES POUR DÉMARRER (2026-09-16) =========================== */
.ex-depart{display:flex; flex-direction:column; gap:8px; margin:10px 0 0; padding:10px 12px;
  border-radius:var(--radius-sm); background:var(--surface-2); border:1px solid var(--line);}
.ex-tete{display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:12.5px; color:var(--text-2);}
.ex-fermer{background:none; border:none; color:var(--text-3); font:inherit; cursor:pointer; padding:0 4px; line-height:1;}
.ex-fermer:hover{color:var(--text);}
.ex-grille{display:grid; grid-template-columns:repeat(auto-fit, minmax(96px, 1fr)); gap:10px;}
.ex-vignette{display:flex; flex-direction:column; gap:4px; text-decoration:none; color:var(--text-3); font-size:11px; text-align:center;}
.ex-vignette img{width:100%; aspect-ratio:1; object-fit:cover; border-radius:8px; background:var(--surface-3); border:1px solid var(--line);}
.ex-vignette:hover img{border-color:var(--accent-bright);}

/* ===== LIEN D'ÉVITEMENT (2026-09-16) ======================================= */
/* Hors écran tant qu'il n'a pas le focus — jamais `display:none`, qui le
   retirerait aussi de l'ordre de tabulation et le rendrait donc inutile. */
.a11y-evitement{position:absolute; left:-9999px; top:0; z-index:9999;
  padding:10px 16px; border-radius:0 0 8px 0; font-weight:700; font-size:14px;
  background:var(--accent-bright); color:var(--sur-vert-lien); text-decoration:none;}
.a11y-evitement:focus{left:0;}

/* ---- « Prévenez-moi à sa sortie » (assets/prevenir-sortie.js, 18/09) ------
   Posé sous le bouton d'une carte d'application en test ou en développement.
   Il vit DANS le lien de la carte : le module empêche la navigation. */
.app-tile-prevenir{
  display:block; width:100%; margin-top:6px; padding:6px 10px;
  font:inherit; font-size:11.5px; font-weight:600; line-height:1.3;
  color:var(--text-2); background:transparent;
  border:1px dashed var(--line-strong); border-radius:var(--radius-sm);
  cursor:pointer; transition:color .15s, border-color .15s, background .15s;
}
.app-tile-prevenir:hover,
.app-tile-prevenir:focus-visible{ color:var(--text); border-color:var(--accent-bright); outline:none; box-shadow:var(--ring); }
.app-tile-prevenir.suivi{ color:var(--ok); border-style:solid; border-color:var(--ok); background:var(--ok-soft); }

/* =========================================================================
   📱 LE STUDIO MOBILE — LES OUTILS AU TÉLÉPHONE (2026-09-20)
   =========================================================================
   MESURÉ AVANT D'ÉCRIRE UNE LIGNE (375 × 812, vrai server.js, médaille) :
   barre du haut COLLANTE de 148 à 195 px, gouttière de 56 px sur toute la
   hauteur, aperçu qui ne commençait qu'à y = 763 — presque un écran à
   descendre pour voir sa pièce, puis à remonter pour bouger un curseur — et
   61 à 83 % de cet aperçu recouverts par ses propres incrustations. Régler et
   voir étaient deux gestes qu'on ne pouvait pas faire en même temps.

   LE DESSIN : l'aperçu tient le haut de l'écran et n'en bouge jamais ; les
   réglages montent du bas dans une feuille à trois crans (repliée / moitié /
   plein écran). Au cran du milieu, celui par défaut, on voit SA PIÈCE et le
   curseur qu'on déplace. Les actions qui font sortir de l'outil descendent
   dans une barre à portée de pouce.

   🔑 RIEN NE DISPARAÎT AU TÉLÉPHONE : les 4 gestes d'édition passent dans
   l'en-tête de la feuille, la carte des cotes derrière une pastille (une
   touche), le pied de page et la présentation de l'outil dans « ⋯ Plus ».

   ⚠️ LA MISE EN PAGE EST ICI, EN CSS PUR, pas dans un script : elle s'applique
   au premier rendu (aucun clignotement) et un outil FUTUR en hérite sans une
   ligne à écrire, du moment qu'il reprend le gabarit (`.tool-row`,
   `aside.sidebar`, `.viewport-container`). `assets/mobile.js` n'ajoute que ce
   que le CSS ne sait pas faire : déplacer des boutons (les MÊMES nœuds), la
   poignée qu'on fait glisser, l'onglet « ⋯ Plus ». Sans lui la page reste
   utilisable : la barre du haut devient une bande qui défile au doigt.
   Le banc `scripts/tester-mobile.js` mesure tout ça outil par outil, et le
   contrôle n°20 de `verifier-tout.js` refuse une page d'outil sans ossature.

   ⚠️ RIEN AU-DESSUS DE 860 px : tout ce bloc vit dans une media query et les
   classes `mob-*` ne sont posées que sous ce seuil. Le rendu PC ne peut pas
   changer par construction.
   ========================================================================= */
@media (max-width: 860px){

  :root{
    /* La barre du haut, la feuille de réglages et la barre d'actions. Les
       deux dernières sont réécrites par assets/mobile.js (hauteur réelle de
       la barre, hauteur choisie de la feuille) : les valeurs ci-dessous sont
       celles qui s'appliquent SANS script. */
    --mob-haut: 54px;
    --mob-feuille: 46dvh;
    --mob-barre: 0px;
    --mob-bas: env(safe-area-inset-bottom, 0px);
    --mob-rayon: 16px;
    /* Hauteur réellement mangée par le clavier du téléphone, mesurée par
       `visualViewport` (assets/mobile.js). ⚠️ Sans elle, la feuille reste
       calée sur le bas de la fenêtre de MISE EN PAGE — que le clavier ne
       change pas — et le champ qu'on est en train de remplir se retrouve
       DERRIÈRE le clavier. */
    --mob-clavier: 0px;
  }

  /* La page ne défile plus : l'outil occupe l'écran, et c'est la feuille de
     réglages qui a son propre défilement. ⚠️ Posé par le script seulement
     (`mob-js`) : sans lui, le pied de page reste au bas d'une page qui
     défile, donc atteignable. */
  html.mob-js.mob-outil, html.mob-js.mob-outil body{
    overflow:hidden !important; height:100%;
    overscroll-behavior:none;
  }

  /* ---- 1. La gouttière de 56 px rendue à la page -----------------------
     Le menu latéral occupait une bande pleine hauteur à gauche (#sideNav est
     en `inset:0 auto 0 0`). Au téléphone il se réduit à sa poignée, posée
     DANS la barre du haut ; il reprend toute la hauteur dès qu'il s'ouvre,
     sinon son panneau n'aurait nulle part où se déplier. */
  #sideNav{ inset:0 auto auto 0; }
  #sideNav.nav-open{ inset:0 auto 0 0; }
  #sideNavHandle{
    width:46px; height:var(--mob-haut); border-right:0;
    background:transparent;
  }
  /* ⚠️ Le panneau garde sa bordure droite même à LARGEUR ZÉRO : un filet clair
     d'1 px traversait tout l'écran de haut en bas, en plein milieu de l'aperçu
     (vu sur capture, pas déduit). Il ne revient qu'une fois le menu ouvert. */
  #sideNav:not(.nav-open) #sideNavPanel{ border-right:0; }
  /* 🔴🔴 LE MENU REPLIÉ AVALAIT LES TOUCHERS DU COIN HAUT GAUCHE (21/09,
     « 3DMaking View » ne faisait rien au doigt, sur plusieurs outils).
     MESURÉ : le bouton est à sa place (8, 62, 50 × 36) et vivant, mais
     `elementFromPoint` en son centre rend `NAV#sideNav` — replié, `#sideNav`
     garde un cadre INVISIBLE aussi haut que sa liste de liens (panneau à
     largeur nulle, pas à hauteur nulle) et porte `z-index:100` contre 5 pour
     le rail des vues.
     ⚠️ Défaut CRÉÉ par le studio mobile : sur PC le rail est en bas au
     centre, loin du menu ; c'est nous qui l'avons remonté dans ce coin.
     ➡️ Replié, seule sa POIGNÉE reçoit le doigt ; ouvert, tout redevient
     cliquable. Contrôlé au vrai toucher par `scripts/tester-mobile.js`
     (contre-épreuve : règle retirée → 14 rouges). */
  #sideNav:not(.nav-open){ pointer-events:none; }
  #sideNav:not(.nav-open) #sideNavHandle{ pointer-events:auto; }
  #sideNav.nav-open #sideNavHandle{ height:auto; background:var(--surface); border-right:1px solid var(--line); }
  #sideNavPanel{ padding-top:10px; }

  /* ---- 2. La barre du haut : UNE seule ligne ---------------------------
     ⚠️ `overflow-x:auto` est le filet de sécurité : si assets/mobile.js ne
     tourne pas, les boutons ne sont pas perdus, la bande défile au doigt.
     Avec le script, il n'en reste que l'identité (Polys, cloche, compte) et
     la bande ne défile plus parce qu'elle n'en a plus besoin. */
  #topbar.app-topbar-tool{
    position:sticky; top:0; z-index:40;
    margin:0 !important; width:100% !important;
    height:var(--mob-haut); min-height:var(--mob-haut);
    padding:0 8px 0 50px !important;
    display:flex; flex-wrap:nowrap !important; align-items:center; gap:6px;
    overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    border-radius:0; border-left:0; border-right:0; border-top:0;
    box-shadow:none;
  }
  #topbar.app-topbar-tool::-webkit-scrollbar{ display:none; }
  #topbar.app-topbar-tool > *{ flex:0 0 auto; }
  #topbar.app-topbar-tool .app-brand img{ height:26px !important; }
  /* Le titre prend ce qui reste et se coupe proprement, comme la barre de
     titre de n'importe quelle application de téléphone. ⚠️ `min-width:0` n'est
     pas décoratif : sans lui un enfant de flex refuse de descendre sous la
     largeur de son texte, et c'est LUI qui pousse le menu compte hors de
     l'écran (mesuré : 486 px de contenu pour 375 de barre). */
  #topbar.app-topbar-tool h1{
    flex:1 1 0 !important; min-width:0;
    font-size:12.5px !important; font-weight:750;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  #topbar.app-topbar-tool .balance-pill{
    margin-left:0 !important; max-width:78px; font-size:10.5px !important;
    padding:5px 9px !important;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* « Se connecter » écrit en toutes lettres pèse 115 px sur 375. Il reste
     lisible, il est simplement tenu en laisse — et une fois connecté ce
     bouton n'est plus qu'un avatar de 36 px. */
  #topbar.app-topbar-tool .account-menu-trigger{
    max-width:90px; min-height:36px; padding:9px 9px !important; font-size:11px !important;
    display:inline-flex; align-items:center;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  #topbar.app-topbar-tool .tagline{ display:none !important; }
  /* Sous 420 px, le logo de la barre fait doublon avec celui du menu latéral,
     qui est à 46 px de là et mène au même endroit : ces 32 px reviennent au
     titre de l'outil. */
  @media (max-width: 419px){
    #topbar.app-topbar-tool .app-brand{ display:none !important; }
  }

  /* ---- 3. L'ossature : l'aperçu en haut, la feuille en bas -------------
     `.tool-row` ne met plus rien en page : ses deux panneaux sont posés en
     `fixed`, donc elle se réduit à rien et le pied de page passe DERRIÈRE.
     C'est voulu : au téléphone l'outil occupe l'écran, et le pied de page
     est remis à portée par l'onglet « ⋯ Plus » (assets/mobile.js). */
  #app{ padding:0 !important; gap:0 !important; min-height:0 !important; }
  .tool-row{
    display:block !important; height:0 !important; min-height:0 !important;
    gap:0 !important; overflow:visible !important;
  }

  .tool-row .viewport-container,
  .tool-row #chromaVue2D{
    position:fixed !important; left:0; right:0;
    top:var(--mob-haut);
    bottom:calc(var(--mob-feuille) + var(--mob-barre) + var(--mob-bas) + var(--mob-clavier));
    width:auto !important; height:auto !important;
    min-height:0 !important; max-height:none !important; margin:0 !important;
    border-radius:0 !important; border-left:0 !important; border-right:0 !important; border-top:0 !important;
    z-index:5;
  }
  /* Chromaplaque est le seul outil à DEUX aperçus (l'image à plat et la
     pièce en 3D). Ils se superposent, et une bascule posée par le script
     choisit lequel on regarde. Sans script c'est le 2D qui gagne : c'est là
     que se fait le vrai travail sur cet outil-là. */
  .tool-row #chromaVue2D{ z-index:6; }
  .tool-row .mob-vue-off{ display:none !important; }

  .tool-row .sidebar{
    position:fixed !important; left:0; right:0; top:auto !important;
    bottom:calc(var(--mob-barre) + var(--mob-bas) + var(--mob-clavier));
    width:auto !important; min-width:0 !important; max-width:none !important;
    height:var(--mob-feuille) !important; max-height:none !important;
    margin:0 !important;
    border-radius:var(--mob-rayon) var(--mob-rayon) 0 0;
    border-left:0 !important; border-right:0 !important; border-bottom:0 !important;
    box-shadow:0 -12px 34px rgba(var(--ombre-rgb), .6);
    z-index:20;
    transition:height .24s cubic-bezier(.22,.61,.36,1);
  }
  /* Pendant qu'on fait glisser la poignée, la hauteur suit le doigt : une
     transition ferait traîner la feuille derrière le doigt. */
  html.mob-glisse .tool-row .sidebar{ transition:none !important; }

  /* 🔴 `flex:0 0 auto` N'EST PAS DÉCORATIF, et ça s'est vu au doigt seulement.
     La feuille est une colonne flex : repliée à 113 px, la barre d'onglets se
     laissait ÉCRASER à 16 px alors que ses boutons en font 40 — ils
     débordaient par-dessus le contenu, et c'est le contenu qui recevait le
     clic (`elementFromPoint` rendait `.sidebar-content`). Les onglets étaient
     visibles et morts, exactement au cran où l'on s'en sert le plus. */
  .tool-row .sidebar > .tabs-nav,
  .tool-row .sidebar > .mob-poignee,
  .tool-row .sidebar > .sidebar-header{ flex:0 0 auto !important; }
  .tool-row .sidebar-content{
    flex:1 1 auto !important; min-height:0 !important;
    overscroll-behavior:contain;
    -webkit-overflow-scrolling:touch;
    padding:12px 14px 26px !important;
  }
  /* ⚠️ `overscroll-behavior:contain` n'est pas cosmétique : sans lui, arriver
     au bout de la liste des réglages fait défiler la PAGE derrière, qui est
     figée — le doigt glisse et rien ne bouge, on croit l'outil bloqué. */

  /* 🔴 UN SEUL DÉFILEMENT DANS LA FEUILLE — signalé le 21/09 sur le créateur
     de maillot, onglet « Modèle & Textes » : la carte « Textes » (les seuls
     champs qu'on vient remplir) paraissait ÉCRASÉE, ses champs hors
     d'atteinte. LA CAUSE VIENT DU GABARIT DE BUREAU : il prévoit des cartes
     qui se PARTAGENT une colonne de 560 px — `.card.grow` prend la hauteur
     restante et c'est SON corps qui défile (`.card > .card-body{min-height:0;
     overflow:auto}`). Dans une feuille de 334 px, la carte « Modèle » et le
     cadre mangent tout ; la carte en `grow` se replie sur un défilement
     imbriqué que le pouce n'atteint pas, et le défilement extérieur n'a rien
     à faire défiler puisque la colonne « tient » par écrasement.
     Contre-épreuve : règle retirée → 615 px de cette carte redeviennent
     inatteignables. Écrit sur `.card` / `.card-body` / `.tab-pane` et non sur
     le maillot : une carte future en `grow` en hérite. */
  .tool-row .sidebar .tab-pane,
  .tool-row .sidebar .card,
  .tool-row .sidebar .card > .card-body{
    flex:0 0 auto !important;
    min-height:auto !important;
    max-height:none !important;
  }
  .tool-row .sidebar .card > .card-body{ overflow:visible !important; }

  /* ---- 4. Les onglets : une bande qui défile, des cibles de 44 px ------ */
  .tool-row .tabs-nav{
    flex-wrap:nowrap !important; overflow-x:auto; overflow-y:hidden;
    padding:7px 10px !important; gap:6px !important;
    scrollbar-width:none; border-radius:var(--mob-rayon) var(--mob-rayon) 0 0;
  }
  .tool-row .tabs-nav::-webkit-scrollbar{ display:none; }
  .tool-row .tabs-nav .tab-btn{
    flex:0 0 auto !important; min-height:40px; padding:8px 13px !important;
    font-size:12px !important; white-space:nowrap;
  }
  .tool-row .tabs-nav .tab-btn svg{ flex:0 0 auto; }

  /* ---- 5. Les incrustations de l'aperçu, remises à leur place ----------
     Quatre îlots se marchaient dessus sur 307 px de large. Au téléphone :
     le choix de vue en HAUT À GAUCHE, l'état d'imprimabilité en HAUT À
     DROITE, le mode d'aperçu en BAS AU CENTRE, et la carte des dimensions
     derrière une pastille en BAS À GAUCHE. */
  .mob-vue .vs-label,
  .viewport-container .vs-label{ display:none !important; }
  .mob-vue .vs-rail,
  .viewport-container .vs-rail{
    top:8px; bottom:auto !important; left:8px; right:auto !important;
    justify-content:flex-start;
  }
  /* Le panneau des vues s'ouvrait VERS LE HAUT (il était en bas de l'écran) :
     remonté en haut, il sortirait de l'écran. Il s'ouvre donc vers le bas. */
  .mob-vue .vs-panel,
  .viewport-container .vs-panel{
    bottom:auto !important; top:calc(100% + 8px) !important;
    left:0 !important; transform:none !important;
    max-height:60vh; overflow-y:auto;
  }
  .viewport-container .vs-trigger{ min-height:36px; }
  /* ⚠️ `flex-wrap:wrap` la faisait tomber sur DEUX lignes au téléphone : un
     pavé de 188 x 82 px posé en plein milieu de la pièce. Sur une ligne, et
     rangée dans le coin BAS DROIT — pas au centre, où elle chevauchait la
     pastille des dimensions de 43 px (mesuré) — elle ne cache plus rien. */
  /* ⚠️ `#previewModeBar` est le même objet sous un autre nom : c'est celui du
     créateur de maillot, qui porte en plus un bouton par plateau. Il montait à
     188 x 126 px — TROIS lignes en plein milieu du maillot. Le chercher par
     `#pvModeBar` seul, c'était ne corriger que quatorze outils sur quinze. */
  /* ⚠️ DES BORNES, PAS UN CENTRAGE. Centrée, elle se plaçait au milieu de
     l'aperçu sans savoir que ses voisins sont ASYMÉTRIQUES : la pastille de
     vue (82 px) à gauche, le bouton plein écran (34 px) à droite. Sur le
     créateur de maillot, elle recouvrait la pastille de vue (mesuré). Avec un
     `left` et un `right` explicites, elle occupe exactement la place libre et
     défile à l'intérieur quand il y a plus de boutons que de largeur. */
  .viewport-container .pv-mode-bar,
  .viewport-container #pvModeBar,
  .viewport-container #previewModeBar{
    top:8px !important; bottom:auto !important;
    left:98px !important; right:50px !important; transform:none !important;
    max-width:none !important; width:auto;
    flex-wrap:nowrap !important; justify-content:flex-start;
    overflow-x:auto; scrollbar-width:none;
  }
  /* Chromaplaque a DEUX aperçus, donc une bascule de plus, elle aussi en haut
     au centre. Le mode d'aperçu descend d'un cran pour lui laisser la place :
     deux pastilles au même endroit, c'est une pastille qu'on ne peut pas
     toucher. */
  html.mob-deux-vues .viewport-container .pv-mode-bar,
  html.mob-deux-vues .viewport-container #pvModeBar{ top:54px !important; }
  .viewport-container .pv-mode-bar::-webkit-scrollbar,
  .viewport-container #pvModeBar::-webkit-scrollbar,
  .viewport-container #previewModeBar::-webkit-scrollbar{ display:none; }
  /* La « vue éclatée » du maillot : un curseur qui a besoin de largeur. On la
     monte d'un cran pour qu'elle ne recouvre plus la pastille des cotes ni le
     choix de plateau, et on la resserre en hauteur. */
  .viewport-container #explodeBar{
    left:8px !important; right:8px !important; width:auto !important;
    bottom:50px !important; padding:5px 9px !important;
  }
  .viewport-container #explodeBar .hud-slider-card{ gap:2px !important; padding:0 !important; }
  .viewport-container #explodeBar .hud-slider-label{ font-size:9.5px !important; }
  .viewport-container #explodeBar input[type=range]{ height:22px; margin:0; }
  .viewport-container .pv-mode-btn{
    min-height:36px; padding:8px 10px !important; font-size:11px !important;
    white-space:nowrap; flex:0 0 auto;
  }
  /* LES QUATRE COINS, ET RIEN AU MILIEU : vue en haut à gauche, mode
     d'aperçu en haut au centre, cotes en bas à gauche, état en bas à droite.
     Mesuré : côte à côte en bas, cotes (183 px) et mode (190 px) se
     chevauchaient de 14 px sur 375 — l'un des deux était intouchable. */
  /* 🔴 Ancrée par la classe de la PAGE (.mob-outil), pas par une classe posée sur la
     pastille : les 14 outils réécrivent `pill.className` à chaque reconstruction, donc
     `.mob-etat` disparaissait au premier réglage touché et la pastille tombait tout en
     bas du document, hors de l'écran (mesuré le 01/10 : y = 1 400 px sur 844). */
  .mob-outil #statusPill, #statusPill.mob-etat{
    position:fixed !important; top:auto !important; left:auto !important;
    bottom:calc(var(--mob-feuille) + var(--mob-barre) + var(--mob-bas) + var(--mob-clavier) + 8px);
    right:8px; z-index:12;
    max-width:46%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  #correctionModeBanner.mob-correction{
    position:fixed !important; top:auto !important; left:auto !important;
    bottom:calc(var(--mob-feuille) + var(--mob-barre) + var(--mob-bas) + var(--mob-clavier) + 44px);
    right:8px; z-index:12;
  }

  /* La pastille qui ouvre la carte des dimensions (en bas à gauche, là où la
     carte s'ouvrira : le doigt n'a pas à traverser l'écran). */
  .mob-cotes{
    position:absolute; z-index:7; bottom:8px; left:8px;
    max-width:calc(100% - 130px); min-height:34px;
    padding:7px 12px; border-radius:999px; cursor:pointer;
    font:inherit; font-size:11.5px; font-weight:750; color:var(--sur-accent);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    background:rgba(var(--voile-apercu-rgb), .86); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
    border:1px solid rgba(var(--voile-rgb), .16);
  }
  .mob-cotes.ouvert{ border-color:var(--accent-bright); color:var(--accent-bright); }

  /* Chromaplaque : la bascule entre ses deux aperçus, posée au centre en haut
     puisque ses deux voisins (vue et imprimabilité) tiennent les coins. */
  .mob-vues{
    position:fixed; z-index:10;
    display:flex; gap:3px; padding:3px;
    border-radius:10px; background:rgba(var(--voile-apercu-rgb), .86);
    -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
    border:1px solid rgba(var(--voile-rgb), .16);
  }
  .mob-vue-btn{
    min-height:36px; padding:8px 12px; border:0; border-radius:8px; cursor:pointer;
    font:inherit; font-size:11.5px; font-weight:700; color:var(--texte-bouton-doux); background:transparent;
  }
  .mob-vue-btn.actif{ background:var(--ok); color:var(--sur-accent-bright); }

  /* La carte des dimensions : cachée derrière sa pastille, ouverte au doigt.
     ⚠️ Elle n'est PAS retirée — ses lignes (#specDim, #specWeight, le coût
     matière et son sélecteur) restent les mêmes nœuds, mis à jour par
     updateMaterialSpecs() et assets/cout-matiere.js comme sur PC. */
  html.mob-js .viewport-container #pvSpecs,
  html.mob-js .viewport-container #previewSpecs{ display:none !important; }
  html.mob-js .viewport-container #pvSpecs.mob-ouvert,
  html.mob-js .viewport-container #previewSpecs.mob-ouvert{
    display:flex !important;
    left:8px !important; right:8px !important; bottom:8px !important;
    width:auto !important; max-width:none !important;
    max-height:58%; overflow-y:auto; overscroll-behavior:contain;
    pointer-events:auto !important; z-index:8;
  }
  html.mob-js .viewport-container #pvSpecs.mob-ouvert .cm-select{ min-height:40px; }

  /* Chromaplaque, aperçu 2D. Mesuré : tête 110 + image 220 + pied 114 = 444
     px dans un panneau de 334 — le pied passait sous l'écran. Onglets sur une
     ligne qui défile, l'image prend ce qui reste. */
  #chromaVue2D{ display:flex; flex-direction:column; }
  #chromaVue2D .cv2-tete{ flex:0 0 auto; }
  #chromaVue2D .cv2-onglets{
    flex-wrap:nowrap !important; overflow-x:auto; scrollbar-width:none;
  }
  #chromaVue2D .cv2-onglets::-webkit-scrollbar{ display:none; }
  /* Sans `nowrap`, chaque onglet coupait son libellé sur DEUX lignes et la
     rangée montait à 100 px — prise sur l'image, qui n'en a que 150. */
  #chromaVue2D .cv2-onglet{ white-space:nowrap; flex:0 0 auto; }
  /* Et TOUT le panneau s'arrête au-dessus de la bascule « Image / Pièce 3D »,
     qui est ancrée au bas de la zone d'aperçu. Rembourrer le seul pied ne
     suffisait pas : son texte défile, la marge finissait SOUS la partie
     visible et les mots passaient toujours derrière la bascule (vu sur
     capture). C'est la bande du bas qu'il faut réserver, pas la marge. */
  html.mob-deux-vues #chromaVue2D{ padding-bottom:50px; }
  #chromaVue2D .cv2-scene{ flex:1 1 auto; min-height:0; }
  #chromaVue2D .cv2-pied{
    flex:0 0 auto; max-height:104px; overflow-y:auto; overscroll-behavior:contain;
  }
  /* La bascule « Image / Pièce 3D » se range en HAUT À DROITE, où il n'y a
     plus rien depuis que l'état de la pièce est descendu en bas : décaler la
     tête de 46 px pour lui faire de la place aurait coûté ces 46 px à
     l'image, qui n'en a que 150 au total. */
  /* ⚠️ Deux placements essayés et mesurés avant celui-ci : en haut, elle
     recouvrait les onglets de la vue 2D. La ligne du bas se lit d'un coup
     d'œil — cotes à gauche, aperçu choisi au centre, état à droite — et
     aucun des trois ne touche les deux autres. */
  .mob-vues{
    left:50%; right:auto; transform:translateX(-50%);
    top:auto !important;
    bottom:calc(var(--mob-feuille) + var(--mob-barre) + var(--mob-bas) + var(--mob-clavier) + 8px);
  }
  /* La pastille des cotes se resserre pour laisser passer la bascule : son
     texte est un résumé, la carte complète s'ouvre d'une touche. */
  html.mob-deux-vues .mob-cotes{ max-width:104px; }

  /* ---- 6. La poignée de la feuille + les 4 gestes d'édition ------------ */
  .mob-poignee{
    position:relative; flex:0 0 auto;
    display:flex; align-items:center; gap:6px;
    padding:6px 10px 4px; touch-action:none;
    border-radius:var(--mob-rayon) var(--mob-rayon) 0 0;
  }
  .mob-poignee::before{
    content:''; position:absolute; top:6px; left:50%; transform:translateX(-50%);
    width:42px; height:4px; border-radius:99px; background:var(--line-strong);
  }
  /* ⚠️ `min-width` N'EST PAS DÉCORATIF : la zone qu'on fait glisser est le
     seul reste de la poignée qui soit un vrai bouton. Sans plancher, un geste
     de plus dans la rangée la réduirait à quelques pixels et la feuille
     deviendrait impossible à ouvrir au doigt, sans que rien ne le signale. */
  .mob-poignee-zone{
    flex:1 1 auto; align-self:stretch; min-height:30px; min-width:56px;
    background:none; border:0; padding:0; cursor:grab; font:inherit; color:transparent;
  }
  /* Cinq gestes depuis le 21/09 (« 📁 » a rejoint « ↺ ») : la rangée défile
     plutôt que de déborder si un outil en ajoutait un sixième. */
  .mob-poignee-gestes{
    display:flex; align-items:center; gap:4px; flex:0 1 auto; margin-top:8px;
    overflow-x:auto; scrollbar-width:none;
  }
  .mob-poignee-gestes::-webkit-scrollbar{ display:none; }
  .mob-poignee-gestes > button{ flex:0 0 auto; }
  .mob-poignee-gestes > button{
    min-width:40px; min-height:40px; width:auto !important; height:auto !important;
    margin:0 !important; padding:6px 9px !important;
    font:inherit !important; font-size:13px !important; font-weight:650 !important;
    line-height:1 !important; white-space:nowrap;
    color:var(--text) !important; background:var(--surface-2) !important;
    border:1px solid var(--line) !important; border-radius:10px !important;
    box-shadow:none !important; cursor:pointer;
  }
  .mob-poignee-gestes > button:disabled{ opacity:.4; }
  /* Sur PC ces quatre boutons vivent dans l'aperçu (assets/barre-apercu.js).
     Au téléphone ils remontent dans la feuille : c'est là qu'est le pouce
     quand on règle, et l'aperçu leur était de toute façon trop étroit. */
  html.mob-js .viewport-container .bo-barre{ display:none !important; }

  /* ⚠️ VU SUR LA CAPTURE DU 21/09, invisible à tout banc non connecté en
     ADMINISTRATEUR : le journal 🐞 (`#debugToggleBtn`, créé en ligne par
     chaque page, `fixed; bottom:16px; left:16px; z-index:999999`) se posait
     SUR le premier bouton de la barre du bas — sur tous les outils, tout le
     temps. Rangé dans le coin haut gauche de l'aperçu, sous la pastille des
     vues : le seul coin de la scène qui ne porte rien. */
  html.mob-js.mob-outil #debugToggleBtn{
    top:calc(var(--mob-haut) + 52px) !important; bottom:auto !important;
    left:10px !important; right:auto !important;
    width:30px !important; height:30px !important;
    font-size:13px !important; opacity:.8;
  }
  html.mob-js.mob-outil #debugErrorBanner{ top:var(--mob-haut) !important; }

  /* ---- 7. La barre d'actions basse ------------------------------------ */
  .mob-actions{
    position:fixed; left:0; right:0; bottom:var(--mob-clavier); z-index:30;
    display:flex; align-items:center; gap:8px;
    padding:8px 10px calc(8px + var(--mob-bas));
    background:linear-gradient(180deg, rgba(var(--voile-rgb), .03), rgba(var(--voile-rgb), 0)), var(--surface);
    border-top:1px solid var(--line);
    box-shadow:0 -4px 18px rgba(var(--ombre-rgb), .45);
  }
  .mob-actions > *{ flex:0 0 auto; margin:0 !important; }
  /* Le bouton d'export prend toute la place qui reste : c'est l'action qui
     compte, et un doigt ne vise pas un bouton de 90 px au bord de l'écran. */
  .mob-actions .mob-principal,
  .mob-actions #exportDropdown{ flex:1 1 auto; min-width:0; }
  .mob-actions #exportDropdown > button,
  .mob-actions .mob-principal{
    width:100% !important; min-height:46px !important;
    font-size:14px !important; font-weight:750 !important;
    justify-content:center; white-space:nowrap;
  }
  .mob-actions .mob-icone{
    display:inline-flex; align-items:center; justify-content:center;
    width:46px !important; min-width:46px; height:46px !important;
    padding:0 !important; font-size:17px !important; line-height:1 !important;
    color:var(--text) !important; background:var(--surface-2) !important;
    border:1px solid var(--line-strong) !important; border-radius:12px !important;
    box-shadow:none !important; cursor:pointer;
  }
  .mob-actions .mob-icone:disabled{ opacity:.4; }
  .mob-actions .mob-icone[hidden]{ display:none !important; }

  /* Le menu « Exporter » s'ouvrait sous son bouton ; depuis la barre basse il
     n'y a plus de « sous ». Il devient une feuille, au-dessus de la barre. */
  html.mob-js .mob-actions .dropdown-menu{
    position:fixed !important; top:auto !important;
    left:8px !important; right:8px !important; width:auto !important;
    bottom:calc(var(--mob-barre) + var(--mob-bas) + 8px) !important;
    border-radius:14px; z-index:60;
    max-height:70dvh; overflow-y:auto;
  }
  html.mob-js .mob-actions .dropdown-menu button{ min-height:48px; font-size:13.5px; }

  /* ---- 8. (libre) -----------------------------------------------------
     ⏸️ CE BLOC PORTAIT L'ONGLET « ⋯ Plus », RETIRÉ LE 21/09 sur décision de
     l'utilisateur : il coûtait un onglet permanent dans une rangée de 375 px.
     Il contenait le PIED DE PAGE (qui reste dans la page et sur les 8 pages
     qui défilent, simplement plus rapatrié dans la feuille d'un outil) et la
     PHRASE de présentation de l'outil, retirée elle aussi sur sa décision.
     Rien d'autre n'y vivait — vérifié dans `monterPlus` avant de le retirer. */

  /* ---- 9. Les cibles et les champs ------------------------------------
     ⚠️ 16 px sur les champs n'est pas un choix de goût : en dessous, iOS
     ZOOME la page à la mise au point et on ne revient jamais au cadrage
     d'avant. Le créateur de maillot le faisait déjà pour lui seul. */
  .tool-row .sidebar input[type=text],
  .tool-row .sidebar input[type=number],
  .tool-row .sidebar input[type=email],
  .tool-row .sidebar input[type=tel],
  .tool-row .sidebar input[type=url],
  .tool-row .sidebar input[type=password],
  .tool-row .sidebar input[type=search],
  .tool-row .sidebar textarea,
  .tool-row .sidebar select{ font-size:16px !important; min-height:44px; }
  /* 24 px : une case à cocher de 20 px se rate au pouce. On ne va pas jusqu'à
     34 — la zone utile d'une case, c'est la case ET son libellé, et grossir
     davantage déformerait des dizaines de lignes de réglages. */
  .tool-row .sidebar input[type=checkbox],
  .tool-row .sidebar input[type=radio]{ width:24px; height:24px; }
  .tool-row .sidebar button{ min-height:40px; }
  .tool-row .sidebar input[type=range]{ height:40px; }
  .tool-row .sidebar input[type=range]::-webkit-slider-thumb{ width:22px; height:22px; }
  .tool-row .sidebar input[type=range]::-moz-range-thumb{ width:22px; height:22px; }
  /* Deux colonnes de réglages sur 350 px donnent deux colonnes illisibles. */
  .tool-row .sidebar .card-row,
  .tool-row .sidebar .row2,
  .tool-row .sidebar .grid-2{ grid-template-columns:1fr !important; }

  /* Cibles relevées sous 34 px par `scripts/tester-mobile.js`, outil par
     outil. Ce ne sont pas des retouches de goût : ce sont les seules
     commandes du site qu'un doigt ratait encore.
       • `.cv2-onglet`  — les 4 onglets de la vue 2D de Chromaplaque (27 px)
       • `.pv-anim-btn` — « ▶ Tester l'articulation » (32 px)
       • `.view-btn`    — les rotations du créateur de statue (23 px) */
  .viewport-container .cv2-onglet,
  #chromaVue2D .cv2-onglet{ min-height:34px; padding:7px 10px !important; }
  #chromaVue2D .cv2-zoom button,
  .viewport-container .cv2-zoom button{ min-width:34px; min-height:34px; }
  .viewport-container .pv-anim-btn{ min-height:36px; }
  /* Le bandeau « Reprendre vos derniers réglages ? » (assets/design-saves.js) :
     ses deux boutons décident du sort d'un brouillon, on ne les rate pas. */
  .ds-brouillon button{ min-height:36px; padding:8px 14px; }
  .tool-row .sidebar .view-btn,
  .viewport-container .view-btn{ min-height:34px; padding:6px 10px !important; }

  /* Le bandeau « Reprendre vos derniers réglages ? » est posé en `fixed` à
     16 px du bas par design-saves : au téléphone il tombait PAR-DESSUS la
     barre d'actions, donc par-dessus « Exporter ». Il remonte au-dessus
     d'elle, et prend toute la largeur pour rester lisible. */
  .ds-brouillon{
    left:8px !important; right:8px !important; max-width:none !important;
    bottom:calc(var(--mob-barre) + var(--mob-bas) + 10px) !important;
  }

  /* ---- 10. Les fenêtres : pleine largeur ------------------------------- */
  .modal, .export-choice-modal{
    width:calc(100vw - 20px) !important; max-width:none !important;
    max-height:86dvh; overflow-y:auto;
  }

  /* ---- 11. Téléphone COUCHÉ (et tout écran court) ----------------------
     À 844 × 390 une feuille par le bas ne laisse rien à l'aperçu. Elle passe
     donc sur le côté, comme sur PC — c'est la même idée, tournée d'un quart
     de tour, et aucun réglage ne change de place dans la feuille. */
  @media (orientation:landscape) and (max-height:560px){
    .tool-row .sidebar{
      top:var(--mob-haut) !important; bottom:0 !important;
      left:auto !important; right:0;
      width:min(54vw, 420px) !important; height:auto !important;
      border-radius:0; border-left:1px solid var(--line) !important;
      box-shadow:-12px 0 34px rgba(var(--ombre-rgb), .6);
      transition:none;
    }
    .tool-row .viewport-container,
    .tool-row #chromaVue2D{
      bottom:0 !important; right:min(54vw, 420px) !important;
    }
    .mob-actions{
      left:auto; right:0; width:min(54vw, 420px);
      bottom:0; border-top:1px solid var(--line);
    }
    .mob-poignee::before{ display:none; }
    .mob-poignee-zone{ display:none; }
    .tool-row .sidebar{ padding-bottom:calc(var(--mob-barre) + var(--mob-bas)); }
  }
}

/* =========================================================================
   📱 LE RESTE DU SITE AU TÉLÉPHONE (2026-09-20)
   =========================================================================
   Elles avaient un défaut de DOIGT : 14 à 26 cibles par page sous les 34 px,
   dont les liens du pied de page à 14 px de haut, présents partout. Et une
   chose qui MANQUAIT tout court — voir `.topbar-nav-links` juste en dessous.

   🔴🔴 ET UN DÉFAUT DE LARGEUR QUE CE COMMENTAIRE A NIÉ PENDANT UN JOUR.
   Il annonçait « zéro débordement sur 25 pages » : mesuré, et faux, parce que
   le banc mesure DÉCONNECTÉ et que c'est la session qui remplit la barre.
   Connecté en admin, la pastille pèse 168 px au lieu de 78 : l'accueil
   débordait de 312 px, la galerie et les applications de 218. Aucun banc ne
   pouvait le voir. ➡️ `tester-mobile.js` simule désormais une barre d'admin.
   ⚠️ Les règles qui suivent ne CACHENT pas le débordement
   (`overflow-x:hidden` aurait tu le symptôme) : elles rendent chaque morceau
   réductible — et lui donnent un PLANCHER, sans quoi il s'effondre (21/09).
   ========================================================================= */
@media (max-width: 860px){

  /* ---- La barre du haut des pages qui ne sont pas des outils ----------
     Mesuré à 375 px, session administrateur : logo 267 px (accueil) ou
     167 (galerie), actions 326 = pastille 168 + cloche 40 + compte 113.
     Les outils ont leur propre traitement (`#topbar.app-topbar-tool`, plus
     spécifique) et ne sont pas concernés : ils mesuraient déjà 0. */
  /* 🔴 LA PAGE NE FAIT PLUS PILE L'ÉCRAN — signalé sur capture le 21/09
     (« le centrage de la page aussi »). Onze pages réservent une gouttière
     pleine hauteur au menu latéral : `#page{margin-left:46px;
     max-width:calc(100vw - 46px)}`. Au téléphone ce menu n'est plus qu'une
     poignée posée DANS la barre du haut : la gouttière ne protège plus rien
     et décale toute la page vers la droite.
     ⚠️ `100%` et JAMAIS `100vw` : sur plusieurs navigateurs `100vw` compte la
     barre de défilement et rouvre à lui seul le débordement qu'on ferme ici. */
  #page{ margin-left:0 !important; max-width:100% !important; width:100%; }

  /* La poignée du menu (46 px) garde sa place, et elle seule : le reste de
     la barre part de 52 px au lieu de 46 px de gouttière sur toute la page. */
  .app-topbar-inner, .topbar-inner{
    padding-left:52px !important; padding-right:10px !important;
    gap:8px !important; min-width:0;
  }
  /* 🔴 `flex:0 1 auto` + `min-width:0` + une image en `max-width:100%` =
     LOGO LARGE DE ZÉRO, mesuré. C'est le piège classique de flexbox avec une
     image : le parent peut se réduire sans plancher, l'image n'a plus de
     largeur propre à défendre, et le tout s'effondre en silence — la marque
     disparaissait purement et simplement de l'accueil.
     ➡️ La marque ne se réduit PAS ; c'est sa HAUTEUR qu'on limite, et sa
     largeur suit les proportions. Sur l'accueil le logo passe de 267 à
     ~134 px, sur la galerie de 167 à ~84. */
  /* ⚠️ LA CIBLE TACTILE EST LE LIEN, PAS L'IMAGE. Premier essai : grossir le
     logo jusqu'à 34 px pour satisfaire la cible minimale — il reprenait alors
     142 px de large et la barre débordait de 11 px une fois connecté. On
     sépare les deux : le lien garde ses 36 px de haut (donc le doigt le
     trouve), l'image reste petite (donc la barre tient). */
  /* ⚠️ `min-width` EST CE QUI SAUVE LE LOGO. Premier essai du 21/09 :
     `flex:0 1 auto` + `min-width:0` + une image en `max-width:100%` l'avait
     réduit à ZÉRO — la marque disparaissait de l'accueil sans rien lever. Avec
     un plancher, la même mécanique devient utile : la marque rend quelques
     pixels quand la barre est trop pleine, et jamais plus que ça.
     `height:auto` + `max-height` ENSEMBLE : avec une hauteur FIXE et une
     largeur plafonnée, l'image garderait ses proportions de force et sortirait
     écrasée. */
  .app-brand{
    flex:0 0 auto; min-width:0;
    min-height:36px; display:flex; align-items:center;
  }
  /* ⚠️⚠️ UNE HAUTEUR FIXE, ET RIEN D'AUTRE — deux variantes mesurées le 21/09,
     toutes deux fausses : `min-width:0` + `max-width:100%` → LOGO LARGE DE
     ZÉRO (le pourcentage se calcule sur une largeur qui dépend du contenu, la
     boucle se résout à rien, la marque disparaît sans rien lever) ;
     `height:auto` + `max-height` → logo collé à son plancher MÊME QUAND IL Y A
     LA PLACE (la base du flex part de la taille INTRINSÈQUE du SVG). */
  .app-brand img{ height:30px !important; width:auto !important; max-width:none; }
  .topbar-actions{ flex:0 1 auto; min-width:0; gap:6px !important; }
  #notifBloc, #accountMenuMount{ flex:0 0 auto; }
  /* La pastille se réduit avant tout le monde : c'est le seul morceau dont
     la fin (« (admin) ») n'apprend rien à celui qui la lit. */
  /* ⚠️ `.app-topbar`, PAS `#topbar` : l'identifiant n'existe que sur les
     pages d'OUTIL (`<header id="topbar" class="app-topbar-tool">`). Écrite
     avec lui, cette règle ne s'appliquait nulle part ailleurs — elle était
     dans la feuille, lisible, et inerte sur les 9 pages visées. C'est la
     mesure qui l'a dit : la pastille restait à 168 px. */
  /* ⚠️ DES PLANCHERS, PAS SEULEMENT DES PLAFONDS — défaut introduit le 21/09,
     vu seulement SUR LE SITE EN LIGNE. Avec `min-width:0` seul, la pastille
     absorbait toute la compression : 73 px connecté, mais **20 px** déconnecté
     (« Se connecter » pèse 113) — un moignon qui n'affiche plus rien. */
  .app-topbar .balance-pill{
    flex:0 1 auto; min-width:56px; max-width:40vw;
    font-size:11.5px !important; padding:5px 9px !important;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* ⚠️ ON RACCOURCIT LE TEXTE, ON NE LE COUPE PAS. Un `text-overflow` seul
     rendait « ◉ Poly… » : la pastille perdait justement le chiffre qu'on
     vient y lire. Les 24 pages balisent donc les deux parties dont un
     téléphone n'a pas besoin — le mot « Polys : », que l'icône ◉ dit déjà,
     et « (admin) », que l'administrateur sait. Reste « ◉ 1 240 » ou
     « ◉ illimité ». Sur ordinateur la phrase est rendue entière, au
     caractère près : ces deux balises n'existent que sous 860 px. */
  .balance-pill .pill-mot, .balance-pill .pill-long{ display:none !important; }
  /* ⚠️ DÉFAUT QUE J'AI INTRODUIT PUIS FERMÉ, trouvé par le banc en 3 minutes :
     j'avais écrit `.account-menu-trigger .account-menu-label{display:none}`
     SANS le garde `.is-user`. Connecté, l'avatar reste et tout va bien ; mais
     DÉCONNECTÉ ce libellé est le seul contenu du bouton — il tombait à 20 px
     de large sur les 15 outils, c'est-à-dire que la porte d'entrée du compte
     devenait un bouton vide et intouchable. `assets/style.css` cache déjà
     cette étiquette pour un compte identifié (`.account-menu.is-user`, plus
     haut) : il n'y avait rien à ajouter, seulement à ne pas défaire. */
  /* « Se connecter » est la porte d'entrée d'un visiteur : il se réduit, mais
     jamais au point de devenir illisible. */
  .account-menu-trigger{
    max-width:130px; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
  }
  .account-menu:not(.is-user) .account-menu-trigger{ min-width:104px; }

  /* 🔑 SUR 375 px ON NE PEUT PAS TOUT AVOIR. Mesuré : marque 125 + pastille 71
     + cloche 40 + « Se connecter » 113 font 375 à eux seuls. On sacrifie donc
     ce qui n'apprend RIEN : pour un VISITEUR la pastille affiche « Polys : … »,
     une attente qui ne se résout jamais faute de compte, quand « Se connecter »
     est sa seule porte d'entrée.
     ⚠️ `:has()` regarde en AVANT (la pastille PRÉCÈDE le menu compte, aucun
     sélecteur classique ne remonte). Sans lui la règle est ignorée : la
     pastille garde son plancher — on dégrade, on ne casse pas. */
  .topbar-actions:has(.account-menu:not(.is-user)) .balance-pill{ display:none !important; }

  /* ---- Ce qui disparaissait ------------------------------------------
     🔴 `.topbar-nav-links{display:none}` sous 860 px : au téléphone, « Outils
     3D », « Fonctionnement » et « FAQ » n'existaient NULLE PART — le menu
     latéral ne porte que les pages, pas ces ancres. assets/mobile.js les
     déplace dans le menu latéral sous « Sur cette page » ; cette règle-ci les
     y habille. Le `display:none` d'origine reste : c'est bien dans le menu
     qu'on veut les lire, pas dans une barre de 375 px. */
  #sideNavPanel .mob-ancres{ display:flex; flex-direction:column; }
  /* Le titre du groupe se sépare de ce qui PRÉCÈDE, pas de ce qu'il annonce :
     `.side-title` est dessiné pour l'en-tête du menu et porte un trait EN
     DESSOUS, qui coupait le titre de sa propre liste. */
  #sideNavPanel .mob-ancres .side-title{
    border-bottom:0; border-top:1px solid var(--line);
    padding:13px 20px 6px; margin:8px 0 2px;
  }
  #sideNavPanel .mob-ancres .topbar-link{
    display:flex; align-items:center; min-height:40px;
    padding:9px 12px; margin:2px 8px; border-radius:var(--radius-sm);
    font-size:13px; font-weight:650; color:var(--text); text-decoration:none;
  }
  #sideNavPanel .mob-ancres .topbar-link:hover{ background:var(--accent-soft); color:var(--accent); }

  /* ---- Les cibles trop petites, relevées une à une --------------------
     Aucune n'est agrandie « pour faire joli » : chacune a été mesurée sous
     34 px par `scripts/tester-mobile.js`, page par page. */
  .balance-pill{ min-height:34px; display:inline-flex; align-items:center; }
  .preview-tab-btn{ min-height:34px; }
  .app-tile-prevenir{ min-height:34px; }
  .gf-tri-btn, .gf-filtre-btn{ min-height:34px; }
  .apps-switch-btn{ min-height:40px; }
  .notif-bouton{ min-width:40px; min-height:40px; }

  /* Le pied de page : ses liens faisaient 14 px de haut, sur TOUTES les
     pages. Un lien de 14 px se rate une fois sur deux au pouce.
     ⚠️ `#siteFooter` en tête n'est pas du zèle : CHAQUE page redéfinit
     `.footer-links a` dans son propre <style>, chargé APRÈS cette feuille —
     à spécificité égale c'est la page qui gagne, et la règle serait inerte.
     Mesuré : sans l'identifiant, les liens restaient à 15 px. */
  #siteFooter .footer-links a, #siteFooter .footer-bottom a{
    display:inline-block; padding:10px 0; line-height:1.25;
  }

  /* Même raison — la galerie habille ces boutons dans son propre <style>.
     Le cœur (« j'aime »), l'étoile (favori) et le nom du créateur faisaient
     21 à 24 px de haut sur une carte qu'on parcourt au pouce. */
  .gal-fav-btn, .gal-like-btn, .gal-maker-btn{ min-height:34px !important; }
  .modal-close{ min-width:40px !important; min-height:40px !important; }
  .secondary-btn{ min-height:40px !important; }
  /* Les deux liens de la fenêtre de connexion (« créer un compte », « mot de
     passe oublié ») : 14 px de haut, et ce sont les deux seules portes de
     sortie de cette fenêtre. */
  #goToSignup, #goToReset{ display:inline-block; padding:10px 0; }
  /* Les cases à cocher de tout le site, pas seulement celles des outils.
     ⚠️ `!important` ET PAS AUTREMENT : la galerie déclare `.gf-case input`
     (une classe + un élément) et notre `input[type=checkbox]` (un attribut +
     un élément) pèse EXACTEMENT le même poids — à égalité, c'est le <style>
     de la page qui gagne parce qu'il est chargé après. La règle était là,
     lisible dans la feuille, et parfaitement inerte : 16 px mesurés. */
  /* ⚠️ 30/09 : SAUF les interrupteurs ON/OFF du panneau (.switch-onoff, 62 × 28 px) :
     réduits à 24 px, leur pastille recouvrait le mot « OFF » (vu sur iPhone). */
  input[type=checkbox]:not(.switch-onoff), input[type=radio]{ width:24px !important; height:24px !important; }
  .footer-inner{ gap:24px; }
  .footer-cols{ gap:26px; }

  /* ---- Les champs de formulaire --------------------------------------
     ⚠️ 16 px n'est pas une taille de confort : EN DESSOUS, iOS ZOOME la page
     dès qu'on touche le champ, et ne revient jamais au cadrage d'avant. C'est
     le défaut le plus courant d'un site au téléphone, et il était partout
     (33 à 36 px de haut, 13 à 14 px de texte). */
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=hidden]),
  select, textarea{
    font-size:16px;
    min-height:44px;
  }
  textarea{ min-height:88px; }
  .btn, button.btn{ min-height:44px; }

  /* Deux colonnes de 170 px ne sont pas deux colonnes. */
  .cpt2-deux, .cpt2-deux-inegal, .ce-deux-inegal, .fc-tuiles{ grid-template-columns:1fr !important; }

  /* ---- Les tableaux ---------------------------------------------------
     Un tableau ne se replie pas : il défile, dans son propre cadre, plutôt
     que de pousser toute la page vers la droite. */
  .tableau-defilant, .table-wrap, .cpt2-table-wrap{
    overflow-x:auto; -webkit-overflow-scrolling:touch;
  }
}

/* -------------------------------------------------------------------------
   La vitrine de l'accueil (`?embed=1`) n'est pas un outil : elle ne montre
   qu'une pièce qui tourne dans un cadre de quelques centaines de pixels.
   Tout ce qui précède y serait au mieux inutile, au pire une feuille de
   réglages par-dessus la vitrine. On remet donc l'ossature d'origine.
   ------------------------------------------------------------------------- */
@media (max-width: 860px){
  html.embed-mode .tool-row .viewport-container,
  body.embed-mode .tool-row .viewport-container{
    position:static !important; inset:auto !important;
    width:100% !important; height:100% !important; z-index:auto;
  }
  html.embed-mode .tool-row, body.embed-mode .tool-row{ height:100% !important; display:flex !important; }
  html.embed-mode .mob-actions, body.embed-mode .mob-actions,
  html.embed-mode .mob-poignee, body.embed-mode .mob-poignee{ display:none !important; }
}

/* ===== Mes données — version lisible (2026-09-25, assets/mes-donnees-lisible.js) ===== */
.md-ov{position:fixed; inset:0; z-index:2000; background:var(--surface); overflow:auto; padding:20px 16px;}
.md-boite{max-width:1100px; margin:0 auto; display:flex; flex-direction:column; gap:12px;}
.md-actions{display:flex; gap:8px; justify-content:flex-end; flex-wrap:wrap; position:sticky; top:0; padding:6px 0; background:var(--surface); z-index:1;}
.md-doc{color:var(--text); font-size:14px; line-height:1.5;}
.md-doc h1{font-size:22px; margin:0 0 4px;}
.md-doc h2{font-size:13px; text-transform:uppercase; letter-spacing:.08em; color:var(--accent-bright); margin:22px 0 8px; border-bottom:1px solid var(--line-strong); padding-bottom:3px;}
.md-sous{color:var(--text-2); margin:0 0 12px; font-size:13px;}
.md-note{color:var(--text-2); font-size:12.5px; margin:4px 0;}
.md-sommaire{margin:0; padding-left:20px; display:grid; gap:2px;}
.md-fiche{display:grid; grid-template-columns:max-content 1fr; gap:4px 18px; margin:0;}
.md-fiche dt{color:var(--text-2);} .md-fiche dd{margin:0; font-weight:600; overflow-wrap:anywhere;}
.md-tab{overflow-x:auto; border:1px solid var(--line); border-radius:var(--radius-sm);}
.md-tab table{width:100%; border-collapse:collapse; font-size:12.5px;}
.md-tab th,.md-tab td{text-align:left; padding:5px 8px; border-bottom:1px solid var(--line); vertical-align:top;}
.md-tab th{font-size:11px; text-transform:uppercase; letter-spacing:.04em; color:var(--text-2); white-space:nowrap;}
.md-tab td{overflow-wrap:anywhere; min-width:70px;}
@media (max-width:600px){ .md-fiche{grid-template-columns:1fr;} .md-fiche dd{margin-bottom:6px;} }
/* À l'impression : le document seul, en noir sur papier blanc, quel que soit le thème. */
@media print{
  body.md-en-impression > *:not(.md-ov){display:none !important;}
  body.md-en-impression .md-ov{position:static; background:var(--papier-fond); padding:0; overflow:visible;}
  body.md-en-impression .md-actions{display:none !important;}
  body.md-en-impression .md-doc, body.md-en-impression .md-doc h2, body.md-en-impression .md-sous,
  body.md-en-impression .md-note, body.md-en-impression .md-fiche dt, body.md-en-impression .md-tab th{color:var(--papier-encre-noire);}
  body.md-en-impression .md-tab{overflow:visible; border-color:var(--papier-encre-noire);}
  body.md-en-impression .md-tab table{font-size:9.5px;}
  body.md-en-impression{background:var(--papier-fond) !important;}
}

/* ===== Carte cadeau imprimable (2026-09-25, assets/carte-cadeau-imprimable.js) ===== */
.cci-ov{position:fixed; inset:0; z-index:2000; background:var(--surface); overflow:auto; padding:20px 16px;}
.cci-boite{max-width:980px; margin:0 auto; display:flex; flex-wrap:wrap; gap:24px; align-items:flex-start; justify-content:center;}
.cci-reglages{flex:1 1 260px; max-width:340px; display:flex; flex-direction:column; gap:10px;}
.cci-champ{display:flex; flex-direction:column; gap:4px;}
.cci-champ .ds-champ{width:100%;}
/* La carte : format A6 paysage (148 × 105 mm), papier blanc quel que soit le thème. */
.cci-carte{flex:0 0 auto; width:148mm; max-width:100%; min-height:105mm; box-sizing:border-box; padding:7mm 8mm; border-radius:4mm;
  background:var(--papier-fond); color:var(--papier-encre-noire); border:1.2mm solid var(--accent); display:flex; flex-direction:column; gap:2mm;
  -webkit-print-color-adjust:exact; print-color-adjust:exact;}
.cci-tete{display:flex; align-items:center; justify-content:space-between; gap:4mm;}
.cci-logo{width:34mm; height:auto; display:block;}
.cci-etiquette{font-size:9pt; font-weight:800; letter-spacing:.14em; text-transform:uppercase; color:var(--accent);}
.cci-carte p{margin:0;}
.cci-pour{font-size:12pt; font-weight:700;}
.cci-montant{font-size:28pt; font-weight:900; letter-spacing:-.01em; color:var(--accent); line-height:1.05;}
.cci-soustitre{font-size:9pt;}
.cci-message{font-size:11pt; font-style:italic; white-space:pre-line; overflow-wrap:anywhere;}
.cci-de{font-size:10pt; text-align:right;}
.cci-pied{margin-top:auto; display:flex; gap:5mm; align-items:center; padding-top:3mm; border-top:0.3mm solid var(--accent);}
.cci-qr{width:24mm; height:24mm; flex:0 0 auto;}
.cci-qr svg{display:block; width:100%; height:100%;}
.cci-code{font:800 13pt ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing:.06em;}
.cci-aide{font-size:7.5pt; line-height:1.35;}
@media print{
  @page{size:A4; margin:12mm;}
  body.cci-en-impression > *:not(.cci-ov){display:none !important;}
  body.cci-en-impression .cci-ov{position:static; background:var(--papier-fond); padding:0; overflow:visible;}
  body.cci-en-impression .cci-reglages{display:none !important;}
  body.cci-en-impression .cci-boite{display:block;}
  body.cci-en-impression{background:var(--papier-fond) !important;}
}

/* Registre des demandes RGPD (admin, migration 123) */
.rgpd-titre{font-size:13px; margin:14px 0 8px;}
