/* ============================================================
   Blocs des intégrateurs — feuille servie par le CDN gndad
   Préfixe de classe : .cf-*

   FICHIER GÉNÉRÉ — NE PAS ÉDITER À LA MAIN.
   Thème  : @gndad/theme 0.1.0
   Source : documentation-integration @ 285471c33173a19acb1de8b1609113908a511d69
   Copie  : 2026-09-05T23:03:10.214Z (packages/theme/blocks, tools/blocks/sync-blocks.mjs)
   Build  : packages/theme/scripts/build-cf.mjs
   Ordre  : _base, champ-description, catalog-header, section-separator, champ-texte, champ-textarea, champ-select, telephone, date-3-selects, date-native, radio-checkbox, toggle, option-card, upload-fichier-unique, stepper-traitement, upload-multi, upload-multi-production, extras
   ============================================================ */

/* ============================================================
   BLOC : _base
   Regles communes a TOUS les blocs : police du formulaire, libelles,
   asterisque obligatoire, boite de champ 48px, placeholder, focus vert.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
.formio-form { font-family: 'Poppins', system-ui, -apple-system, sans-serif; color:#111827; }

/* ---- Labels de champ ---- */
.formio-form .col-form-label { font-size:14px; font-weight:600; color:#000; margin-bottom:6px; padding:0; }
/* Conservee pour les hotes qui chargent cf.css avec un Form.io anterieur a 5 :
   c'est la seule source d'asterisque en 4.x. Sous Form.io 5.3.0 elle est MORTE,
   neutralisee par `.col-form-label.field-required` + pseudo-element `after`,
   avec `content:none!important`,
   de formio.full.min.css (specificite egale, mais !important cote vendor). La
   surcharger exigerait un !important qui casserait le rendu en 4.x : on laisse
   le vendor gagner et on stylise son span, ci-dessous. */
.formio-form .field-required:after { content:" *"; color:#DC2626; }

/* Form.io 5.3.0 emet dans chaque label obligatoire deux noeuds absents de la
   version 4.x, et deplace l'asterisque du pseudo-element vers un vrai noeud :
     span.required-star     porte l'asterisque, en rouge vendor rgb(235,0,0) ;
     span.visually-hidden   porte le texte "required field" pour les lecteurs
                            d'ecran.
   `visually-hidden` est une classe utilitaire de Bootstrap 5 ; le vendor
   embarque Bootstrap 4.6.2, qui nomme la sienne `sr-only`. Ni bootstrap.min.css
   ni formio.full.min.css ne definissent donc `visually-hidden`, et le texte
   s'affichait en clair dans le libelle : "Pays * required field".
   On fournit l'utilitaire manquant sans toucher au DOM : le texte reste dans
   l'arbre d'accessibilite, il cesse seulement d'etre peint. */
.formio-form .visually-hidden {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}
/* Aligne l'asterisque du vendor sur le rouge du systeme (#DC2626 au lieu de
   son rgb(235,0,0)), pour que 4.x et 5.x rendent la meme couleur. */
.formio-form .col-form-label .required-star { color:#DC2626; }

/* ---- Composant masque ----
   Form.io pose la classe `formio-hidden` sur tout composant dont le schema
   porte `hidden: true`. MESURE 5.3.0 : le vendor n en fait presque RIEN — sa
   seule declaration est `.formio-hidden{margin:0}`, il n y a AUCUN display:none.
   Le moteur se contente de ne pas rendre le contenu du composant ; le noeud
   enveloppe, lui, reste dans le DOM.
   Pour un composant de type bloc, le resultat est une boite de hauteur nulle,
   donc invisible par accident. Mais pour un `columns`, le noeud porte la classe
   Bootstrap `row` et reste `display:flex` — et s il a une hauteur minimale (cas
   mesure : `.cf-upload-live{min-height:240px}`), il continue d occuper 240px de
   vide au milieu du formulaire.
   On rend donc le masquage effectif, une fois pour toutes. Sans !important la
   regle perdrait contre `.row{display:flex}` de Bootstrap sur les composants
   `columns` : specificite egale (0,1,0), et Bootstrap est charge apres nous
   dans certaines pages. */
.formio-form .formio-hidden { display: none !important; }

/* ---- GOUTTIERE UNIQUE DU CATALOGUE : 40px ----
   REGLE UNIQUE qui gouverne l ecart entre deux champs d une meme ligne.
   Bootstrap pose .row{margin:0 -15px} plus .col-*{padding:0 15px}, soit une
   gouttiere de 30px. On transpose le MEME mecanisme a 40px : marge -20 et
   padding 20. La marge negative compense exactement le padding, donc les BORDS
   EXTERIEURS de la ligne ne bougent pas d un pixel : seul l ecart interieur
   change, et chaque champ absorbe la difference — il PERD ici 15px de largeur.
   Mesure a 1200px : bords 244 et 956 inchanges, gouttiere 10 vers 40, champs
   351 vers 336. Somme conservee : 351+10+351 = 336+40+336 = 712.
   ECHELLE DES ECARTS DU CATALOGUE — trois valeurs, une seule logique : plus
   deux elements sont lies, plus ils sont proches.
     0px   deux colonnes qui forment UNE boite bordee unique (.cf-phone) ;
     10px  les sous-parties d UN champ (les 3 selects du composant natif day,
           bloc date-native) ;
     40px  deux champs distincts (la regle ci-dessous).
   Le 10px de date-native n est donc PAS un oubli de propagation : il est
   volontairement plus serre que la gouttiere generale. Voir son commentaire.

   EXCEPTION .cf-phone : ses deux colonnes ne sont pas deux champs voisins,
   elles forment UNE seule boite bordee (indicatif et numero fusionnes). Ses
   declarations padding:0 et margin:0 portent !important dans le bloc
   telephone et priment donc sur celles-ci : sa gouttiere reste a 0. Ne jamais
   retirer ces !important, un ecart de 40px couperait la boite en deux.

   Le composant natif `day` emet une div.row simple, sans la classe
   formio-component-columns : il echappe au selecteur ci-dessous et sa
   gouttiere est fixee A PART, a 10px, dans le bloc date-native.
   L exclusion de .cf-phone est STRUCTURELLE (:not) et non laissee a la cascade.
   Mesure qui a impose ce choix : dans le fork de recapitulatif/preview.html toutes
   les declarations portent !important ; a importance egale c est la specificite qui
   tranche, et (0,3,0) pour cette regle battait (0,1,0) de .cf-phone. La boite du
   telephone s est retrouvee coupee par une gouttiere interieure. Avec :not(.cf-phone)
   la regle ne matche tout simplement plus la ligne du telephone, quelle que soit
   l importance des declarations. Ne pas retirer ce :not. */
/* La DEMI-gouttiere est exposee en propriete personnalisee : c est la seule
   valeur a changer pour retailler toutes les gouttieres, et elle est lisible
   par les blocs qui doivent s aligner sur le padding des colonnes. Cas reel :
   le bloc upload-fichier-unique pose son overlay en position:absolute ; comme
   Bootstrap met position:relative sur toute .col-*, cet overlay se dimensionne
   sur la boite de PADDING de la colonne et debordait donc d exactement une
   gouttiere. Il consomme desormais --cf-gutter pour retomber sur la boite de
   contenu. Ne pas remplacer ces var() par des litteraux. */
.formio-form { --cf-gutter: 20px; }
.formio-form .formio-component-columns.row:not(.cf-phone) { margin-left:calc(-1 * var(--cf-gutter)); margin-right:calc(-1 * var(--cf-gutter)); }
.formio-form .formio-component-columns.row:not(.cf-phone) > [class*=col-] { padding-left:var(--cf-gutter); padding-right:var(--cf-gutter); }

/* ---- Champs texte / select / textarea (hauteur fixe 48px) ---- */
.cf-field .form-control:not(textarea),
.cf-field select.form-control {
  box-sizing:border-box; border:1px solid #D1D5DB; border-radius:8px;
/* HAUTEUR DE BOITE : 48px. WCAG 2.5.5 (Target Size) recommande 44x44 px au
   minimum pour une cible tactile ; 48px passe le seuil avec de la marge.
   REGLE DE DERIVATION, a appliquer partout : tout element pose A L INTERIEUR
   d une boite bordee de 1px vaut 48 - 2 = 46px (ancien couple 40 / 38). Cela
   concerne .choices__list--single, le conteneur interne du select pays et
   l input du numero, dans les blocs champ-select et telephone.
   Le centrage vertical ne depend PAS de ces nombres : voir line-height:normal
   ci-dessous. C est ce qui rend ce changement de hauteur sans effet de bord. */
  padding:0 14px; height:48px; max-height:48px;
  /* line-height:normal et NON une valeur en pixels. Une hauteur de ligne figee
     (38px = 40 moins les 2px de bordure) ne centrait le texte que par
     coincidence : elle ne vaut que pour une boite de cette hauteur exacte ET
     pour les metriques de la police effectivement resolue. Des que Poppins est
     remplacee par un repli, ou que la boite change de hauteur, le texte se
     decale. Le centrage vertical vient desormais d ailleurs, et il est stable :
       - input : le moteur centre nativement son editeur interne dans la boite ;
       - div.form-control du select pays : display:flex + align-items:center
         (bloc telephone) ;
       - .choices__inner et .choices__list--single : display:flex +
         align-items:center (bloc champ-select).
     Ne pas reintroduire de line-height en pixels ici. */
  font-size:14px; line-height:normal; color:#111827; box-shadow:none; width:100%;
}
/* Placeholder : #767676 est le gris le plus clair qui conserve un ratio de
   contraste de 4.5:1 sur fond blanc (WCAG 2.1 AA, texte normal). Ne pas
   l eclaircir. La declaration ci-dessous couvre textfield, textarea, email,
   number, url et le champ numero du bloc telephone (tous portent form-control).
   Elle prime sur .form-control::placeholder de bootstrap (#6c757d) par sa
   specificite (0,2,0 contre 0,1,0). L opacite reste a 1, heritee de bootstrap.
   TAILLE : 16px, demande explicite. Le texte SAISI reste a 14px (font-size de
   la boite, plus haut) : le texte retrecit donc au moment ou l usager tape.
   Effet mesure, assume, et signale — la demande portait sur le placeholder.
   Le textarea est une exception : il est exclu de la regle de boite par
   :not(textarea), sa font-size vient du 1rem de bootstrap, il etait deja a
   16px des deux cotes et ne saute pas. */
.cf-field .form-control::placeholder { color:#767676; font-size:16px; }
.cf-field .form-control:not(textarea):focus,
.cf-field select.form-control:focus {
  border-color:#16a34a; box-shadow:0 0 0 3px rgba(22,163,74,.12); outline:none;
}

/* ============================================================
   BLOC : champ-description
   Texte d aide affiche SOUS un champ, rendu par la propriete NATIVE
   `description` du composant Form.io. Aucun htmlelement ajoute, aucune
   manipulation du DOM : Form.io 5.3.0 emet deja, apres le champ, un noeud

       div.form-text.text-muted

   qui porte ce texte. Le bloc ne fait que l habiller.

   Rendu obtenu : 14px, #434343, 6px de respiration sous le champ.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */

/* ---- Description native d un champ (portee .cf-field) ----
   Defaut vendor mesure sur le socle embarque (bootstrap 4.6.2) :
     .form-text  = display block + margin-top .25rem, AUCUNE font-size ;
     .text-muted = color #6c757d, avec !important.
   Le noeud est un div, pas un `small` : il n herite donc pas du
   font-size .875em de `small` et retombe sur le 1rem du body, soit 16px
   mesures avant ce bloc. Les deux valeurs a corriger sont donc la taille
   (16px vers 14px) et la couleur (#6c757d vers #434343).

   Le !important de la couleur n est pas decoratif : `.text-muted` porte
   deja !important cote vendor, et aucune specificite ne bat un !important.
   Celui de la taille serait inutile, aucune regle vendor ne la fixe : il
   n y est pas.

   La respiration de 6px reprend exactement l ecart libelle vers champ pose
   par _base (.col-form-label, margin-bottom 6px) au lieu des 4px du vendor :
   le texte d aide se lit alors comme le pendant bas du libelle. Cette marge
   est la SEULE geometrie posee ici. Le bloc ne suppose aucune hauteur de
   champ et reste valable que la boite mesure 40px ou 48px.

   Portee volontairement limitee a .cf-field. Les blocs de televersement
   traitent deja leur propre description autrement (upload-fichier-unique et
   upload-multi la masquent, upload-multi-production la peint en 12px) et
   aucun d eux ne porte cf-field : les deux motifs ne se croisent jamais.

   NE JAMAIS poser de description sur un composant cache (type hidden) :
   Form.io rend le texte malgre tout, en clair et pleine hauteur (96px
   mesures sur ce projet), sans le champ qui le justifie. */
.cf-field .form-text.text-muted {
  display: block;
  margin: 6px 0 0;
  font-size: 14px;
  line-height: 1.45;
  color: #434343 !important;
}

/* ============================================================
   BLOC : catalog-header
   En-tete decoratif du catalogue (pastille "i" + titre h2).

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- En-tete du catalogue ---- */
.cf-header { display:flex; align-items:center; gap:10px; margin:6px 0 26px; }
.cf-header .cf-info {
  width:22px; height:22px; flex:0 0 22px; display:inline-flex; align-items:center;
  justify-content:center; border-radius:50%; background:#111827; color:#fff;
  font-size:13px; font-weight:700; font-style:italic; line-height:1;
}
.cf-header h2 { font-size:18px; font-weight:600; margin:0; color:#111827; }

/* ============================================================
   BLOC : section-separator
   Separateur de section. AUCUNE regle CSS : le stylage est 100% inline
   dans le champ "content" du htmlelement (voir README du bloc).

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Separateur de section : stylage inline dans le contenu htmlelement
   (le sanitizer Form.io supprime `content` et casse les pseudo-elements
   ecrits avec deux caracteres deux-points) ---- */

/* ============================================================
   BLOC : champ-texte
   Champ texte simple. AUCUNE regle propre : entierement peint par _base
   (boite 40px, placeholder, focus). Le fichier existe pour que la
   convention "un dossier = un bloc" reste vraie et que la concatenation
   soit exhaustive.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */

/* ============================================================
   BLOC : champ-textarea
   Zone de texte multiligne. Ne surcharge que la hauteur et le padding ;
   le reste vient de _base.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
.cf-field textarea.form-control {
  min-height:128px; max-height:none; height:auto; line-height:1.5;
  padding:11px 14px; resize:vertical;
}

/* ============================================================
   BLOC : champ-select
   Select habille par le widget choices.js embarque dans formio.full.min.js.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Select (widget choices.js) ---- */
.cf-field.formio-component-select { overflow:visible; }
.cf-field .choices { margin-bottom:0; overflow:visible; }
.cf-field .choices.is-open { position:relative; z-index:30; }
.cf-field .choices__inner {
  box-sizing:border-box; border:1px solid #D1D5DB; border-radius:8px;
  padding:0 30px 0 14px; min-height:48px; max-height:48px; height:48px;
  font-size:14px; color:#111827; background:#fff;
  display:flex; align-items:center;
}
/* 46px = 48 (boite) moins les 2px de bordure : cet element vit A L INTERIEUR
   de .choices__inner. C est le display:flex + align-items:center qui centre
   le texte, pas cette hauteur ; min-height ne sert qu a donner au flex une
   boite pleine hauteur sur laquelle centrer. */
.cf-field .choices__list--single {
  padding:0; display:flex; align-items:center; min-height:46px; width:100%;
}
.cf-field .choices__item--selectable { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Placeholder du select ferme. Meme #767676 que les champs texte (gris le plus
   clair a 4.5:1 sur fond blanc). Le vendor pose .choices__placeholder{opacity:.5},
   ce qui melangerait la couleur au fond blanc et ferait remonter la luminance a
   ~#BBB, sous le seuil : opacity:1 est donc load-bearing, ne pas la retirer.
   TAILLE : 16px. Attention, .choices__placeholder est un VRAI element (pas un
   pseudo-element) : la font-size s y applique directement et prime sur le 14px
   herite de la boite. Le vendor declare .choices__list--dropdown .choices__item
   { font-size:14px } a specificite EGALE (0,2,0) — c est l ordre de declaration
   qui tranche, et notre feuille est injectee apres le vendor. Verifie par mesure
   dans le dropdown ouvert. La valeur choisie, elle, reste a 14px. */
.cf-field .choices__placeholder { color:#767676; opacity:1; font-size:16px; }
.cf-field .choices[data-type*=select-one]:after {
  top:50%; margin-top:-2.5px; border-color:#9CA3AF transparent transparent;
}
.cf-field .choices__button { display:none; }
.cf-field .choices__list--dropdown {
  box-sizing:border-box; border:1px solid #D1D5DB; border-radius:8px;
  margin-top:4px; background:#fff; z-index:40;
  max-height:240px; overflow-y:auto !important; overflow-x:hidden;
  -webkit-overflow-scrolling:touch;
}
.cf-field .choices__list--dropdown .choices__list {
  max-height:none; overflow:visible;
}
.cf-field .choices__list--dropdown .choices__item {
  padding:10px 14px; font-size:14px; line-height:1.35;
  white-space:normal; overflow:visible; text-overflow:clip;
}
.cf-field .choices__list--dropdown .choices__input {
  box-sizing:border-box; width:100%; padding:10px 14px;
  border:none; border-bottom:1px solid #E5E7EB; font-size:14px;
}
/* Champ de recherche du dropdown ouvert (input.choices__input--cloned, place par
   choices.js a l interieur de .choices__list--dropdown pour un select-one).
   Il ne porte PAS la classe form-control : ni la regle du bloc _base ni celle de
   bootstrap ne l atteignaient, et son placeholder tombait sur le defaut du
   moteur, mesure a rgb(117,117,117). On l aligne explicitement, opacite comprise
   (le vendor ne fixe pas d opacite ici, certains moteurs en appliquent une).
   TAILLE : 16px, comme les autres placeholders. */
.cf-field .choices__input::placeholder { color:#767676; opacity:1; font-size:16px; }

/* ---- Variante « pays » : select a drapeau (.cf-country) ----
   Meme gabarit de drapeau que le selecteur d'indicatif du bloc telephone, pour
   que les deux champs a drapeau du catalogue soient identiques.

   Le template du select emet `class="fi fis fi-XX"` : le suffixe `fis` fait
   pointer flag-icons vers flags/1x1/XX.svg, dont la source est carree. Dans une
   boite 20x20, `cover` ne rogne donc rien. Avec les SVG 4x3 par defaut, `cover`
   coupait 25% de la largeur et tronquait l'etoile blanche du drapeau togolais
   sous le masque circulaire.
   Les 6 pays de la liste (tg, bj, bf, ci, gh, fr) ont leur SVG dans
   vendor/flag-icons/flags/1x1/. Ajouter un pays exige d'y ajouter son SVG,
   faute de quoi le drapeau sera vide (404 silencieux). */
.cf-field.cf-country .fi {
  width:20px; height:20px; min-width:20px; min-height:20px;
  border-radius:50%; background-size:cover; background-position:center;
  display:inline-block; vertical-align:middle; margin-right:8px;
  box-shadow:0 0 0 1px rgba(0,0,0,0.08); overflow:hidden; flex-shrink:0;
}
/* Chevron moderne, identique a celui du selecteur d'indicatif. On neutralise
   le triangle plein de choices.js (dessine en bordures) plutot que de le
   masquer : il sert de support au chevron.
   Forme a UN SEUL deux-points : le sanitizer Form.io reecrit les
   pseudo-elements a deux-points doubles et la regle serait perdue. */
.cf-field.cf-country .choices[data-type*=select-one]:after {
  right:14px; top:50%; margin-top:0;
  width:6px; height:6px;
  border:none; border-right:1.8px solid #111827; border-bottom:1.8px solid #111827;
  transform:translateY(-65%) rotate(45deg);
  pointer-events:none;
}
.cf-field.cf-country .choices[data-type*=select-one].is-open:after {
  transform:translateY(-25%) rotate(-135deg);
}

/* ============================================================
   BLOC : telephone
   Telephone : indicatif (select) + numero (textfield) fusionnes dans un
   cadre unique de 48px (les elements internes valent 48 moins 2px de bordure,
   soit 46px). Les deux commentaires sentinelles qui encadrent le
   corps de ce fichier (premiere et derniere ligne de la region) delimitent
   la portion que sync-css.cjs recopie dans la feuille embarquee de
   sandbox/recapitulatif/preview.html : ne pas les dupliquer ni les
   supprimer, build-css.mjs verifie qu'elles apparaissent exactement une
   fois chacune dans cf.css.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* cf-block:phone:begin — region synchronisee automatiquement, ne pas editer
   ailleurs qu'ici : sync-css.js la recopie telle quelle dans les blobs cfStyles
   des .form.json et, apres reindentation + ajout de !important, dans la feuille
   embarquee de sandbox/recapitulatif/preview.html.
   ATTENTION : aucun caractere « inferieur a » dans cette feuille, meme en
   commentaire. Le blob est injecte via un composant htmlelement Form.io, dont
   le sanitizer analyse le contenu comme du HTML : un `(inferieur)div ...` dans
   un commentaire CSS lui fait supprimer TOUT l'element style, et la page perd
   sa mise en forme (constate par rendu : 406 -> 405 noeuds, le noeud STYLE
   disparaissant). */
/* ---- Telephone : indicatif + numero fusionnes dans un conteneur unique ---- */
.cf-phone {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  align-items: center !important;
  box-sizing: border-box !important;
  border: 1px solid #D1D5DB !important;
  border-radius: 8px !important;
  background: #ffffff !important;
  /* 48px : meme boite que tous les autres champs (voir _base). */
  height: 48px !important;
  min-height: 48px !important;
  max-height: 48px !important;
  padding: 0 !important;
  /* Form.io construit un composant columns en lui posant la classe Bootstrap
     "row", qui porte margin:0 -15px pour compenser le padding de ses colonnes.
     Ici les colonnes sont deja mises a padding:0 plus bas : la marge negative
     n est donc plus compensee et la boite deborde de 15 px de chaque cote.
     Mesure avant correction : champ telephone 371 px contre 341 px pour le
     champ texte voisin, soit exactement les 2 x 15 px. On neutralise. */
  margin-left: 0 !important;
  margin-right: 0 !important;
  overflow: visible !important;
  transition: border-color .15s, box-shadow .15s !important;
}
.cf-phone:focus-within {
  border-color: #16a34a !important;
  box-shadow: 0 0 0 3px rgba(22,163,74,.12) !important;
}
/* Les deux colonnes sont sorties de la grille Bootstrap.
   Bootstrap pose `.col-md-5 { flex:0 0 41.666667%; max-width:41.666667% }` et
   `.col-md-7 { flex:0 0 58.333333%; max-width:58.333333% }`. `flex` et `width`
   etaient deja neutralises plus bas, mais PAS `max-width` — d'ou les deux
   symptomes mesures dans Chromium sur un .cf-phone large de 371px (369px de
   contenu) : la colonne de l'indicatif restait plafonnee a 41,667% = 153,75px
   alors que son contenu n'en demande qu'une centaine, et celle du numero
   restait plafonnee a 58,333% = 215,25px, ce qui aurait laisse un vide a droite
   une fois la premiere colonne retrecie. `max-width:none` sur les deux libere
   le calcul : la premiere se dimensionne sur son contenu, la seconde absorbe
   tout le reste (flex-grow:1). */
.cf-phone > [class*=col-] {
  padding: 0 !important;
  height: 100% !important;
  display: flex !important;
  align-items: center !important;
  overflow: visible !important;
  max-width: none !important;
}
/* Colonne de l'indicatif : dimensionnee par son contenu, pas par la grille.
   min-width = plancher qui fige la largeur quel que soit l'indicatif choisi
   (« +33 » est plus etroit que « +228 »). Calcul, chaque terme mesure dans
   Chromium sur la page du catalogue :
     padding gauche du conteneur interne ............... 14,00 px
     drapeau (.fi, boite carree) ....................... 20,00 px
     marge droite du drapeau ...........................  8,00 px
     texte du plus large indicatif (« +229 »/« +226 ») . 33,08 px  (mesure
        canvas avec la police reellement appliquee a l'item ; « +228 »,
        « +225 » et « +233 » donnent 33,04 px, « +33 » 25,27 px)
     padding droit du conteneur (degagement chevron) ... 24,00 px
                                                        ---------
                                             total ..... 99,08 px
   Plancher retenu : 104px, soit le total arrondi au pixel superieur (100px)
   + 4px de marge pour absorber l'ecart de metriques entre Poppins (charge par
   le portail) et la police de repli system-ui (utilisee par les apercus du
   catalogue, ou Poppins n'est pas embarque). Le plus etroit (« +33 ») ne
   demande que 91,27 px : il est remonte au plancher, donc la largeur ne bouge
   plus d'un indicatif a l'autre et il ne reste aucun vide. */
.cf-phone > [class*=col-]:first-child {
  flex: 0 0 auto !important;
  width: auto !important;
  min-width: 104px !important;
}
/* Form.io ajoute, a cote du widget choices.js, un champ de saisie technique
   `.formio-select-autocomplete-input` destine a l'autocompletion du navigateur :
   invisible (2px de haut), il ne porte aucune donnee et n'apparait pas dans
   submission.data. Sa largeur par defaut de champ texte (185px mesures) est
   sans effet tant que la colonne est dimensionnee par la grille, mais des que
   celle-ci se dimensionne sur son contenu (max-width:none plus haut) elle
   devient la largeur intrinseque la plus grande et impose 185px a tout le champ.
   On la ramene a zero. Avant correction il debordait deja de sa colonne (185px
   dans une colonne de 153,75px) : le neutraliser ne change donc rien de visible. */
.cf-field.cf-phone-country .formio-select-autocomplete-input {
  width: 0 !important;
  min-width: 0 !important;
  padding: 0 !important;
  border: 0 !important;
}
/* Colonne du numero : absorbe toute la largeur restante.
   NE PAS utiliser `:last-child` — c'etait le cas jusqu'ici et la regle ne
   matchait RIEN : Form.io ajoute apres les colonnes un div
   `.formio-errors.invalid-feedback` qui est le vrai dernier enfant de .cf-phone.
   Le defaut restait invisible tant que Bootstrap fournissait 41,667% + 58,333%
   = 100% ; des que la premiere colonne se dimensionne sur son contenu, la
   seconde restait a 58,333% et laissait un vide de ~50px a droite (mesure). */
.cf-phone > [class*=col-]:not(:first-child) {
  flex: 1 1 auto !important;
  width: auto !important;
}
.cf-field.cf-phone-country {
  margin: 0 !important;
  height: 100% !important;
  width: 100% !important;
}
.cf-field.cf-phone-country .choices {
  margin: 0 !important;
  height: 100% !important;
}
/* Suppression stricte de tout icone parasite Semantic UI ou chevron avant le drapeau */
.cf-field.cf-phone-country .dropdown.icon,
.cf-field.cf-phone-country i.dropdown,
.cf-field.cf-phone-country i.icon,
.cf-field.cf-phone-country .choices__button {
  display: none !important;
}
/* Conteneur interne REEL du widget choices.js.
   Form.io n'emet aucun `.choices__inner` : le DOM rendu est
     div.choices  puis, en enfant direct,
     div.form-control.ui.fluid.selection.dropdown
   Ce second div porte donc `form-control` et heritait du cadre de la regle
   `.cf-field .form-control:not(textarea)` (bordure 1px, radius 8px, fond blanc,
   hauteur 48px) — c'est lui, et non un `.choices__inner`, qui dessinait le
   sous-cadre imbrique visible a l'interieur de .cf-phone.
   Neutralisation ciblee : le selecteur ci-dessous pese 4 classes (0,4,0) contre
   (0,2,1) pour la regle generique, et il est declare plus loin dans le fichier.
   Aucun `!important` n'est donc necessaire ICI (verifie par rendu :
   border-width 0px). Dans la copie embarquee de recapitulatif/preview.html, ou
   la regle generique porte elle-meme `!important`, sync-css.js ajoute
   `!important` a toutes les declarations de cette region — sinon la
   neutralisation y perdrait la cascade.
   Les classes `ui fluid selection dropdown` sont des reliquats Semantic UI :
   `.ui.selection.dropdown` et `.ui.fluid.dropdown` n'apparaissent 0 fois dans
   bootstrap.min.css, formio.full.min.css et flag-icons.min.css — elles ne
   portent donc aucune contrainte de largeur.
   L'element select natif n'a besoin d'aucune regle : choices.js lui pose [hidden]
   et le vendor applique deja `.choices [hidden]{display:none!important}` ; il
   mesure 0x0 dans le rendu. L'ancienne regle qui le masquait est supprimee. */
.cf-field.cf-phone-country .choices > .form-control,
.cf-field.cf-phone-country .choices > .form-control:focus {
  border: none;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  outline: none;
  /* 46px = 48 (cadre .cf-phone) moins ses 2px de bordure. */
  height: 46px;
  min-height: 46px;
  max-height: 46px;
  /* padding gauche identique aux autres champs (.cf-field .form-control : 0 14px).
     padding droit = degagement du chevron : carre de 6px pose a right:10px et
     tourne de 45deg, soit une emprise de 6*V2 = 8,49px -> 18,49px depuis le bord
     droit ; 24px laisse ~5,5px de respiration. */
  padding: 0 24px 0 14px;
  display: flex;
  align-items: center;
  cursor: pointer;
  width: 100%;
}
.cf-field.cf-phone-country .choices__list--single {
  padding: 0 !important;
  display: flex !important;
  align-items: center !important;
  font-size: 14px !important;
  font-weight: 500 !important;
  color: #111827 !important;
}
/* choices.js reserve 25px a droite de l'item pour son bouton « Remove item »,
   masque plus haut : on recupere cet espace mort. Le degagement du chevron est
   deja assure par le padding droit du conteneur ci-dessus.
   Specificite (0,4,0) contre (0,2,0) pour
   `.choices__item.choices__item--selectable{padding-right:25px}` du vendor. */
.cf-field.cf-phone-country .choices__list--single .choices__item--selectable {
  padding-right: 0;
}
/* Drapeau parfaitement rond.
   Le gabarit du telephone utilise la variante CARREE de flag-icons : le template
   du select emet `class="fi fis fi-XX"`, et `fis` fait pointer flag-icons vers
   flags/1x1/XX.svg (source 512x512). Dans une boite 20x20, `cover` ne rogne donc
   plus rien et n'agrandit plus l'image. Avec les SVG 4x3 d'origine, `cover`
   coupait 25% de la largeur et decalait puis tronquait l'etoile blanche du
   drapeau togolais sous le masque circulaire (le glyphe parasite en « a »).
   Le champ Pays a ete aligne sur ce meme gabarit : il emet lui aussi `fis` et
   porte `.cf-country`, dont les regles vivent dans le bloc champ-select. Les
   deux champs a drapeau du catalogue sont donc identiques. Toute evolution du
   gabarit doit etre repercutee dans les deux blocs. */
.cf-phone .fi,
.cf-field.cf-phone-country .fi {
  width: 20px !important;
  height: 20px !important;
  min-width: 20px !important;
  min-height: 20px !important;
  border-radius: 50% !important;
  background-size: cover !important;
  background-position: center !important;
  display: inline-block !important;
  vertical-align: middle !important;
  margin-right: 8px !important;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.08) !important;
  overflow: hidden !important;
  flex-shrink: 0 !important;
}
/* Chevron moderne unique après l'indicatif (+228 ⌄) */
.cf-field.cf-phone-country .choices[data-type*=select-one]:after {
  content: "" !important;
  position: absolute !important;
  right: 10px !important;
  top: 50% !important;
  width: 6px !important;
  height: 6px !important;
  border: none !important;
  border-right: 1.8px solid #111827 !important;
  border-bottom: 1.8px solid #111827 !important;
  transform: translateY(-65%) rotate(45deg) !important;
  pointer-events: none !important;
}
.cf-field.cf-phone-country .choices[data-type*=select-one].is-open:after {
  transform: translateY(-25%) rotate(-135deg) !important;
}
.cf-field.cf-phone-country .choices__list--dropdown {
  position: absolute !important;
  top: 100% !important;
  left: 0 !important;
  margin-top: 4px !important;
  min-width: 200px !important;
  width: max-content !important;
  border: 1px solid #D1D5DB !important;
  border-radius: 8px !important;
  box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1) !important;
  background: #fff !important;
  z-index: 9999 !important;
}
.cf-field.cf-phone-number {
  margin: 0 !important;
  height: 100% !important;
  width: 100% !important;
}
.cf-field.cf-phone-number .form-control {
  border: none !important;
  border-radius: 0 8px 8px 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  /* 46px = 48 (cadre .cf-phone) moins ses 2px de bordure. */
  height: 46px !important;
  max-height: 46px !important;
  padding: 0 12px 0 4px !important;
  font-size: 14px !important;
  color: #111827 !important;
  width: 100% !important;
}
.cf-field.cf-phone-number .form-control:focus {
  outline: none !important;
  box-shadow: none !important;
  border: none !important;
}
/* Placeholder du champ numero. Meme #767676 que le reste du catalogue (gris le
   plus clair a 4.5:1 sur fond blanc). Le !important est celui de toute la
   region phone, recopiee avec !important dans recapitulatif/preview.html. */
.cf-field.cf-phone-number .form-control::placeholder {
  color: #767676 !important;
  /* 16px : meme taille que tous les placeholders du catalogue. Le numero saisi
     reste a 14px (regle .cf-field.cf-phone-number .form-control ci-dessus). */
  font-size: 16px !important;
}
/* cf-block:phone:end */

/* ============================================================
   BLOC : date-3-selects
   Date saisie en 3 selects (jour / mois / annee) + son sur-libelle.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Date : 3 selects avec espacement uniforme ----
   BLOC PERIME : remplace par `date-native` (composant natif Form.io `day`).
   Les deux regles ci-dessous sont conservees pour les integrations qui
   utilisent encore le fragment a trois selects.
   Elles sont desormais PORTEES SUR `.formio-component-columns` : sans cette
   restriction, `.cf-date { margin: 0 -6px }` s'appliquerait aussi au
   composant natif — qui porte la meme classe `cf-date` mais n'est pas un
   `columns` — et le decalerait de 6px hors de sa colonne (mesure).
   La gouttiere du bloc natif est geree par `date-native.css`.

   GOUTTIERE : les deux declarations qui vivaient ici (marge -6 / padding 6,
   soit 12px) ont ete RETIREES. La gouttiere du catalogue est gouvernee par la
   regle unique de _base (40px), dont le selecteur
   `.formio-form .formio-component-columns.row:not(.cf-phone) > [class*=col-]`
   pese (0,5,0) contre (0,3,0) ici : les declarations locales etaient devenues
   du code mort, silencieusement ecrase. Une seule constante, un seul endroit. */

/* ---- Date (label au-dessus des 3 selects) ---- */
.cf-sublabel { font-size:14px; font-weight:600; color:#000; margin:0 0 6px; }

/* ============================================================
   BLOC : date-native
   Date saisie par le composant NATIF Form.io `day` (un seul
   composant, trois selects Jour / Mois / Annee) + son sur-libelle.

   Remplace le bloc `date-3-selects`, conserve a cote pour les
   integrations qui n'ont pas encore migre. Voir README.md pour
   le detail de la migration (cles, format de valeur, i18n).

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */

/* ---- Gouttiere ----
   Le composant natif emet une div.row Bootstrap contenant trois
   div.col col-xs-N. Les classes col-xs-* n'existent pas en
   Bootstrap 4 : les trois colonnes retombent sur `.col`
   (flex:1 1 0) et se partagent la largeur a parts egales, ce qui
   reproduit exactement les width 4/4/4 de l'ancien bloc.
   Reste la gouttiere : la .row de Bootstrap vaut 30px (marge -15 + padding
   15). Cette div.row ne porte PAS la classe formio-component-columns : elle
   echappe a la regle unique de _base, on y recopie donc la MEME constante.
   Pourquoi 10px ALORS QUE la gouttiere generale vaut 40px : les trois selects
   Jour/Mois/Annee sont les sous-parties d UN SEUL champ, sous un libelle
   unique. Regle de lecture : plus deux elements sont lies, plus ils doivent
   etre proches. Les ecarter de 40px comme deux champs sans rapport
   dissocierait visuellement une date en trois controles independants.
   Cet arbitrage a deja ete rendu une fois EN SENS INVERSE : quand la
   gouttiere generale valait 10px, laisser cette ligne a 12px inversait la
   hierarchie (les morceaux d un champ plus ecartes que deux champs distincts)
   et elle avait ete ramenee a 10px. A 40px l argument se retourne, et 10px
   devient la bonne valeur : nettement plus serre que la gouttiere generale,
   ce qui est exactement le signal de groupement recherche.
   NE PAS aligner cette ligne sur la constante de _base par souci de
   coherence : ce serait une fausse coherence. Echelle complete du catalogue :
   0 (boite fusionnee .cf-phone), 10 (sous-parties d un champ, ici),
   40 (deux champs distincts, bloc _base). */
.cf-date > .row { margin-left:-5px; margin-right:-5px; }
.cf-date > .row > [class*=col] { padding-left:5px; padding-right:5px; }

/* ---- Champ d'autocompletion ----
   Form.io place a cote de chaque select un input texte
   `.formio-select-autocomplete-input` destine a l'autocompletion du
   navigateur. Il ne porte aucune donnee et n'apparait pas dans
   submission.data, mais sa largeur intrinseque de champ texte devient
   le min-content de la colonne flex. MESURE sous 5.3.0 : des que le
   bloc est en demi-largeur (434px), les colonnes tombent a
   208 / 208 / 446 et l'ANNEE PASSE A LA LIGNE au lieu de trois
   colonnes de 137px. On ramene cette largeur a zero, exactement comme
   pour `.cf-phone-country`. Ne pas supprimer : le defaut est
   invisible tant que le bloc occupe toute la page. */
.cf-date .formio-select-autocomplete-input {
  width: 0;
  min-width: 0;
  padding: 0;
  border: 0;
}

/* ---- Caret (variante select) — REGLE ACTIVE ----
   Le catalogue ET sandbox/recapitulatif sont tous deux en `select` : cette regle
   s applique aux deux. Mesure : appearance:none et background-image posee sur les
   trois select du catalogue.
   Elle serait morte avec la variante input[type=number] — un champ de saisie n a
   pas de liste a derouler, donc pas de caret.
   Quand les trois champs sont des select NATIFS, le navigateur dessine sa propre
   fleche, visiblement differente du caret choices.js du reste du catalogue
   (triangle 10x5 en #9CA3AF, cf. champ-select). On repeint a l'identique.
   La couleur est celle du triangle CSS de `.cf-field .choices:after`. */
.cf-date.formio-component-day select.form-control {
  -webkit-appearance:none;
  -moz-appearance:none;
  appearance:none;
  padding-right:30px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='5' viewBox='0 0 10 5'%3E%3Cpath fill='%239CA3AF' d='M0 0h10L5 5z'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 11.5px center;
  background-size:10px 5px;
}

/* ---- Placeholder ----
   Le composant natif `day` accepte deux types par champ, et ILS NE SE VALENT
   PAS pour le placeholder :

     fields.*.type = "select"  -> le placeholder est la premiere option, de
       valeur vide. MESURE 5.3.0 : elle n est JAMAIS affichee. Le moteur rend
       ses options via renderTemplate("selectOption", { option, selected:false })
       — `selected` est code en dur a false pour toutes les options. Aucune n est
       cochee, selectedIndex vaut -1, et le select ferme est peint VIDE. Verifie
       par rendu : trois boites blanches, seul le chevron visible. Verifie aussi
       en sens inverse : forcer select.value = "" donne selectedIndex 0, le texte
       apparait et la regle :has ci-dessous se declenche (couleur mesuree
       rgb(118,118,118)). La mecanique CSS est juste, c est la selection qui
       manque. Correctifs tentes au niveau du schema, TOUS mesures inefficaces :
       defaultValue absent, null, "", "00/00/0000", "0/0/0", " / / ".

     fields.*.type = "number" -> NON RETENU par le catalogue depuis le retour
       en arriere demande par l utilisateur. Le moteur emet un input[type=number] portant
       un vrai attribut `placeholder`, que le navigateur affiche nativement des
       que le champ est vide. C est la configuration retenue par le catalogue.
       Aucune regle specifique ne serait necessaire : ces inputs portent
       `form-control`, donc `.cf-field .form-control::placeholder` de _base leur
       applique deja #767676 et 16px. Mesure : les trois affichent Jour / Mois /
       Annee en rgb(118,118,118) a 16px, valeur saisie en #111827 a 14px.
       Bonus mesure : minYear/maxYear deviennent des attributs min/max natifs et
       sont REELLEMENT valides (1850 et 2099 rejetes), ce que la variante select
       ne faisait pas — elle se contentait de ne pas les proposer.

   CONFIGURATION ACTUELLE DES DEUX PAGES : `select`. Le catalogue y est revenu sur
   demande, en connaissance de cause — le placeholder n est donc PAS peint a l etat
   vide, c est le prix assume du choix, pas un defaut a corriger. Voir le README.
   La regle :has ci-dessous reste donc MORTE (selectedIndex vaut -1, mesure), et la
   seconde, sur les `option`, est ACTIVE. Ne pas les supprimer : la premiere
   s activera si le moteur est corrige en amont ou si une valeur est pre-remplie,
   ce qui est le cas de sandbox/recapitulatif (defaultValue "21/08/2002"). */
.cf-date.formio-component-day select.form-control:has(option[value=""]:checked) { color:#767676; }
.cf-date.formio-component-day select.form-control option { color:#111827; }

/* ---- Compteurs du input[type=number] — REGLE MORTE AUJOURD HUI ----
   Plus aucun champ du catalogue n est en input[type=number] depuis le retour a la
   variante select : ce selecteur ne matche rien sur les deux pages. Conservee
   parce que le bloc doit rester utilisable dans les deux variantes, et que la
   remettre en service ne doit pas demander de la reecrire.
   Chromium et Firefox dessinent deux fleches de pas dans un champ numerique.
   Aucun autre champ du catalogue n en porte, et elles n ont pas de sens pour une
   date que l on saisit au clavier. On les retire, sans toucher au DOM : le champ
   reste un input[type=number], donc le pave numerique des claviers mobiles
   continue de se declencher. */
.cf-date.formio-component-day input[type=number]::-webkit-outer-spin-button,
.cf-date.formio-component-day input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance:none; appearance:none; margin:0;
}
.cf-date.formio-component-day input[type=number] { -moz-appearance:textfield; appearance:textfield; }

/* ---- Asterisque parasite ----
   Avec `hideLabel: true` et `validate.required: true`, Form.io conserve le
   label principal avec la classe `control-label--hidden`. Sous 5.3.0 cette
   classe vaut `position:absolute` SANS decoupe : l'asterisque rouge de
   `.field-required:after` s'affiche donc a droite du bloc, au-dessus du champ
   Annee (mesure : label de 8,5px a x=1035). L'asterisque legitime est portee
   par le sur-libelle `.cf-sublabel`, on supprime ce doublon.
   Regle conservee : elle vise le seul libelle PRINCIPAL du composant, celui
   qui porte `hideLabel:true`. Elle reste utile si `validate.required` repasse
   a true. Mesure : 0 element porteur de cette classe dans le catalogue
   actuel (validate.required = false), la regle est donc inerte aujourd hui. */
.cf-date.formio-component-day label.control-label--hidden { display:none; }

/* ---- Etat erreur ----
   Form.io pose `formio-error-wrapper` sur le composant et `is-invalid` sur les
   trois select. On garde la bordure rouge du catalogue et on neutralise le
   bandeau que le vendor ajoute autour du bloc. */
.cf-date.formio-component-day.formio-error-wrapper { background:transparent; padding:0; border:0; }
/* Deux branches : `select` est la branche ACTIVE (mesure : 4 elements .is-invalid
   sur une date invalide), `input` couvre la variante number, morte aujourd hui. */
.cf-date.formio-component-day select.form-control.is-invalid,
.cf-date.formio-component-day input.form-control.is-invalid { border-color:#F87171; background-color:#fff; }
.cf-date.formio-component-day .formio-errors { display:block; font-size:12px; color:#DC2626; margin-top:6px; }

/* ============================================================
   BLOC : radio-checkbox
   Boutons radio et cases a cocher personnalises (appearance:none +
   marques dessinees en background, jamais en content).

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Radio / Checkbox personnalises ---- */
.cf-control .form-check { padding-left:0; margin-bottom:0; }
.cf-control .form-check-inline { display:inline-flex; align-items:center; margin-right:26px; }
.cf-control .form-check-input {
  -webkit-appearance:none; appearance:none; width:18px; height:18px; margin:0 8px 0 0;
  border:1.6px solid #C4C7CC; background:#fff; cursor:pointer; position:relative; flex:0 0 18px; top:0;
}
.cf-control input[type=radio].form-check-input { border-radius:50%; }
.cf-control input[type=checkbox].form-check-input { border-radius:5px; }
/* Marques dessinees via background (pas de `content`, supprime par le sanitizer) */
.cf-control input[type=radio].form-check-input:checked {
  border-color:#16a34a; background-color:#16a34a;
  background-image: radial-gradient(circle at 50% 50%, #fff 0, #fff 3.5px, rgba(255,255,255,0) 4px);
  background-repeat:no-repeat;
}
.cf-control input[type=checkbox].form-check-input:checked {
  border-color:#16a34a; background-color:#16a34a;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23ffffff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
  background-size:13px 13px; background-position:center; background-repeat:no-repeat;
}
.cf-control .form-check-label { font-size:14px; color:#000; cursor:pointer; }

/* ---- Declaration sur l'honneur (checkbox obligatoire du recapitulatif) ----
   Checklist 13.4 : boite CARREE (rayon 4px), alignement flex-start, et
   asterisque a la FIN de la phrase — donc porte par le libelle de la case,
   jamais par un libelle de champ place au-dessus (d'ou hideLabel sur le
   composant). Sous Form.io 5.3.0 l'asterisque est le span.required-star
   emis dans le libelle ; sous 4.x c'est le pseudo-element de .field-required
   fourni par _base. Les deux sont couverts. */
.cf-declaration .form-check { display:flex; align-items:flex-start; gap:10px; padding-left:0; }
.cf-declaration input[type=checkbox].form-check-input { border-radius:4px; margin:2px 0 0; }
.cf-declaration .form-check-label { font-size:14px; line-height:1.5; color:#000; }
.cf-declaration .form-check-label .required-star { color:#DC2626; font-weight:700; }
.cf-declaration .form-check-label .visually-hidden { display:none; }
.cf-declaration.formio-component-checkbox > label.col-form-label { display:none; }

/* ============================================================
   BLOC : toggle
   Interrupteur : un selectboxes Form.io restyle en switch 40x22.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Toggle (interrupteur) — selectboxes restyle ---- */
.cf-toggle .form-check { padding-left:0; margin-bottom:0; }
.cf-toggle .form-check-inline { display:inline-flex; align-items:center; margin-right:26px; }
.cf-toggle .form-check-input {
  -webkit-appearance:none; appearance:none; width:40px; height:22px; border-radius:11px;
  border:none; cursor:pointer; transition:background-color .2s; flex:0 0 40px; margin:0 10px 0 0;
  position:static; top:0;
  background-color:#D1D5DB;
  background-image: radial-gradient(circle 9px at 11px 50%, #fff 0, #fff 96%, rgba(255,255,255,0) 100%);
  background-repeat:no-repeat;
}
.cf-toggle .form-check-input:checked {
  background-color:#16a34a;
  background-image: radial-gradient(circle 9px at 29px 50%, #fff 0, #fff 96%, rgba(255,255,255,0) 100%);
}
.cf-toggle .form-check-label { font-size:14px; color:#000; cursor:pointer; }

/* ============================================================
   BLOC : option-card
   Carte d'option : controle + titre + description alignes dans le label.
   Se combine OBLIGATOIREMENT avec radio-checkbox ou toggle.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Cartes d'option (controle + titre + description) ----
   L'input est a l'interieur du label : c'est donc le label qui porte le flex. */
.cf-optcard .form-check { padding:0; margin:0; }
.cf-optcard .form-check-label { display:flex; align-items:flex-start; gap:12px; line-height:1.35; }
.cf-optcard .form-check-input { margin:1px 0 0 0; }

/* ============================================================
   BLOC : upload-fichier-unique
   Televersement d'un fichier : habillage HTML statique + composant file
   Form.io rendu en overlay transparent par-dessus. 4 etats (depot,
   traitement, termine, erreur).

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- File upload dynamique (HTML + overlay) ---- */
.cf-upload-label { font-size:14px; font-weight:600; color:#000; margin:0 0 8px; display:block; }
.cf-upload-label .cf-req { color:#DC2626; }
.cf-upload {
  border:1.5px dashed #D1D5DB; border-radius:12px; padding:22px 16px 20px;
  text-align:center; background:#fff; min-height:200px; box-sizing:border-box;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
}
.cf-upload--error { background:#FEF2F2; border:1.5px dashed #F87171; }
.cf-upload__icon { display:flex; justify-content:center; margin-bottom:10px; line-height:0; }
.cf-upload__icon img { display:block; }
.cf-upload__title { font-size:14px; font-weight:600; color:#111827; margin:0 0 4px; }
.cf-upload__hint { font-size:12px; color:#9CA3AF; margin:0 0 14px; line-height:1.4; }
.cf-upload__btn {
  display:inline-block; padding:10px 22px; background:#16a34a; color:#fff;
  border-radius:8px; font-size:14px; font-weight:600; border:none; cursor:pointer; font-family:inherit;
}

/* ---- Structure overlay ---- */
.cf-upload-live { position:relative; margin-bottom:8px; }
.cf-upload-ui--drop { pointer-events:none; }
.cf-upload-overlay.formio-component-file { margin:0; }
.cf-upload-overlay.formio-component-file.required:after,
.cf-upload-live .cf-upload-overlay .field-required:after { display:none; }
.cf-upload-overlay .form-text.text-muted { display:none; }
.cf-upload-overlay .list-group:has(.list-group-header):not(:has(.list-group-item:not(.list-group-header))) {
  display:none; margin:0; padding:0; border:none;
}
.cf-upload-live:not(:has(.list-group-item:not(.list-group-header))):not(.cf-processing):not(:has(.alert-danger)):not(:has(.status.text-danger)) {
  min-height:240px;
}
/* ---- Zone de depot invisible : largeur alignee sur la colonne ----
   Cet overlay est la vraie zone cliquable de Form.io, rendue transparente et
   posee par-dessus la carte visible. Il est en position:absolute.
   PIEGE MESURE : Bootstrap pose `position:relative` sur toute `.col-*`, donc le
   bloc conteneur de cet overlay est LA COLONNE, et `left:0/right:0` se
   resolvent sur sa boite de PADDING, pas sur sa boite de contenu. Resultat
   mesure a 1200px : overlay 752px dans une colonne dont le contenu fait 712px,
   soit un debordement de 40px — exactement la gouttiere. La zone cliquable
   depassait donc de 20px de chaque cote de la carte visible et mordait sur la
   colonne voisine. Le defaut suivait la gouttiere : 30px a l origine, 10px,
   puis 40px, ce qui l a rendu voyant.
   Correction : on retranche la demi-gouttiere de chaque cote via --cf-gutter,
   definie une seule fois dans _base. Le repli 0px couvre le cas ou le champ
   n est pas dans une colonne a gouttiere. Mesure apres correction : 712px,
   identique a la boite de contenu, a vide comme apres depot. */
.cf-upload-overlay:not(:has(.list-group-item:not(.list-group-header))):not(:has(.alert-danger)):not(:has(.status.text-danger)) {
  position:absolute; left:var(--cf-gutter, 0px); right:var(--cf-gutter, 0px); top:36px; bottom:0; z-index:2;
}
.cf-upload-overlay .fileSelector:not([hidden]) {
  position:absolute; inset:0; opacity:0; cursor:pointer; border:none; background:transparent; min-height:200px; margin:0;
}
.cf-upload-overlay .fileSelector > .fa,
.cf-upload-overlay .fileSelector > .bi { display:none; }
.cf-upload-overlay .fileSelector .browse { position:absolute; inset:0; width:100%; height:100%; opacity:0; display:block; }
.cf-upload-overlay .fileSelector .browse .sr-only { display:none; }
.cf-upload-overlay .fileSelector .loader-wrapper { display:none !important; }

/* ---- Etat traitement (.cf-processing pose par le harnais / le portail) ---- */
.cf-upload-live.cf-processing {
  border:1px solid #E5E7EB; border-radius:12px; background:#fff; min-height:200px;
  display:flex; align-items:center; justify-content:center;
}
.cf-upload-live.cf-processing .cf-upload-ui--stepper { display:block; width:100%; }
.cf-upload-live.cf-processing .cf-upload-ui--drop,
.cf-upload-live.cf-processing .cf-upload-overlay { display:none !important; }

/* ---- Etat termine : carte horizontale (icone + nom/taille + croix a droite) ---- */
.cf-upload-live:has(.list-group-item:not(.list-group-header)):not(.cf-processing):not(:has(.alert-danger)):not(:has(.status.text-danger)) {
  min-height:0;
}
.cf-upload-live:has(.list-group-item:not(.list-group-header)) .cf-upload-ui--drop { display:none; }
.cf-upload-overlay:has(.list-group-item:not(.list-group-header)) { position:static; border:none; background:transparent; padding:0; }
.cf-upload-overlay .list-group-header { display:none !important; }
.cf-upload-overlay .list-group { margin:0; border:none; background:transparent; }
.cf-upload-overlay .list-group-item:not(.list-group-header) {
  display:flex; align-items:center; gap:14px;
  border:1px solid #E5E7EB; border-radius:12px; padding:12px 14px 12px 62px; text-align:left;
  background:#F0FDF4 url("data:image/svg+xml,%3Csvg%20width%3D%2245%22%20height%3D%2245%22%20viewBox%3D%220%200%2045%2045%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cg%20clip-path%3D%22url%28%23clip_pdf%29%22%3E%20%3Cpath%20d%3D%22M33.5996%200L41.0664%207.4668V43.2041C41.0664%2044.0855%2040.3511%2044.7998%2039.4697%2044.7998H5.3291C4.4477%2044.7998%203.73245%2044.0855%203.73242%2043.2041V1.5957C3.73256%200.714398%204.44776%200%205.3291%200H33.5996Z%22%20fill%3D%22%23E4E7EC%22%2F%3E%20%3Cpath%20d%3D%22M41.0664%207.4668H33.5996V0L41.0664%207.4668Z%22%20fill%3D%22%2398A2B3%22%2F%3E%20%3Crect%20x%3D%221.86621%22%20y%3D%2218.6665%22%20width%3D%2241.0667%22%20height%3D%2218.6667%22%20rx%3D%221.59595%22%20fill%3D%22%23CC400C%22%2F%3E%20%3Cpath%20d%3D%22M10.6914%2032.6425V23.3569H14.1735C14.8868%2023.3569%2015.4853%2023.4899%2015.9689%2023.7559C16.4556%2024.0219%2016.8228%2024.3877%2017.0707%2024.8531C17.3216%2025.3156%2017.447%2025.8415%2017.447%2026.431C17.447%2027.0264%2017.3216%2027.5554%2017.0707%2028.0178C16.8198%2028.4803%2016.4495%2028.8445%2015.9599%2029.1105C15.4702%2029.3735%2014.8672%2029.505%2014.1508%2029.505H11.843V28.1221H13.9241C14.3412%2028.1221%2014.6828%2028.0496%2014.9488%2027.9045C15.2148%2027.7594%2015.4113%2027.5599%2015.5382%2027.306C15.6682%2027.0521%2015.7332%2026.7604%2015.7332%2026.431C15.7332%2026.1015%2015.6682%2025.8113%2015.5382%2025.5604C15.4113%2025.3096%2015.2133%2025.1146%2014.9443%2024.9756C14.6783%2024.8335%2014.3352%2024.7625%2013.9151%2024.7625H12.3735V32.6425H10.6914Z%22%20fill%3D%22white%22%2F%3E%20%3Cpath%20d%3D%22M22.0546%2032.6425H18.9081V23.3569H22.1181C23.04%2023.3569%2023.832%2023.5428%2024.4939%2023.9146C25.1589%2024.2834%2025.6697%2024.8138%2026.0264%2025.506C26.3831%2026.1982%2026.5614%2027.0264%2026.5614%2027.9906C26.5614%2028.9579%2026.3815%2029.7891%2026.0219%2030.4843C25.6652%2031.1795%2025.1498%2031.713%2024.4758%2032.0848C23.8047%2032.4566%2022.9977%2032.6425%2022.0546%2032.6425ZM20.5902%2031.1871H21.973C22.6199%2031.1871%2023.1594%2031.0692%2023.5917%2030.8334C24.0239%2030.5946%2024.3488%2030.2395%2024.5665%2029.768C24.7841%2029.2934%2024.8929%2028.701%2024.8929%2027.9906C24.8929%2027.2803%2024.7841%2026.6909%2024.5665%2026.2224C24.3488%2025.7509%2024.0269%2025.3987%2023.6007%2025.166C23.1775%2024.9302%2022.6516%2024.8123%2022.0229%2024.8123H20.5902V31.1871Z%22%20fill%3D%22white%22%2F%3E%20%3Cpath%20d%3D%22M28.1596%2032.6425V23.3569H34.1082V24.767H29.8417V27.2879H33.7001V28.6979H29.8417V32.6425H28.1596Z%22%20fill%3D%22white%22%2F%3E%20%3C%2Fg%3E%20%3Cdefs%3E%20%3CclipPath%20id%3D%22clip_pdf%22%3E%20%3Crect%20width%3D%2244.8%22%20height%3D%2244.8%22%20fill%3D%22white%22%2F%3E%20%3C%2FclipPath%3E%20%3C/defs%3E%20%3C/svg%3E") 14px center / 34px no-repeat;
}
.cf-upload-overlay .list-group-item:not(.list-group-header) .row {
  flex:1 1 auto; display:flex; align-items:center; gap:12px; margin:0; min-width:0;
}
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"] {
  width:auto; max-width:none; flex:0 0 auto; padding:0;
}
/* Cellule du NOM du fichier. Avec uploadOnly:true — configuration de
   production, parce que l URL fabriquee par Form.io ne designe aucune
   ressource du backend GNDAD — le moteur rend un simple texte la ou il rendait
   un `a` : la mise en forme doit donc porter sur la CELLULE. Sa largeur de
   colonne depend de fileTypes : col-md-9 sans, col-md-7 avec. Les deux formes
   sont couvertes pour que le bloc reste utilisable dans les deux reglages. */
.cf-upload-overlay .list-group-item:not(.list-group-header) .col-md-7,
.cf-upload-overlay .list-group-item:not(.list-group-header) .col-md-9 {
  flex:1 1 auto; min-width:0;
  font-size:14px; font-weight:600; color:#111827;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* ---- Nom complet d un fichier tronque ----
   L ellipse preserve la largeur mais masque la fin du nom, qui porte souvent
   l information utile (« ...-version-definitive.pdf »). Le moteur n emet AUCUN
   attribut `title` sur la cellule : une infobulle native est donc impossible
   sans toucher au DOM, ce que le catalogue s interdit. Le nom complet est donc
   revele au survol et au focus clavier, en autorisant le retour a la ligne.
   La LARGEUR reste figee — seule la hauteur de la carte augmente — parce que
   `min-width:0` et la largeur de la colonne restent en place. Verifie par
   mesure : largeur identique au survol. */
.cf-upload-overlay .list-group-item:not(.list-group-header):hover .col-md-7,
.cf-upload-overlay .list-group-item:not(.list-group-header):hover .col-md-9,
.cf-upload-overlay .list-group-item:not(.list-group-header):focus-within .col-md-7,
.cf-upload-overlay .list-group-item:not(.list-group-header):focus-within .col-md-9 {
  white-space:normal; overflow:visible; text-overflow:clip; word-break:break-all;
}
.cf-upload-overlay .list-group-item:not(.list-group-header) .col-md-2 { color:#6B7280; font-size:13px; white-space:nowrap; }
.cf-upload-overlay .list-group-item:not(.list-group-header) .file-type { display:none; }
/* La colonne « Type » entiere, pas seulement son select : avec fileTypes
   renseigne, Form.io ajoute une col-md-2 qui laissait un blanc dans la carte. */
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.file-type) { display:none !important; }
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:last-child { display:none; }
/* Force la visibilite de la colonne de suppression meme si elle est en fin de ligne */
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.bi-trash),
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.fa-remove),
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.fa-trash),
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.fa-times),
.cf-upload-overlay .list-group-item:not(.list-group-header) [class*="col-md-"]:has(.glyphicon-remove),
.cf-upload-overlay .list-group-item:not(.list-group-header) .col-md-1 {
  display: flex !important; align-items:center; gap:6px;
  order:9; margin-left:auto; flex:0 0 auto; width:auto;
}
/* Commande de retrait de l etat « termine ». Le noeud natif est un `i` SANS
   nom accessible : le libelle est pose par `after`, comme dans le bloc
   upload-multi-production — sans quoi la seule commande de la carte est
   annoncee « vide » par un lecteur d ecran. UN SEUL deux-points : la forme
   double ne survit pas au sanitizer Form.io.
   Les selecteurs Font Awesome / glyphicon sont conserves pour les hotes qui
   ne servent pas le jeu Bootstrap Icons de 5.3.0. */
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-trash,
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-x,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-remove,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-trash,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-times,
.cf-upload-overlay .list-group-item:not(.list-group-header) .glyphicon-remove {
  display:inline-flex; align-items:center; justify-content:center;
  box-sizing:border-box; min-width:34px; height:34px; padding:0 10px 0 30px;
  border:1px solid #E5E7EB; border-radius:8px; background-color:#fff;
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%23B42318%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.5%204h11M6.5%204V2.8a.8.8%200%200%201%20.8-.8h1.4a.8.8%200%200%201%20.8.8V4M12.2%204l-.5%209a1%201%200%200%201-1%201H5.3a1%201%200%200%201-1-1l-.5-9M6.6%206.8v4.4M9.4%206.8v4.4%22%2F%3E%3C%2Fsvg%3E");
  background-repeat:no-repeat; background-position:9px center; background-size:16px;
  font-family:inherit; font-size:12.5px; font-weight:600; font-style:normal;
  line-height:1; color:#B42318; cursor:pointer; text-decoration:none; opacity:1;
  transition:background-color .15s, border-color .15s;
}
/* le glyphe de la police ferait doublon avec l image de fond, et n est pas
   garanti charge */
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-trash:before,
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-x:before { content:none; }
.cf-upload-overlay .list-group-item:not(.list-group-header) [ref="removeLink"]:after { content:"Retirer"; }
html[lang="en"] .cf-upload-overlay .list-group-item:not(.list-group-header) [ref="removeLink"]:after { content:"Remove"; }
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-trash:hover,
.cf-upload-overlay .list-group-item:not(.list-group-header) .bi-x:hover,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-remove:hover,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-trash:hover,
.cf-upload-overlay .list-group-item:not(.list-group-header) .fa-times:hover,
.cf-upload-overlay .list-group-item:not(.list-group-header) .glyphicon-remove:hover {
  background-color:#FEF2F2; border-color:#F3B4C0;
}
.cf-upload-overlay .list-group-item:not(.list-group-header) [ref="removeLink"]:focus {
  outline:2px solid #16a34a; outline-offset:2px;
}
.cf-upload-overlay .list-group-item:not(.list-group-header) .sr-only { display:none; }

/* ---- Etat erreur : toute la carte est cliquable pour reessayer ----
   Les regles `:has(.alert-danger)` qui vivaient ici ont ete RETIREES.
   MESURE (scratchpad/t5/p11-alert.js, Form.io 5.3.0) : le seul
   `.alert-danger` de la page est l'alerte de FORMULAIRE
   (div.wrap > div > div.alert.alert-danger), jamais un descendant d'un
   composant file. Les trois declencheurs du bloc comptaient 0 porteur dans
   tous les etats : la branche ne pouvait pas se declencher.
   Elle datait de Form.io 4.x, dont le gabarit `file` emettait bien
   un div de classe "alert alert-danger" dans la carte (bundle 4.21.2, trois
   sites). L'etat d'echec de televersement est desormais couvert par la
   branche `.status.text-danger` ci-dessous, et l'erreur de validation par
   la branche `formio-error-wrapper`.
   Si un hote 4.x devait etre resservi, c'est cette branche qu'il faudrait
   restaurer -- voir le README du bloc. */
.cf-upload-ui--error-head, .cf-upload-ui--retry { display:none; }
/* bouton Reessayer visuel (rendu par l'error-head, clic capte par la croix au-dessus) */
.cf-upload__btn--retry {
  display:inline-flex; align-items:center; gap:8px; padding:9px 20px; margin-top:8px;
  background:#fff; color:#D11135; border:1.5px solid #F3B4C0; border-radius:8px;
  font-size:14px; font-weight:600; font-family:inherit; pointer-events:none;
}
.cf-upload__btn--retry img { display:block; }

@media (max-width: 767.98px) {
  .cf-upload, .cf-upload-live.cf-processing { min-height:180px; }
}


/* ---- Etat erreur sous Form.io 5.3.0 --------------------------------------
   MESURE : le moteur 5.3.0 n'emet PLUS de .alert-danger pour un televersement
   refuse. Il ajoute une ligne
     li.list-group-item / div.row / div.col-md-7 / div.status.text-danger
   dans la liste, laisse la zone de depot native en place (le fichier n'entre
   pas dans la valeur du composant) et pose ref="fileToSyncRemove" sur l'icone.
   Les regles :has(.alert-danger) ci-dessus, ecrites pour le 4.x, ne se
   declenchaient donc jamais : l'echec s'affichait comme une carte VERTE de
   succes. Les selecteurs 4.x sont conserves pour les hotes restes en 4.x. */
.cf-upload-live:has(.status.text-danger):not(:has([ref="restoreFile"])) {
  border:1.5px dashed #F87171; border-radius:12px; background:#FEF2F2;
  padding:22px 16px 20px; min-height:200px;
}
.cf-upload-live:has(.status.text-danger):not(:has([ref="restoreFile"])) .cf-upload-ui--error-head,
.cf-upload-live:has(.status.text-danger):not(:has([ref="restoreFile"])) .cf-upload-ui--retry { display:block; }
.cf-upload-live:has(.status.text-danger) .cf-upload-ui--drop,
.cf-upload-live:has(.status.text-danger) .cf-upload-ui--stepper { display:none !important; }
.cf-upload-overlay:has(.status.text-danger) {
  position:static; border:none; background:transparent; padding:0;
}
/* la ligne du fichier refuse : ni carte verte, ni icone PDF ; le nom et le
   motif du refus sous le bandeau d'erreur */
.cf-upload-overlay .list-group-item:has(.status.text-danger):not(:has([ref="restoreFile"])) {
  display:block; background:none; border:none; padding:0; margin:4px 0 0; text-align:center;
}
.cf-upload-overlay .list-group-item:has(.status.text-danger):not(:has([ref="restoreFile"])) .row { display:block; }
.cf-upload-overlay .list-group-item:has(.status.text-danger):not(:has([ref="restoreFile"])) [class*="col-md-"] { display:none !important; }
.cf-upload-overlay .list-group-item:has(.status.text-danger):not(:has([ref="restoreFile"])) .col-md-7,
.cf-upload-overlay .list-group-item:has(.status.text-danger):not(:has([ref="restoreFile"])) .col-md-9 { display:block !important; width:auto; }
.cf-upload-overlay .list-group-item:has(.status.text-danger) .col-md-7 > div:first-child,
.cf-upload-overlay .list-group-item:has(.status.text-danger) .col-md-9 > div:first-child {
  font-size:13px; font-weight:600; color:#7F1D1D;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.cf-upload-overlay .list-group-item:has(.status.text-danger) .status.text-danger {
  font-size:12px; font-weight:500; color:#B91C1C; margin-top:2px;
  white-space:normal; overflow:visible;
}
/* le bouton « Reessayer » dessine par l'error-head reste inerte : c'est la zone
   de depot native, toujours rendue en 5.3.0, qui capte le clic sur toute la
   carte (regle .cf-upload-overlay .fileSelector plus haut). */
.cf-upload-live:has(.status.text-danger):not(:has([ref="restoreFile"])) .formio-errors { display:none !important; }

/* ---- Etat erreur de VALIDATION (distinct de l echec de televersement) -----
   MESURE : sur `required` non satisfait, Form.io 5.3.0 pose
     formio-error-wrapper has-message
   sur l element racine du composant, celui-la meme qui porte
   cf-upload-overlay. formio.full.min.css le peint alors
   (background-color:#f8d7da; padding:1em) : l overlay, absolu et z-index 2,
   cesse d etre transparent et OCCULTE la zone de depot restee dessous.
   Ni .alert-danger ni .status.text-danger ne sont emis dans ce cas : les
   deux blocs ci-dessus ne se declenchent pas. C est ce trou de couverture,
   et non un selecteur casse, que comblent les trois regles suivantes.
   La zone de depot n a jamais ete retiree du DOM : il ne faut donc RIEN
   reafficher, seulement rendre l overlay a nouveau transparent. */

/* 1. Rendre a l overlay sa transparence et sa geometrie.
      Specificite (0,3,0) contre (0,1,0) du vendor : pas de !important. */
.cf-upload-live .cf-upload-overlay.formio-error-wrapper {
  background-color: transparent;
  border-color: transparent;
  padding: 0;
}

/* 2. C est la zone de depot elle-meme — toujours visible, toujours cliquable —
      qui porte desormais l etat d erreur, avec la palette deja definie par le
      bloc pour .cf-upload--error. Le padding bas reserve la place du message. */
.cf-upload-live:has(.cf-upload-overlay.formio-error-wrapper) .cf-upload--drop {
  background: #FEF2F2;
  border-color: #F87171;
  padding-bottom: 46px;
}

/* 3. Le message natif reste lisible, ancre au bas de la zone, et ne capte
      aucun clic : le clic doit continuer d atteindre .fileSelector
      (position:absolute; inset:0) qui ouvre le selecteur de fichier.
      `width:auto` neutralise le `width:100%` que bootstrap.min.css pose sur
      .invalid-feedback, sans quoi la boite deborde de 15px a droite. */
.cf-upload-live .cf-upload-overlay.formio-error-wrapper .formio-errors {
  position: absolute;
  left: 15px;
  right: 15px;
  bottom: 13px;
  width: auto;
  margin: 0;
  padding: 0;
  text-align: center;
  pointer-events: none;
}
.cf-upload-live .cf-upload-overlay.formio-error-wrapper .formio-errors .error {
  font-size: 12.5px;
  font-weight: 600;
  color: #B91C1C;
}

/* ---- I4 : le bouton « Reessayer » EST la commande native du moteur -------
   MESURE (Form.io 5.3.0) : un televersement refuse laisse le fichier dans
   `filesToSync.filesToUpload` avec status "error". Le SEUL organe qui vide
   cette file est
     li.list-group-item / div.row / div.col-md-1 / i[ref="fileToSyncRemove"]
   dont le gestionnaire natif est
     filesToSync.filesToUpload.splice(t, 1); this.redraw();
   c est-a-dire exactement « annuler la tentative et revenir a la zone de
   depot ». Le bloc le masquait avec la regle generique
     .list-group-item:has(.status.text-danger) [class*="col-md-"] { display:none }
   et dessinait a la place un bouton inerte (pointer-events:none).
   Consequence mesuree AVANT correctif : le clic tombait sur la zone de depot
   native restee dessous, un SECOND televersement partait, et l on obtenait
   l en-tete d echec ET la carte de succes cote a cote, plus le message
   « Un fichier portant ce nom est deja en cours d envoi ».
   Correctif : la zone de depot native est retiree pendant l etat d echec, et
   la commande native est etendue a toute la carte, transparente, par-dessus
   le bouton dessine — meme montage que la branche .alert-danger ci-dessus. */
.cf-upload-overlay:has(.status.text-danger):not(:has([ref="restoreFile"])) .fileSelector { display:none !important; }
/* MESURE : bootstrap pose `position:relative` sur li.list-group-item ; sans
   cette neutralisation, le `inset:0` ci-dessous se resout sur la seule ligne
   du fichier (876x40) au lieu de toute la carte, et le bouton dessine plus
   bas reste hors de la zone cliquable. */
.cf-upload-overlay .list-group-item:has(.status.text-danger),
.cf-upload-overlay .list-group-item:has(.status.text-danger) .row,
.cf-upload-overlay .list-group-item:has(.status.text-danger) [class*="col-md-"] { position:static !important; }
.cf-upload-overlay .list-group-item:has(.status.text-danger) [class*="col-md-"]:has([ref="fileToSyncRemove"]) {
  display:block !important; width:auto; padding:0; margin:0;
}
.cf-upload-overlay .list-group-item:has(.status.text-danger) [ref="fileToSyncRemove"] {
  position:absolute; inset:0; width:auto; height:auto; margin:0; padding:0;
  opacity:0; cursor:pointer; display:block; font-size:0; background:none; z-index:5;
}

/* ---- Etat echec de RETRAIT (distinct de l echec de televersement) --------
   MESURE (Form.io 5.3.0, corbeille cliquee puis DELETE refuse par le serveur) :
   le moteur a DEJA retire le fichier de la soumission (dataValue vide), garde
   une ligne dans filesToSync.filesToDelete rendue en
     li.list-group-item / div.row / div.col-md-N / div.status.text-danger
   et n emet PAS ref="fileToSyncRemove" mais ref="restoreFile", dont le
   gestionnaire natif remet le fichier dans la soumission puis redessine
   (mesure : dataValue repasse de 0 a 1).
   Le bloc ne distinguait pas les deux echecs : la mise en page de l echec de
   TELEVERSEMENT s appliquait, or elle masque la zone de depot ET toute la
   colonne d actions. La commande de remise se retrouvait donc dans un parent
   display:none — hors souris, hors tabulation, hors arbre d accessibilite —
   et le bouton « Reessayer » dessine ne commandait rien, puisqu il n existe
   aucun fichier a re-televerser. L usager etait enferme sur l etape.
   Ici la zone de depot NATIVE reste affichee et cliquable (premier chemin de
   sortie : deposer un autre fichier) et la commande de remise redevient un
   bouton « Remettre » visible (second chemin), comme dans le bloc voisin
   upload-multi-production. Aucun en-tete « Importation echouee » n est
   montre : ce serait un diagnostic contraire a la cause reelle. */
.cf-upload-live:has([ref="restoreFile"]) { min-height:0; }
.cf-upload-overlay:has([ref="restoreFile"]) { position:static; border:none; background:transparent; }

/* la ligne du retrait echoue : carte rose compacte, posee AU-DESSUS de la
   zone de depot ; meme gabarit que la carte verte de l etat termine. */
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) {
  display:flex; align-items:center; gap:14px; margin:0 0 12px;
  border:1px solid #F3B4C0; border-radius:12px; padding:12px 14px 12px 62px; text-align:left;
  background:#FEF2F2 url("data:image/svg+xml,%3Csvg%20width%3D%2236%22%20height%3D%2236%22%20viewBox%3D%220%200%2036%2036%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8%202h13l7%207v25a2%202%200%200%201-2%202H8a2%202%200%200%201-2-2V4a2%202%200%200%201%202-2z%22%20fill%3D%22%23FFFFFF%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M21%202v7h7%22%20fill%3D%22none%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M18%2015v7M18%2026v.5%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E") 14px center / 30px no-repeat;
}
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) .row {
  flex:1 1 auto; display:flex; align-items:center; gap:12px; margin:0; min-width:0;
}
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) [class*="col-md-"] {
  width:auto; max-width:none; flex:0 0 auto; padding:0;
}
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) .col-md-7,
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) .col-md-9 { flex:1 1 auto; min-width:0; }
.cf-upload-overlay .list-group-item:has([ref="restoreFile"]) .col-md-2 {
  color:#6B7280; font-size:13px; white-space:nowrap;
}

/* Commande native de remise, restylee en bouton nomme. Le noeud est un `i`
   sans nom accessible : le libelle est pose par `after`. UN SEUL deux-points,
   la forme double ne survit pas au sanitizer Form.io. Le glyphe de la police
   Bootstrap Icons est neutralise pour eviter le doublon avec l image de fond. */
.cf-upload-overlay .list-group-item [ref="restoreFile"] {
  display:inline-flex; align-items:center; justify-content:center;
  box-sizing:border-box; min-width:34px; height:34px; padding:0 10px 0 30px;
  border:1px solid #E5E7EB; border-radius:8px; background-color:#fff;
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%23374151%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.8%208a5.2%205.2%200%201%200%201.6-3.7%22%2F%3E%3Cpath%20d%3D%22M2.4%202.6v3.2h3.2%22%2F%3E%3C%2Fsvg%3E");
  background-repeat:no-repeat; background-position:9px center; background-size:16px;
  font-family:inherit; font-size:12.5px; font-weight:600; font-style:normal;
  line-height:1; color:#000; cursor:pointer; text-decoration:none; opacity:1;
  transition:background-color .15s, border-color .15s;
}
.cf-upload-overlay .list-group-item [ref="restoreFile"]:before { content:none; }
.cf-upload-overlay .list-group-item [ref="restoreFile"]:after { content:"Remettre"; }
.cf-upload-overlay .list-group-item [ref="restoreFile"]:hover {
  background-color:#F9FAFB; border-color:#D1D5DB;
}
.cf-upload-overlay .list-group-item [ref="restoreFile"]:focus {
  outline:2px solid #16a34a; outline-offset:2px;
}
html[lang="en"] .cf-upload-overlay .list-group-item [ref="restoreFile"]:after { content:"Restore"; }

/* Zone de depot NATIVE rendue visible : dans cet etat l overlay est en flux
   normal, la zone dessinee en HTML (.cf-upload-ui--drop) reste masquee et
   c est le noeud .fileSelector du moteur qui porte l habillage — meme montage
   que le bloc upload-multi-production, deja verifie par rendu. */
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector {
  position:static; inset:auto; opacity:1; box-sizing:border-box; margin:0;
  display:flex; flex-direction:column; align-items:center; justify-content:flex-start;
  gap:2px; min-height:196px;
  border:1.5px dashed #D1D5DB; border-radius:12px; padding:76px 16px 22px;
  text-align:center; font-size:14px; font-weight:600; color:#111827;
  background:#fff url("data:image/svg+xml,%3Csvg%20width%3D%2228%22%20height%3D%2228%22%20viewBox%3D%220%200%2028%2028%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M8.16667%203.49984C6.878%203.49984%205.83333%204.54451%205.83333%205.83317V22.1665C5.83333%2023.4552%206.878%2024.4998%208.16667%2024.4998H10.5C11.1443%2024.4998%2011.6667%2025.0222%2011.6667%2025.6665C11.6667%2026.3108%2011.1443%2026.8332%2010.5%2026.8332H8.16667C5.58934%2026.8332%203.5%2024.7438%203.5%2022.1665V5.83317C3.5%203.25584%205.58934%201.1665%208.16667%201.1665H15.8607C17.2457%201.1665%2018.5591%201.78168%2019.4457%202.84564L23.4184%207.61282C24.1173%208.45149%2024.5%209.50864%2024.5%2010.6003V22.1665C24.5%2024.7438%2022.4107%2026.8332%2019.8333%2026.8332H17.5C16.8557%2026.8332%2016.3333%2026.3108%2016.3333%2025.6665C16.3333%2025.0222%2016.8557%2024.4998%2017.5%2024.4998H19.8333C21.122%2024.4998%2022.1667%2023.4552%2022.1667%2022.1665V11.6665H19.8333C17.256%2011.6665%2015.1667%209.57717%2015.1667%206.99984V3.49984H8.16667ZM21.6259%209.10658C21.6862%209.17901%2021.7418%209.2547%2021.7926%209.33317H19.8333C18.5447%209.33317%2017.5%208.2885%2017.5%206.99984V4.1727C17.5535%204.2255%2017.6046%204.28111%2017.6532%204.33941L21.6259%209.10658Z%22%20fill%3D%22%23475367%22%2F%3E%20%3Cpath%20d%3D%22M14.7751%2016.6279C14.3331%2016.2349%2013.6669%2016.2349%2013.2249%2016.6279L11.4749%2018.1834C10.9933%2018.6115%2010.95%2019.3489%2011.378%2019.8305C11.7525%2020.2518%2012.3638%2020.3377%2012.8333%2020.066V25.6665C12.8333%2026.3108%2013.3557%2026.8332%2014%2026.8332C14.6443%2026.8332%2015.1667%2026.3108%2015.1667%2025.6665V20.066C15.6363%2020.3377%2016.2475%2020.2518%2016.622%2019.8305C17.05%2019.3489%2017.0067%2018.6115%2016.5251%2018.1834L14.7751%2016.6279Z%22%20fill%3D%22%23475367%22%2F%3E%20%3C%2Fsvg%3E") center 30px / 28px no-repeat;
}
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector:hover {
  border-color:#43A842; background-color:#F0FDF4;
}
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector .bi,
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector .fa { display:none; }
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector .browse {
  position:static; inset:auto; width:auto; height:auto; opacity:1; display:inline-block;
  margin-top:12px; padding:10px 22px; background:#16a34a; color:#fff;
  border-radius:8px; font-size:14px; font-weight:600; text-decoration:none; cursor:pointer;
}
.cf-upload-live .cf-upload-overlay:has([ref="restoreFile"]) .fileSelector .browse:hover {
  background:#15803D; color:#fff; text-decoration:none;
}

/* Le message natif « ... est obligatoire » reste utile ici (la soumission ne
   contient plus de fichier), mais il doit revenir en flux normal : la regle de
   l etat d erreur de validation plus haut le pose en absolu, ce qui, l overlay
   n etant plus positionne, le resoudrait sur toute la hauteur du composant. */
.cf-upload-live:has([ref="restoreFile"]) .cf-upload-overlay .formio-errors,
.cf-upload-live:has([ref="restoreFile"]) .cf-upload-overlay.formio-error-wrapper .formio-errors {
  position:static; left:auto; right:auto; bottom:auto; width:auto;
  margin:8px 0 0; padding:0; text-align:center; pointer-events:auto; display:block;
}

/* ============================================================
   BLOC : stepper-traitement
   Sous-bloc reutilisable : stepper 3 etapes anime, pilote par l'attribut
   data-active pose par du JS externe. Aucune donnee, aucun vendor.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Stepper de traitement (3 etapes) : cf. on.png ---- */
.cf-upload-ui--stepper { display:none; }
.cf-stepper { position:relative; display:flex; align-items:flex-start; padding:30px 14px 24px; width:100%; box-sizing:border-box; }
.cf-step { flex:1; display:flex; flex-direction:column; align-items:center; position:relative; }
.cf-step__dot {
  width:34px; height:34px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center;
  background:#fff; border:1.5px solid #E5E7EB; color:#9CA3AF; font-weight:600; font-size:14px;
  position:relative; z-index:1; transition:background-color .25s, border-color .25s, box-shadow .25s, color .25s, transform .25s;
}
.cf-step__label { margin-top:10px; font-size:13px; color:#9CA3AF; text-align:center; line-height:1.3; transition:color .25s; }
/* ligne : gris de bord a bord, rempli en vert avec gradient anime */
.cf-stepper__track, .cf-stepper__fill { position:absolute; top:45.5px; height:3px; z-index:0; border-radius:3px; }
.cf-stepper__track { left:6%; right:5%; background:#E5E7EB; }
.cf-stepper__fill {
  left:6%; width:0;
  background:linear-gradient(90deg, #006A4F, #34d399, #006A4F);
  background-size:200% 100%;
  animation:cf-fill-shine 2s linear infinite;
  transition:width .5s cubic-bezier(0.4, 0, 0.2, 1);
}
.cf-stepper[data-active="1"] .cf-stepper__fill { width:11%; }
.cf-stepper[data-active="2"] .cf-stepper__fill { width:44%; }
.cf-stepper[data-active="3"] .cf-stepper__fill { width:78%; }
/* etapes atteintes (faites + courante) : pastille verte pleine + label vert */
.cf-stepper[data-active="1"] .cf-step:nth-child(-n+1) .cf-step__dot,
.cf-stepper[data-active="2"] .cf-step:nth-child(-n+2) .cf-step__dot,
.cf-stepper[data-active="3"] .cf-step:nth-child(-n+3) .cf-step__dot { background:#006A4F; border-color:#006A4F; color:#fff; }
.cf-stepper[data-active="1"] .cf-step:nth-child(-n+1) .cf-step__label,
.cf-stepper[data-active="2"] .cf-step:nth-child(-n+2) .cf-step__label,
.cf-stepper[data-active="3"] .cf-step:nth-child(-n+3) .cf-step__label { color:#006A4F; font-weight:600; }
/* etape COURANTE : halo vert pulsant + pop scale + arc rotatif */
.cf-stepper[data-active="1"] .cf-step:nth-child(1) .cf-step__dot,
.cf-stepper[data-active="2"] .cf-step:nth-child(2) .cf-step__dot,
.cf-stepper[data-active="3"] .cf-step:nth-child(3) .cf-step__dot {
  animation: cf-step-pop .3s ease-out, cf-step-pulse 1.8s infinite ease-in-out .3s;
}
.cf-stepper[data-active="1"] .cf-step:nth-child(1) .cf-step__dot:before,
.cf-stepper[data-active="2"] .cf-step:nth-child(2) .cf-step__dot:before,
.cf-stepper[data-active="3"] .cf-step:nth-child(3) .cf-step__dot:before {
  content: "";
  position: absolute;
  inset: -3.5px;
  border: 2px solid transparent;
  border-top-color: #004D36;
  border-radius: 50%;
  animation: cf-spin 1.2s linear infinite;
  box-sizing: border-box;
}

@keyframes cf-fill-shine {
  0% { background-position:200% 0; }
  100% { background-position:0 0; }
}
@keyframes cf-step-pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.15); }
  100% { transform: scale(1); }
}
@keyframes cf-step-pulse {
  0% { box-shadow:0 0 0 0 rgba(0,106,79,.4); }
  70% { box-shadow:0 0 0 8px rgba(0,106,79,0); }
  100% { box-shadow:0 0 0 0 rgba(0,106,79,0); }
}
@keyframes cf-spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* ============================================================
   BLOC : upload-multi
   Televersement multiple rendu en grille de vignettes carrees avec croix
   de suppression.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Multi-upload (cf-multi) : grille de vignettes + croix de suppression ---- */
.cf-multi { margin-bottom:8px; }
.cf-multi > label.col-form-label { display:none; }
.cf-multi .form-text.text-muted { display:none; }
.cf-multi.formio-component-file.required:after,
.cf-multi .field-required:after { display:none; }

/* Liste des vignettes -> grille */
.cf-multi > div:has([ref="fileImage"]) {
  display:flex; flex-wrap:wrap; gap:14px; margin:0 0 14px;
}
.cf-multi div:has(> span > [ref="fileImage"]) { margin:0; padding:0; }

/* Carte-vignette */
.cf-multi span:has(> [ref="fileImage"]) {
  position:relative; display:inline-flex; width:104px; height:104px;
  border:1px solid #E5E7EB; border-radius:10px; overflow:hidden; background:#F3F4F6;
}
.cf-multi [ref="fileImage"] {
  width:104px !important; height:104px; object-fit:cover; display:block; margin:0; cursor:default;
}

/* Croix de suppression, coin haut-droit de chaque vignette */
.cf-multi [ref="removeLink"] {
  position:absolute; top:5px; right:5px; width:22px; height:22px; z-index:2;
  border-radius:50%; cursor:pointer; font-size:0; opacity:1; border:none; padding:0;
  background:#fff url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2712%27%20height%3D%2712%27%20viewBox%3D%270%200%2016%2016%27%3E%3Cpath%20fill%3D%27none%27%20stroke%3D%27%23D11135%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20d%3D%27M3%203l10%2010M13%203L3%2013%27%2F%3E%3C%2Fsvg%3E") center/12px no-repeat;
  box-shadow:0 1px 4px rgba(0,0,0,.28);
}
.cf-multi [ref="removeLink"]:hover { background-color:#FEE2E2; }

/* Zone de depot charte */
.cf-multi .fileSelector {
  border:1.5px dashed #D1D5DB; border-radius:12px; padding:18px 16px;
  text-align:center; background:#fff; color:#6B7280; font-size:13px; position:relative;
}
.cf-multi .fileSelector:hover { border-color:#43A842; background:#F0FDF4; }
.cf-multi .fileSelector .fa,
.cf-multi .fileSelector .bi { display:none; }
.cf-multi .fileSelector a.browse,
.cf-multi .fileSelector .browse { color:#16a34a; font-weight:600; text-decoration:none; cursor:pointer; }

/* ============================================================
   BLOC : upload-multi-production
   Televersement MULTIPLE en configuration de production GNDAD
   (multiple:true, uploadOnly:true, image:false, storage:url).

   Variante « tableau » du bloc upload-multi, qui rend une grille de
   vignettes et n'accepte que des images (image:true). Les deux modes ne
   peuvent pas coexister sur le meme composant : ce bloc-ci est celui qui
   suit site/docs/03-formulaire/06b-upload-multiple-production.md, donc
   celui a utiliser des qu'un PDF doit etre accepte.

   Habillage 100% CSS du DOM natif Form.io 5.3.0, mesure :
     ul.list-group / li.list-group-item.list-group-header    (entete)
     ul.list-group / li.list-group-item / div.row
                     div.col-md-9 (nom) div.col-md-2 (taille)
                     div.col-md-1 / i.bi.bi-trash[ref=removeLink]
     div.fileSelector[ref=fileDrop] / i.bi.bi-cloud-upload
                     + texte i18n + a.browse + div.loader-wrapper
     div.form-text.text-muted (description)
   Aucun noeud n'est ajoute ni deplace : le libelle visible est porte par
   un htmlelement voisin, exactement comme dans upload-fichier-unique.

   Fichier autonome. Regenere cf.css via :
     node tooling/form-compiler/scripts/build-css.mjs
   NE JAMAIS editer cf.css directement : il est genere depuis ce fichier.
   Aucun caractere "inferieur a" ne doit apparaitre ici, meme en
   commentaire : le sanitizer Form.io supprimerait tout le bloc style.
   ============================================================ */
/* ---- Multi-upload production (cf-multi-prod) : liste en cartes ---- */
.cf-multi-prod { margin-bottom:8px; position:relative; }
/* le libelle visible est celui du htmlelement voisin multiUploadLabel */
/* Le libelle etant masque, l'asterisque du vendor se retrouve flottant dans le
   coin haut-droit du composant. Il est neutralise ici : c'est le htmlelement de
   libelle qui porte l'asterisque, une seule fois (checklist 13.3). */
.cf-multi-prod.formio-component-file.required:after,
.cf-multi-prod .field-required:after { display:none; }

/* ---- Zone de depot, meme charte que le bloc upload-fichier-unique ---- */
.cf-multi-prod .fileSelector {
  box-sizing:border-box; display:flex; flex-direction:column; align-items:center;
  justify-content:flex-start; gap:2px; min-height:196px;
  border:1.5px dashed #D1D5DB; border-radius:12px; padding:76px 16px 22px;
  text-align:center; font-size:14px; font-weight:600; color:#111827;
  background:#fff url("data:image/svg+xml,%3Csvg%20width%3D%2228%22%20height%3D%2228%22%20viewBox%3D%220%200%2028%2028%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%20%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M8.16667%203.49984C6.878%203.49984%205.83333%204.54451%205.83333%205.83317V22.1665C5.83333%2023.4552%206.878%2024.4998%208.16667%2024.4998H10.5C11.1443%2024.4998%2011.6667%2025.0222%2011.6667%2025.6665C11.6667%2026.3108%2011.1443%2026.8332%2010.5%2026.8332H8.16667C5.58934%2026.8332%203.5%2024.7438%203.5%2022.1665V5.83317C3.5%203.25584%205.58934%201.1665%208.16667%201.1665H15.8607C17.2457%201.1665%2018.5591%201.78168%2019.4457%202.84564L23.4184%207.61282C24.1173%208.45149%2024.5%209.50864%2024.5%2010.6003V22.1665C24.5%2024.7438%2022.4107%2026.8332%2019.8333%2026.8332H17.5C16.8557%2026.8332%2016.3333%2026.3108%2016.3333%2025.6665C16.3333%2025.0222%2016.8557%2024.4998%2017.5%2024.4998H19.8333C21.122%2024.4998%2022.1667%2023.4552%2022.1667%2022.1665V11.6665H19.8333C17.256%2011.6665%2015.1667%209.57717%2015.1667%206.99984V3.49984H8.16667ZM21.6259%209.10658C21.6862%209.17901%2021.7418%209.2547%2021.7926%209.33317H19.8333C18.5447%209.33317%2017.5%208.2885%2017.5%206.99984V4.1727C17.5535%204.2255%2017.6046%204.28111%2017.6532%204.33941L21.6259%209.10658Z%22%20fill%3D%22%23475367%22%2F%3E%20%3Cpath%20d%3D%22M14.7751%2016.6279C14.3331%2016.2349%2013.6669%2016.2349%2013.2249%2016.6279L11.4749%2018.1834C10.9933%2018.6115%2010.95%2019.3489%2011.378%2019.8305C11.7525%2020.2518%2012.3638%2020.3377%2012.8333%2020.066V25.6665C12.8333%2026.3108%2013.3557%2026.8332%2014%2026.8332C14.6443%2026.8332%2015.1667%2026.3108%2015.1667%2025.6665V20.066C15.6363%2020.3377%2016.2475%2020.2518%2016.622%2019.8305C17.05%2019.3489%2017.0067%2018.6115%2016.5251%2018.1834L14.7751%2016.6279Z%22%20fill%3D%22%23475367%22%2F%3E%20%3C%2Fsvg%3E") center 30px / 28px no-repeat;
}
.cf-multi-prod .fileSelector:hover { border-color:#43A842; background-color:#F0FDF4; }
/* l'icone du vendor (jeu Bootstrap Icons) est remplacee par l'image de fond */
.cf-multi-prod .fileSelector .bi,
.cf-multi-prod .fileSelector .fa { display:none; }
.cf-multi-prod .fileSelector a.browse,
.cf-multi-prod .fileSelector .browse {
  display:inline-block; margin-top:12px; padding:10px 22px;
  background:#16a34a; color:#fff; border-radius:8px;
  font-size:14px; font-weight:600; text-decoration:none; cursor:pointer;
}
.cf-multi-prod .fileSelector a.browse:hover { background:#15803D; color:#fff; text-decoration:none; }
.cf-multi-prod .fileSelector .loader-wrapper { display:none !important; }

/* ---- Contrainte annoncee a l'usager (propriete description) ---- */
.cf-multi-prod .form-text.text-muted {
  display:block; font-size:12px; color:#9CA3AF; margin:10px 2px 0; line-height:1.45;
}

/* ---- Liste des fichiers deposes : une carte par fichier ---- */
.cf-multi-prod ul.list-group { margin:0 0 14px; padding:0; border:none; background:transparent; }
.cf-multi-prod .list-group-header { display:none !important; }
/* Icone NEUTRE (feuille de document) : ce champ accepte PDF, JPG et PNG,
   une pastille « PDF » mentirait sur la moitie des depots. */
.cf-multi-prod .list-group-item {
  display:flex; align-items:center; margin:0 0 10px; text-align:left;
  border:1px solid #E5E7EB; border-radius:12px; padding:12px 14px 12px 62px;
  background:#F0FDF4 url("data:image/svg+xml,%3Csvg%20width%3D%2236%22%20height%3D%2236%22%20viewBox%3D%220%200%2036%2036%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8%202h13l7%207v25a2%202%200%200%201-2%202H8a2%202%200%200%201-2-2V4a2%202%200%200%201%202-2z%22%20fill%3D%22%23FFFFFF%22%20stroke%3D%22%2316A34A%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M21%202v7h7%22%20fill%3D%22none%22%20stroke%3D%22%2316A34A%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M11%2020h14M11%2025h14M11%2015h7%22%20stroke%3D%22%2316A34A%22%20stroke-width%3D%221.6%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E") 14px center / 30px no-repeat;
}
.cf-multi-prod .list-group-item .row {
  flex:1 1 auto; display:flex; align-items:center; gap:12px; margin:0; min-width:0;
}
.cf-multi-prod .list-group-item [class*="col-md-"] {
  width:auto; max-width:none; flex:0 0 auto; padding:0;
}
.cf-multi-prod .list-group-item .col-md-9 {
  flex:1 1 auto; min-width:0; font-size:14px; font-weight:600; color:#111827;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* ---- Nom complet d un fichier tronque ----
   L ellipse preserve la largeur mais masque la fin du nom, qui porte souvent
   l information utile (« ...-version-definitive.pdf »). Le moteur n emet AUCUN
   attribut `title` sur la cellule : une infobulle native est donc impossible
   sans toucher au DOM, ce que le catalogue s interdit. Le nom complet est donc
   revele au survol et au focus clavier, en autorisant le retour a la ligne.
   La LARGEUR reste figee — seule la hauteur de la carte augmente — parce que
   `min-width:0` et la largeur de la colonne restent en place. Verifie par
   mesure : largeur identique au survol. */
.cf-multi-prod .list-group-item:hover .col-md-9,
.cf-multi-prod .list-group-item:focus-within .col-md-9 {
  white-space:normal; overflow:visible; text-overflow:clip; word-break:break-all;
}
.cf-multi-prod .list-group-item .col-md-2 {
  color:#6B7280; font-size:13px; font-weight:400; white-space:nowrap;
}
.cf-multi-prod .list-group-item .col-md-1 { margin-left:auto; }
.cf-multi-prod .list-group-item .status { font-size:12px; font-weight:400; white-space:normal; }
.cf-multi-prod .list-group-item .status.text-danger { color:#D11135; }
.cf-multi-prod .list-group-item .file-type { display:none; }
.cf-multi-prod .list-group-item .visually-hidden { display:none; }
/* fichier en erreur : la carte passe au rouge, l'icone verte serait mensongere */
.cf-multi-prod .list-group-item:has(.text-danger) {
  background-color:#FEF2F2; border-color:#F3B4C0;
}

/* ---- Croix native VISIBLE, restylee en bouton « Retirer » ----
   La croix native etait masquee parce que le DELETE natif vise fileInfo.url
   (indexe par nom de fichier) alors que le backend indexe par uid. La cause
   est le TRANSPORT, pas la croix : elle est corrigee par la surcharge
   instance.deleteFile posee en logic/customAction sur filePiecesJointes.
   La croix redevient donc le chemin de retrait PAR FICHIER, seul chemin
   per-ligne possible (Form.io ne rend aucun composant de schema par ligne).
   Noeuds reels mesures sous Form.io 5.3.0 :
     li.list-group-item / div.row / div.col-md-1.justify-center
       / i.bi.bi-trash[ref="removeLink"]                   retrait d un fichier
       / i.bi.bi-arrow-counterclockwise[ref="restoreFile"] remise apres echec
   Le glyphe de la police Bootstrap Icons n est pas garanti charge : il est
   neutralise (font-size:0 herite du reset ci-dessous) et remplace par une
   image de fond, comme l icone de la zone de depot plus haut dans ce fichier. */
.cf-multi-prod .list-group-item .col-md-1 {
  margin-left:auto; display:flex; align-items:center; gap:6px;
}
.cf-multi-prod .list-group-item [ref="removeLink"],
.cf-multi-prod .list-group-item [ref="restoreFile"] {
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  box-sizing:border-box; min-width:34px; height:34px; padding:0 10px 0 30px;
  border:1px solid #E5E7EB; border-radius:8px; background-color:#fff;
  background-repeat:no-repeat; background-position:9px center; background-size:16px;
  font-family:inherit; font-size:12.5px; font-weight:600; font-style:normal;
  line-height:1; color:#B42318; cursor:pointer; text-decoration:none;
  transition:background-color .15s, border-color .15s;
}
/* Le vendor 5.3.0 pose la classe `bi` sur ce noeud : sa police d icones dessine
   deja une corbeille dans le pseudo-element `before`. Elle ferait DOUBLON avec
   l image de fond ci-dessus, et elle n est pas garantie chargee. Elle est donc
   neutralisee ici. UN SEUL deux-points : la forme double ne survit pas au
   sanitizer Form.io. */
.cf-multi-prod .list-group-item [ref="removeLink"]:before,
.cf-multi-prod .list-group-item [ref="restoreFile"]:before { content:none; }
.cf-multi-prod .list-group-item [ref="removeLink"] {
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%23B42318%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.5%204h11M6.5%204V2.8a.8.8%200%200%201%20.8-.8h1.4a.8.8%200%200%201%20.8.8V4M12.2%204l-.5%209a1%201%200%200%201-1%201H5.3a1%201%200%200%201-1-1l-.5-9M6.6%206.8v4.4M9.4%206.8v4.4%22%2F%3E%3C%2Fsvg%3E");
}
/* Le noeud natif est un `i` sans nom accessible : le libelle est pose ici.
   UN SEUL deux-points — la forme double ne survit pas au sanitizer. */
.cf-multi-prod .list-group-item [ref="removeLink"]:after { content:"Retirer"; }
.cf-multi-prod .list-group-item [ref="removeLink"]:hover {
  background-color:#FEF2F2; border-color:#F3B4C0;
}
.cf-multi-prod .list-group-item [ref="removeLink"]:focus,
.cf-multi-prod .list-group-item [ref="restoreFile"]:focus {
  outline:2px solid #16a34a; outline-offset:2px;
}
/* Remise en liste apres un echec de suppression serveur (chemin natif). */
.cf-multi-prod .list-group-item [ref="restoreFile"] {
  color:#000;
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22%23374151%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpath%20d%3D%22M2.8%208a5.2%205.2%200%201%200%201.6-3.7%22%2F%3E%3Cpath%20d%3D%22M2.4%202.6v3.2h3.2%22%2F%3E%3C%2Fsvg%3E");
}
.cf-multi-prod .list-group-item [ref="restoreFile"]:after { content:"Remettre"; }
.cf-multi-prod .list-group-item [ref="restoreFile"]:hover {
  background-color:#F9FAFB; border-color:#D1D5DB;
}

/* Le libelle de ces deux commandes est porte par le CSS, il echappe donc au
   bloc i18n du formulaire. Variante anglaise adossee a l attribut `lang` que
   le portail pose sur la balise racine ; sans cet attribut, rien ne change. */
html[lang="en"] .cf-multi-prod .list-group-item [ref="removeLink"]:after { content:"Remove"; }
html[lang="en"] .cf-multi-prod .list-group-item [ref="restoreFile"]:after { content:"Restore"; }

/* ---- Bouton « Vider la liste » (seul chemin de suppression reel) ---- */
.cf-clear-list, .formio-component-viderPiecesJointes { text-align:right; margin:4px 0 14px; }
.cf-clear-list .btn, .formio-component-viderPiecesJointes .btn {
  padding:5px 14px; font-size:12px; font-weight:600; line-height:1.4; border-radius:6px;
}
.cf-clear-list .field-required, .cf-clear-list .help-block,
.formio-component-viderPiecesJointes .field-required,
.formio-component-viderPiecesJointes .help-block { display:none; }

/* ---- Etat erreur de VALIDATION : harmonisation de la palette ----
   MESURE : sur `required` non satisfait, Form.io 5.3.0 pose
   `formio-error-wrapper has-message` sur l element racine du composant, que
   formio.full.min.css peint en #f8d7da avec padding:1em. Ce composant-ci est
   en flux normal (pas d overlay absolu comme l upload unique) : il n y a donc
   aucune occlusion, seulement un rose Bootstrap hors palette du catalogue.
   Specificite (0,2,0) contre (0,1,0) du vendor : pas de !important. Le padding
   du vendor est conserve, il ne casse aucune geometrie ici. */
.cf-multi-prod.formio-error-wrapper {
  background-color:#FEF2F2; border-color:#F87171; border-radius:12px;
}

/* Carte d un fichier en echec : l icone de document VERTE serait mensongere,
   elle passe au rouge de la meme famille. Le commentaire de la regle
   `:has(.text-danger)` plus haut l annoncait deja sans le faire. */
.cf-multi-prod .list-group-item:has(.text-danger) {
  background-image:url("data:image/svg+xml,%3Csvg%20width%3D%2236%22%20height%3D%2236%22%20viewBox%3D%220%200%2036%2036%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M8%202h13l7%207v25a2%202%200%200%201-2%202H8a2%202%200%200%201-2-2V4a2%202%200%200%201%202-2z%22%20fill%3D%22%23FFFFFF%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M21%202v7h7%22%20fill%3D%22none%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.6%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M18%2015v7M18%2026v.5%22%20stroke%3D%22%23D11135%22%20stroke-width%3D%221.8%22%20stroke-linecap%3D%22round%22%2F%3E%3C%2Fsvg%3E");
}


/* ============================================================
   Ajouts compilateur — neutralise le chrome du conteneur `well`
   utilisé comme wrapper .cf-upload-live (le CSS catalogue scope
   les états d'upload sous ce wrapper).
   ============================================================ */
.cf-upload-live > .card,
.cf-upload-live.formio-component-well > .card.card-body {
  border: none; background: transparent; padding: 0; margin: 0; box-shadow: none;
}
.cf-upload-live .card.card-body { border: none; background: transparent; padding: 0; margin: 0; }
