diff --git a/internal/ajean/ui/index.html b/internal/ajean/ui/index.html
index 41538bf..7f0ab2b 100644
--- a/internal/ajean/ui/index.html
+++ b/internal/ajean/ui/index.html
@@ -816,10 +816,12 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
/* Modale ouverte mais pas encore remplie : on affiche le voile de chargement À LA
PLACE du formulaire et du pied. Estomper le formulaire ne suffisait pas — on
voyait quand même les champs s'écrire un par un derrière le voile. */
-/* Fond TRANSPARENT : le formulaire est déjà rendu invisible par `visibility`,
- un fond opaque ne servirait qu'à masquer aussi le titre de la modale. */
-.modal-load{display:none;position:absolute;inset:0;z-index:2;align-items:center;justify-content:center;
- pointer-events:none}
+/* Voile OPAQUE, posé sous l'en-tête (le titre reste lisible) : `visibility:hidden`
+ sur le formulaire suffirait en théorie, mais un fond plein garantit qu'aucun
+ reste du preset précédent (moteur coché, réglages) ne puisse apparaître une
+ fraction de seconde. --mh = hauteur mesurée de l'en-tête, posée à l'ouverture. */
+.modal-load{display:none;position:absolute;inset:var(--mh,52px) 0 0 0;z-index:2;
+ align-items:center;justify-content:center;background:var(--bg);border-radius:0 0 12px 12px}
.modal-ov.loading .modal-load{display:flex}
/* visibility (et non display) : le formulaire garde sa place, donc la carte a
déjà sa taille finale — la révéler ne provoque plus d'à-coup de hauteur. */
@@ -2480,8 +2482,13 @@ async function openItem(kind, key){
rawToggle.style.display = 'none';
rawBody.style.display = '';
}
- document.getElementById('modal').classList.add('loading');
+ const modalEl=document.getElementById('modal');
+ modalEl.classList.add('loading');
showModal('modal');
+ // Le voile s'arrête au bas de l'en-tête : on lui passe sa hauteur réelle. ⚠️ À
+ // mesurer APRÈS l'affichage — un élément en display:none mesure 0.
+ const mHead=modalEl.querySelector('.modal-head');
+ if(mHead) modalEl.querySelector('.modal-card').style.setProperty('--mh', mHead.offsetHeight+'px');
// ⚠️ APRÈS showModal, jamais avant : le corps de la modale est un conteneur
// défilant RÉUTILISÉ d'une ouverture à l'autre, et écrire scrollTop sur un
// élément en display:none ne fait RIEN (il n'a pas de boîte de rendu) — le
diff --git a/internal/ajean/ui/src/js/07-models.js b/internal/ajean/ui/src/js/07-models.js
index bfaa09a..17de80a 100644
--- a/internal/ajean/ui/src/js/07-models.js
+++ b/internal/ajean/ui/src/js/07-models.js
@@ -102,8 +102,13 @@ async function openItem(kind, key){
rawToggle.style.display = 'none';
rawBody.style.display = '';
}
- document.getElementById('modal').classList.add('loading');
+ const modalEl=document.getElementById('modal');
+ modalEl.classList.add('loading');
showModal('modal');
+ // Le voile s'arrête au bas de l'en-tête : on lui passe sa hauteur réelle. ⚠️ À
+ // mesurer APRÈS l'affichage — un élément en display:none mesure 0.
+ const mHead=modalEl.querySelector('.modal-head');
+ if(mHead) modalEl.querySelector('.modal-card').style.setProperty('--mh', mHead.offsetHeight+'px');
// ⚠️ APRÈS showModal, jamais avant : le corps de la modale est un conteneur
// défilant RÉUTILISÉ d'une ouverture à l'autre, et écrire scrollTop sur un
// élément en display:none ne fait RIEN (il n'a pas de boîte de rendu) — le
diff --git a/internal/ajean/ui/src/styles.css b/internal/ajean/ui/src/styles.css
index 3e4a24c..89dff4a 100644
--- a/internal/ajean/ui/src/styles.css
+++ b/internal/ajean/ui/src/styles.css
@@ -790,10 +790,12 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
/* Modale ouverte mais pas encore remplie : on affiche le voile de chargement À LA
PLACE du formulaire et du pied. Estomper le formulaire ne suffisait pas — on
voyait quand même les champs s'écrire un par un derrière le voile. */
-/* Fond TRANSPARENT : le formulaire est déjà rendu invisible par `visibility`,
- un fond opaque ne servirait qu'à masquer aussi le titre de la modale. */
-.modal-load{display:none;position:absolute;inset:0;z-index:2;align-items:center;justify-content:center;
- pointer-events:none}
+/* Voile OPAQUE, posé sous l'en-tête (le titre reste lisible) : `visibility:hidden`
+ sur le formulaire suffirait en théorie, mais un fond plein garantit qu'aucun
+ reste du preset précédent (moteur coché, réglages) ne puisse apparaître une
+ fraction de seconde. --mh = hauteur mesurée de l'en-tête, posée à l'ouverture. */
+.modal-load{display:none;position:absolute;inset:var(--mh,52px) 0 0 0;z-index:2;
+ align-items:center;justify-content:center;background:var(--bg);border-radius:0 0 12px 12px}
.modal-ov.loading .modal-load{display:flex}
/* visibility (et non display) : le formulaire garde sa place, donc la carte a
déjà sa taille finale — la révéler ne provoque plus d'à-coup de hauteur. */