/**
 * Modales et panneaux du builder.
 *
 * Écrit à plat, sans imbrication `& {}` : cette feuille part sur le front, où
 * d'anciens Safari ont déjà coûté une barre de header à ce projet.
 */

.atb-modal {
    --atb-modal-bg: #fff;
    --atb-modal-color: var(--atb-secondary, #363c42);
    --atb-modal-backdrop: rgba(25, 39, 67, .45);
    --atb-modal-radius: 6px;
    --atb-modal-shadow: 0 24px 60px rgba(25, 39, 67, .22);
    --atb-modal-padding: 1.25rem;
    --atb-modal-width: 34rem;
    --atb-modal-drawer-width: 22rem;

    /* Bornes du panneau latéral : toute la hauteur par défaut. Le panneau
       glissant, lui, se cale sous l'en-tête (voir plus bas). */
    --atb-modal-top: 0px;
    --atb-modal-bottom: 0px;
}

/* ── Peau contre structure ─────────────────────────────────────────────────
   Tout ce qui est APPARENCE (fond, couleur, ombre, rayon, largeur, padding,
   bordure) est écrit dans `:where()`, spécificité zéro : une classe utilitaire
   posée sur le bloc (`atb-bg-primary`, `atb-text-light`, `atb-rounded-xl`,
   `atb-w-full`…) l'emporte alors toujours, quel que soit l'ordre des feuilles.
   Sans ça, cette feuille — imprimée en pied de page — battait `utilities.css`
   à spécificité égale, et un `atb-bg-primary` laissait le panneau blanc.

   La STRUCTURE (position, bornes, marges, transform, transition) garde sa
   spécificité : elle doit battre `.atb-column > * { margin-bottom }` du thème,
   qui plaquait la modale centrée en bas de l'écran. Les styles UA de `dialog`
   et de `[popover]` sont d'une origine inférieure : `:where()` suffit à les
   écarter. */
/* Qualifiée par l'élément — `:is(div, dialog)` vaut 0-0-1 — parce que le reset
   du thème (`div, span, … { background: transparent }`) bat un `:where()` nu :
   le panneau glissant, un `<div>`, ressortait transparent. À 0-0-1 la peau
   égale le reset et gagne par ordre (cette feuille s'imprime après), tout en
   restant sous la moindre classe utilitaire (0-1-0). */
:is(div, dialog):where(.atb-modal) {
    padding: 0;
    border: 0;
    overflow: hidden;   /* le corps défile, la boîte clippe (coins arrondis) */
    color: var(--atb-modal-color);
    background: var(--atb-modal-bg);
    box-shadow: var(--atb-modal-shadow);
}

/* Les tailles aussi : `max-width` de `dialog` et `height: fit-content` de
   `[popover]` sont des styles UA à écarter, pas de la structure — un
   `atb-max-w-full` ou un `atb-h-full` doit pouvoir les reprendre. */
:is(div, dialog):where(.atb-modal--classic) {
    width: min(var(--atb-modal-width), calc(100vw - 2rem));
    max-width: none;
    max-height: min(85vh, calc(100vh - 2rem));
    max-height: min(85dvh, calc(100dvh - 2rem));
    border-radius: var(--atb-modal-radius);
}

:is(div, dialog):where(.atb-modal--borne) {
    height: auto;
    max-height: none;
    max-width: none;
}

:is(div, dialog):where(.atb-modal--left, .atb-modal--right) {
    width: min(var(--atb-modal-drawer-width), 88vw);
}

/* Feuille haute ou basse : pleine largeur, haute comme son contenu, jamais
   plus que l'espace entre les bornes — même cascade vh → dvh → --atb-vvh que
   le corps. */
:is(div, dialog):where(.atb-modal--top, .atb-modal--bottom) {
    width: auto;
    max-height: calc(100vh - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
    max-height: calc(100dvh - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
    max-height: calc(var(--atb-vvh, 100dvh) - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
}

/* Hauteur d'une feuille par paliers (champ « Hauteur de la feuille ») : une
   fraction de l'espace disponible entre les bornes, même cascade d'unités. */
.atb-modal--sheet-h-half  { --atb-modal-sheet-ratio: .5; }
.atb-modal--sheet-h-three { --atb-modal-sheet-ratio: .75; }
.atb-modal--sheet-h-full  { --atb-modal-sheet-ratio: 1; }

:is(div, dialog):where(.atb-modal--sheet-h-half, .atb-modal--sheet-h-three, .atb-modal--sheet-h-full) {
    height: calc((100vh - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px)) * var(--atb-modal-sheet-ratio));
    height: calc((100dvh - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px)) * var(--atb-modal-sheet-ratio));
    height: calc((var(--atb-vvh, 100dvh) - var(--atb-modal-top) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px)) * var(--atb-modal-sheet-ratio));
}

/* Détaché des bords (champ « Détaché des bords », une option) : le panneau
   flotte à 1rem, coins arrondis. Toute la géométrie des côtés passe par
   `--atb-modal-inset`, il n'y a que la variable à poser. */
.atb-modal--detached {
    --atb-modal-inset: 1rem;
}

:is(div, dialog):where(.atb-modal--detached) {
    border-radius: 16px;
}

/* Paliers de la modale centrée (champ « Taille de la modale »), sur le modèle
   de Popup Maker : des fractions de la largeur d'écran, toujours plafonnées
   par la règle unique ci-dessus (`100vw - 2rem`). Une taille personnalisée
   arrive en style inline sur la même variable. « Ajustée » rend au `<dialog>`
   sa largeur naturelle, celle de son contenu. */
.atb-modal--size-tiny   { --atb-modal-width: 30vw; }
.atb-modal--size-small  { --atb-modal-width: 40vw; }
.atb-modal--size-medium { --atb-modal-width: 60vw; }
.atb-modal--size-large  { --atb-modal-width: 80vw; }
.atb-modal--size-xlarge { --atb-modal-width: 95vw; }

:is(div, dialog):where(.atb-modal--size-auto) {
    width: fit-content;
    max-width: calc(100vw - 2rem);
}

/* Sur mobile, les petites fractions n'ont plus de sens (30 % de 390px = 117px) :
   elles rejoignent la plus grande. Le défaut (34rem) est déjà plafonné. */
@media (max-width: 768px) {
    .atb-modal--size-tiny,
    .atb-modal--size-small,
    .atb-modal--size-medium,
    .atb-modal--size-large {
        --atb-modal-width: 95vw;
    }
}

/* Paliers de largeur du panneau latéral (champ « Largeur du panneau »). Ils ne
   changent que la variable : la règle de largeur reste unique, et une classe
   utilitaire `atb-w-*` continue de l'emporter. */
.atb-modal--w-sm   { --atb-modal-drawer-width: 18rem; }
.atb-modal--w-lg   { --atb-modal-drawer-width: 28rem; }
.atb-modal--w-xl   { --atb-modal-drawer-width: 36rem; }
.atb-modal--w-half { --atb-modal-drawer-width: 50vw; }

/* Sur mobile, un panneau latéral prend toute la largeur, quel que soit le
   palier : à 18rem sur un écran de 390px il ne resterait qu'une bande de page
   inutilisable derrière, et le contenu du panneau serait à l'étroit. Même
   seuil que le pas `-sm` des utilitaires. */
@media (max-width: 768px) {
    :is(div, dialog):where(.atb-modal--left, .atb-modal--right) {
        width: calc(100% - 2 * var(--atb-modal-inset, 0px));
    }

    /* « Laisser une bande » : 12 % de page visible, un fond à toucher pour
       refermer — sinon la croix serait le seul moyen. */
    :is(div, dialog):where(.atb-modal--mobile-band) {
        width: calc(88vw - 2 * var(--atb-modal-inset, 0px));
    }
}

:is(div, dialog):where(.atb-modal--full) {
    width: auto;
}

/* `::backdrop` hérite des variables de son élément depuis 2023-2024 (Chrome
   122, Safari 17.4, Firefox 120) ; avant, il n'héritait de rien. D'où le
   repli dans `var()`, sans quoi le fond serait transparent sur ces versions. */
.atb-modal::backdrop {
    background: var(--atb-modal-backdrop, rgba(25, 39, 67, .45));
    opacity: 0;
    transition: opacity calc(.28s * var(--atb-modal-speed, 1)) ease;
}

/* Flou de la page derrière (champ « Flou du fond »). */
.atb-modal--blur-sm::backdrop { backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.atb-modal--blur-md::backdrop { backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.atb-modal--blur-lg::backdrop { backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); }

.atb-modal.is-open::backdrop {
    opacity: 1;
}

/* Le fond ne défile plus derrière un panneau ouvert : sans cela, la page glisse
   sous le doigt et l'on perd sa place en refermant. */
.atb-modal-open,
.atb-modal-open body {
    overflow: hidden;
}

/* iOS ignore `overflow: hidden` sur le corps : le JS refuse en plus
   `touchmove` hors du corps défilant du panneau. Pas de `position: fixed` sur
   le body — il remettrait `scrollY` à zéro et décrocherait un en-tête collant
   piloté par le défilement (Divi), emportant la borne haute avec lui. */
.atb-modal-open {
    overscroll-behavior: none;
}

/* ── Modale centrée ────────────────────────────────────────────────────── */

/* `margin: auto` RÉAFFIRMÉ, et ce n'est pas de la redondance : le thème force un
   `margin-bottom: 0` qui gagnait sur la valeur par défaut du navigateur. Avec
   `inset: 0`, une marge basse à zéro et une marge haute automatique plaquaient la
   modale contre le bas de l'écran. Mesuré : `margin: 607px 16px 0px`. */
.atb-modal--classic {
    margin: auto;
    opacity: 0;
    transform: translateY(12px) scale(.98);
    transition: opacity calc(.24s * var(--atb-modal-speed, 1)) ease, transform calc(.24s * var(--atb-modal-speed, 1)) ease;
}

/* Position à l'écran (champ « Position ») : des marges, rien d'autre — la
   même mécanique que le centrage. `auto` du côté libre, le décalage du côté
   collé. Écrit APRÈS `.atb-modal--classic` pour le battre à spécificité égale. */
.atb-modal--pos-top          { margin: var(--atb-modal-offset, 1rem) auto auto; }
.atb-modal--pos-bottom       { margin: auto auto var(--atb-modal-offset, 1rem); }
.atb-modal--pos-top-left     { margin: var(--atb-modal-offset, 1rem) auto auto var(--atb-modal-offset, 1rem); }
.atb-modal--pos-top-right    { margin: var(--atb-modal-offset, 1rem) var(--atb-modal-offset, 1rem) auto auto; }
.atb-modal--pos-bottom-left  { margin: auto auto var(--atb-modal-offset, 1rem) var(--atb-modal-offset, 1rem); }
.atb-modal--pos-bottom-right { margin: auto var(--atb-modal-offset, 1rem) var(--atb-modal-offset, 1rem) auto; }

/* Sur mobile, un coin n'a plus de sens : la boîte fait déjà toute la largeur
   moins ses marges, et un décalage horizontal la poussait hors de l'écran
   (mesuré : `left: -32px` avec 4rem). On ne garde que l'ancrage vertical,
   centré, avec un décalage plafonné à 1rem. */
@media (max-width: 768px) {
    .atb-modal--pos-top,
    .atb-modal--pos-top-left,
    .atb-modal--pos-top-right {
        margin: min(var(--atb-modal-offset, 1rem), 1rem) auto auto;
    }

    .atb-modal--pos-bottom,
    .atb-modal--pos-bottom-left,
    .atb-modal--pos-bottom-right {
        margin: auto auto min(var(--atb-modal-offset, 1rem), 1rem);
    }
}

/* ── Panneau latéral ───────────────────────────────────────────────────── */

/* `top` ET `bottom` posés : la boîte est bornée par ses deux bords, aucune
   unité de viewport n'est nécessaire pour la PLACER. Commun au panneau modal
   et au panneau glissant. */
.atb-modal--borne {
    position: fixed;
    top: calc(var(--atb-modal-top) + var(--atb-modal-inset, 0px));
    bottom: calc(var(--atb-modal-bottom) + var(--atb-modal-inset, 0px));
    margin: 0;
    transition: transform calc(.28s * var(--atb-modal-speed, 1)) ease;
}

/* ── Panneau glissant : non modal ──────────────────────────────────────────
   Ni fond assombri, ni piège à focus, ni fermeture au clic extérieur : la page
   reste utilisable derrière. C'est le motif du menu burger piloté depuis
   l'en-tête. Rendu par un `popover` en mode `manual`, qui donne le calque
   supérieur sans les contraintes du modal. */
.atb-modal--panel {
    /* Sous l'en-tête par défaut : la variable est mesurée au chargement par
       `atb-modal.js`, la valeur figée de custom.css ne servant que de repli.
       Une borne saisie dans le bloc l'emporte (posée en style inline). */
    --atb-modal-top: var(--atb-header-offset, 0px);
    inset: auto;   /* les bornes ci-dessus reprennent la main */
    top: calc(var(--atb-modal-top) + var(--atb-modal-inset, 0px));
    bottom: calc(var(--atb-modal-bottom) + var(--atb-modal-inset, 0px));
}

.atb-modal--panel::backdrop {
    background: none;
}

/* L'ombre d'une modale (24px de décalage, 60px de flou, 22 %) est faite pour
   un fond assombri ; posée sur une page lisible, sous un panneau borné, elle
   fait une bande sombre. Plus courte et plus légère ici. */
.atb-modal--panel {
    --atb-modal-shadow: 0 8px 24px rgba(25, 39, 67, .12);
}

/* Le bas du panneau glissant est le bas de la VUE, pas `bottom: 0` : la boîte
   reçoit une hauteur explicite, en cascade `vh → dvh → --atb-vvh`, et suit
   donc la barre d'URL et le clavier virtuel d'un mobile (`--atb-vvh` vient de
   `visualViewport`). `bottom: 0` d'un élément fixe dépend du navigateur et de
   son mode de repli (sans API Popover, le panneau n'est plus dans le calque
   supérieur et un ancêtre transformé le rattache à lui). Les feuilles haut/bas
   gardent leur hauteur au contenu ou par palier. */
/* La hauteur se calcule sur la borne haute À L'OUVERTURE (`--atb-modal-top-ref`,
   posée par le JS) : ensuite seule la position suit l'en-tête, et un en-tête
   non collant qui défile EMPORTE le panneau au lieu de le faire grandir. */
.atb-modal--panel.atb-modal--left,
.atb-modal--panel.atb-modal--right,
.atb-modal--panel.atb-modal--full {
    bottom: auto;
    height: calc(100vh - var(--atb-modal-top-ref, var(--atb-modal-top)) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
    height: calc(100dvh - var(--atb-modal-top-ref, var(--atb-modal-top)) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
    height: calc(var(--atb-vvh, 100dvh) - var(--atb-modal-top-ref, var(--atb-modal-top)) - var(--atb-modal-bottom) - 2 * var(--atb-modal-inset, 0px));
}

/* Non modal : la molette ou le doigt sur un panneau qui n'a rien (ou plus
   rien) à faire défiler continue vers la page — c'est un menu déroulant, pas
   une modale. Quand la page est bloquée (sans borne basse), `overflow: hidden`
   sur html tranche de toute façon. */
.atb-modal--panel .atb-modal__body {
    overscroll-behavior: auto;
}

/* Repli sans l'API Popover : le panneau reste visible et utilisable, mais sans
   calque supérieur — il peut se faire recouvrir par une section suivante. */
.atb-modal--panel:not(.is-fallback):not(:popover-open) {
    display: none;
}

.atb-modal--panel.is-fallback {
    display: block;
    z-index: 900;
}

/* Chaque côté pose sa géométrie ET les vecteurs dont les animations d'entrée
   ont besoin : d'où l'on glisse (`--atb-modal-dx/dy`), autour de quoi l'on
   tourne (`--atb-modal-origin`), et les rotations 3D de la bascule et de la
   porte. Les animations, plus bas, ne connaissent que ces variables — une
   règle par animation, pas une par couple animation × côté. */
.atb-modal--left {
    --atb-modal-dx: -100%;
    --atb-modal-dy: 0;
    --atb-modal-origin: left center;
    --atb-modal-flip: rotateY(80deg);
    --atb-modal-swing: rotateY(100deg);
    --atb-modal-clip: inset(-120px 100% -120px -120px);
    --atb-modal-pslide: translateX(calc(-100% - var(--atb-modal-inset, 0px)));
    --atb-modal-pshift: none;
    --atb-modal-pclip: inset(-120px);
    --atb-modal-pflap: rotateY(88deg);
    --atb-modal-corner-l: left top;
    --atb-modal-corner-r: left bottom;
    left: var(--atb-modal-inset, 0px);
    right: auto;
    transform: translateX(calc(-100% - var(--atb-modal-inset, 0px)));
}

.atb-modal--right {
    --atb-modal-dx: 100%;
    --atb-modal-dy: 0;
    --atb-modal-origin: right center;
    --atb-modal-flip: rotateY(-80deg);
    --atb-modal-swing: rotateY(-100deg);
    --atb-modal-clip: inset(-120px -120px -120px 100%);
    --atb-modal-pslide: translateX(calc(100% + var(--atb-modal-inset, 0px)));
    --atb-modal-pshift: none;
    --atb-modal-pclip: inset(-120px);
    --atb-modal-pflap: rotateY(-88deg);
    --atb-modal-corner-l: right top;
    --atb-modal-corner-r: right bottom;
    left: auto;
    right: var(--atb-modal-inset, 0px);
    transform: translateX(calc(100% + var(--atb-modal-inset, 0px)));
}

/* Haut et bas : une seule borne, l'autre libre — la feuille est haute comme
   son contenu. Corps en flex pour que l'en-tête reste comptée dans la limite. */
.atb-modal--top,
.atb-modal--bottom {
    left: var(--atb-modal-inset, 0px);
    right: var(--atb-modal-inset, 0px);
    display: flex;
    flex-direction: column;
}

.atb-modal--top {
    --atb-modal-dx: 0;
    --atb-modal-dy: -100%;
    --atb-modal-origin: center top;
    --atb-modal-flip: rotateX(-80deg);
    --atb-modal-swing: rotateX(-100deg);
    --atb-modal-clip: inset(-120px -120px 100% -120px);
    --atb-modal-pslide: none;
    --atb-modal-pshift: translateY(-16px);
    --atb-modal-pclip: var(--atb-modal-clip);
    --atb-modal-pflap: rotateX(-88deg);
    --atb-modal-corner-l: left top;
    --atb-modal-corner-r: right top;
    bottom: auto;
    transform: translateY(calc(-100% - var(--atb-modal-inset, 0px)));
}

.atb-modal--bottom {
    --atb-modal-dx: 0;
    --atb-modal-dy: 100%;
    --atb-modal-origin: center bottom;
    --atb-modal-flip: rotateX(80deg);
    --atb-modal-swing: rotateX(100deg);
    --atb-modal-clip: inset(100% -120px -120px -120px);
    --atb-modal-pslide: none;
    --atb-modal-pshift: translateY(16px);
    --atb-modal-pclip: var(--atb-modal-clip);
    --atb-modal-pflap: rotateX(88deg);
    --atb-modal-corner-l: left bottom;
    --atb-modal-corner-r: right bottom;
    top: auto;
    transform: translateY(calc(100% + var(--atb-modal-inset, 0px)));
}

.atb-modal--top .atb-modal__body,
.atb-modal--bottom .atb-modal__body {
    max-height: none;
}

.atb-modal--full {
    --atb-modal-dx: 0;
    --atb-modal-dy: -100%;
    --atb-modal-origin: center top;
    --atb-modal-flip: rotateX(-80deg);
    --atb-modal-swing: rotateX(-100deg);
    --atb-modal-clip: inset(-120px -120px 100% -120px);
    --atb-modal-pslide: none;
    --atb-modal-pshift: translateY(-16px);
    --atb-modal-pclip: var(--atb-modal-clip);
    --atb-modal-pflap: rotateX(-88deg);
    --atb-modal-corner-l: left top;
    --atb-modal-corner-r: right top;
    left: var(--atb-modal-inset, 0px);
    right: var(--atb-modal-inset, 0px);
    transform: translateY(-8px);
    opacity: 0;
    transition: opacity calc(.24s * var(--atb-modal-speed, 1)) ease, transform calc(.24s * var(--atb-modal-speed, 1)) ease;
}

.atb-modal--classic {
    --atb-modal-dx: 0;
    --atb-modal-dy: 40px;
    --atb-modal-origin: center;
    --atb-modal-flip: rotateX(-80deg);
    --atb-modal-swing: rotateX(-100deg);
}

/* ── Animations d'entrée (champ « Animation d'entrée ») ─────────────────────
   Sans attribut, chaque rendu garde son mouvement propre ci-dessus. Avec, on
   ne définit que l'ÉTAT FERMÉ : l'état ouvert est toujours `transform: none`,
   et c'est la transition qui fait le trajet — la fermeture est le même chemin
   à l'envers, sans seconde série de règles. Repères : animista.net
   (slide-in, scale-in, flip-in, swing-in). Les rotations 3D portent leur
   `perspective()` dans la fonction même : dans le calque supérieur, un
   `<dialog>` n'a aucun parent à qui la demander. */
.atb-modal[data-atb-anim] {
    transition: opacity calc(.28s * var(--atb-modal-speed, 1)) ease, transform calc(.28s * var(--atb-modal-speed, 1)) ease;
}

.atb-modal[data-atb-anim="slide"] {
    opacity: 1;
    transform: translate(var(--atb-modal-dx), var(--atb-modal-dy));
}

/* Le rebond n'est qu'un glissement à courbe « back » : la boîte dépasse sa
   place puis revient. Pas de keyframes, la fermeture reste symétrique. */
.atb-modal[data-atb-anim="bounce"] {
    opacity: 1;
    transform: translate(var(--atb-modal-dx), var(--atb-modal-dy));
    transition: transform calc(.5s * var(--atb-modal-speed, 1)) cubic-bezier(.34, 1.56, .64, 1);
}

.atb-modal[data-atb-anim="fade"] {
    opacity: 0;
    transform: none;
}

.atb-modal[data-atb-anim="scale"] {
    opacity: 0;
    transform: scale(.6);
    transform-origin: var(--atb-modal-origin);
}

.atb-modal[data-atb-anim="flip"] {
    opacity: 0;
    transform: perspective(1200px) var(--atb-modal-flip);
    transform-origin: center;
    transition-duration: calc(.4s * var(--atb-modal-speed, 1));
}

.atb-modal[data-atb-anim="swing"] {
    opacity: 0;
    transform: perspective(1200px) var(--atb-modal-swing);
    transform-origin: var(--atb-modal-origin);
    transition-duration: calc(.45s * var(--atb-modal-speed, 1));
}

/* ── Animations propres à la modale centrée ────────────────────────────────
   Même principe — l'état fermé seulement — mais des mouvements qui ont un sens
   pour une boîte au milieu de l'écran : zoom avant / arrière, glissement
   depuis un bord de l'écran (`100vw`/`100vh` : d'assez loin pour partir hors
   champ quelle que soit la taille), et les mêmes avec rebond. */
.atb-modal[data-atb-anim="zoom-in"],
.atb-modal[data-atb-anim="bounce-in"] {
    opacity: 0;
    transform: scale(.6);
}

.atb-modal[data-atb-anim="zoom-out"],
.atb-modal[data-atb-anim="bounce-out"] {
    opacity: 0;
    transform: scale(1.3);
}

.atb-modal[data-atb-anim="slide-top"],
.atb-modal[data-atb-anim="bounce-top"] {
    opacity: 1;
    transform: translateY(-100vh);
}

.atb-modal[data-atb-anim="slide-bottom"],
.atb-modal[data-atb-anim="bounce-bottom"] {
    opacity: 1;
    transform: translateY(100vh);
}

.atb-modal[data-atb-anim="slide-left"] {
    opacity: 1;
    transform: translateX(-100vw);
}

.atb-modal[data-atb-anim="slide-right"] {
    opacity: 1;
    transform: translateX(100vw);
}

/* Les rebonds : la courbe « back » fait dépasser la boîte puis la ramène.
   L'opacité garde sa courbe douce pour ne pas clignoter au point de retour. */
.atb-modal[data-atb-anim="bounce-in"],
.atb-modal[data-atb-anim="bounce-out"],
.atb-modal[data-atb-anim="bounce-top"],
.atb-modal[data-atb-anim="bounce-bottom"] {
    transition: opacity calc(.3s * var(--atb-modal-speed, 1)) ease, transform calc(.5s * var(--atb-modal-speed, 1)) cubic-bezier(.34, 1.56, .64, 1);
}

/* Vitesse (champ « Vitesse ») : un multiplicateur sur toutes les durées,
   `calc(.28s * var(--atb-modal-speed))` — le JS lit la durée résultante dans
   le style calculé, il n'a rien à savoir. */
.atb-modal--speed-fast { --atb-modal-speed: .5; }
.atb-modal--speed-slow { --atb-modal-speed: 1.8; }

/* ── Plein écran et comportement mobile ─────────────────────────────────────
   Le palier « Plein écran » (toutes tailles d'écran) et le champ « Sur mobile »
   (sous 768px) partagent la même recette : la boîte prend le viewport, sans
   marge ni rayon, et le corps défile. La feuille basse s'y ajoute : collée en
   bas, coins arrondis en haut, et elle MONTE — quelle que soit l'animation
   choisie pour le grand écran, d'où la règle sur `[data-atb-anim]` aussi,
   écrite avant `.is-open` pour lui laisser le dernier mot. */
.atb-modal--size-full,
.atb-modal--size-full[data-atb-anim] {
    margin: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    border-radius: 0;
}

@media (max-width: 768px) {
    .atb-modal--mobile-full,
    .atb-modal--mobile-full[data-atb-anim] {
        margin: 0;
        width: 100%;
        height: 100%;
        max-width: none;
        max-height: none;
        border-radius: 0;
    }

    .atb-modal--mobile-sheet,
    .atb-modal--mobile-sheet[data-atb-anim] {
        margin: auto 0 0;
        width: 100%;
        max-width: none;
        max-height: 92vh;
        max-height: 92dvh;
        border-radius: var(--atb-modal-sheet-radius, 16px) var(--atb-modal-sheet-radius, 16px) 0 0;
        opacity: 1;
        transform: translateY(100%);
        transition: transform calc(.32s * var(--atb-modal-speed, 1)) ease;
    }
}

/* L'état ouvert, unique, APRÈS toutes les animations : il doit les battre. */
.atb-modal.is-open {
    transform: none;
    opacity: 1;
}

/* ── Animations du panneau glissant ─────────────────────────────────────────
   Le panneau reste DANS ses bornes : un glissement depuis le haut de l'écran
   le ferait passer par-dessus l'en-tête qui le pilote. Ses animations partent
   donc de sa borne, sans en sortir : rideau (`clip-path` dévoilé depuis la
   borne — le `-120px` laisse passer l'ombre entière, à 40px elle était coupée net), déploiement (échelle depuis la
   borne), rabat (charnière 3D sur la borne). Le glissement, lui, est un vrai
   translate sur les côtés gauche/droit — le panneau vient de hors champ — et
   un rideau à peine décalé sur haut/bas/pleine largeur. Chaque côté fournit
   ses valeurs en variables ; les règles ici, à 0-3-0, battent les animations
   génériques du même nom, et l'état ouvert qui suit les bat toutes. */
.atb-modal--panel[data-atb-anim] {
    transition: opacity calc(.3s * var(--atb-modal-speed, 1)) ease,
                transform calc(.3s * var(--atb-modal-speed, 1)) ease,
                clip-path calc(.36s * var(--atb-modal-speed, 1)) ease;
}

/* Glissement : sur les côtés, la boîte vient de hors champ ; sur haut, bas et
   pleine largeur, c'est le CONTENU qui glisse à l'intérieur de la boîte
   dévoilée en rideau — la boîte clippe (`overflow: hidden`), donc rien ne
   dépasse jamais de la borne. Un décalage posé sur la boîte elle-même, même
   de 12px, passait par-dessus l'en-tête le temps de l'entrée. */
.atb-modal--panel[data-atb-anim="slide"] {
    opacity: 1;
    transform: var(--atb-modal-pslide);
    clip-path: var(--atb-modal-pclip);
}

.atb-modal--panel[data-atb-anim="slide"] > * {
    transform: var(--atb-modal-pshift);
    transition: transform calc(.3s * var(--atb-modal-speed, 1)) ease;
}

/* Zooms (animista scale-in-top / scale-in-tl / scale-in-tr) : le panneau
   grandit depuis un point de sa borne — son milieu, ou l'un de ses coins,
   comme s'il sortait du bouton placé là. Le point d'ancrage ne bouge pas,
   donc rien ne sort de la borne. Une courbe « back » légère pour le relief. */
.atb-modal--panel[data-atb-anim="scale"],
.atb-modal--panel[data-atb-anim="scale-tl"],
.atb-modal--panel[data-atb-anim="scale-tr"] {
    opacity: 0;
    transform: scale(.3);
    transition-timing-function: cubic-bezier(.25, 1.2, .5, 1);
    transition-duration: calc(.36s * var(--atb-modal-speed, 1));
}

.atb-modal--panel[data-atb-anim="scale"]    { transform-origin: var(--atb-modal-origin); }
.atb-modal--panel[data-atb-anim="scale-tl"] { transform-origin: var(--atb-modal-corner-l); }
.atb-modal--panel[data-atb-anim="scale-tr"] { transform-origin: var(--atb-modal-corner-r); }

.atb-modal--panel[data-atb-anim="curtain"] {
    opacity: 1;
    transform: none;
    clip-path: var(--atb-modal-clip);
}

/* Rabat : charnière sur la borne, 88° et pas 100° — au-delà de 90° la plaque
   passe derrière l'horizontale et sa projection ressort au-dessus de la borne
   (mesuré : 20px au-dessus de l'en-tête). */
.atb-modal--panel[data-atb-anim="flap"] {
    opacity: 0;
    transform: perspective(1200px) var(--atb-modal-pflap);
    transform-origin: var(--atb-modal-origin);
    transition-duration: calc(.45s * var(--atb-modal-speed, 1));
}

.atb-modal--panel[data-atb-anim="fade"] {
    opacity: 0;
    transform: none;
}

.atb-modal--panel.is-open[data-atb-anim] {
    transform: none;
    opacity: 1;
    clip-path: inset(-120px);
}

.atb-modal--panel.is-open[data-atb-anim] > * {
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .atb-modal,
    .atb-modal[data-atb-anim],
    .atb-modal::backdrop {
        transition: none;
    }
}

/* ── Thèmes (champ « Thème ») ─────────────────────────────────────────────
   Des presets d'apparence, sur le modèle des thèmes de Popup Maker. Chacun ne
   fait que régler les variables de la peau : les classes utilitaires du bloc
   (`atb-bg-*`, `atb-rounded-*`…) continuent de passer par-dessus, et
   « Classique » ne pose rien. */

/* Contenu seul : ni fond, ni ombre, ni marge — la boîte disparaît, il ne reste
   que le contenu (une image, une vidéo, un bloc déjà habillé) et la croix,
   posée dans le coin comme sur une visionneuse. Le titre reste porté par
   `aria-label`, mais n'a plus de barre où s'afficher. */
.atb-modal--theme-content {
    --atb-modal-bg: transparent;
    --atb-modal-shadow: none;
    --atb-modal-padding: 0;
    --atb-modal-radius: 0;
    --atb-modal-color: #fff;
}

/* La croix reste DANS le flux, au-dessus du contenu : une croix posée en
   absolu recouvrait le haut du contenu (un titre, le coin d'une image). La
   barre ne garde que sa hauteur, alignée à droite, et réserve son espace. */
.atb-modal--theme-content .atb-modal__head {
    justify-content: flex-end;
    padding: 0 0 .5rem;
    border: 0;
}

.atb-modal--theme-content .atb-modal__title {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.atb-modal--theme-content .atb-modal__close {
    min-width: 40px;
    min-height: 40px;
    border-radius: 50%;
    color: var(--atb-neutral, #192743);
    background: #fff;
    box-shadow: 0 4px 14px rgba(25, 39, 67, .3);
}

/* Carte : plus ronde, plus aérée, sans filet sous l'en-tête. L'aération se
   paie en largeur utile : sur mobile elle revient à la marge normale. */
.atb-modal--theme-card {
    --atb-modal-radius: 16px;
    --atb-modal-padding: 2rem;
    --atb-modal-shadow: 0 30px 80px rgba(25, 39, 67, .3);
}

@media (max-width: 768px) {
    .atb-modal--theme-card {
        --atb-modal-padding: 1.25rem;
    }
}

.atb-modal--theme-card .atb-modal__head {
    border-bottom: 0;
    padding-bottom: 0;
}

/* Sombre : bleu nuit, texte clair, filet translucide. */
.atb-modal--theme-dark {
    --atb-modal-bg: var(--atb-neutral, #192743);
    --atb-modal-color: #fff;
    --atb-modal-rule: rgba(255, 255, 255, .15);
    --atb-modal-backdrop: rgba(25, 39, 67, .7);
}

/* ── Aperçu du builder ──────────────────────────────────────────────────────
   L'œil de l'éditeur : la boîte à plat, ouverte, dans le flux. Ni calque, ni
   position, ni animation — on voit ce qu'on compose. */
.atb-modal--preview,
.atb-modal--preview[data-atb-anim] {
    position: static;
    display: flex;
    flex-direction: column;
    inset: auto;
    height: auto;
    max-height: none;
    margin: 0 auto;
    transform: none;
    opacity: 1;
    clip-path: none;
    transition: none;
    outline: 1px dashed rgba(108, 63, 181, .5);
    outline-offset: 4px;
}

.atb-modal--preview > * {
    transform: none;
}

/* Une modale ouverte ne s'imprime pas par-dessus la page. */
@media print {
    .atb-modal,
    .atb-modal::backdrop {
        display: none !important;
    }
}

/* ── Structure interne ─────────────────────────────────────────────────── */

/* La poignée d'une feuille : une barre de préhension qui annonce le geste.
   `touch-action: none` : c'est d'elle (et de l'en-tête) que part le glissement
   de fermeture d'une feuille, le corps gardant son défilement. */
.atb-modal__handle {
    flex: none;
    width: 2.5rem;
    height: 4px;
    margin: .5rem auto;
    border-radius: 2px;
    background: currentColor;
    opacity: .3;
    touch-action: none;
}

.atb-modal--top .atb-modal__handle,
.atb-modal--bottom .atb-modal__handle {
    cursor: grab;
}

/* Fermeture au geste : un tiroir latéral se laisse tirer sur toute sa surface
   (`pan-y` : le corps défile toujours verticalement) ; une feuille seulement
   par son en-tête et sa poignée. `is-dragging` coupe la transition le temps
   que le panneau suit le doigt. */
[data-atb-modal-swipe="left"],
[data-atb-modal-swipe="right"] {
    touch-action: pan-y;
}

[data-atb-modal-swipe="top"] .atb-modal__head,
[data-atb-modal-swipe="bottom"] .atb-modal__head {
    touch-action: none;
}

.atb-modal.is-dragging,
.atb-modal--panel.is-dragging[data-atb-anim] {   /* 0-3-0 : les animations du panneau sont à ce niveau */
    transition: none;
}

.atb-modal__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: var(--atb-modal-padding);
    border-bottom: 1px solid var(--atb-modal-rule, rgba(108, 121, 147, .3));
}

/* La balise du titre est au choix (h2, h3, p…) : l'apparence, elle, ne bouge
   pas — on neutralise ce que le thème donne à ses titres. */
.atb-modal__title {
    margin: 0;
    padding: 0;
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.3;
    color: inherit;
}

/* Ombres de défilement : le corps signale qu'il reste du contenu à faire
   défiler, en haut comme en bas, sans JavaScript — deux dégradés qui
   défilent avec le contenu (`local`) masquent deux ombres qui restent
   (`scroll`). Les dégradés sont couleur du fond ; sur un fond transparent
   (thème « Contenu seul ») ils le sont aussi, et rien n'apparaît. */
.atb-modal__body {
    background:
        linear-gradient(var(--atb-modal-bg) 30%, transparent) top / 100% 40px no-repeat local,
        linear-gradient(transparent, var(--atb-modal-bg) 70%) bottom / 100% 40px no-repeat local,
        radial-gradient(farthest-side at 50% 0, rgba(25, 39, 67, .16), transparent) top / 100% 14px no-repeat scroll,
        radial-gradient(farthest-side at 50% 100%, rgba(25, 39, 67, .16), transparent) bottom / 100% 14px no-repeat scroll;
}

.atb-modal--theme-content .atb-modal__body {
    background: none;
}

/* Le corps défile, pas la boîte : l'en-tête et son bouton de fermeture doivent
   rester atteignables quel que soit le contenu. La boîte OUVERTE est une
   colonne flex et le corps en prend le reste (`min-height: 0`, sans quoi un
   enfant flex refuse de descendre sous la hauteur de son contenu) — c'est ce
   qui garantit la croix visible même quand le contenu dépasse : sans cela, le
   corps débordait de la boîte, `overflow: hidden` coupait la fin du contenu,
   et sur un panneau latéral l'en-tête mangeait ses derniers pixels. Scopé sur
   `[open]` : `display` posé sur un `<dialog>` fermé l'afficherait.

   Trois déclarations en cascade, chacune écartée par les navigateurs qui ne la
   comprennent pas : `vh` comme socle, `dvh` pour suivre la barre d'URL, et
   `--atb-vvh` — écrite par le JavaScript depuis `visualViewport` — pour le
   clavier virtuel, qu'AUCUNE unité de viewport ne connaît. */
.atb-modal[open] {
    display: flex;
    flex-direction: column;
}

.atb-modal__body {
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--atb-modal-padding);
    overflow: auto;
    overscroll-behavior: contain;
    max-height: calc(100vh - var(--atb-modal-top) - var(--atb-modal-bottom));
    max-height: calc(100dvh - var(--atb-modal-top) - var(--atb-modal-bottom));
    max-height: calc(var(--atb-vvh, 100dvh) - var(--atb-modal-top) - var(--atb-modal-bottom));
}

.atb-modal--classic .atb-modal__body {
    max-height: none;
}

/* Dans un panneau glissant sans en-tête, le corps porte seul la hauteur. */
.atb-modal--panel {
    display: flex;
    flex-direction: column;
}

.atb-modal--panel .atb-modal__body {
    max-height: none;
}

.atb-modal__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44 px : la cible de fermeture d'un panneau ne se rate pas au doigt. */
    min-width: 44px;
    min-height: 44px;
    flex: none;
    background: none;
    border: 0;
    color: inherit;
    cursor: pointer;
    border-radius: var(--atb-modal-radius);
}

/* La croix hérite la couleur du panneau et ne signale le survol que par
   l'opacité : elle reste visible sur un fond de couleur (`atb-bg-primary`),
   là où un passage au bleu de marque la ferait disparaître. */
.atb-modal__close:hover,
.atb-modal__close:focus-visible {
    opacity: .65;
}

.atb-modal__close-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.atb-modal__close-icon {
    position: relative;
    display: block;
    width: 1em;
    height: 1em;
}

.atb-modal__close-icon::before,
.atb-modal__close-icon::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 0;
    width: 100%;
    height: 2px;
    background: currentColor;
}

.atb-modal__close-icon::before { transform: rotate(45deg); }
.atb-modal__close-icon::after  { transform: rotate(-45deg); }

/* ── Déclencheurs ──────────────────────────────────────────────────────────
   Il n'y a pas de bloc déclencheur : n'importe quel élément porte
   `data-atb-modal-open` via son champ Attributs. On ne lui impose aucune
   apparence — c'est son propre bloc qui la donne — sauf le curseur, qui dit
   qu'il réagit, et le focus visible que le JS rend possible sur un élément
   qui n'était pas focusable. */
[data-atb-modal-open] {
    cursor: pointer;
}

[data-atb-modal-synthetic]:focus-visible {
    outline: 2px solid var(--atb-primary, #0099dd);
    outline-offset: 2px;
}
