/* =============================================================================
   EXTRANET COURTIER — Thème « Ematea × Lumo Data »
   -----------------------------------------------------------------------------
   Chargé par extranet/includes/header.php APRÈS le bloc <style> existant.
   Il COMPLÈTE les styles historiques (.btn-shine, .glass-panel, .table-premium,
   .mesh-bg, .animate-*) — il ne les remplace pas.

   Principes :
   - Blanc dominant, cartes nettes, ombres chaudes, contrastes forts (Lumo Data)
     sur les codes Ematea (sunset / midnight / filigrane orangé).
   - 100 % marque blanche : chaque couleur passe par var(--wl-*) avec un
     fallback sunset. Aucun #teal, aucune dépendance externe.
   - Mobile-first, `prefers-reduced-motion` respecté, aucun !important sauf
     nécessité (surcharge Bootstrap).

   Sommaire :
     1. Tokens              7. Étapes (macro-statuts)
     2. Filigrane / shell   8. Tooltips Bootstrap
     3. Verre               9. Révélations & squelettes
     4. Boutons            10. Kanban
     5. Cartes & KPI       11. Mocks animés
     6. Pills, onglets…    12. Responsive & impression
============================================================================= */

/* =============================================================================
   1. TOKENS
============================================================================= */
:root {
    /* --- Marque (white-label) --- */
    --em-primary:        var(--wl-primary, #F27A54);
    --em-primary-dark:   var(--wl-primary-dark, #D65A3B);
    --em-primary-light:  var(--wl-primary-light, #FFE5DE);
    --em-accent:         var(--wl-accent, #E9C46A);
    /* Teinte profonde : utilisée pour le TEXTE sur fond clair teinté (contraste AA).
       Recalculée depuis la couleur du cabinet dans le @supports en fin de bloc. */
    --em-primary-deep:   #B04225;

    /* --- Encre & surfaces --- */
    --em-ink:       var(--wl-secondary, #0E2A3D);
    --em-ink-2:     #3E647D;
    --em-ink-3:     #7A98AB;
    --em-muted:     #64748B;
    --em-surface:   #FFFFFF;
    --em-surface-2: #F8FAFC;
    --em-surface-3: #F1F5F9;
    --em-line:      rgba(14, 42, 61, .08);
    --em-line-2:    rgba(14, 42, 61, .14);

    /* --- Sémantique --- */
    --em-success: #16A34A;
    --em-warn:    #F59E0B;
    --em-danger:  #DC2626;
    --em-info:    #3B82F6;

    /* --- Rayons --- */
    --em-r-sm:   12px;
    --em-r:      16px;
    --em-r-lg:   20px;
    --em-r-xl:   24px;
    --em-r-pill: 9999px;

    /* --- Ombres (froides pour la profondeur, chaudes pour l'accent) --- */
    --em-sh-xs: 0 1px 2px rgba(14, 42, 61, .05);
    --em-sh-sm: 0 4px 16px -6px rgba(14, 42, 61, .10), 0 1px 2px rgba(14, 42, 61, .04);
    --em-sh-md: 0 12px 30px -12px rgba(14, 42, 61, .16), 0 2px 6px rgba(14, 42, 61, .04);
    --em-sh-lg: 0 26px 60px -24px rgba(14, 42, 61, .26), 0 4px 12px rgba(14, 42, 61, .05);
    --em-sh-warm:    0 18px 40px -18px rgba(242, 122, 84, .35);
    --em-sh-warm-lg: 0 28px 60px -22px rgba(242, 122, 84, .42);

    /* --- Mouvement (courbe du site public) --- */
    --em-ease:      cubic-bezier(.22, 1, .36, 1);
    --em-dur-fast:  .16s;
    --em-dur:       .28s;
    --em-dur-slow:  .6s;
    /* Trio utilisé par les scènes .emc-* (frise CTA) : accélérer/décélérer par
       étape de keyframe plutôt qu'une seule courbe pour tout le cycle. */
    --em-ease-out:  cubic-bezier(.16, 1, .3, 1);
    --em-ease-in:   cubic-bezier(.7, 0, .84, 0);
    --em-ease-pop:  cubic-bezier(.34, 1.56, .64, 1);

    /* --- Typo --- */
    --em-font-display: 'Bricolage Grotesque', 'Plus Jakarta Sans', system-ui, sans-serif;
    --em-font-sans:    'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;

    /* --- Filigrane --- */
    --em-grid-size: 20px;
    --em-grid-tint: #94A3B8; /* mi-teinte grise du filigrane, mixée avec --em-primary */
    --em-grid-line: rgba(195, 143, 134, .085);
}

/* Les propriétés personnalisées n'ont pas de repli natif (une valeur invalide
   n'est détectée qu'à la substitution) : on ne dérive les teintes de la marque
   que si color-mix() est réellement supporté. Sinon, les valeurs sunset
   ci-dessus restent en place. */
@supports (color: color-mix(in srgb, #000 50%, #fff)) {
    :root {
        --em-primary-deep: color-mix(in srgb, var(--em-primary-dark) 82%, #2B0A02);
        --em-grid-line:    color-mix(in srgb, color-mix(in srgb, var(--em-primary) 50%, var(--em-grid-tint) 50%) 9%, transparent);
        --em-sh-warm:      0 18px 40px -18px color-mix(in srgb, var(--em-primary) 40%, transparent);
        --em-sh-warm-lg:   0 28px 60px -22px color-mix(in srgb, var(--em-primary) 46%, transparent);
    }
}

/* =============================================================================
   2. FILIGRANE / SHELL
   Grille fine, fixe au viewport, DERRIÈRE tout le contenu : le calque ne
   bouge jamais, seul le contenu (en flux normal) défile par-dessus.
   Masque : opacité nulle en bas → estompée puis pleine vers le haut.
============================================================================= */
/* Aucun `position: relative` sur <body> : les deux calques sont `fixed`, et
   repositionner le body changerait le bloc conteneur des éléments absolus
   existants (risque de régression sur les pages historiques). */
body.em-shell::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(to bottom, var(--em-grid-line) 0 1px, transparent 1px),
        linear-gradient(to right,  var(--em-grid-line) 0 1px, transparent 1px);
    background-size: var(--em-grid-size) var(--em-grid-size);
    -webkit-mask-image: linear-gradient(to bottom,
        rgba(0, 0, 0, 1) 0%,
        rgba(0, 0, 0, .92) 26%,
        rgba(0, 0, 0, .55) 58%,
        rgba(0, 0, 0, .18) 80%,
        rgba(0, 0, 0, 0) 96%);
            mask-image: linear-gradient(to bottom,
        rgba(0, 0, 0, 1) 0%,
        rgba(0, 0, 0, .92) 26%,
        rgba(0, 0, 0, .55) 58%,
        rgba(0, 0, 0, .18) 80%,
        rgba(0, 0, 0, 0) 96%);
}

/* Halo chaud très discret en haut de page — donne la lumière « Lumo » sans
   toucher au fond .mesh-bg défini dans header.php. */
body.em-shell::after {
    content: "";
    position: fixed;
    inset: 0 0 auto 0;
    height: 420px;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(900px 380px at 78% -12%, rgba(242, 122, 84, .10), transparent 62%),
        radial-gradient(700px 320px at 12% -18%, rgba(233, 196, 106, .09), transparent 60%);
}

/* Titres de page — Bricolage Grotesque (display de la marque). */
.em-title {
    font-family: var(--em-font-display);
    font-weight: 700;
    letter-spacing: -.025em;
    color: var(--em-ink);
    line-height: 1.1;
    font-size: clamp(1.5rem, 1.15rem + 1.4vw, 2rem);
    margin: 0;
}
.em-sub {
    color: var(--em-ink-2);
    font-size: .875rem;
    margin: .35rem 0 0;
}
.em-eyebrow {
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--em-primary-deep);
}
.em-page-head {
    display: flex;
    flex-direction: column;
    gap: .85rem;
    margin-bottom: 1.25rem;
}
@media (min-width: 768px) {
    .em-page-head {
        flex-direction: row;
        align-items: flex-end;
        justify-content: space-between;
        gap: 1.5rem;
    }
}

/* Bandeau d'accueil orange (dashboard) — demandé en revue design : le bloc
   chaud de bienvenue existait avant la refonte, on le restaure, en plus
   lumineux/plat (dégradé resserré + halo doux, pas de gloss). */
.em-hero {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    border-radius: var(--em-r-xl);
    padding: 1.35rem 1.4rem;
    background: linear-gradient(135deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    box-shadow: var(--em-sh-warm-lg);
    color: #fff;
}
.em-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(560px 280px at 92% -14%, rgba(255, 255, 255, .20), transparent 62%),
        radial-gradient(420px 220px at 6% 128%, rgba(0, 0, 0, .12), transparent 60%);
    pointer-events: none;
}
@media (min-width: 900px) {
    .em-hero { padding: 1.6rem 1.9rem; gap: 1.1rem; }
}
/* Rangée texte + mock animé — c'était le rôle de .em-hero lui-même avant que le
   widget marque blanche s'ajoute en pleine largeur EN DESSOUS des deux (cf.
   .em-hero-wl) ; on isole donc cette mise en page dans un enfant dédié. */
.em-hero-top {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
@media (min-width: 900px) {
    .em-hero-top { flex-direction: row; align-items: center; justify-content: space-between; gap: 1.5rem; }
}
.em-hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .82);
}
.em-hero-title {
    font-family: var(--em-font-display);
    font-weight: 700;
    letter-spacing: -.025em;
    line-height: 1.1;
    color: #fff;
    font-size: clamp(1.4rem, 1.05rem + 1.3vw, 1.85rem);
    margin: .3rem 0 .35rem;
}
.em-hero-sub {
    color: rgba(255, 255, 255, .88);
    font-size: .82rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 52ch;
}
.em-hero-side { flex-shrink: 0; }

/* Widget « lien marque blanche » dans le hero — barre pleine largeur, verre
   translucide posé sur le dégradé chaud (repli couleur pleine si
   backdrop-filter est indisponible). Volontairement sous .em-hero-top (texte +
   mock) plutôt que dans la colonne de texte : le lien complet (avec son code
   de parrainage) a besoin de toute la largeur pour ne pas se tronquer. */
.em-hero-wl {
    padding: .7rem .9rem;
    border-radius: var(--em-r-lg);
    background: rgba(255, 255, 255, .14);
    border: 1px solid rgba(255, 255, 255, .22);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .em-hero-wl { background: rgba(184, 76, 40, .55); }
}
.em-hero-wl-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin-bottom: .55rem;
}
.em-hero-wl-title {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    flex-shrink: 0;
    font-size: .78rem;
    font-weight: 700;
    color: #fff;
}
/* L'explication tient sur la même ligne que le titre (gain d'une ligne de
   hauteur) : elle occupe l'espace restant et s'ellipse plutôt que de forcer
   un retour à la ligne sur les largeurs de hero étroites. En dessous de 560px
   il ne reste plus assez de place pour l'ellipser proprement (elle tronque
   après 2-3 mots) : on la retire du flux et .em-hero-wl-info prend le relais. */
.em-hero-wl-sub {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .74rem;
    color: rgba(255, 255, 255, .78);
}
/* Bulle d'info tactile (mobile) : cachée en desktop où .em-hero-wl-sub suffit. */
.em-hero-wl-info {
    display: none;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: rgba(255, 255, 255, .16);
    border: 1px solid rgba(255, 255, 255, .28);
    color: #fff;
    font-size: .78rem;
    padding: 0;
    cursor: pointer;
}
@media (max-width: 560px) {
    .em-hero-wl-sub { display: none; }
    .em-hero-wl-info { display: inline-flex; }
    /* Sans la phrase (flex:1) pour la pousser, la roue crantée resterait
       collée à la bulle d'info : on la renvoie explicitement à l'extrémité
       droite, alignée avec « Ouvrir » sur la ligne du dessous. */
    .em-hero-wl-gear { margin-left: auto; }
}
.em-hero-wl-gear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .16);
    border: 1px solid rgba(255, 255, 255, .28);
    color: #fff;
    font-size: .78rem;
    text-decoration: none;
    transition: background .15s ease, transform .15s ease;
}
.em-hero-wl-gear:hover { background: rgba(255, 255, 255, .28); transform: rotate(45deg); color: #fff; }
.em-hero-wl-row { display: flex; align-items: center; gap: .4rem; }
.em-hero-wl-input {
    flex: 1;
    min-width: 0;
    max-width: 45.4rem;
    padding: .4rem .6rem;
    border-radius: var(--em-r-sm);
    border: 1px solid rgba(255, 255, 255, .28);
    background: rgba(0, 0, 0, .12);
    color: #fff;
    font-size: .72rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.em-hero-wl-input:focus { outline: 2px solid rgba(255, 255, 255, .55); outline-offset: 1px; }
.em-hero-wl-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .5rem .8rem;
    border-radius: var(--em-r-sm);
    border: none;
    background: #fff;
    color: var(--em-primary-dark);
    font-size: .72rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: transform .15s ease;
}
.em-hero-wl-btn:hover { transform: translateY(-1px); color: var(--em-primary-dark); }
/* « Ouvrir » : icône seule, forme ronde comme la roue crantée juste au-dessus
   (même taille qu'elle), repoussée à l'extrémité droite de la ligne — donc
   sous la roue crantée — via une marge automatique plutôt que collée à
   « Copier ». */
.em-hero-wl-btn--ghost {
    margin-left: auto;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .16);
    border: 1px solid rgba(255, 255, 255, .28);
    color: #fff;
    font-size: .78rem;
}
.em-hero-wl-btn--ghost:hover { color: #fff; }
.em-hero-wl-setup {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .76rem;
    font-weight: 700;
    color: #fff;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ═══ Filigrane animé du bandeau d'accueil ═════════════════════════════
   Une seule scène à droite du hero : 4 mini-interfaces (leads → OCR →
   tarifs → signature) se relaient au MÊME emplacement, boucle de 16 s
   (4 s chacune). La carte est inclinée en perspective et flotte doucement ;
   chaque scène entre avec son propre mouvement (chute / glissé / pop /
   glissé) souligné d'un reflet « flash » qui balaie la carte, joue son
   contenu, puis se dissout vers l'avant pour laisser la place.
   Un rail 01→04 (chiffre, label, points de progression) donne la lecture
   du parcours.
   Règles de survie :
   1. ne JAMAIS animer en CSS un élément positionné par attribut transform
      SVG (le transform CSS écrase la position) — on n'anime que des
      éléments sans attribut transform, avec transform-box: fill-box ;
   2. toute remise en position d'un élément se fait pendant qu'il est déjà
      à opacité nulle (sinon on le verrait repartir en arrière) ;
   3. l'état de repos des règles EST l'état final : `animation: none`
      (reduced-motion) affiche la scène SIGNÉ terminée, sans mouvement. */

/* Trame de points, plus dense à droite (sous le mock), invisible sous le texte */
.em-hero-veil {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: radial-gradient(circle, rgba(255, 255, 255, .30) 1px, transparent 1.4px);
    background-size: 22px 22px;
    -webkit-mask-image: linear-gradient(90deg, transparent 20%, rgba(0, 0, 0, .55) 52%, #000 78%);
    mask-image: linear-gradient(90deg, transparent 20%, rgba(0, 0, 0, .55) 52%, #000 78%);
}

.em-hero-journey { display: none; position: relative; pointer-events: none; }
.em-hero-journey::before {
    content: "";
    position: absolute;
    inset: -30% -14%;
    background: radial-gradient(closest-side, rgba(255, 255, 255, .17), transparent 72%);
    filter: blur(24px);
}
/* Ombre portée au sol : ancre la carte inclinée dans le dégradé */
.em-hero-journey::after {
    content: "";
    position: absolute;
    left: 16%;
    right: 4%;
    bottom: -12px;
    height: 22px;
    background: radial-gradient(closest-side, rgba(120, 30, 5, .38), transparent 72%);
    filter: blur(9px);
}
.em-hero-journey svg {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
    transform: rotateX(7deg) rotateY(-11deg);
    animation: emj-float 7s ease-in-out infinite;
}
@media (min-width: 900px) {
    .em-hero-journey {
        display: block;
        flex: 0 0 236px;
        width: 236px;
        height: 136px;
        margin-left: .5rem;
        perspective: 800px;
    }
}
@keyframes emj-float {
    0%, 100% { transform: rotateX(7deg) rotateY(-11deg) translateY(0); }
    50%      { transform: rotateX(7deg) rotateY(-11deg) translateY(-5px); }
}

/* ── Rail de progression (persistant) ── */
.ems-dot { fill: #fff; opacity: .6; transform-box: fill-box; transform-origin: center; }
.ems-dot--1 { animation: emj-dot1 16s ease infinite; }
.ems-dot--2 { animation: emj-dot2 16s ease infinite; }
.ems-dot--3 { animation: emj-dot3 16s ease infinite; }
.ems-dot--4 { animation: emj-dot4 16s ease infinite; }

/* ── Scènes : entrée flash, tenue, dissolution vers l'avant ── */
.ems {
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    filter: drop-shadow(0 12px 16px rgba(140, 40, 12, .30));
}
.ems--1 { animation: emj-s1 16s infinite; }
.ems--2 { animation: emj-s2 16s infinite; }
.ems--3 { animation: emj-s3 16s infinite; }
.ems--4 { opacity: 1; animation: emj-s4 16s infinite; }

@keyframes emj-s1 {
    0%, .6%     { transform: translateY(-46px) rotate(-3deg); opacity: 0; animation-timing-function: cubic-bezier(.3, 1.36, .44, 1); }
    3.4%        { transform: translateY(0) rotate(0deg); opacity: 1; }
    22.6%       { transform: translateY(0) rotate(0deg); opacity: 1; animation-timing-function: cubic-bezier(.5, 0, .8, .4); }
    24.4%       { transform: translateY(-8px) scale(1.05); opacity: 0; }
    24.5%, 100% { transform: translateY(-46px) rotate(-3deg); opacity: 0; }
}
@keyframes emj-s2 {
    0%, 25.6%   { transform: translateX(72px) rotate(2.5deg); opacity: 0; animation-timing-function: cubic-bezier(.3, 1.36, .44, 1); }
    28.4%       { transform: translateX(0) rotate(0deg); opacity: 1; }
    47.6%       { transform: translateX(0) rotate(0deg); opacity: 1; animation-timing-function: cubic-bezier(.5, 0, .8, .4); }
    49.4%       { transform: translateY(-8px) scale(1.05); opacity: 0; }
    49.5%, 100% { transform: translateX(72px) rotate(2.5deg); opacity: 0; }
}
@keyframes emj-s3 {
    0%, 50.6%   { transform: scale(.45); opacity: 0; animation-timing-function: cubic-bezier(.34, 1.56, .64, 1); }
    52.4%       { transform: scale(1.07); opacity: 1; }
    53.6%       { transform: scale(1); }
    72.6%       { transform: scale(1); opacity: 1; animation-timing-function: cubic-bezier(.5, 0, .8, .4); }
    74.4%       { transform: translateY(-8px) scale(1.05); opacity: 0; }
    74.5%, 100% { transform: scale(.45); opacity: 0; }
}
@keyframes emj-s4 {
    0%, 75.6%   { transform: translateX(72px) rotate(-2.5deg); opacity: 0; animation-timing-function: cubic-bezier(.3, 1.36, .44, 1); }
    78.4%       { transform: translateX(0) rotate(0deg); opacity: 1; }
    97.8%       { transform: translateX(0) rotate(0deg); opacity: 1; animation-timing-function: cubic-bezier(.5, 0, .8, .4); }
    99.6%       { transform: translateY(-6px) scale(1.04); opacity: 0; }
    99.7%, 100% { transform: translateX(72px) rotate(-2.5deg); opacity: 0; }
}

/* ── Habillage commun des cartes ── */
.ems-card    { fill: rgba(255, 255, 255, .09); stroke: #fff; stroke-opacity: .8; stroke-width: 1.5; }
.ems-line    { fill: none; stroke: #fff; stroke-width: 1.1; opacity: .25; }
.ems-tdot    { fill: #fff; opacity: .35; }
.ems-tx      { fill: #fff; opacity: .5; }
.ems-tx--dim { opacity: .3; }
.ems-row     { fill: rgba(255, 255, 255, .08); stroke: #fff; stroke-width: 1; stroke-opacity: .25; }
.ems-av      { fill: none; stroke: #fff; stroke-width: 1.4; opacity: .5; }
.ems-status  { fill: rgba(255, 255, 255, .3); }
.ems-logo    { fill: rgba(255, 255, 255, .24); }
.ems-price   { fill: rgba(255, 255, 255, .34); }
.ems-price--best { fill: rgba(255, 255, 255, .85); }
.ems-pill-bg { fill: rgba(255, 255, 255, .17); stroke: #fff; stroke-width: 1.1; stroke-opacity: .45; }
.ems-pill-tx { fill: #fff; opacity: .75; }
.ems-check   { fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: .95; }

/* ── Mock filigrane « marque blanche » : anneau de sélection de palette ── */
.wlm-ring {
    fill: none;
    stroke: #fff;
    stroke-width: 1.6;
    opacity: 0;
    transform-box: fill-box;
    transform-origin: center;
    animation: wlm-ring 16s ease infinite;
}
@keyframes wlm-ring {
    0%, .8%     { opacity: 0; transform: scale(.6); animation-timing-function: cubic-bezier(.34, 1.56, .64, 1); }
    2.2%        { opacity: 1; transform: scale(1.15); }
    3.4%        { transform: scale(1); }
    22.6%       { opacity: 1; transform: scale(1); animation-timing-function: ease; }
    24.6%, 100% { opacity: 0; transform: scale(.6); }
}

.ems-num {
    font-family: var(--em-font-display);
    font-size: 40px;
    font-weight: 800;
    text-anchor: middle;
    fill: rgba(255, 255, 255, .07);
    stroke: #fff;
    stroke-width: .9;
    stroke-opacity: .55;
    letter-spacing: 1px;
}
.ems-label {
    font-family: var(--em-font-sans);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 1.8px;
    text-anchor: middle;
    fill: #fff;
    opacity: .85;
}

/* ── Reflet « flash » balayant la carte à chaque entrée ── */
.ems-glint { fill: url(#emsGlint); opacity: 0; animation: emj-glint 16s linear infinite; }
.ems--1 .ems-glint { animation-delay: .3s; }
.ems--2 .ems-glint { animation-delay: 4.3s; }
.ems--3 .ems-glint { animation-delay: 8.3s; }
.ems--4 .ems-glint { animation-delay: 12.3s; }
@keyframes emj-glint {
    0%        { transform: translateX(-140px); opacity: 0; }
    .3%       { opacity: 1; }
    2.8%      { transform: translateX(300px); opacity: 1; }
    3%, 100%  { transform: translateX(300px); opacity: 0; }
}

/* ── Scène 1 : les fiches tombent (ressort du hero du site) ── */
.ems1-fall { opacity: 1; animation: emj-fall 16s infinite; animation-delay: var(--d2, 0s); }
.ems1-pill { opacity: 1; transform-box: fill-box; transform-origin: center; animation: emj-s1pill 16s var(--em-ease) infinite; }
@keyframes emj-fall {
    0%, 4.2%    { transform: translateY(-30px); opacity: 0; animation-timing-function: cubic-bezier(.5, 0, .75, 0); }
    5.2%        { opacity: 1; }
    6.2%        { transform: translateY(2px); opacity: 1; animation-timing-function: cubic-bezier(.22, 1, .36, 1); }
    6.9%        { transform: translateY(0); }
    24.6%       { transform: translateY(0); opacity: 1; }
    24.8%, 100% { transform: translateY(-30px); opacity: 0; }
}
@keyframes emj-s1pill {
    0%, 9.6%    { transform: scale(0); opacity: 0; }
    10.8%       { transform: scale(1.2); opacity: 1; }
    11.6%       { transform: scale(1); }
    24.6%       { transform: scale(1); opacity: 1; }
    24.8%, 100% { transform: scale(0); opacity: 0; }
}

/* ── Scène 2 : faisceau de scan, texte révélé dans son sillage ── */
.ems2-scan { animation: emj-sweep 16s infinite; }
.ems2-beam { fill: #fff; opacity: 0; animation: emj-beamop 16s linear infinite; }
.ems2-glow { opacity: 0; animation: emj-beamop 16s linear infinite; }
.ems2-rev  { fill: #fff; opacity: .55; animation: emj-rev 16s ease infinite; animation-delay: var(--d2, 0s); }
.ems-brk   { fill: none; stroke: #fff; stroke-width: 1.6; stroke-linecap: round; opacity: 0; animation: emj-brk 16s ease infinite; }
.ems2-chip { opacity: 1; transform-box: fill-box; transform-origin: center; animation: emj-chip 16s var(--em-ease) infinite; }
@keyframes emj-sweep {
    0%, 29% { transform: translateY(0); animation-timing-function: cubic-bezier(.45, .05, .55, .95); }
    35.5%   { transform: translateY(84px); }
    100%    { transform: translateY(84px); }
}
@keyframes emj-beamop {
    0%, 28.6% { opacity: 0; }
    29.4%     { opacity: 1; }
    35%       { opacity: 1; }
    36%, 100% { opacity: 0; }
}
@keyframes emj-rev {
    0%, 29.6%   { opacity: 0; }
    30.6%       { opacity: .55; }
    47.4%       { opacity: .55; }
    49.6%, 100% { opacity: 0; }
}
@keyframes emj-brk {
    0%, 28.8%   { opacity: 0; }
    29.8%       { opacity: .8; }
    35.6%       { opacity: .8; }
    36.8%, 100% { opacity: 0; }
}
@keyframes emj-chip {
    0%, 36.2%   { transform: scale(0); opacity: 0; }
    37.4%       { transform: scale(1.18); opacity: 1; }
    38.2%       { transform: scale(1); }
    47.4%       { transform: scale(1); opacity: 1; }
    49.6%, 100% { transform: scale(0); opacity: 0; }
}

/* ── Scène 3 : les offres se rangent, la meilleure ressort ── */
.ems3-slide { opacity: 1; animation: emj-slide 16s infinite; animation-delay: var(--d2, 0s); }
.ems3-best {
    fill: rgba(255, 255, 255, .16);
    stroke: #fff;
    stroke-width: 1.4;
    stroke-opacity: .6;
    opacity: 1;
    transform-box: fill-box;
    transform-origin: center;
    animation: emj-best 16s var(--em-ease) infinite;
}
@keyframes emj-slide {
    0%, 54.2%   { transform: translateX(-26px); opacity: 0; animation-timing-function: cubic-bezier(.3, 1.3, .45, 1); }
    56.2%       { transform: translateX(0); opacity: 1; }
    72.4%       { transform: translateX(0); opacity: 1; }
    74.6%, 100% { transform: translateX(-26px); opacity: 0; }
}
@keyframes emj-best {
    0%, 59.4%   { transform: scale(.9); opacity: 0; }
    60.8%       { transform: scale(1.05); opacity: 1; }
    61.8%       { transform: scale(1); }
    72.4%       { transform: scale(1); opacity: 1; }
    74.6%, 100% { transform: scale(.9); opacity: 0; }
}

/* ── Scène 4 : paraphe à la plume + tampon de validation ── */
.ems4-doc  { fill: #fff; opacity: .5; animation: emj-doc 16s ease infinite; animation-delay: var(--d2, 0s); }
.ems4-rule { fill: none; stroke: #fff; stroke-width: 1.2; stroke-dasharray: 3 3; opacity: .45; animation: emj-rule 16s ease infinite; }
.ems4-sig {
    fill: none;
    stroke: #fff;
    stroke-width: 2.1;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 100;
    stroke-dashoffset: 0;
    opacity: 1;
    animation: emj-sig 16s ease-in-out infinite;
}
.ems4-nib {
    display: none;
    fill: #fff;
    opacity: 0;
    offset-path: path("M95,112 C100,92 108,116 115,100 C119,89 124,112 130,101 C136,90 141,108 148,97 C155,86 163,106 178,88");
    animation: emj-nib 16s ease-in-out infinite;
    filter: drop-shadow(0 0 5px rgba(255, 255, 255, .9));
}
@supports (offset-path: path("M0,0 L1,1")) { .ems4-nib { display: block; } }
.ems4-seal    { opacity: 1; transform-box: fill-box; transform-origin: center; animation: emj-seal 16s var(--em-ease) infinite; }
.ems4-seal-bg { fill: rgba(255, 255, 255, .2); stroke: #fff; stroke-width: 1.5; stroke-opacity: .7; }
.ems4-rip {
    fill: none; stroke: #fff; stroke-width: 1.5; opacity: 0;
    transform-box: fill-box; transform-origin: center;
    animation: emj-rip 16s ease-out infinite;
}
@keyframes emj-doc {
    0%, 79.2%   { opacity: 0; }
    80.2%       { opacity: .5; }
    97.6%       { opacity: .5; }
    99.7%, 100% { opacity: 0; }
}
@keyframes emj-rule {
    0%, 80.2%   { opacity: 0; }
    81.2%       { opacity: .45; }
    97.6%       { opacity: .45; }
    99.7%, 100% { opacity: 0; }
}
@keyframes emj-sig {
    0%, 81.4%   { stroke-dashoffset: 100; opacity: 0; }
    82%         { stroke-dashoffset: 100; opacity: 1; }
    87.6%       { stroke-dashoffset: 0; opacity: 1; }
    97.6%       { stroke-dashoffset: 0; opacity: 1; }
    99.7%, 100% { stroke-dashoffset: 0; opacity: 0; }
}
@keyframes emj-nib {
    0%, 81.9% { offset-distance: 0%; opacity: 0; }
    82.5%     { opacity: 1; }
    87%       { opacity: 1; }
    87.6%     { offset-distance: 100%; opacity: 0; }
    100%      { offset-distance: 100%; opacity: 0; }
}
@keyframes emj-seal {
    0%, 88.4%   { transform: scale(0); opacity: 0; }
    89.8%       { transform: scale(1.22); opacity: 1; }
    90.8%       { transform: scale(1); }
    97.6%       { transform: scale(1); opacity: 1; }
    99.7%, 100% { transform: scale(0); opacity: 0; }
}
@keyframes emj-rip {
    0%, 89.2%   { transform: scale(.5); opacity: 0; }
    90.2%       { opacity: .5; }
    93.6%       { transform: scale(2); opacity: 0; }
    93.7%, 100% { transform: scale(.5); opacity: 0; }
}

/* ── Points de progression ── */
@keyframes emj-dot1 {
    0%, .8% { opacity: .25; transform: scale(1); }
    2.2%    { opacity: 1; transform: scale(1.4); }
    22.6%   { opacity: 1; transform: scale(1.4); }
    25%     { opacity: .6; transform: scale(1); }
    98%     { opacity: .6; }
    100%    { opacity: .25; }
}
@keyframes emj-dot2 {
    0%, 25.8% { opacity: .25; transform: scale(1); }
    27.2%     { opacity: 1; transform: scale(1.4); }
    47.6%     { opacity: 1; transform: scale(1.4); }
    50%       { opacity: .6; transform: scale(1); }
    98%       { opacity: .6; }
    100%      { opacity: .25; }
}
@keyframes emj-dot3 {
    0%, 50.8% { opacity: .25; transform: scale(1); }
    52.2%     { opacity: 1; transform: scale(1.4); }
    72.6%     { opacity: 1; transform: scale(1.4); }
    75%       { opacity: .6; transform: scale(1); }
    98%       { opacity: .6; }
    100%      { opacity: .25; }
}
@keyframes emj-dot4 {
    0%, 75.8% { opacity: .25; transform: scale(1); }
    77.2%     { opacity: 1; transform: scale(1.4); }
    97.8%     { opacity: 1; transform: scale(1.4); }
    99.4%     { opacity: .6; transform: scale(1); }
    100%      { opacity: .25; }
}

/* Sans animation : la carte reste inclinée, seule la scène SIGNÉ (état
   final, paraphe tracé, tampon posé) est visible — les éléments transitoires
   (faisceau, plume, reflets, onde) ont une opacité de repos nulle. */
@media (prefers-reduced-motion: reduce) {
    .em-hero-journey svg, .em-hero-journey * { animation: none !important; }
}

/* Pills sur fond chaud (sélecteur de période dans le hero) : contraste inversé. */
.em-pill--on-dark {
    background: rgba(255, 255, 255, .16);
    border-color: rgba(255, 255, 255, .30);
    color: #fff;
}
.em-pill--on-dark:hover { color: #fff; border-color: rgba(255, 255, 255, .55); background: rgba(255, 255, 255, .24); box-shadow: none; }
.em-pill--on-dark.is-active {
    background-color: #fff;
    background-image: none;
    color: var(--em-primary-deep);
    border-color: transparent;
    box-shadow: 0 10px 22px -10px rgba(0, 0, 0, .3);
}
.em-pill--on-dark .em-pill-count { background: rgba(255, 255, 255, .22); color: #fff; }
.em-pill--on-dark.is-active .em-pill-count { background: var(--em-primary-light); color: var(--em-primary-deep); }

/* =============================================================================
   3. VERRE
============================================================================= */
.em-glass {
    background: rgba(255, 255, 255, .78);
    -webkit-backdrop-filter: blur(16px) saturate(150%);
            backdrop-filter: blur(16px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: var(--em-r-lg);
    box-shadow: var(--em-sh-md);
}
.em-glass--flat { box-shadow: var(--em-sh-sm); }

/* Barre collante (top bar mobile, en-tête de tableau, barre de sélection). */
.em-glass-bar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: rgba(255, 255, 255, .82);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
            backdrop-filter: blur(18px) saturate(150%);
    border-bottom: 1px solid rgba(14, 42, 61, .06);
}

/* Barre d'actions groupées : pleine largeur en bas sur mobile, flottante ≥ md. */
.em-bulkbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    padding: .7rem .9rem calc(.7rem + env(safe-area-inset-bottom, 0px));
    background: rgba(255, 255, 255, .9);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
            backdrop-filter: blur(18px) saturate(150%);
    border-top: 1px solid rgba(14, 42, 61, .08);
    box-shadow: 0 -18px 40px -24px rgba(14, 42, 61, .35);
    animation: em-fade-up var(--em-dur) var(--em-ease) both;
}
@media (min-width: 768px) {
    .em-bulkbar {
        left: auto;
        right: 1.5rem;
        bottom: 1.5rem;
        border-radius: var(--em-r-pill);
        border: 1px solid rgba(255, 255, 255, .6);
        padding: .55rem .7rem;
        box-shadow: var(--em-sh-lg);
    }
}

/* =============================================================================
   4. BOUTONS
   Style plat et contemporain : dégradé discret + ombre chaude douce comme
   seuls indices de profondeur. Pas de reflet diagonal ni de liseré brillant
   (retirés en revue design — lisaient comme un bouton « jeu vidéo » plutôt
   que comme une pilule SaaS moderne). L'interactivité passe par le lift
   (translateY) et l'approfondissement de l'ombre au survol, pas par le gloss.
============================================================================= */
.em-btn,
.em-btn-ghost,
.em-btn-soft {
    position: relative;
    isolation: isolate;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    padding: .62rem 1.15rem;
    border: 1px solid transparent;
    border-radius: var(--em-r-pill);
    font-family: var(--em-font-sans);
    font-size: .82rem;
    font-weight: 700;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: transform var(--em-dur) var(--em-ease),
                box-shadow var(--em-dur) var(--em-ease),
                background-color var(--em-dur-fast) ease,
                border-color var(--em-dur-fast) ease,
                color var(--em-dur-fast) ease;
}
/* Dégradé VERTICAL très resserré (5 %) : juste assez pour suggérer un volume
   subtil, jamais un bouton "bonbon". Identique quelle que soit la largeur de
   la pilule (un angle diagonal rendrait un bouton large plus clair qu'un
   bouton court). */
.em-btn {
    color: #fff;
    background-color: var(--em-primary);
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    box-shadow: var(--em-sh-warm);
}
.em-btn:hover { color: #fff; transform: translateY(-2px); box-shadow: var(--em-sh-warm-lg); }
.em-btn:active { transform: translateY(0); box-shadow: var(--em-sh-warm); }

.em-btn-ghost {
    color: var(--em-ink);
    background: rgba(255, 255, 255, .72);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
            backdrop-filter: blur(12px) saturate(140%);
    border-color: rgba(14, 42, 61, .10);
    box-shadow: var(--em-sh-xs);
}
.em-btn-ghost:hover {
    color: var(--em-primary-deep);
    background: #fff;
    border-color: var(--em-primary-light);
    transform: translateY(-2px);
    box-shadow: var(--em-sh-sm);
}

/* Variante « teinte de marque » (actions secondaires fortes). */
.em-btn-soft {
    color: var(--em-primary-deep);
    background: var(--em-primary-light);
    background: color-mix(in srgb, var(--em-primary) 9%, #fff);
    border-color: var(--em-primary-light);
    border-color: color-mix(in srgb, var(--em-primary) 24%, #fff);
}
.em-btn-soft:hover {
    background: var(--em-primary-light);
    transform: translateY(-2px);
    box-shadow: var(--em-sh-sm);
}

.em-btn:active,
.em-btn-ghost:active,
.em-btn-soft:active { transform: translateY(0); }

.em-btn:focus-visible,
.em-btn-ghost:focus-visible,
.em-btn-soft:focus-visible {
    outline: 2px solid var(--em-primary-dark);
    outline-offset: 2px;
}

.em-btn[disabled], .em-btn.is-disabled,
.em-btn-ghost[disabled], .em-btn-ghost.is-disabled,
.em-btn-soft[disabled], .em-btn-soft.is-disabled {
    opacity: .5;
    pointer-events: none;
    transform: none;
    box-shadow: none;
}

.em-btn-sm { padding: .42rem .8rem; font-size: .74rem; }
.em-btn-lg { padding: .82rem 1.5rem; font-size: .92rem; }
.em-btn-block { width: 100%; }

/* Case à cocher de SÉLECTION partagée (liste, carte, formulaire…) — pas un
   interrupteur : pour un pouce glissant, voir .em-seg-toggle / .em-civilite-toggle
   (plus loin dans ce fichier) ou .cr-sw (comparator-results.css), un composant
   différent, à ne pas toucher ici.
   Rendu natif conservé (pas d'`appearance: none`) pour l'accessibilité —
   lecteur d'écran, zoom, thème système — `accent-color` pilote juste la
   teinte de la coche. Le contour et le rebond au clic sont la seule couche
   ajoutée par-dessus. */
.em-checkbox {
    width: 19px;
    height: 19px;
    margin: 0;
    flex: 0 0 auto;
    vertical-align: middle;
    border: 2px solid var(--em-ink-3);
    border-radius: 6px;
    accent-color: var(--em-primary-dark);
    cursor: pointer;
    transition: transform var(--em-dur-fast) var(--em-ease),
                box-shadow var(--em-dur-fast) var(--em-ease),
                border-color var(--em-dur-fast) ease;
}
.em-checkbox:hover { border-color: var(--em-ink-2); }
.em-checkbox:checked {
    border-color: var(--em-primary-dark);
    /* Léger « pop » à la coche : grossissement bref + ombre chaude qui se
       dépose, pas de rebond élastique (courbe d'accélération standard). */
    transform: scale(1.08);
    box-shadow: 0 3px 10px -3px color-mix(in srgb, var(--em-primary) 45%, transparent);
}
.em-checkbox:active { transform: scale(.92); }
.em-checkbox:focus-visible {
    outline: 2px solid var(--em-primary-dark);
    outline-offset: 2px;
}
.em-checkbox:disabled,
.em-checkbox.is-disabled {
    opacity: .5;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* Menu « Ajouter » du dashboard (Bootstrap dropdown) : reskin à la palette de
   marque + suppression du décalage au clic. Sans ces règles le menu retombe
   sur les valeurs par défaut de Bootstrap (bleu, 1rem, pas de largeur alignée). */
.em-add-dropdown .dropdown-toggle::after { display: none; } /* le bouton porte déjà son propre chevron FA */
.em-add-dropdown .dropdown-toggle.em-btn:hover,
.em-add-dropdown .dropdown-toggle.em-btn:active,
.em-add-dropdown .dropdown-toggle.em-btn[aria-expanded="true"] {
    /* Le menu est positionné au clic par rapport au bouton : s'il est encore
       en train de « lever » (translateY du hover), le menu ouvre décalé. */
    transform: none;
}
.em-add-dropdown .dropdown-menu {
    width: 100%;
    min-width: 100%;
    font-size: .82rem;
}
.em-add-dropdown .dropdown-item {
    /* Bootstrap force width:100% sur .dropdown-item ; combiné à la marge
       gauche/droite de header.php (margin: 2px 6px), la largeur fixe ne
       « rend » pas cette marge et le fond déborde à droite au lieu d'être
       centré — d'où le highlight collé à droite au survol. */
    width: auto;
}
/* Icône des options du menu « Ajouter » : largeur fixe pour que le texte de
   chaque ligne démarre au même x, quelle que soit la largeur naturelle du
   glyphe (keyboard/csv/link). */
.em-add-dropdown .em-dd-ic { width: 16px; text-align: center; flex-shrink: 0; }
.em-add-dropdown .dropdown-item:hover,
.em-add-dropdown .dropdown-item:focus,
.em-add-dropdown .dropdown-item.active,
.em-add-dropdown .dropdown-item:active {
    background-color: var(--em-primary-light);
    color: var(--em-primary-deep);
}

/* Modales « Inviter un prospect » / « Ajouter un client » (page Clients) :
   effet « liquid glass » — voile clair, panneau en dégradé blanc → coral
   très doux avec liseré lumineux (bordure dégradée + reflet interne), CTA
   « brillance » identique au header. */
.em-invite-modal { background: rgba(21, 28, 44, .32); backdrop-filter: blur(7px) saturate(120%); -webkit-backdrop-filter: blur(7px) saturate(120%); }
.em-invite-panel {
    position: relative;
    /* Bordure dégradée (technique double-background padding-box / border-box) :
       simule le liseré qui capte la lumière sur un bord de verre. */
    background:
        linear-gradient(165deg, rgba(255,255,255,.95) 0%, rgba(255,248,245,.92) 45%, rgba(255,224,213,.72) 100%) padding-box,
        linear-gradient(165deg, rgba(255,255,255,.95), rgba(242,122,84,.4)) border-box;
    border: 1px solid transparent;
    -webkit-backdrop-filter: blur(24px) saturate(160%);
            backdrop-filter: blur(24px) saturate(160%);
    border-radius: var(--em-r-lg);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .85),
        0 28px 64px -22px color-mix(in srgb, var(--em-primary) 35%, transparent),
        0 10px 28px -12px rgba(14, 42, 61, .16);
}
.em-invite-icon { background: var(--em-primary-light); color: var(--em-primary-deep); }
.em-invite-input {
    background: rgba(255, 255, 255, .65);
    transition: background-color var(--em-dur-fast) ease, border-color var(--em-dur-fast) ease, box-shadow var(--em-dur-fast) ease;
}
.em-invite-input:focus {
    background: #fff;
    border-color: var(--em-primary-light);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--em-primary) 16%, transparent);
}
.em-invite-cta {
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    color: #fff;
    border: none;
    box-shadow: var(--em-sh-warm);
    cursor: pointer;
}
.em-invite-cta:hover { box-shadow: var(--em-sh-warm-lg); }
.em-invite-cta:disabled { opacity: .6; cursor: default; }
.em-invite-link-btn { background-color: var(--em-primary); border: none; cursor: pointer; transition: background-color var(--em-dur-fast) ease; }
.em-invite-link-btn:hover { background-color: var(--em-primary-dark); }

/* Toggle « Civilité » (Madame / Monsieur) — remplace le <select> par un sélecteur
   à glissière : le thumb translate en douceur d'un côté à l'autre. */
.em-civilite-toggle {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    background: rgba(255, 255, 255, .65);
    border: 1px solid #e2e8f0;
    border-radius: var(--em-r-lg, 14px);
    padding: 3px;
    overflow: hidden;
}
.em-civilite-thumb {
    position: absolute;
    top: 3px; bottom: 3px; left: 3px;
    width: calc(50% - 3px);
    border-radius: calc(var(--em-r-lg, 14px) - 3px);
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    box-shadow: var(--em-sh-warm);
    opacity: 0;
    transform: translateX(0);
    transition: transform var(--em-dur, .28s) cubic-bezier(.4, 0, .2, 1), opacity var(--em-dur-fast, .15s) ease;
    pointer-events: none;
}
.em-civilite-toggle.is-mme .em-civilite-thumb { opacity: 1; transform: translateX(0); }
.em-civilite-toggle.is-m .em-civilite-thumb { opacity: 1; transform: translateX(100%); }
.em-civilite-opt {
    position: relative;
    z-index: 1;
    background: transparent;
    border: none;
    padding: 8px 10px;
    font-size: 13px;
    font-weight: 600;
    color: #94a3b8;
    cursor: pointer;
    border-radius: calc(var(--em-r-lg, 14px) - 3px);
    transition: color var(--em-dur-fast, .15s) ease;
}
.em-civilite-opt[aria-pressed="true"] { color: #fff; }

/* Modale « Fiche client » (dashboard, clients, comparateur) : ouvre la fiche
   360° par-dessus la page en cours au lieu d'y naviguer. Même recette « verre
   liquide » que .em-invite-panel (bordure dégradée en double-background,
   reflet interne, ombre teintée), scène derrière laissée bien plus lisible
   qu'une v1 — le voile teinte sans écraser, contenu chargé en AJAX par
   extranet-ui.js (EmFicheModal). */
.em-fiche-modal {
    position: fixed;
    inset: 0;
    z-index: 70;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
    /* Voile clair teinté chaud (blanc/orangé) plutôt que gris/sombre — le
       relief qui fait « sortir » la modale vient de l'ombre du panneau
       ci-dessous, pas d'un voile assombri. Flou et teinte allégés (retour
       utilisateur : la page hôte disparaissait presque entièrement derrière
       un voile trop opaque) — la page reste identifiable en arrière-plan. */
    background: rgba(255, 246, 240, .22);
    -webkit-backdrop-filter: blur(9px) saturate(150%);
            backdrop-filter: blur(9px) saturate(150%);
    animation: em-fade-up var(--em-dur) var(--em-ease) both;
}
.em-fiche-modal.hidden { display: none; }
.em-fiche-panel {
    position: relative;
    width: 100%;
    max-width: 1120px;
    /* Hauteur fixe (pas max-height seul) : la modale garde la même taille
       quel que soit l'onglet actif ou le volume de contenu — seul le corps
       scrolle en interne, le panneau ne doit jamais "sauter" au changement
       d'onglet (retour utilisateur 2026-08-29). */
    height: 90vh;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    /* Bordure dégradée (padding-box / border-box) : même technique que
       .em-invite-panel, liseré qui capte la lumière sur un bord de verre. */
    background:
        linear-gradient(160deg, rgba(255,255,255,.94) 0%, rgba(249,251,253,.9) 50%, rgba(255,240,234,.72) 100%) padding-box,
        linear-gradient(160deg, rgba(255,255,255,.95), rgba(242,122,84,.38)) border-box;
    border: 1px solid transparent;
    -webkit-backdrop-filter: blur(30px) saturate(180%);
            backdrop-filter: blur(30px) saturate(180%);
    border-radius: var(--em-r-xl);
    /* Voile clair (peu de contraste tout seul) : le relief qui détache la
       modale de la page vient d'ici — ombre plus large/marquée que .em-glass. */
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        0 34px 74px -22px color-mix(in srgb, var(--em-primary) 26%, transparent),
        0 14px 34px -16px rgba(14, 42, 61, .22);
}
.em-fiche-modal-close {
    position: absolute;
    top: .9rem;
    right: .9rem;
    z-index: 5;
    width: 2.1rem;
    height: 2.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--em-r-pill);
    border: 1px solid rgba(255, 255, 255, .7);
    background: rgba(255, 255, 255, .75);
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    color: var(--em-ink-2);
    cursor: pointer;
    box-shadow: var(--em-sh-xs);
    transition: color var(--em-dur-fast) ease, transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
.em-fiche-modal-close:hover { color: var(--em-primary-deep); transform: translateY(-1px) rotate(90deg); box-shadow: var(--em-sh-sm); }
.em-fiche-panel-body {
    overflow-y: auto;
    padding: 1.5rem 1.5rem 1.75rem;
}
@media (min-width: 768px) { .em-fiche-panel-body { padding: 1.85rem 2rem 2.25rem; } }
/* La page hôte reste figée derrière la modale (pas de double scroll). */
body.em-fiche-modal-open { overflow: hidden; }

/* Bouton icône rond (actions de ligne de tableau). */
.em-btn-icon {
    width: 2rem;
    height: 2rem;
    padding: 0;
    border-radius: var(--em-r-pill);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--em-line);
    background: #fff;
    color: var(--em-ink-2);
    cursor: pointer;
    transition: color var(--em-dur-fast) ease, border-color var(--em-dur-fast) ease,
                transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
.em-btn-icon:hover {
    color: var(--em-primary-deep);
    border-color: var(--em-primary-light);
    transform: translateY(-1px);
    box-shadow: var(--em-sh-sm);
}

/* =============================================================================
   5. CARTES & KPI
============================================================================= */
.em-card {
    position: relative;
    background: var(--em-surface);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-lg);
    box-shadow: var(--em-sh-sm);
    transition: transform var(--em-dur) var(--em-ease),
                box-shadow var(--em-dur) var(--em-ease),
                border-color var(--em-dur-fast) ease;
}
/* Carte cliquable : neutralise le style de lien par défaut. */
a.em-card, a.em-card:hover { color: inherit; text-decoration: none; }
.em-card--pad { padding: 1.15rem; }
@media (min-width: 768px) { .em-card--pad { padding: 1.35rem; } }

/* Sub-card "par assuré" — même module que le formulaire de souscription (comparateur.php),
   réimplémenté avec les tokens --em-* (mêmes valeurs que les couleurs codées en dur là-bas). */
.em-subcard {
    background: var(--em-surface-2);
    border: 1.5px solid var(--em-line-2);
    border-radius: .875rem;
    padding: 1rem;
}
.em-field-lbl {
    display: block;
    font-size: .6875rem;
    font-weight: 700;
    color: var(--em-ink-3);
    letter-spacing: .06em;
    text-transform: uppercase;
    margin-bottom: .375rem;
}
.em-subcard-input {
    width: 100%;
    padding: .5rem .75rem;
    background: var(--em-surface);
    border: 1.5px solid var(--em-line-2);
    border-radius: .75rem;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--em-ink);
    outline: none;
    transition: border-color .15s, box-shadow .15s;
}
.em-subcard-input:focus {
    border-color: var(--em-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--em-primary) 12%, transparent);
}
.em-autosave-status {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-size: .75rem;
    font-weight: 700;
    color: var(--em-ink-3);
}
.em-autosave-status--saved { color: var(--em-success); }
.em-autosave-status--error { color: var(--em-danger); cursor: pointer; text-decoration: underline; }
.em-field-invalid { border-color: var(--em-danger) !important; }
.em-field-err {
    margin: .25rem 0 0;
    font-size: .6875rem;
    font-weight: 600;
    color: var(--em-danger);
}
.em-gender-btn {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
    padding: .5rem;
    border-radius: .75rem;
    border: 2px solid var(--em-line-2);
    background: var(--em-surface);
    color: var(--em-ink-3);
    font-size: .6875rem;
    font-weight: 700;
    cursor: pointer;
    transition: all .15s;
}
.em-gender-btn:has(input:checked) {
    border-color: var(--em-primary);
    background: var(--em-primary-light);
    color: var(--em-primary-dark);
}
.em-risk-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .625rem;
    width: 100%;
    padding: .75rem .875rem;
    border-radius: .875rem;
    border: 1.5px solid var(--em-line-2);
    background: var(--em-surface);
    color: var(--em-ink-3);
    text-align: left;
    cursor: pointer;
    transition: all .15s;
}
.em-risk-btn .em-risk-check {
    flex-shrink: 0;
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 999px;
    border: 1px solid var(--em-line-2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .5625rem;
    color: transparent;
}
.em-risk-btn:not(.em-risk-km):has(input:checked) {
    border-color: var(--em-primary);
    background: var(--em-primary-light);
    color: var(--em-ink);
}
.em-risk-btn:not(.em-risk-km):has(input:checked) .em-risk-check {
    background: var(--em-primary);
    border-color: var(--em-primary);
    color: #fff;
}
.em-risk-btn:not(.em-risk-km):has(input:checked) i {
    color: var(--em-primary-dark);
}
/* Déplacements professionnels : pas de case oui/non, les quatre tranches
   répondent seules. Elles occupent la place que la pastille ✓ occupe sur les
   autres risques — à droite du libellé, sur la même ligne — pour que la rangée
   garde leur hauteur au lieu de former un gros bloc. */
.em-risk-km {
    /* Les tranches restent sur la rangée du libellé : c'est le TEXTE qui se replie
       si la colonne est étroite, jamais la ligne de tranches qui tombe dessous.
       container-type : les pastilles passent en libellés courts selon la largeur
       réelle de la rangée, et non celle de la fenêtre — la fiche a une ou deux
       colonnes selon le point de rupture. */
    container-type: inline-size;
    flex-wrap: nowrap;
    cursor: default;
}
.em-risk-km-text { min-width: 0; }
/* Teinte du bloc km. Une tranche est TOUJOURS cochée, donc un :has(input:checked)
   nu l'allumerait en permanence — y compris sur « 0–15 000 », qui n'est pas un
   risque. On teste donc l'absence de la première tranche, ce qui reste du CSS pur
   et suit le clic sans attendre un rechargement. */
.em-risk-km:not(:has(input[value="lt15"]:checked)) {
    border-color: var(--em-primary);
    background: var(--em-primary-light);
    color: var(--em-ink);
}
.em-risk-km:not(:has(input[value="lt15"]:checked)) i { color: var(--em-primary-dark); }
.em-risk-detail {
    display: flex;
    flex-wrap: nowrap;
    flex-shrink: 0;
    gap: 3px;
    margin-left: auto;
}
/* Libellés longs par défaut, courts dès que la rangée descend sous 500 px —
   la place gagnée revient au titre, qui reste ainsi sur une seule ligne. */
.em-seg-court { display: none; }
@container (max-width: 500px) {
    .em-seg-long { display: none; }
    .em-seg-court { display: inline; }
}
/* Compactes à dessein : les quatre tranches doivent tenir sur UNE ligne, aussi
   bien à droite du libellé (fiche à deux colonnes) qu'en dessous une fois
   repliées (mobile, où la carte ne fait plus que ~270 px utiles). */
.em-seg-btn {
    white-space: nowrap;
    padding: .25rem .375rem;
    border-radius: .5rem;
    border: 1.5px solid var(--em-line-2);
    background: var(--em-surface);
    color: var(--em-ink-3);
    font-size: .625rem;
    font-weight: 700;
    cursor: pointer;
    transition: all .15s;
}
.em-seg-btn:hover { border-color: var(--em-primary); }
.em-seg-btn:has(input:checked) {
    border-color: var(--em-primary);
    background: var(--em-primary);
    color: #fff;
}
/* Mobile : la fiche passe en une colonne et la carte ne laisse plus que ~240 px
   utiles — juste sous la largeur des quatre tranches. On récupère les quelques
   pixels manquants sur le padding de la rangée et la taille des pastilles,
   plutôt que de laisser la ligne se couper en 3 + 1. */
@media (max-width: 640px) {
    /* Mobile : la carte ne fait plus que ~250 px utiles — titre ET tranches sur la
       même rangée deviennent impossibles, on autorise alors le repli. */
    .em-risk-km { padding-left: .625rem; padding-right: .625rem; flex-wrap: wrap; }
    .em-risk-detail { flex-wrap: wrap; }
    .em-seg-btn { font-size: .5625rem; padding: .25rem .3125rem; }
}

/* Lift : automatique pour les cartes réellement cliquables, sinon .em-card--lift. */
.em-card:hover { box-shadow: var(--em-sh-md); }
a.em-card:hover,
button.em-card:hover,
.em-card--lift:hover,
.em-card.is-clickable:hover {
    transform: translateY(-4px);
    border-color: var(--em-primary-light);
    border-color: color-mix(in srgb, var(--em-primary) 22%, #fff);
    box-shadow: var(--em-sh-lg), var(--em-sh-warm);
}
.em-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: 1rem 1.15rem;
    border-bottom: 1px solid var(--em-line);
}
.em-card-title {
    font-family: var(--em-font-display);
    font-size: .95rem;
    font-weight: 700;
    letter-spacing: -.015em;
    color: var(--em-ink);
    margin: 0;
}
/* Titre + sous-titre des 4 cartes CTA du dashboard : le conteneur doit pouvoir
   rétrécir sous sa largeur de contenu (min-width: 0) pour que l'ellipse du
   sous-titre se déclenche au lieu de pousser l'icône hors de la carte. */
.em-cta-copy { min-width: 0; flex: 1 1 auto; }
.em-cta-copy .em-card-title,
.em-cta-sub {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Resserre le titre et le sous-titre : sans line-height explicite, ils héritent
   du 1.5 de Tailwind (Preflight), ce qui ouvre un grand vide entre les deux
   lignes malgré le mt-0.5 léger du sous-titre. */
.em-cta-copy .em-card-title { line-height: 1.2; }
.em-cta-sub { line-height: 1.3; margin-top: .1rem; }
.em-cta-card .em-stat-ico,
.em-cta-card .em-badge { flex-shrink: 0; }
.em-card-body { padding: 1.15rem; }
.em-card-foot {
    padding: .8rem 1.15rem;
    border-top: 1px solid var(--em-line);
    background: var(--em-surface-2);
    border-radius: 0 0 var(--em-r-lg) var(--em-r-lg);
}

/* --- KPI --- */
.em-stat {
    position: relative;
    overflow: hidden;
    background: var(--em-surface);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-lg);
    padding: 1.05rem 1.15rem;
    box-shadow: var(--em-sh-sm);
    transition: transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
.em-stat::after {
    /* liseré chaud en haut de carte, révélé au survol */
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: linear-gradient(90deg, var(--em-primary), var(--em-primary-dark));
    opacity: 0;
    transition: opacity var(--em-dur) var(--em-ease);
}
.em-stat:hover { transform: translateY(-3px); box-shadow: var(--em-sh-md), var(--em-sh-warm); }
.em-stat:hover::after { opacity: 1; }
.em-stat-label {
    display: block;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--em-ink-3);
    margin-bottom: .4rem;
}
.em-stat-value {
    font-family: var(--em-font-display);
    font-weight: 700;
    letter-spacing: -.03em;
    line-height: 1.05;
    color: var(--em-ink);
    font-size: clamp(1.65rem, 1.35rem + 1.1vw, 2.15rem);
}
.em-stat-unit { font-size: .55em; font-weight: 700; color: var(--em-ink-3); margin-left: .15em; }
/* Valeur à zéro (rien à afficher ce mois-ci) : encre plus douce pour ne pas
   donner à "0" le même poids visuel qu'un vrai résultat. */
.em-stat-value.is-muted { color: var(--em-ink-3); }
.em-stat-delta {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .5rem;
    padding: .18rem .5rem;
    border-radius: var(--em-r-pill);
    font-size: .72rem;
    font-weight: 800;
    color: var(--em-muted);
    background: var(--em-surface-3);
}
.em-stat-delta.is-up   { color: #15803D; background: #E9F8EE; }
.em-stat-delta.is-down { color: #B91C1C; background: #FDECEC; }
.em-stat-ico {
    position: absolute;
    top: .9rem;
    right: .9rem;
    width: 2.1rem;
    height: 2.1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--em-r-sm);
    font-size: .85rem;
    color: var(--em-primary-deep);
    background: var(--em-primary-light);
    background: color-mix(in srgb, var(--em-primary) 10%, #fff);
}

/* KPI cliquable : au survol, on montre que la carte mène à un graphique
   (le vrai graphique existe déjà plus bas sur la page — on y saute plutôt
   que d'inventer un second mini-graphique séparé). */
.em-stat.is-clickable { cursor: pointer; }
.em-stat-hint {
    position: absolute;
    right: .9rem;
    bottom: .8rem;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .68rem;
    font-weight: 800;
    color: var(--em-primary-deep);
    opacity: 0;
    transform: translateY(3px);
    transition: opacity var(--em-dur) var(--em-ease), transform var(--em-dur) var(--em-ease);
    pointer-events: none;
}
.em-stat.is-clickable:hover .em-stat-hint { opacity: 1; transform: translateY(0); }
.em-stat.is-clickable:hover .em-stat-ico { opacity: 0; }

/* Mini-graphique (sparkline SVG, données réelles des 12 derniers mois). */
.em-spark-wrap { margin-top: .5rem; height: 26px; }
.em-spark { display: block; width: 100%; height: 100%; overflow: visible; }
.em-spark polyline { opacity: .8; transition: opacity var(--em-dur) var(--em-ease); }
.em-stat:hover .em-spark polyline { opacity: 1; }

/* Cible d'un clic KPI → graphique : pulse d'accroche bref, pas une simple ancre. */
.em-chart-pulse { animation: em-chart-pulse 1.4s var(--em-ease); }
@keyframes em-chart-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--em-primary) 45%, transparent); }
    70%  { box-shadow: 0 0 0 14px color-mix(in srgb, var(--em-primary) 0%, transparent); }
    100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--em-primary) 0%, transparent); }
}

/* Séparateur discret entre sections empilées verticalement (dashboard). */
.em-divider {
    height: 1px;
    border: 0;
    margin: .15rem 0;
    background: linear-gradient(90deg, transparent, var(--em-line-2) 15%, var(--em-line-2) 85%, transparent);
}

/* =============================================================================
   KPI + GRAPHIQUES — panneau unifié (dashboard uniquement)
   Volontairement DISTINCT de .em-stat (réutilisé tel quel sur relances.php et
   fiche_shell.php en cartes autonomes) : ici les 4 indicateurs et les 2
   graphiques vivent dans UN seul .em-card, séparés par de fins traits internes
   plutôt que 6 cartes ombrées côte à côte — rendu volontairement plus léger.
============================================================================= */
.em-kpi-strip {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 1024px) { .em-kpi-strip { grid-template-columns: repeat(4, 1fr); } }

.em-kpi-col {
    position: relative;
    display: block;
    padding: 1.15rem 1.25rem 2.1rem;
    border-bottom: 1px solid var(--em-line);
    text-decoration: none;
    color: inherit;
    transition: background-color var(--em-dur-fast) ease, transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
.em-kpi-col:nth-child(odd)  { border-right: 1px solid var(--em-line-2); }
@media (min-width: 1024px) {
    .em-kpi-col { border-bottom: 0; border-right: 1px solid var(--em-line-2); }
    .em-kpi-col:last-child { border-right: 0; }
}
/* En 2 colonnes (mobile + tablette), seule la dernière rangée doit perdre son
   trait bas : avec 4 cartes ça tombe sur les 2 dernières (nth-child(3) et (4)). */
@media (max-width: 1023px) {
    .em-kpi-col:nth-child(3),
    .em-kpi-col:nth-child(4) { border-bottom: 0; }
}
.em-kpi-col.is-clickable { cursor: pointer; }
.em-kpi-col.is-clickable:hover {
    background: var(--em-surface-2);
    z-index: 1;
}
.em-kpi-col.is-clickable:hover .em-stat-hint { opacity: 1; transform: translateY(0); }

/* En-tête icône + libellé de chaque indicateur. */
.em-kpi-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .5rem;
}
.em-kpi-head .em-stat-label { margin-bottom: 0; }

/* Icône sobre, sans badge coloré : registre "relevé financier", pas "app
   ludique". Un seul indicateur (le réalisé, celui qui compte vraiment) porte
   la couleur de marque ; les trois autres restent en encre neutre — la
   hiérarchie se lit dans la retenue, pas dans un arc-en-ciel de pastilles. */
.em-kpi-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    color: var(--em-ink-3);
}
.em-kpi-ico--primary { color: var(--em-primary-deep); }
.em-kpi-col.is-clickable:hover .em-kpi-ico { color: var(--em-ink); }
.em-kpi-col.is-clickable:hover .em-kpi-ico--primary { color: var(--em-primary-deep); }

/* En 2 colonnes sur petit mobile (~160-180px/carte), l'habillage desktop
   (padding généreux, libellé en majuscules espacées, valeur jusqu'à 2.15rem)
   déborde ou force une carte disproportionnée par rapport à ses 3 voisines. */
@media (max-width: 639px) {
    .em-kpi-col { padding: .85rem .85rem 1.7rem; }
    .em-kpi-head { gap: .35rem; margin-bottom: .35rem; }
    .em-kpi-ico { font-size: .7rem; }
    .em-stat-label { font-size: 9.5px; letter-spacing: .05em; }
    .em-stat-value { font-size: clamp(1.25rem, 1.05rem + 2.2vw, 1.65rem); }
    .em-stat-delta { font-size: .64rem; padding: .14rem .4rem; margin-top: .35rem; }
    .em-stat-hint { font-size: .6rem; right: .7rem; bottom: .6rem; }
    .em-spark-wrap { height: 20px; margin-top: .35rem; }
}

/* Desktop (4 colonnes) : le padding-bottom généreux + la sparkline pleine
   hauteur, pensés pour laisser respirer une carte seule, gonflent inutilement
   toute la rangée une fois les 4 cartes côte à côte — l'espace sous la
   sparkline reste vide (le hint "Voir les commissions" n'apparaît qu'au
   survol). On resserre uniquement ici : mobile/tablette gardent leur confort. */
@media (min-width: 1024px) {
    .em-kpi-col { padding: 1.1rem 1.25rem 1.3rem; }
    .em-spark-wrap { height: 18px; margin-top: .4rem; }
    .em-stat-hint { bottom: .5rem; }
}

.em-charts-strip {
    display: grid;
    grid-template-columns: 1fr;
}
@media (min-width: 1024px) { .em-charts-strip { grid-template-columns: 3fr 2fr; } }
.em-chart-col { padding: 1.15rem 1.25rem; }
.em-chart-col + .em-chart-col { border-top: 1px solid var(--em-line); }
@media (min-width: 1024px) {
    .em-chart-col + .em-chart-col { border-top: 0; border-left: 1px solid var(--em-line); }
}
.em-chart-col-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .85rem; }

/* =============================================================================
   6. PILLS, ONGLETS, ÉTATS VIDES, DIVERS
============================================================================= */
.em-pills { display: flex; flex-wrap: wrap; gap: .4rem; }
.em-pill {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .42rem .85rem;
    border: 1px solid var(--em-line-2);
    border-radius: var(--em-r-pill);
    background: #fff;
    color: var(--em-ink-2);
    font-size: .78rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--em-dur-fast) ease, border-color var(--em-dur-fast) ease,
                background-color var(--em-dur-fast) ease, box-shadow var(--em-dur) var(--em-ease);
}
.em-pill:hover { color: var(--em-primary-deep); border-color: var(--em-primary-light); box-shadow: var(--em-sh-xs); }
.em-pill.is-active {
    color: #fff;
    background-color: var(--em-primary);
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    border-color: transparent;
    box-shadow: var(--em-sh-warm);
}
.em-pill-count {
    min-width: 1.25rem;
    padding: 0 .35rem;
    border-radius: var(--em-r-pill);
    background: var(--em-surface-3);
    color: var(--em-ink-2);
    font-size: .68rem;
    font-weight: 800;
    text-align: center;
}
.em-pill.is-active .em-pill-count { background: rgba(255, 255, 255, .26); color: #fff; }

/* Onglets */
.em-tabs {
    position: relative;
    display: inline-flex;
    max-width: 100%;
    gap: .25rem;
    padding: .3rem;
    border-radius: var(--em-r-pill);
    background: color-mix(in srgb, var(--em-primary-light) 38%, #fff);
    overflow-x: auto;
    scrollbar-width: none;
}
.em-tabs::-webkit-scrollbar { display: none; }
.em-tab {
    position: relative;
    z-index: 1;
    flex: 0 0 auto;
    padding: .48rem .9rem;
    border: 0;
    border-radius: var(--em-r-pill);
    background: transparent;
    color: var(--em-primary-dark);
    font-size: .8rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--em-dur-fast) ease;
}
.em-tab:hover { color: var(--em-primary-deep); }
.em-tab.is-active { color: var(--em-ink); font-weight: 700; }

/* Pastille glissante posée derrière l'onglet actif — un seul élément par
   groupe `.em-tabs`, repositionné en JS (transform + width) à chaque
   changement d'onglet plutôt que stylée sur chaque bouton. */
.em-tab-indicator {
    position: absolute;
    top: .3rem;
    left: 0;
    height: calc(100% - .6rem);
    width: 0;
    border-radius: var(--em-r-pill);
    background: linear-gradient(180deg, #fff 0%, #FFFCFB 100%);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        inset 0 -1px 1px color-mix(in srgb, var(--em-primary) 10%, transparent),
        0 3px 8px -4px color-mix(in srgb, var(--em-primary) 40%, transparent);
    opacity: 0;
    z-index: 0;
    pointer-events: none;
    transition: transform var(--em-dur) var(--em-ease), width var(--em-dur) var(--em-ease);
    will-change: transform, width;
}

/* Transition de panneau d'onglet : à poser sur le panneau qui devient visible. */
.em-tabfade { animation: em-tab-in .26s var(--em-ease) both; }

/* Badge générique + pastille d'état */
.em-badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .22rem .55rem;
    border-radius: var(--em-r-pill);
    font-size: .7rem;
    font-weight: 800;
    letter-spacing: .01em;
    background: var(--em-surface-3);
    color: var(--em-ink-2);
}
.em-badge--warn   { background: #FEF6E7; color: #B45309; }
.em-badge--danger { background: #FDECEC; color: #B91C1C; }
.em-badge--ok     { background: #E9F8EE; color: #15803D; }
.em-dot { width: .5rem; height: .5rem; border-radius: var(--em-r-pill); background: currentColor; flex: 0 0 auto; }

/* Avatar dégradé marque */
.em-avatar {
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--em-r-pill);
    font-size: .72rem;
    font-weight: 800;
    color: #fff;
    background-color: var(--em-primary);
    background-image: linear-gradient(135deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    box-shadow: var(--em-sh-warm);
}
.em-avatar--lg { width: 3.25rem; height: 3.25rem; font-size: 1rem; }

/* Barre de progression */
.em-progress {
    height: 6px;
    border-radius: var(--em-r-pill);
    background: var(--em-surface-3);
    overflow: hidden;
}
.em-progress > i {
    display: block;
    height: 100%;
    border-radius: inherit;
    background-color: var(--em-primary);
    background-image: linear-gradient(90deg, var(--em-primary), var(--em-primary-dark));
    transition: width var(--em-dur-slow) var(--em-ease);
}

/* État vide */
.em-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .85rem;
    padding: 2.25rem 1.25rem;
    text-align: center;
}
.em-empty-title {
    font-family: var(--em-font-display);
    font-size: 1rem;
    font-weight: 700;
    color: var(--em-ink);
    margin: 0;
}
.em-empty-text { font-size: .84rem; color: var(--em-ink-2); max-width: 34rem; margin: 0; }

/* Toasts (créés par extranet-ui.js) */
.em-toast-wrap {
    position: fixed;
    top: .75rem;
    right: .75rem;
    left: .75rem;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    pointer-events: none;
}
@media (min-width: 640px) {
    .em-toast-wrap { left: auto; top: 1rem; right: 1rem; width: min(24rem, 92vw); }
}
.em-toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    padding: .75rem .85rem;
    border-radius: var(--em-r);
    border: 1px solid rgba(255, 255, 255, .6);
    border-left: 3px solid var(--em-primary);
    background: rgba(255, 255, 255, .88);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
            backdrop-filter: blur(18px) saturate(150%);
    box-shadow: var(--em-sh-lg);
    color: var(--em-ink);
    font-size: .84rem;
    font-weight: 600;
    animation: em-toast-in .32s var(--em-ease) both;
}
.em-toast.is-out { animation: em-toast-out .22s ease both; }
.em-toast-msg { flex: 1 1 auto; min-width: 0; }
.em-toast-ico { margin-top: .1rem; color: var(--em-primary-dark); }
.em-toast-x {
    flex: 0 0 auto;
    border: 0;
    background: transparent;
    color: var(--em-ink-3);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 .1rem;
}
.em-toast-x:hover { color: var(--em-ink); }
.em-toast--success { border-left-color: var(--em-success); }
.em-toast--success .em-toast-ico { color: var(--em-success); }
.em-toast--error { border-left-color: var(--em-danger); }
.em-toast--error .em-toast-ico { color: var(--em-danger); }
.em-toast--warn { border-left-color: var(--em-warn); }
.em-toast--warn .em-toast-ico { color: #B45309; }
.em-toast--info { border-left-color: var(--em-info); }
.em-toast--info .em-toast-ico { color: #1D4ED8; }

/* =============================================================================
   7. ÉTAPES (macro-statuts) — voir includes/statuts.php
   Fond teinté 12 % + texte foncé : contraste AA garanti (≥ 4.5:1).
============================================================================= */
.em-stage {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .28rem .6rem;
    border-radius: var(--em-r-pill);
    font-size: .72rem;
    font-weight: 800;
    line-height: 1.3;
    white-space: nowrap;
    border: 1px solid transparent;
    --em-stage-c: var(--em-muted);
    --em-stage-t: #334155;
}
/* Couleurs en classe doublée : includes/statuts.php pose aussi des utilitaires
   Tailwind de repli (bg-orange-100 / text-orange-700…) et le CDN Tailwind
   injecte sa feuille APRÈS ce fichier. La spécificité (0,2,0) garde donc le
   thème maître de la palette — indispensable en marque blanche, où « contact »
   doit suivre --wl-primary et non l'orange Tailwind. */
.em-stage.em-stage {
    color: var(--em-stage-t);
    background: var(--em-surface-3);
    background: color-mix(in srgb, var(--em-stage-c) 12%, #fff);
    border-color: var(--em-line-2);
    border-color: color-mix(in srgb, var(--em-stage-c) 26%, #fff);
}
.em-stage::before {
    content: "";
    width: .45rem;
    height: .45rem;
    flex: 0 0 auto;
    border-radius: var(--em-r-pill);
    background: var(--em-stage-c);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--em-stage-c) 18%, transparent);
}
/* statuts.php peut insérer une icône : la pastille ferait alors doublon. */
.em-stage:has(.em-stage-ico)::before { display: none; }
.em-stage-ico { color: var(--em-stage-t); opacity: .75; }
.em-stage-label { min-width: 0; }
.em-stage-count {
    margin-left: .15rem;
    padding: 0 .3rem;
    border-radius: var(--em-r-pill);
    background: rgba(255, 255, 255, .6);
    background: color-mix(in srgb, var(--em-stage-c) 20%, #fff);
    font-size: .68rem;
}

.em-stage.stage-lead      { --em-stage-c: #3B82F6; --em-stage-t: #1D4ED8; }
.em-stage.stage-contact   { --em-stage-c: var(--em-primary); --em-stage-t: var(--em-primary-deep); }
.em-stage.stage-devis     { --em-stage-c: #8B5CF6; --em-stage-t: #6D28D9; }
.em-stage.stage-signature { --em-stage-c: #F59E0B; --em-stage-t: #B45309; }
.em-stage.stage-gagne     { --em-stage-c: #16A34A; --em-stage-t: #15803D; }
.em-stage.stage-perdu     { --em-stage-c: #64748B; --em-stage-t: #334155; }

/* Variante pleine (en-tête de colonne kanban, bandeaux) — même règle de
   spécificité doublée que ci-dessus. */
.em-stage--solid.em-stage--solid {
    color: #fff;
    background: var(--em-stage-c);
    border-color: transparent;
}
.em-stage--solid::before { background: #fff; box-shadow: none; }
.em-stage--solid .em-stage-ico { color: #fff; opacity: .9; }
.em-stage--solid .em-stage-count { background: rgba(255, 255, 255, .25); color: #fff; }
/* Contact en plein : le texte reste blanc sur l'orange foncé (AA). */
.em-stage--solid.stage-contact.em-stage--solid { background: var(--em-primary-dark); }

/* =============================================================================
   7bis. EN-TÊTE FICHE CLIENT (.em-fh-*) — extranet/courtier/includes/fiche_shell.php

   Deux étages de densité opposée, et c'est ce contraste qui fait la structure :
   en haut l'identité, aérée (une personne) ; en dessous le rail d'état, dense
   et cloisonné (des données). Le courtier n'a plus à deviner ce qui est une
   information et ce qui est un bouton — les actions vivent dans l'étage aéré,
   les états dans le rail, jamais l'inverse.

   Les filets du rail viennent d'un `gap: 1px` sur un fond `--em-line` : ils se
   redessinent tout seuls quel que soit le nombre de colonnes après repli, là où
   des `border-left` laisseraient un filet orphelin en début de ligne.
============================================================================= */
.em-fh { display: flex; flex-direction: column; gap: 1.1rem; }

/* ── Étage 1 : identité + actions ─────────────────────────────────────────── */
.em-fh-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem 1.5rem;
    flex-wrap: wrap;
}
.em-fh-id { display: flex; align-items: center; gap: 1rem; min-width: 0; flex: 1 1 20rem; }
.em-fh-name {
    margin: 0;
    font-size: clamp(1.2rem, 1.02rem + .55vw, 1.45rem);
    line-height: 1.15;
    letter-spacing: -.015em;
    overflow-wrap: anywhere;
}
.em-fh-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .3rem .5rem;
    margin: .3rem 0 0;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--em-ink-3);
}
.em-fh-meta-sep { opacity: .45; }

/* Actions : jamais dans le rail — une rangée qui se replie d'un bloc sous
   l'identité quand la place manque.
   `flex-shrink: 1` + `min-width: 0` sont indispensables : avec `flex: 0 0 auto`
   la barre garde sa largeur max-content, le `flex-wrap` interne ne se déclenche
   jamais et les derniers boutons (DER, cloche) débordent hors de la carte. */
.em-fh-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    flex: 0 1 auto;
    min-width: 0;
    justify-content: flex-start;
}
@media (min-width: 1024px) { .em-fh-actions { justify-content: flex-end; } }

/* ── Étage 2 : rail d'état ────────────────────────────────────────────────── */
/* Pas de `overflow: hidden` ici : ça arrondirait proprement les coins visibles,
   mais ça découperait aussi le dropdown Tom Select du select d'étape (qui doit
   déborder sous le rail). Les coins sont donc arrondis segment par segment —
   seuls ceux qui touchent un bord extérieur du rail, par disposition (1/2/4
   colonnes selon la largeur). 4 segments fixes : Contact, Étape, Prochain
   rappel, Compte & consentement. */
.em-fh-rail {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1px;                               /* devient le filet (fond ci-dessous) */
    background: var(--em-line);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-sm);
}
@media (min-width: 640px)  { .em-fh-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .em-fh-rail { grid-template-columns: 1.5fr 1fr 1fr 1fr; } }

.em-fh-seg {
    display: flex;
    flex-direction: column;
    gap: .45rem;
    min-width: 0;
    padding: .7rem .85rem .75rem;
    background: var(--em-surface-2);
}
/* 1 colonne (<640px) : empilé, seuls le 1er et le dernier touchent un bord. */
.em-fh-seg:first-child { border-radius: var(--em-r-sm) var(--em-r-sm) 0 0; }
.em-fh-seg:last-child  { border-radius: 0 0 var(--em-r-sm) var(--em-r-sm); }
/* 2 colonnes (640–1099px) : grille 2×2, chaque coin est un segment différent. */
@media (min-width: 640px) and (max-width: 1099px) {
    .em-fh-seg:nth-child(1) { border-radius: var(--em-r-sm) 0 0 0; }
    .em-fh-seg:nth-child(2) { border-radius: 0 var(--em-r-sm) 0 0; }
    .em-fh-seg:nth-child(3) { border-radius: 0 0 0 var(--em-r-sm); }
    .em-fh-seg:nth-child(4) { border-radius: 0 0 var(--em-r-sm) 0; }
}
/* 4 colonnes (≥1100px) : une seule ligne, seuls les bords gauche/droit arrondissent. */
@media (min-width: 1100px) {
    .em-fh-seg:first-child { border-radius: var(--em-r-sm) 0 0 var(--em-r-sm); }
    .em-fh-seg:last-child  { border-radius: 0 var(--em-r-sm) var(--em-r-sm) 0; }
}
.em-fh-seg-label {
    font-size: 9.5px;
    font-weight: 800;
    letter-spacing: .085em;
    text-transform: uppercase;
    color: var(--em-ink-3);
}
.em-fh-seg-body { display: flex; flex-direction: column; align-items: flex-start; gap: .35rem; min-width: 0; }
.em-fh-empty { font-size: 11.5px; font-weight: 600; color: var(--em-ink-3); }

/* Contact : le lien reste l'action principale (mailto:/tel:), la copie ne se
   découvre qu'au survol/focus — même économie de bruit que .cl-c-actions. */
.em-fh-contact { display: flex; align-items: center; gap: .3rem; min-width: 0; max-width: 100%; }
.em-fh-contact-link {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    min-width: 0;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--em-ink);
    text-decoration: none;
}
.em-fh-contact-link > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.em-fh-contact-link > i { flex: 0 0 auto; font-size: 10px; color: var(--em-ink-3); }
.em-fh-contact-link:hover { color: var(--em-primary-deep); }
.em-fh-contact-link:hover > i { color: var(--em-primary); }
.em-fh-contact-actions {
    display: flex;
    flex: 0 0 auto;
    opacity: 0;
    transform: translateX(-3px);
    transition: opacity var(--em-dur-fast) ease, transform var(--em-dur-fast) ease;
}
.em-fh-contact:hover .em-fh-contact-actions,
.em-fh-contact:focus-within .em-fh-contact-actions { opacity: 1; transform: none; }
@media (pointer: coarse) { .em-fh-contact-actions { opacity: 1; transform: none; } }
.em-fh-mini { width: 1.55rem; height: 1.55rem; font-size: 9.5px; }

/* Étape : un vrai <select> (pas un badge cliquable) — changement AJAX
   immédiat, même contrat que .cl-stage-select du kanban (clients.php), mais
   habillé avec la palette EM_MACROS (déjà globale) plutôt que la palette
   --cl-* du cockpit clients (scopée à #clPage, non disponible ici). La teinte
   arrive par variable inline (--em-fh-stage-c, posée en PHP depuis
   EM_MACROS[...]['hex']) : aucune duplication de la table de couleurs. */
.em-fh-stage-wrap {
    position: relative;
    display: inline-flex;
    max-width: 100%;
}
.em-fh-stage-wrap > select.em-fh-stage-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    min-height: 1.85rem;
    padding: .3rem .95rem .3rem .7rem;
    border-radius: var(--em-r-pill);
    border: 1px solid color-mix(in srgb, var(--em-fh-stage-c) 32%, transparent);
    background: color-mix(in srgb, var(--em-fh-stage-c) 12%, #fff);
    color: color-mix(in srgb, var(--em-fh-stage-c) 55%, black);
    font-family: var(--em-font-sans);
    font-size: .72rem;
    font-weight: 800;
    cursor: pointer;
    outline: none;
}
.em-fh-stage-wrap > select.em-fh-stage-select:focus-visible {
    outline: 2px solid var(--em-primary-dark);
    outline-offset: 2px;
}

/* --- Tom Select adopté (select-init.js enrichit tout <select> au chargement) --- */
.em-fh-stage-wrap .ts-wrapper { width: auto; max-width: 100%; }
.em-fh-stage-wrap .ts-control {
    background: color-mix(in srgb, var(--em-fh-stage-c) 12%, #fff) !important;
    border: 1px solid color-mix(in srgb, var(--em-fh-stage-c) 32%, transparent) !important;
    border-radius: var(--em-r-pill) !important;
    padding: .3rem 1.55rem .3rem .7rem !important;
    min-height: 1.85rem !important;
    box-shadow: none !important;
}
.em-fh-stage-wrap .ts-control .item,
.em-fh-stage-wrap .ts-control input {
    font-size: .72rem !important;
    font-weight: 800 !important;
    color: color-mix(in srgb, var(--em-fh-stage-c) 55%, black) !important;
}
.em-fh-stage-wrap .ts-control:after {
    border-right-color: color-mix(in srgb, var(--em-fh-stage-c) 60%, transparent);
    border-bottom-color: color-mix(in srgb, var(--em-fh-stage-c) 60%, transparent);
}
.em-fh-stage-wrap .ts-wrapper.focus .ts-control,
.em-fh-stage-wrap .ts-wrapper.dropdown-active .ts-control {
    border-color: var(--em-fh-stage-c) !important;
    background: #fff !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--em-fh-stage-c) 18%, transparent) !important;
}
.em-fh-stage-wrap .ts-dropdown { z-index: 45; min-width: 200px; }
.em-fh-stage-wrap .ts-dropdown .option:hover,
.em-fh-stage-wrap .ts-dropdown .option.active {
    background: color-mix(in srgb, var(--em-fh-stage-c) 14%, #fff) !important;
}
.em-fh-stage-wrap .ts-dropdown .option.selected {
    background: color-mix(in srgb, var(--em-fh-stage-c) 20%, #fff) !important;
    font-weight: 800;
}

/* --- État sauvegarde (POST set_status_kanban en vol) --- */
.em-fh-stage-wrap.is-saving > select.em-fh-stage-select,
.em-fh-stage-wrap.is-saving .ts-control { opacity: .6; pointer-events: none; }
.em-fh-stage-wrap.is-saving .ts-control:after { opacity: 0; }
.em-fh-stage-wrap.is-saving::after {
    content: "";
    position: absolute;
    right: .6rem;
    top: 50%;
    width: 11px;
    height: 11px;
    margin-top: -5.5px;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--em-fh-stage-c) 30%, #fff);
    border-top-color: var(--em-fh-stage-c);
    z-index: 3;
    pointer-events: none;
    animation: emFhSpin .7s linear infinite;
}
@keyframes emFhSpin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .em-fh-contact-actions { transition: none; }
    .em-fh-stage-wrap.is-saving::after { animation: none; }
}

/* =============================================================================
   8. TOOLTIPS BOOTSTRAP (surcharge)
   Chaque CTA / KPI / badge de l'extranet porte data-bs-toggle="tooltip".
============================================================================= */
.tooltip {
    --bs-tooltip-bg: rgba(14, 42, 61, .82);
    --bs-tooltip-color: #ffffff;
    --bs-tooltip-opacity: 1;
    --bs-tooltip-border-radius: 10px;
    --bs-tooltip-font-size: 11.5px;
    --bs-tooltip-padding-x: .65rem;
    --bs-tooltip-padding-y: .45rem;
    --bs-tooltip-max-width: 260px;
    font-family: var(--em-font-sans);
}
.tooltip .tooltip-inner {
    background-color: rgba(14, 42, 61, .82);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    backdrop-filter: blur(10px) saturate(140%);
    color: #fff;
    border-radius: 10px;
    padding: .45rem .65rem;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .002em;
    line-height: 1.4;
    text-align: left;
    max-width: 260px;
    box-shadow: 0 8px 22px -10px rgba(14, 42, 61, .4);
}
/* Flèche assortie (BS 5.3 : sélecteurs auto + placements explicites). */
.bs-tooltip-auto[data-popper-placement^="top"] .tooltip-arrow::before,
.bs-tooltip-top .tooltip-arrow::before    { border-top-color: rgba(14, 42, 61, .82); }
.bs-tooltip-auto[data-popper-placement^="bottom"] .tooltip-arrow::before,
.bs-tooltip-bottom .tooltip-arrow::before { border-bottom-color: rgba(14, 42, 61, .82); }
.bs-tooltip-auto[data-popper-placement^="left"] .tooltip-arrow::before,
.bs-tooltip-start .tooltip-arrow::before  { border-left-color: rgba(14, 42, 61, .82); }
.bs-tooltip-auto[data-popper-placement^="right"] .tooltip-arrow::before,
.bs-tooltip-end .tooltip-arrow::before    { border-right-color: rgba(14, 42, 61, .82); }

/* =============================================================================
   9. RÉVÉLATIONS, SQUELETTES, ANIMATIONS
============================================================================= */
.em-reveal {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity .7s var(--em-ease), transform .7s var(--em-ease);
}
.em-reveal.is-in { opacity: 1; transform: none; }

.em-stagger > * {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity .6s var(--em-ease), transform .6s var(--em-ease);
}
.em-stagger.is-in > * { opacity: 1; transform: none; }
.em-stagger.is-in > *:nth-child(1)  { transition-delay: .04s; }
.em-stagger.is-in > *:nth-child(2)  { transition-delay: .10s; }
.em-stagger.is-in > *:nth-child(3)  { transition-delay: .16s; }
.em-stagger.is-in > *:nth-child(4)  { transition-delay: .22s; }
.em-stagger.is-in > *:nth-child(5)  { transition-delay: .28s; }
.em-stagger.is-in > *:nth-child(6)  { transition-delay: .34s; }
.em-stagger.is-in > *:nth-child(7)  { transition-delay: .40s; }
.em-stagger.is-in > *:nth-child(8)  { transition-delay: .46s; }
.em-stagger.is-in > *:nth-child(9)  { transition-delay: .52s; }
.em-stagger.is-in > *:nth-child(10) { transition-delay: .58s; }

/* Squelettes de chargement */
.em-skeleton {
    position: relative;
    overflow: hidden;
    border-radius: var(--em-r-sm);
    background: linear-gradient(90deg, #EEF2F6 0%, #F8FAFC 42%, #EEF2F6 84%);
    background-size: 220% 100%;
    animation: em-shimmer 2.4s linear infinite;
}
.em-skeleton--line { height: .7rem; border-radius: var(--em-r-pill); }
.em-skeleton--title { height: 1.1rem; width: 45%; border-radius: var(--em-r-pill); }
.em-skeleton--card { height: 6.5rem; border-radius: var(--em-r-lg); }
.em-skeleton--avatar { width: 2.25rem; height: 2.25rem; border-radius: var(--em-r-pill); }

@keyframes em-fade-up {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}
@keyframes em-tab-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}
@keyframes em-shimmer {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}
@keyframes em-pulse-dot {
    0%, 100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, .45); }
    50%      { box-shadow: 0 0 0 6px rgba(22, 163, 74, 0); }
}
@keyframes em-toast-in {
    from { opacity: 0; transform: translateY(-10px) scale(.97); }
    to   { opacity: 1; transform: none; }
}
@keyframes em-toast-out {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateY(-8px) scale(.98); }
}
@keyframes em-rise {
    from { transform: scaleY(.25); }
    to   { transform: scaleY(1); }
}
@keyframes em-float {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-5px); }
}

.em-anim-fade-up { animation: em-fade-up .45s var(--em-ease) both; }

/* Machine à écrire (nom tapé au clavier) */
.em-typewriter { display: inline-block; }
.em-typewriter.is-typing {
    border-right: .09em solid currentColor;
    padding-right: .05em;
    animation: em-caret-blink .8s step-end infinite;
}
@keyframes em-caret-blink {
    0%, 100% { border-color: currentColor; }
    50%      { border-color: transparent; }
}

/* =============================================================================
   10. KANBAN
============================================================================= */
.em-kanban {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    overflow-x: auto;
    padding-bottom: .75rem;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
}
.em-kanban-col {
    flex: 0 0 min(85vw, 17.5rem);
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 12rem);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-lg);
    background: rgba(248, 250, 252, .78);
    transition: background-color var(--em-dur-fast) ease, border-color var(--em-dur-fast) ease,
                box-shadow var(--em-dur) var(--em-ease);
}
@media (min-width: 1024px) { .em-kanban-col { flex: 1 1 0; min-width: 15rem; } }

.em-kanban-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .7rem .8rem;
    border-bottom: 1px solid var(--em-line);
    position: sticky;
    top: 0;
    /* quasi opaque : les cartes ne doivent pas transparaître sous l'en-tête collé */
    background: rgba(248, 250, 252, .96);
    -webkit-backdrop-filter: blur(6px);
            backdrop-filter: blur(6px);
    border-radius: var(--em-r-lg) var(--em-r-lg) 0 0;
    z-index: 1;
}
.em-kanban-list {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: .6rem;
    overflow-y: auto;
    min-height: 5rem;
}
.em-kanban-card {
    background: #fff;
    border: 1px solid var(--em-line);
    border-radius: var(--em-r);
    padding: .7rem .8rem;
    box-shadow: var(--em-sh-xs);
    cursor: grab;
    transition: transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease),
                border-color var(--em-dur-fast) ease;
}
.em-kanban-card:hover {
    transform: translateY(-2px);
    border-color: var(--em-primary-light);
    border-color: color-mix(in srgb, var(--em-primary) 24%, #fff);
    box-shadow: var(--em-sh-sm);
}
.em-kanban-card:active { cursor: grabbing; }
.em-kanban-card.is-dragging {
    opacity: .45;
    transform: rotate(1.5deg) scale(.98);
    box-shadow: var(--em-sh-lg);
}
/* Drop direct (statut cible sans ambiguïté) : le temps du POST. */
.em-kanban-card.is-saving {
    opacity: .55;
    pointer-events: none;
}
/* Colonne survolée pendant le drag */
.em-kanban-col.is-over {
    background: var(--em-primary-light);
    background: color-mix(in srgb, var(--em-primary) 7%, #fff);
    border-color: var(--em-primary);
    border-color: color-mix(in srgb, var(--em-primary) 40%, #fff);
    box-shadow: var(--em-sh-warm);
}
/* Emplacement fantôme inséré au point de dépôt */
.em-kanban-ghost {
    height: 3.25rem;
    border-radius: var(--em-r);
    border: 1px dashed var(--em-primary);
    border-color: color-mix(in srgb, var(--em-primary) 45%, #fff);
    background: var(--em-primary-light);
    background: color-mix(in srgb, var(--em-primary) 6%, #fff);
}
.em-kanban-col.is-collapsed .em-kanban-list { display: none; }
.em-kanban-col.is-collapsed { flex: 0 0 3.25rem; }
.em-kanban-col.is-collapsed .em-kanban-head { flex-direction: column; padding: .7rem .4rem; }
.em-kanban-more {
    margin: .2rem .6rem .6rem;
    text-align: center;
    font-size: .74rem;
    font-weight: 700;
    color: var(--em-primary-deep);
}

/* =============================================================================
   11. MOCKS ANIMÉS (esprit Lumo Data)
   Mini-fenêtre CSS pure : 3 pastilles, lignes shimmer, badge « EN DIRECT ».
   Remplace les grosses icônes figées dans les cartes CTA et les états vides.
============================================================================= */
.em-mock {
    position: relative;
    width: 100%;
    max-width: 14rem;
    border-radius: var(--em-r);
    border: 1px solid var(--em-line);
    background: #fff;
    box-shadow: var(--em-sh-sm);
    overflow: hidden;
    transition: transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
.em-card:hover .em-mock,
.em-mock--float { transform: translateY(-3px); box-shadow: var(--em-sh-md); }
.em-mock--sm { max-width: 10rem; }
.em-mock--wide { max-width: none; }

.em-mock-bar {
    display: flex;
    align-items: center;
    gap: .3rem;
    padding: .45rem .55rem;
    border-bottom: 1px solid var(--em-line);
    background: linear-gradient(180deg, #fff, #FBFCFD);
}
.em-mock-dot { width: .4rem; height: .4rem; border-radius: var(--em-r-pill); background: #E2E8F0; }
.em-mock-dot:nth-child(1) { background: var(--em-primary); background: color-mix(in srgb, var(--em-primary) 55%, #fff); }
.em-mock-dot:nth-child(2) { background: #FBE3B8; }
.em-mock-dot:nth-child(3) { background: #DCE7EF; }
.em-mock-title {
    margin-left: .3rem;
    height: .4rem;
    width: 38%;
    border-radius: var(--em-r-pill);
    background: #EEF2F6;
}

.em-mock-body { display: grid; gap: .45rem; padding: .7rem .6rem .9rem; }
.em-mock-line {
    height: .42rem;
    border-radius: var(--em-r-pill);
    background: linear-gradient(90deg, #EEF2F6 0%, #F8FAFC 42%, #EEF2F6 84%);
    background-size: 220% 100%;
    animation: em-shimmer 2.6s linear infinite;
}
.em-mock-line:nth-child(1) { width: 92%; }
.em-mock-line:nth-child(2) { width: 68%; animation-delay: .25s; }
.em-mock-line:nth-child(3) { width: 80%; animation-delay: .5s; }
.em-mock-line:nth-child(4) { width: 54%; animation-delay: .75s; }
.em-mock-line--accent {
    background: var(--em-primary-light);
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--em-primary) 26%, #fff) 0%,
        color-mix(in srgb, var(--em-primary) 12%, #fff) 42%,
        color-mix(in srgb, var(--em-primary) 26%, #fff) 84%);
    background-size: 220% 100%;
}

/* Mini-graphe à barres (variante « production »). */
.em-mock-bars {
    display: flex;
    align-items: flex-end;
    gap: .28rem;
    height: 3.1rem;
    padding: 0 .6rem .7rem;
}
.em-mock-bars > i {
    flex: 1 1 0;
    display: block;
    border-radius: 3px 3px 0 0;
    transform-origin: bottom;
    background: var(--em-primary);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--em-primary) 85%, #fff),
        color-mix(in srgb, var(--em-primary-dark) 70%, #fff));
    animation: em-rise 1.6s var(--em-ease) infinite alternate;
}
.em-mock-bars > i:nth-child(1) { height: 42%; animation-delay: 0s; }
.em-mock-bars > i:nth-child(2) { height: 68%; animation-delay: .12s; }
.em-mock-bars > i:nth-child(3) { height: 52%; animation-delay: .24s; }
.em-mock-bars > i:nth-child(4) { height: 88%; animation-delay: .36s; }
.em-mock-bars > i:nth-child(5) { height: 64%; animation-delay: .48s; }

/* Badge « EN DIRECT » pulsant */
.em-mock-badge {
    position: absolute;
    right: .45rem;
    bottom: .45rem;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .18rem .45rem;
    border-radius: var(--em-r-pill);
    background: rgba(255, 255, 255, .9);
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    border: 1px solid rgba(14, 42, 61, .07);
    box-shadow: var(--em-sh-xs);
    font-size: .55rem;
    font-weight: 900;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: #15803D;
}
.em-mock-badge::before {
    content: "";
    width: .34rem;
    height: .34rem;
    border-radius: var(--em-r-pill);
    background: var(--em-success);
    animation: em-pulse-dot 2s ease-in-out infinite;
}
/* Variante marque (badge orange, ex. « Nouveau »). */
.em-mock-badge--brand { color: var(--em-primary-deep); }
.em-mock-badge--brand::before {
    background: var(--em-primary);
    animation: none;
}

/* =============================================================================
   11 bis. FRISE CTA — mocks animés (namespace .emc-*)
   Une scène SVG par carte, propre à l'action, en boucle continue et
   indépendante (durées désaccordées pour que les 4 cartes ne respirent pas au
   même rythme). Distinct du bandeau héro (.ems-*/.emj-, section « JOURNEY »
   plus haut) : ici pas de relais entre scènes, chaque carte tourne seule.
   Règle héritée du bandeau : jamais de transform CSS animée sur un élément qui
   porte aussi un attribut transform SVG statique (l'un écrase l'autre) — toute
   inclinaison/rotation fixe vit sur un <g> parent séparé, jamais animé. L'état
   de repos des règles CSS (hors animation) est l'état final : c'est le repli
   naturel pour prefers-reduced-motion, qui se contente de couper l'animation.
============================================================================= */
.emc-body { padding: .5rem .5rem .6rem; display: flex; align-items: center; justify-content: center; }
.emc-body svg {
    display: block; width: 100%; height: 64px; overflow: visible;
    opacity: .85; transition: opacity var(--em-dur) var(--em-ease);
}
.em-card:hover .emc-body svg { opacity: 1; }

.emc-row-bg { fill: var(--em-surface-3); }
.emc-row-tx { fill: var(--em-ink-3); opacity: .55; }
.emc-row-tx--dim { opacity: .35; }
.emc-check { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* Avatar « tête de profil » partagé, défini une fois dans dashboard.php et
   référencé via <use href="#emcAvatar"> dans les cartes 1, 3 et 4. */
.emc-avatar-bg { fill: var(--em-surface-3); stroke: var(--em-line-2); stroke-width: .5; }
.emc-avatar-fig { fill: var(--em-ink-3); opacity: .68; }

/* ---- Scène 1 : Ajouter des leads — un fichier tombe, se pose, se dissout
   pendant que la liste se construit ligne par ligne. Budget de visibilité :
   ~90 % du cycle affiche quelque chose (fichier OU liste), le reste
   (~10 %, à cheval sur la fin/le début de boucle) est le seul temps mort. ---- */
.emc-scene--leads { --emc-dur: 5.4s; }
.emc-l-file {
    opacity: 0; transform: translateY(-30px) scale(.94);
    transform-box: fill-box; transform-origin: center;
    animation: emcLeadFile var(--emc-dur) var(--em-ease) infinite;
}
.emc-l-filebg { fill: color-mix(in srgb, var(--em-primary) 14%, #fff); stroke: var(--em-primary); stroke-opacity: .5; }
.emc-l-filefold { fill: color-mix(in srgb, var(--em-primary) 24%, #fff); }
.emc-l-fileline { fill: var(--em-primary); opacity: .55; }
@keyframes emcLeadFile {
    0%   { opacity: 0; transform: translateY(-30px) scale(.94); }
    4%   { opacity: 0; transform: translateY(-30px) scale(.94); animation-timing-function: var(--em-ease-pop); }
    13%  { opacity: 1; transform: translateY(0) scale(1); animation-timing-function: linear; }
    23%  { opacity: 1; transform: translateY(0) scale(1); animation-timing-function: var(--em-ease-in); }
    30%  { opacity: 0; transform: translateY(5px) scale(.96); }
    100% { opacity: 0; transform: translateY(-30px) scale(.94); }
}
.emc-l-row { opacity: 1; transform: translateY(0); animation: emcLeadRow var(--emc-dur) var(--em-ease) infinite; animation-delay: var(--d, 0s); }
@keyframes emcLeadRow {
    0%   { opacity: 0; transform: translateY(6px); }
    26%  { opacity: 0; transform: translateY(6px); animation-timing-function: var(--em-ease-pop); }
    34%  { opacity: 1; transform: translateY(0); animation-timing-function: linear; }
    90%  { opacity: 1; transform: translateY(0); animation-timing-function: var(--em-ease-in); }
    97%  { opacity: 0; transform: translateY(-4px); }
    100% { opacity: 0; transform: translateY(6px); }
}

/* ---- Scène 2 : Nouvelle étude — les logos partenaires glissent depuis la
   gauche, le meilleur ressort (fond teinté + reflet + ombre portée douce).
   La 1ère ligne (delay 0s) est visible dès ~14 % du cycle, temps mort minimal. ---- */
.emc-scene--tarifs { --emc-dur: 5.8s; }
.emc-t-brand { filter: grayscale(1) opacity(.62); }
.emc-t-price { fill: var(--em-ink-3); opacity: .4; }
.emc-t-price--best { fill: var(--em-primary-deep); }
.emc-t-row { opacity: 1; transform: translateX(0); animation: emcTarifRow var(--emc-dur) var(--em-ease) infinite; animation-delay: var(--d, 0s); }
@keyframes emcTarifRow {
    0%   { opacity: 0; transform: translateX(-18px); }
    3%   { opacity: 0; transform: translateX(-18px); animation-timing-function: var(--em-ease-pop); }
    14%  { opacity: 1; transform: translateX(0); animation-timing-function: linear; }
    90%  { opacity: 1; transform: translateX(0); animation-timing-function: var(--em-ease-in); }
    97%  { opacity: 0; transform: translateX(7px); }
    100% { opacity: 0; transform: translateX(-18px); }
}
.emc-t-best-bg {
    fill: color-mix(in srgb, var(--em-primary) 13%, #fff); stroke: var(--em-primary); stroke-opacity: .35;
    opacity: 1; transform: scale(1); transform-box: fill-box; transform-origin: center;
    filter: drop-shadow(0 0 0 transparent);
    animation: emcTarifBest var(--emc-dur) var(--em-ease) infinite;
}
@keyframes emcTarifBest {
    0%   { opacity: 0; transform: scale(.96); filter: drop-shadow(0 0 0 transparent); }
    16%  { opacity: 0; transform: scale(.96); filter: drop-shadow(0 0 0 transparent); animation-timing-function: var(--em-ease-pop); }
    28%  { opacity: 1; transform: scale(1); filter: drop-shadow(0 4px 10px color-mix(in srgb, var(--em-primary) 38%, transparent)); animation-timing-function: linear; }
    90%  { opacity: 1; transform: scale(1); filter: drop-shadow(0 4px 10px color-mix(in srgb, var(--em-primary) 38%, transparent)); animation-timing-function: var(--em-ease-in); }
    97%  { opacity: 0; transform: scale(.96); filter: drop-shadow(0 0 0 transparent); }
    100% { opacity: 0; transform: scale(.96); filter: drop-shadow(0 0 0 transparent); }
}
.emc-t-glint { fill: #fff; opacity: 0; transform: translateX(-10px); animation: emcTarifGlint var(--emc-dur) linear infinite; }
@keyframes emcTarifGlint {
    0%   { opacity: 0; transform: translateX(-10px); }
    33%  { opacity: 0; transform: translateX(-10px); animation-timing-function: ease-out; }
    37%  { opacity: .6; transform: translateX(0px); animation-timing-function: linear; }
    48%  { opacity: .6; transform: translateX(150px); animation-timing-function: ease-in; }
    51%  { opacity: 0; transform: translateX(150px); }
    100% { opacity: 0; transform: translateX(-10px); }
}

/* ---- Scène 3 : Relancer — le message se pose, puis une enveloppe (email) et
   une bulle SMS partent chacune de leur côté ; une coche confirme l'envoi.
   Le message est visible quasiment tout le cycle (arrive à 4 %). ---- */
.emc-scene--relance { --emc-dur: 5.6s; }
.emc-r-msg-bg { fill: var(--em-surface-3); }
.emc-r-msg { opacity: 1; transform: translateY(0); animation: emcRelanceMsg var(--emc-dur) var(--em-ease) infinite; }
@keyframes emcRelanceMsg {
    0%   { opacity: 0; transform: translateY(4px); }
    4%   { opacity: 1; transform: translateY(0); }
    92%  { opacity: 1; transform: translateY(0); animation-timing-function: var(--em-ease-in); }
    97%  { opacity: 0; transform: translateY(-3px); }
    100% { opacity: 0; transform: translateY(4px); }
}
.emc-r-env-body { fill: color-mix(in srgb, var(--em-primary) 16%, #fff); stroke: var(--em-primary); stroke-opacity: .55; }
.emc-r-env-flap { fill: none; stroke: var(--em-primary); stroke-opacity: .7; stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; }
.emc-r-envelope {
    opacity: 0; transform: translate(0, 0) scale(.85);
    transform-box: fill-box; transform-origin: center;
    animation: emcRelanceEnvelope var(--emc-dur) var(--em-ease) infinite;
}
@keyframes emcRelanceEnvelope {
    0%   { opacity: 0; transform: translate(0, 0) scale(.85); }
    13%  { opacity: 0; transform: translate(0, 0) scale(.85); animation-timing-function: var(--em-ease-pop); }
    18%  { opacity: 1; transform: translate(3px, -1px) scale(1); animation-timing-function: var(--em-ease-out); }
    35%  { opacity: 0; transform: translate(48px, -10px) scale(.94); }
    100% { opacity: 0; transform: translate(0, 0) scale(.85); }
}
.emc-r-sms-body { fill: color-mix(in srgb, var(--em-primary) 16%, #fff); stroke: var(--em-primary); stroke-opacity: .55; }
.emc-r-sms-tail { fill: color-mix(in srgb, var(--em-primary) 16%, #fff); stroke: var(--em-primary); stroke-opacity: .55; stroke-width: 1.1; stroke-linejoin: round; }
.emc-r-sms-line { fill: var(--em-primary); opacity: .55; }
.emc-r-sms {
    opacity: 0; transform: translate(0, 0) scale(.85);
    transform-box: fill-box; transform-origin: center;
    animation: emcRelanceSms var(--emc-dur) var(--em-ease) infinite;
}
@keyframes emcRelanceSms {
    0%   { opacity: 0; transform: translate(0, 0) scale(.85); }
    25%  { opacity: 0; transform: translate(0, 0) scale(.85); animation-timing-function: var(--em-ease-pop); }
    30%  { opacity: 1; transform: translate(3px, 1px) scale(1); animation-timing-function: var(--em-ease-out); }
    47%  { opacity: 0; transform: translate(46px, 11px) scale(.94); }
    100% { opacity: 0; transform: translate(0, 0) scale(.85); }
}
.emc-r-sent-bg { fill: #E9F8EE; stroke: var(--em-success); stroke-opacity: .4; }
.emc-r-sent-check { stroke: var(--em-success); }
.emc-r-sent {
    opacity: 1; transform: scale(1); transform-box: fill-box; transform-origin: center;
    animation: emcRelanceSent var(--emc-dur) var(--em-ease) infinite;
}
@keyframes emcRelanceSent {
    0%   { opacity: 0; transform: scale(.75); }
    49%  { opacity: 0; transform: scale(.75); animation-timing-function: var(--em-ease-pop); }
    58%  { opacity: 1; transform: scale(1); animation-timing-function: linear; }
    90%  { opacity: 1; transform: scale(1); animation-timing-function: var(--em-ease-in); }
    96%  { opacity: 0; transform: scale(.9); }
    100% { opacity: 0; transform: scale(.75); }
}

/* ---- Scène 4 : Suivi clients — les lignes sont déjà en place, seul le badge
   de statut de chacune se coche l'un après l'autre. ---- */
.emc-scene--suivi { --emc-dur: 6.2s; }
.emc-s-row { opacity: 1; transform: scale(1); transform-box: fill-box; transform-origin: center; animation: emcSuiviRow var(--emc-dur) var(--em-ease) infinite; animation-delay: var(--d, 0s); }
@keyframes emcSuiviRow {
    0%   { opacity: 0; transform: scale(.96); }
    3%   { opacity: 0; transform: scale(.96); animation-timing-function: var(--em-ease-pop); }
    11%  { opacity: 1; transform: scale(1); animation-timing-function: linear; }
    92%  { opacity: 1; transform: scale(1); animation-timing-function: var(--em-ease-in); }
    97%  { opacity: 0; transform: scale(.98); }
    100% { opacity: 0; transform: scale(.96); }
}
.emc-s-pill-bg { fill: #E9F8EE; animation: emcSuiviPillBg var(--emc-dur) ease infinite; animation-delay: var(--d, 0s); }
@keyframes emcSuiviPillBg {
    0%   { fill: #EEF2F6; }
    16%  { fill: #EEF2F6; animation-timing-function: ease-in-out; }
    28%  { fill: rgba(245, 158, 11, .20); }
    46%  { fill: rgba(245, 158, 11, .20); animation-timing-function: ease-in-out; }
    58%  { fill: #E9F8EE; }
    100% { fill: #E9F8EE; }
}
.emc-s-pill-check {
    stroke: var(--em-success); opacity: 1; transform: scale(1);
    transform-box: fill-box; transform-origin: center;
    animation: emcSuiviPillCheck var(--emc-dur) ease infinite; animation-delay: var(--d, 0s);
}
@keyframes emcSuiviPillCheck {
    0%   { opacity: 0; transform: scale(.6); }
    56%  { opacity: 0; transform: scale(.6); animation-timing-function: var(--em-ease-pop); }
    64%  { opacity: 1; transform: scale(1); }
    100% { opacity: 1; transform: scale(1); }
}

/* =============================================================================
   12. RESPONSIVE, TABLES, IMPRESSION
============================================================================= */
/* Air entre le mock et le bouton CTA des 4 cartes du dashboard — évite d'utiliser
   les utilitaires Tailwind mt- / mb- ici : Bootstrap (chargé sur la même page
   pour les modales/dropdowns) définit ses propres classes mt- / mb- en
   !important et les emporterait silencieusement. */
.em-cta-mock { margin-top: .5rem; margin-bottom: 1.1rem; }

/* Les 4 cartes CTA du dashboard : 2×2 dès mobile (au lieu d'empilées), avec un
   habillage resserré pour que le titre, le mock animé et le bouton restent
   lisibles à mi-largeur d'écran. La grille reste en 2 colonnes (grid-cols-2)
   jusqu'au breakpoint lg (1024px) de Tailwind — l'habillage resserré doit donc
   couvrir toute cette plage et pas seulement <640px, sans quoi les cartes
   entre 640px et 1023px gardent titres/boutons taille desktop dans une
   largeur de carte deux fois plus étroite que prévu, et débordent. */
@media (max-width: 1023px) {
    .em-cta-card.em-card--pad { padding: .8rem .75rem; }
    /* En 2 colonnes mobile (~163px/carte), le titre/sous-titre en ellipse
       1 ligne coupe des mots entiers (« Ajouter des lea… ») : la carte est
       trop étroite pour ce traitement pensé pour le format 4 colonnes
       desktop. On repasse en habillage 2 lignes (clamp, pas de coupe au
       milieu d'un mot) plutôt que de continuer à tronquer. */
    .em-cta-card .em-card-title {
        font-size: .74rem;
        line-height: 1.25;
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    .em-cta-card .em-cta-sub {
        font-size: 9.5px;
        line-height: 1.3;
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
    .em-cta-card .em-stat-ico { width: 1.7rem; height: 1.7rem; font-size: .7rem; }
    .em-cta-card .em-mock { margin-top: .4rem; margin-bottom: .75rem; }
    .em-cta-card .em-btn,
    .em-cta-card .em-btn-soft,
    .em-cta-card .em-btn-ghost { padding: .5rem .6rem; font-size: .72rem; gap: .35rem; }
    /* Menu « Ajouter » : à taille desktop (.82rem + padding 1rem hérité de
       Bootstrap), le texte des options ("Importer un CSV", "Lien
       d'invitation") remplit toute la largeur de la carte étroite et
       déborde visuellement de la liste. La colonne icône (16px + gap .5rem)
       ne rétrécit pas avec le texte : sur la ligne la plus longue ("Lien
       d'invitation"), elle grignote une part disproportionnée de la largeur
       et pousse le texte contre le bord droit, alors que le bord gauche
       garde son padding intact — on la resserre pour rendre de la place au
       texte et rééquilibrer les deux côtés. */
    /* Le texte des options ("Importer un CSV" est la plus large, pas "Lien
       d'invitation" comme on pourrait le penser) déborde du padding de
       l'item en nowrap dès que le menu est calé pile sur la largeur du
       bouton (~137px) : on l'élargit de quelques pixels de chaque côté en
       piochant dans le padding de la carte (encore libre autour du bouton),
       et .dropdown-center (Bootstrap 5.3) le garde centré sous le bouton
       malgré cette largeur supérieure. */
    .em-cta-card .em-add-dropdown .dropdown-menu { font-size: .74rem; width: calc(100% + 26px); min-width: calc(100% + 26px); }
    .em-cta-card .em-add-dropdown .dropdown-item { padding-left: .55rem; padding-right: .55rem; gap: .4rem; }
    .em-cta-card .em-add-dropdown .em-dd-ic { width: 13px; font-size: .82em; }
    /* La carte qui porte un dropdown ouvert doit dominer ses voisines de
       grille (rangée du dessous) : sans ça le menu déroulé, plus haut que
       l'espace restant dans la carte, se mélange visuellement à la carte
       suivante au lieu de flotter clairement au-dessus. */
    .em-cta-card:has(.dropdown-menu.show) { position: relative; z-index: 5; }
}

/* Cartes graphiques (Production mensuelle / Funnel) : padding resserré et
   hauteur de canevas adaptée sur mobile pour laisser plus de place aux axes. */
@media (max-width: 639px) {
    .em-chart-col { padding: .9rem .7rem; }
    .em-chart-col-head { margin-bottom: .6rem; flex-wrap: wrap; }
    .em-chart-col-head .em-card-title { font-size: .85rem; }
}
.em-chart-canvas { position: relative; height: 220px; width: 100%; max-width: 100%; }
.em-chart-canvas canvas { max-width: 100%; }
@media (max-width: 639px) {
    .em-chart-canvas--funnel { height: 250px; }
}
/* Funnel : léger halo coloré sous les barres pour un rendu plus vivant/pétillant
   sans alourdir le composant (pas de plugin Chart.js supplémentaire). */
.em-chart-canvas--funnel canvas { filter: drop-shadow(0 6px 14px rgba(14, 42, 61, .12)); }
/* Wrapper de table scrollable : aucune page ne doit défiler horizontalement. */
.em-scroll-x {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
}
.em-scroll-x > table { width: 100%; }

/* Liste « cartes » de secours pour les tableaux sur très petit écran. */
@media (max-width: 480px) {
    .em-hide-xs { display: none !important; }
}

/* Contenu principal : espace pour la bulk bar mobile quand elle est présente. */
body.em-shell.has-bulkbar { padding-bottom: 5.5rem; }
@media (min-width: 768px) { body.em-shell.has-bulkbar { padding-bottom: 0; } }

/* =============================================================================
   MUR DE LOGOS PARTENAIRES (dashboard)
   Repris du mur de logos du site public (.logo-marquee / js/logos.js) : deux
   rangées disjointes en boucle infinie, sens opposés — mêmes logos réels,
   mêmes fichiers assets/logos/, adaptés aux tokens --em-* de l'extranet.
   Peuplé par extranet/assets/em-logo-marquee.js.
============================================================================= */
.em-logo-marquee { margin-top: .25rem; }
.em-logo-eyebrow {
    display: flex; align-items: center; justify-content: center; gap: 1rem;
    margin: 0 auto 1.5rem; font-size: 10.5px; font-weight: 800; text-transform: uppercase;
    letter-spacing: .28em; color: var(--em-ink-3);
}
.em-logo-eyebrow::before, .em-logo-eyebrow::after {
    content: ""; height: 1px; width: clamp(20px, 6vw, 60px);
    background: linear-gradient(90deg, transparent, var(--em-primary-light));
}
.em-logo-eyebrow::after { transform: scaleX(-1); }

.em-logo-row {
    position: relative; overflow: hidden; padding-block: 1rem;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}
.em-logo-row--gap { margin-top: .3rem; }
.em-logo-track {
    display: flex; width: max-content; align-items: center; gap: 2.75rem;
    animation: em-logo-marquee 60s linear infinite; will-change: transform;
}
.em-logo-track--reverse { animation-direction: reverse; animation-duration: 76s; }
.em-logo-row:hover .em-logo-track, .em-logo-row:focus-within .em-logo-track { animation-play-state: paused; }
@keyframes em-logo-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.em-logo-mark {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    height: 2.2rem; transition: transform .5s cubic-bezier(.2, .7, .2, 1);
}
.em-logo-mark:hover { transform: translateY(-3px); }
a.em-logo-mark { color: inherit; text-decoration: none; cursor: pointer; }
a.em-logo-mark:focus-visible { outline: 2px solid var(--em-primary-dark, #D65A3B); outline-offset: 3px; border-radius: 4px; }
.em-logo-mark img {
    height: 1.65rem; width: auto; max-width: 130px; user-select: none;
    filter: grayscale(1) opacity(.5); transition: filter .5s ease;
}
.em-logo-mark:hover img { filter: grayscale(0) opacity(1); }
.em-logo-word { display: none; }
.em-logo-mark.is-word { height: auto; }
.em-logo-mark.is-word .em-logo-word {
    display: inline-flex; font-family: var(--em-font-display); font-weight: 700;
    font-size: 1.05rem; letter-spacing: -.01em; white-space: nowrap; color: var(--em-ink-2); opacity: .6;
}
.em-logo-mark.is-word:hover .em-logo-word { opacity: 1; }

@media (max-width: 640px) {
    .em-logo-track { gap: 2rem; animation-duration: 42s; }
    .em-logo-track--reverse { animation-duration: 54s; }
    .em-logo-mark { height: 1.9rem; }
    .em-logo-mark img { height: 1.45rem; max-width: 100px; }
}

/* Accessibilité : lecteurs d'écran uniquement. */
.em-sr {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* --- Mouvement réduit : on garde le décor, on coupe l'animation. --- */
@media (prefers-reduced-motion: reduce) {
    body.em-shell::before { background-position: 0 0; }
    .em-reveal, .em-stagger > * { opacity: 1; transform: none; transition: none; }
    .em-skeleton, .em-mock-line, .em-mock-bars > i, .em-mock-badge::before,
    .em-typewriter.is-typing, .em-logo-track { animation: none; }
    .emc-l-file, .emc-l-row, .emc-t-row, .emc-t-best-bg, .emc-t-glint,
    .emc-r-msg, .emc-r-envelope, .emc-r-sms, .emc-r-sent,
    .emc-s-row, .emc-s-pill-bg, .emc-s-pill-check { animation: none; }
    .emc-body svg { opacity: 1; }
    .em-btn:hover, .em-btn-ghost:hover, .em-btn-soft:hover,
    .em-card--lift:hover, a.em-card:hover, button.em-card:hover,
    .em-stat:hover, .em-kanban-card:hover, .em-mock--float { transform: none; }
    .em-toast, .em-tabfade, .em-anim-fade-up, .em-bulkbar { animation: none; }
    .em-tab-indicator { transition: none; }
}

/* --- Impression : pas de décor, pas d'ombres. --- */
@media print {
    body.em-shell::before,
    body.em-shell::after,
    .em-bulkbar,
    .em-toast-wrap { display: none !important; }
    .em-card, .em-stat, .em-glass {
        box-shadow: none !important;
        border-color: #E2E8F0 !important;
        background: #fff !important;
    }
}

/* =============================================================================
   13. APERÇU RÉALISTE DES MODÈLES — CADRES D'APPAREILS & BASCULE SEGMENTÉE
   -----------------------------------------------------------------------------
   Onglet « Modèles email & SMS » (mon-compte.php) : le courtier doit voir son
   modèle tel que son client le recevra. Trois cadres — MacBook (email
   desktop), iPhone (email mobile) et iPhone (conversation SMS/iMessage) —
   plus le chrome Gmail simulé autour de l'iframe desktop, la bulle iMessage,
   et la bascule « Variables | Valeurs test » / « Ordinateur | Mobile » qui
   réutilise la mécanique du pouce glissant de .em-civilite-toggle (mêmes
   variables --em-dur / --em-r-lg / --em-sh-warm, même courbe : harmonisation,
   pas duplication).

   Le rendu HTML de l'email lui-même vit dans une <iframe srcdoc> (isolation
   du bac à sable, voir template_preview.php) : ce fichier ne touche jamais à
   son contenu, seulement au cadre qui l'entoure.
============================================================================= */

/* Jetons locaux, additifs (ce bloc :root complète celui de tête de fichier,
   il ne le remplace pas — même principe que le @supports color-mix plus
   haut). Le biseau slate et les deux ombres portées sont le CHÂSSIS PHYSIQUE
   d'un appareil, pas une couleur de marque : littéraux assumés, jamais de
   var(--em-primary) ici. */
:root {
    --em-dev-bezel:      linear-gradient(180deg, #1E293B 0%, #020617 100%);
    /* MacBook : liseré clair en haut du capot (reflet). */
    --em-dev-shadow:     0 25px 60px -15px rgba(15, 23, 42, .4), inset 0 1px 0 rgba(255, 255, 255, .08);
    /* iPhone (email + SMS) : simple filet de contour plutôt qu'un reflet. */
    --em-dev-shadow-alt: 0 25px 60px -15px rgba(15, 23, 42, .4), inset 0 0 0 1px rgba(15, 23, 42, .6);
}

/* --- MacBook (rendu desktop de l'email) -------------------------------------
   Capot biseauté (webcam en encoche haute + écran) posé sur une base bombée
   qui déborde de part et d'autre — comme un vrai capot ouvert vu de face.
   Le débord de la base est négatif et RESPONSIVE via clamp() : il se
   resserre sur petit écran au lieu de pousser le composant hors du viewport
   (exigence « se réduit sans déborder »). L'iframe garde sa largeur native
   de 800px ; c'est le JS qui lui applique transform:scale() et mesure sa
   hauteur réelle (scrollHeight) — ce fichier ne fait que fournir la fenêtre
   qui la cadre et la rogne (overflow:hidden). */
.em-dev-mac {
    position: relative;
    width: 100%;
    max-width: 640px;
    margin-inline: auto;
    padding: 8px 8px 10px;
    box-sizing: border-box;
    background-image: var(--em-dev-bezel);
    border-radius: 14px 14px 0 0;
    box-shadow: var(--em-dev-shadow);
}
.em-dev-mac-cam {
    position: relative;
    width: 27%;
    min-width: 64px;
    max-width: 110px;
    height: 14px;
    margin: 0 auto;
    background-image: var(--em-dev-bezel);
    border-radius: 0 0 10px 10px;
}
/* Pastille de la webcam, centrée dans l'encoche. */
.em-dev-mac-cam::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 6px; height: 6px;
    margin: -3px 0 0 -3px;
    border-radius: 50%;
    background: #334155;
}
.em-dev-mac-screen {
    position: relative;
    overflow: hidden;
    margin-top: 8px;
    aspect-ratio: 16 / 9;
    background: #fff;
    border-radius: 6px;
    box-shadow: inset 0 0 0 1px rgba(51, 65, 85, .4);
}
/* Largeur native forcée à 800px pour que les media queries desktop de
   l'email se déclenchent réellement ; l'échelle vient du JS (transform,
   piloté par mesure réelle du conteneur), .62 n'est qu'un repli visuel
   avant que le JS ne prenne la main. */
.em-dev-mac-screen iframe {
    display: block;
    width: 800px;
    border: 0;
    transform: scale(.62);
    transform-origin: top left;
}
/* Base biseautée : deux bandes qui débordent du capot (mx négatif), plus
   resserrées sur petit écran grâce au clamp(). */
.em-dev-mac-base {
    position: relative;
    height: 10px;
    margin-inline: clamp(-14px, -3.2vw, -6px);
    background-image: linear-gradient(180deg, #CBD5E1 0%, #94A3B8 55%, #64748B 100%);
}
.em-dev-mac-base::after {
    content: "";
    position: absolute;
    left: clamp(-22px, -5vw, -8px);
    right: clamp(-22px, -5vw, -8px);
    bottom: -5px;
    height: 5px;
    background-image: linear-gradient(180deg, #64748B 0%, #475569 100%);
    border-radius: 0 0 10px 10px;
}
/* Encoche d'ouverture (loquet du capot), centrée en bas de la base. */
.em-dev-mac-hinge {
    position: absolute;
    z-index: 1;
    left: 50%;
    bottom: -5px;
    width: 20%;
    min-width: 56px;
    max-width: 96px;
    height: 5px;
    transform: translateX(-50%);
    background: #334155;
    border-radius: 0 0 6px 6px;
}

/* --- iPhone (rendu mobile de l'email) ---------------------------------------
   Même biseau/ombre que le MacBook, mais coque verrouillée à 280px de large
   (gabarit physique) : en dessous elle suit simplement la largeur du
   conteneur (width:100%), donc jamais de débordement, aucun clamp requis
   ici. Dynamic Island, PAS d'encoche classique (voir cadre SMS plus bas,
   traitement volontairement différent). */
.em-dev-phone {
    position: relative;
    width: 100%;
    max-width: 280px;
    margin-inline: auto;
    padding: 8px;
    box-sizing: border-box;
    background-image: var(--em-dev-bezel);
    border-radius: 44px;
    box-shadow: var(--em-dev-shadow-alt);
}
.em-dev-phone-screen {
    position: relative;
    overflow: hidden;
    background: #fff;
    border-radius: 36px;
}
/* Ici pas de transform : c'est un zoom CSS injecté dans le <style> de
   l'email lui-même (voir spec de portage §2) qui reflue le texte — une
   simulation par transform le déformerait. Ce fichier se contente de la
   fenêtre de contenu. */
.em-dev-phone-screen iframe {
    display: block;
    width: 100%;
    height: 500px;
    border: 0;
}
.em-dev-island {
    position: absolute;
    z-index: 2;
    top: 8px;
    left: 50%;
    width: 29%;
    min-width: 54px;
    max-width: 82px;
    height: 20px;
    transform: translateX(-50%);
    background: #000;
    border-radius: var(--em-r-pill, 9999px);
}
/* Barre d'état : "9:41" à gauche (chiffres tabulaires, ne gigotent pas),
   pastille de batterie décorative à droite — dessinés en CSS pur, aucune
   dépendance au balisage. */
.em-dev-statusbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 22px 0;
    font-size: 9px;
    font-weight: 700;
    color: #0F172A;
    font-variant-numeric: tabular-nums;
}
.em-dev-statusbar::before { content: "9:41"; }
.em-dev-statusbar::after {
    content: "";
    width: 20px; height: 10px;
    border: 1px solid #0F172A;
    border-radius: 2px;
    opacity: .8;
}

/* --- iPhone (conversation SMS/iMessage) -------------------------------------
   Traitement plus ancien et volontairement DIFFÉRENT du cadre email (voir
   spec de portage §3) : coque noire pleine (pas de dégradé biseauté),
   écran gris iOS clair, encoche classique (pas de Dynamic Island) et barre
   d'accueil en bas. On les garde distincts plutôt que de les fusionner. */
.em-dev-sms {
    position: relative;
    width: 100%;
    max-width: 280px;
    margin-inline: auto;
    padding: 10px;
    box-sizing: border-box;
    background: #000;
    border-radius: 36px;
    box-shadow: var(--em-dev-shadow-alt);
}
.em-dev-sms-screen {
    position: relative;
    overflow: hidden;
    min-height: 420px;
    background: #F2F2F7;
    border-radius: 28px;
}
.em-dev-notch {
    position: absolute;
    z-index: 2;
    top: 0;
    left: 50%;
    width: 39%;
    min-width: 78px;
    max-width: 110px;
    height: 26px;
    transform: translateX(-50%);
    background: #000;
    border-radius: 0 0 14px 14px;
}
.em-dev-home {
    position: absolute;
    z-index: 2;
    bottom: 6px;
    left: 50%;
    width: 36%;
    min-width: 70px;
    max-width: 100px;
    height: 4px;
    transform: translateX(-50%);
    background: rgba(255, 255, 255, .6);
    border-radius: var(--em-r-pill, 9999px);
}

/* --- Chrome Gmail (habillage du cadre MacBook) ------------------------------
   Rejoue la disposition d'un vrai Gmail desktop autour de l'iframe du
   modèle : barre du haut (recherche + avatar), rail de navigation réduit à
   gauche, barre d'actions, puis l'en-tête du message (expéditeur/objet,
   libellé, étoile) juste au-dessus du corps de l'email. Toutes les icônes
   sont dessinées en CSS pur (pseudo-éléments, clip-path) — aucune image,
   aucune dépendance au balisage au-delà des classes listées. */
.em-gmail-bar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 16px;
    background: #fff;
    border-bottom: 1px solid #E8EAED;
}
.em-gmail-search {
    position: relative;
    flex: 1 1 auto;
    height: 30px;
    max-width: 340px;
    padding-left: 30px;
    background: #F1F3F4;
    border-radius: var(--em-r-pill, 9999px);
}
/* Loupe : lentille (::before, cercle) + manche (::after, trait pivoté). */
.em-gmail-search::before {
    content: "";
    position: absolute;
    top: 50%; left: 10px;
    width: 9px; height: 9px;
    margin-top: -7px;
    border: 1.5px solid #5F6368;
    border-radius: 50%;
}
.em-gmail-search::after {
    content: "";
    position: absolute;
    top: 50%; left: 17px;
    width: 6px; height: 1.5px;
    margin-top: 2px;
    background: #5F6368;
    border-radius: 1px;
    transform: rotate(45deg);
    transform-origin: left center;
}
/* Avatar en gradient de marque : contrairement au bleu iOS de la bulle SMS,
   ce cercle représente le compte du cabinet dans son propre webmail — la
   couleur de marque y a toute sa place (cohérent avec la marque blanche). */
.em-gmail-avatar {
    flex: none;
    width: 28px; height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-image: linear-gradient(135deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    border-radius: 50%;
    color: #fff;
    font-size: 12px;
    font-weight: 700;
}
/* Rail de navigation réduit à gauche : bouton « Nouveau message » (pastille
   ::before) + une pile de 3 points représentant les libellés (::after,
   dessinée avec un seul point + box-shadow répété, sans balisage additionnel). */
.em-gmail-side {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    width: 64px;
    flex: none;
    padding-top: 16px;
    background: #F6F8FC;
    border-right: 1px solid #E8EAED;
}
.em-gmail-side::before {
    content: "";
    width: 40px; height: 40px;
    background-image: linear-gradient(180deg, #fff 0%, #F6F9FE 100%);
    border-radius: 16px;
    box-shadow: 0 1px 3px rgba(60, 64, 67, .3), 0 4px 8px rgba(60, 64, 67, .15);
}
.em-gmail-side::after {
    content: "";
    width: 6px; height: 6px;
    margin-top: 10px;
    background: #9AA0A6;
    border-radius: 50%;
    box-shadow: 0 16px 0 #9AA0A6, 0 32px 0 #9AA0A6;
}
/* Barre d'actions (archiver/supprimer/marquer…) simulée par une rangée de
   petits carrés répétés en box-shadow depuis un seul pseudo-élément. */
.em-gmail-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 8px 20px;
    background: #fff;
    border-bottom: 1px solid #E8EAED;
}
.em-gmail-toolbar::before {
    content: "";
    width: 22px; height: 22px;
    background: #F1F3F4;
    border-radius: 5px;
    box-shadow: 26px 0 0 #F1F3F4, 52px 0 0 #F1F3F4, 78px 0 0 #F1F3F4;
}
.em-gmail-msghead {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 24px 12px;
    background: #fff;
}
/* Typo par défaut de l'expéditeur/objet injectés dynamiquement — sélecteur
   de descendance (pas d'enfant direct) pour rester robuste à la structure
   exacte posée par le gabarit PHP/JS. */
.em-gmail-msghead #tplGmailSender {
    display: block;
    font-size: 13px;
    font-weight: 700;
    color: var(--em-ink, #0E2A3D);
}
.em-gmail-msghead #tplGmailSubject {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    font-weight: 500;
    color: var(--em-ink-2, #3E647D);
}
.em-gmail-badge {
    display: inline-block;
    flex: none;
    padding: 2px 9px;
    background: var(--em-surface-3, #F1F5F9);
    border-radius: var(--em-r-pill, 9999px);
    color: var(--em-ink-2, #3E647D);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
    white-space: nowrap;
}
/* Étoile Gmail : silhouette en clip-path plutôt qu'une image, teintée par
   `color` (currentColor) — gris clair par défaut (non cochée), .is-active
   passe à l'or Gmail. */
.em-gmail-star {
    flex: none;
    width: 15px; height: 15px;
    color: #DADCE0;
}
.em-gmail-star::before {
    content: "";
    display: block;
    width: 100%; height: 100%;
    background: currentColor;
    clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}
.em-gmail-star.is-active { color: #F4B400; }

/* --- Bulle iMessage (SMS) ---------------------------------------------------
   Bleu iOS #007AFF LITTÉRAL : ce n'est pas une couleur de marque, c'est
   celle du client de messagerie simulé (jamais var(--em-primary) ici). Pas
   de triangle pour la queue de bulle : c'est l'angle bas-droit resserré
   (6px, contre 18px partout ailleurs) qui la simule — exactement comme sur
   un vrai iPhone. */
.em-imsg-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 16px 0 10px;
    color: #8E8E93;
    font-size: 11px;
    font-weight: 600;
}
.em-imsg-icon {
    flex: none;
    width: 28px; height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-image: linear-gradient(135deg, #60A5FA 0%, #2563EB 100%);
    border-radius: 50%;
}
/* Silhouette de bulle miniature, seule "icône" du contact simulé. */
.em-imsg-icon::before {
    content: "";
    width: 11px; height: 11px;
    background: #fff;
    border-radius: 50% 50% 4px 50%;
}
.em-imsg-bubble {
    max-width: 210px;
    padding: 10px 14px;
    background: #007AFF;
    border-radius: 18px 18px 6px 18px;
    box-shadow: var(--em-sh-xs, 0 1px 2px rgba(14, 42, 61, .05));
    transition: background-color var(--em-dur-fast, .16s) ease;
}
/* Texte du message (le seul <p> sans classe dédiée dans la bulle) — exclut
   explicitement .em-imsg-stop par :not() pour ne jamais entrer en conflit
   de spécificité avec son propre style, quel que soit l'ordre du DOM. */
.em-imsg-bubble p:not(.em-imsg-stop) {
    margin: 0;
    color: #fff;
    font-size: 12px;
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
}
/* État "message vide" (aucun texte saisi) : bulle à demi-opaque plutôt que
   vide de sens en pleine couleur — repli explicitement demandé. */
.em-imsg-bubble.is-empty { background: rgba(0, 122, 255, .5); }
.em-imsg-bubble.is-empty p:not(.em-imsg-stop) { opacity: .7; font-style: italic; }
.em-imsg-stop {
    margin: 2px 0 0;
    color: rgba(255, 255, 255, .7);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.45;
    white-space: pre-wrap;
}
.em-imsg-caption {
    margin: 4px 4px 0 0;
    color: #9CA3AF;
    font-size: 8px;
    font-weight: 500;
    text-align: right;
}

/* --- Bascule segmentée « Variables | Valeurs test », « Ordinateur | Mobile »
   -----------------------------------------------------------------------------
   Même mécanique que .em-civilite-toggle (pouce glissant en absolute, grille
   à 2 colonnes fixes, mêmes variables --em-dur/--em-r-lg/--em-sh-warm, même
   courbe cubic-bezier) : harmonisation volontaire plutôt que duplication.
   Différence assumée : le pouce se pilote via l'attribut data-value du
   conteneur (#tplPreviewMode / #tplPreviewDevice), pas via une classe d'état
   is-* — un seul composant générique sert donc les deux bascules du contrat
   (variables|test ET desktop|mobile), l'attribut portant toujours une
   valeur valide dès le rendu initial (pas besoin de la danse d'opacité
   0→1 de .em-civilite-thumb, réservée au cas où aucun état n'est encore
   choisi). */
.em-seg-toggle {
    position: relative;
    display: inline-grid;
    grid-template-columns: 1fr 1fr;
    padding: 3px;
    background: rgba(255, 255, 255, .65);
    border: 1px solid #E2E8F0;
    border-radius: var(--em-r-lg, 14px);
    overflow: hidden;
}
.em-seg-thumb {
    position: absolute;
    top: 3px; bottom: 3px; left: 3px;
    width: calc(50% - 3px);
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    border-radius: calc(var(--em-r-lg, 14px) - 3px);
    box-shadow: var(--em-sh-warm);
    transform: translateX(0);
    transition: transform var(--em-dur, .28s) cubic-bezier(.4, 0, .2, 1);
    pointer-events: none;
}
.em-seg-toggle[data-value="test"] .em-seg-thumb,
.em-seg-toggle[data-value="mobile"] .em-seg-thumb { transform: translateX(100%); }
.em-seg-opt {
    position: relative;
    z-index: 1;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: calc(var(--em-r-lg, 14px) - 3px);
    color: #94A3B8;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: color var(--em-dur-fast, .15s) ease;
}
.em-seg-opt[aria-pressed="true"] { color: #fff; }

/* --- Petits écrans : le débord du MacBook est déjà résorbé par les clamp()
   ci-dessus ; on referme ici la marge de sécurité et on simplifie le chrome
   Gmail (le rail de navigation n'a plus la place d'exister en dessous de
   ~480px, exactement comme dans le vrai Gmail mobile). --- */
@media (max-width: 480px) {
    .em-dev-mac { padding: 6px 6px 8px; }
    .em-dev-mac-base { margin-inline: -4px; }
    .em-dev-mac-base::after { left: -6px; right: -6px; }
    .em-dev-phone,
    .em-dev-sms { padding: 6px; }
    .em-gmail-side { display: none; }
    .em-gmail-toolbar::before { box-shadow: 26px 0 0 #F1F3F4, 52px 0 0 #F1F3F4; }
}

/* --- Mouvement réduit : le pouce de la bascule ne glisse plus, il saute. --- */
@media (prefers-reduced-motion: reduce) {
    .em-seg-thumb { transition: none; }
    .em-imsg-bubble { transition: none; }
}

/* =============================================================================
   14. BIBLIOTHÈQUE DE MODÈLES — VIGNETTES, BADGES & MODALE D'ÉDITION
   -----------------------------------------------------------------------------
   Onglet « Modèles email & SMS » (mon-compte.php) : la bibliothèque de départ
   (21 modèles système, societe_id = 0) doit se voir au premier coup d'œil —
   plus jamais « Aucun modèle pour le moment ». Grille de vignettes en mode
   Email (miniature du rendu réel, cliquable → modale d'édition qui reprend
   les cadres MacBook/iPhone et la bulle iMessage de la section 13 ci-dessus) ;
   simple liste en mode SMS, sans modale — l'aperçu vit directement dans le
   panneau pendant la frappe.

   Le badge « PAR DÉFAUT » est l'élément le plus important de la carte : c'est
   lui qui répond à « suis-je sûr de ce qui part ? » (résolution cabinet →
   EMATEA → plus petit id, voir includes/template_defaults.php).
============================================================================= */

/* --- Grille de vignettes (mode Email) ---------------------------------------
   3 colonnes en large, 2 en moyen, 1 en étroit — mêmes paliers que le reste
   du fichier (1023/639 ≈ lg/sm Tailwind moins 1px, cf. section 12) pour
   rester cohérent avec la mise en page environnante de l'onglet. */
.em-tpl-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.15rem;
}
@media (max-width: 1023px) { .em-tpl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } }
@media (max-width: 639px)  { .em-tpl-grid { grid-template-columns: minmax(0, 1fr); gap: .85rem; } }

/* --- Carte vignette -----------------------------------------------------------
   Cliquable : le gabarit PHP peut poser un <button> natif ou un conteneur
   role="button"/tabindex — les resets ci-dessous neutralisent le chrome par
   défaut du <button> pour que le rendu soit identique dans les deux cas.
   Élévation douce au survol/focus, même recette que .em-card:hover (section
   5), reprise ici en composant autonome : la vignette a son propre gabarit
   (image en tête, sans le padding uniforme de .em-card) que .em-card ne
   prévoit pas. */
.em-tpl-card {
    display: block;
    width: 100%;
    margin: 0;
    padding: 0;
    background: var(--em-surface);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-lg);
    box-shadow: var(--em-sh-sm);
    overflow: hidden;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: transform var(--em-dur) var(--em-ease),
                box-shadow var(--em-dur) var(--em-ease),
                border-color var(--em-dur-fast) ease;
}
.em-tpl-card:hover,
.em-tpl-card:focus-visible {
    transform: translateY(-4px);
    border-color: var(--em-primary-light);
    border-color: color-mix(in srgb, var(--em-primary) 22%, #fff);
    box-shadow: var(--em-sh-lg), var(--em-sh-warm);
}
.em-tpl-card:focus-visible {
    outline: 2px solid var(--em-primary);
    outline-offset: 2px;
}
.em-tpl-card-head {
    display: flex;
    flex-direction: column;
    gap: .45rem;
    padding: .85rem .95rem .95rem;
}

/* --- Miniature -----------------------------------------------------------
   Ratio fixe 4/3, fond neutre, overflow masqué : la vraie iframe (point D du
   contrat) est posée dedans en pleine largeur puis réduite par transform sur
   .em-tpl-thumb-frame — jamais l'inverse (une largeur réduite côté iframe
   casserait ses propres media queries desktop, cf. .em-dev-mac-screen). */
.em-tpl-thumb {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: var(--em-surface-2);
    border-bottom: 1px solid var(--em-line);
}
/* État « en cours de chargement » : la vignette est paresseuse (Intersection
   Observer côté JS, point D du contrat) — tant qu'elle n'a pas encore été
   rendue, un squelette discret occupe l'espace plutôt qu'un vide. Même
   animation que .em-skeleton (section 9), réutilisée par son nom — ni
   dupliquée ni redéfinie ici. Les deux hameçons (classe .is-loading posée
   par le JS, ou attribut aria-busy s'il est préféré côté accessibilité) sont
   pris en charge. */
.em-tpl-thumb.is-loading,
.em-tpl-thumb[aria-busy="true"] {
    background: linear-gradient(90deg, #EEF2F6 0%, #F8FAFC 42%, #EEF2F6 84%);
    background-size: 220% 100%;
    animation: em-shimmer 2.4s linear infinite;
}
.em-tpl-thumb-frame {
    display: block;
    width: 800px;
    /* Hauteur de repli : sans hauteur explicite, une <iframe> retombe sur sa
       hauteur intrinsèque de 150 px et la vignette n'affiche qu'un filet du haut
       de l'email. Le JS remplace cette valeur par la hauteur réelle du contenu
       dès le chargement (ajusterVignette), et ajuste l'échelle à la carte. */
    height: 1000px;
    border: 0;
    /* Redondant avec l'attribut HTML pointer-events (point D du contrat) :
       filet de sécurité, la vignette ne doit jamais capter le clic — c'est
       la carte entière (.em-tpl-card) qui est cliquable. */
    pointer-events: none;
    transform: scale(.3);
    transform-origin: top left;
    background: #fff;
}

/* --- Badges de la vignette ----------------------------------------------------
   « PAR DÉFAUT » porte la couleur de marque pleine : c'est l'info la plus
   visible de la carte, à dessein. « Source » (Système / Personnalisé) reste
   neutre et discret — ce n'est pas elle qui répond à « qu'est-ce qui part ? ».
   Les deux fonctionnent aussi bien en flux (dans .em-tpl-card-head) qu'en
   overlay posé sur la miniature ; le second contexte est pris en charge par
   les deux règles contextuelles juste en dessous. */
.em-tpl-badge-defaut {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    flex: none;
    width: fit-content;
    padding: .3rem .6rem;
    background-image: linear-gradient(180deg, var(--em-primary) 0%, var(--em-primary-dark) 100%);
    border-radius: var(--em-r-pill);
    color: #fff;
    font-size: .625rem;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    white-space: nowrap;
    box-shadow: var(--em-sh-warm);
}
.em-tpl-badge-defaut i { font-size: .55rem; }
.em-tpl-thumb .em-tpl-badge-defaut {
    position: absolute;
    z-index: 2;
    top: .6rem;
    left: .6rem;
}
.em-tpl-badge-source {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    flex: none;
    width: fit-content;
    padding: .28rem .55rem;
    background: var(--em-surface-3);
    border-radius: var(--em-r-pill);
    color: var(--em-ink-2);
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .02em;
    white-space: nowrap;
}
.em-tpl-thumb .em-tpl-badge-source {
    position: absolute;
    z-index: 2;
    top: .6rem;
    right: .6rem;
    background: rgba(255, 255, 255, .88);
    -webkit-backdrop-filter: blur(4px);
            backdrop-filter: blur(4px);
}
/* Pastille d'étape ciblée par le modèle : ne redéfinit AUCUNE couleur, elle
   ajuste seulement le gabarit de .em-stage (section 7, couleurs pilotées par
   EM_MACROS — includes/statuts.php, clés 'hex'/'tw') au format compact de la
   carte. Classe doublée : même nécessité de spécificité que .em-stage.em-stage
   un peu plus haut, pour l'emporter sur les utilitaires Tailwind
   (px-2.5 py-1 text-[11px]) posés par em_macro_badge(), chargés APRÈS ce
   fichier. */
.em-tpl-badge-etape.em-tpl-badge-etape {
    padding: .22rem .55rem;
    font-size: .625rem;
}

/* --- Modale d'édition (mode Email uniquement — le SMS ne s'ouvre jamais en
   modale, voir .em-tpl-smsrow plus bas) -----------------------------------
   Voile plein écran + panneau centré large : contrairement à .em-invite-panel
   (simple confirmation), ce panneau doit contenir à la fois le formulaire ET
   le cadre MacBook (.em-dev-mac, max-width 640px, section 13) sans le
   comprimer — d'où une largeur bien supérieure. Même grammaire « verre
   liquide » que .em-invite-panel/.em-fiche-panel (bordure dégradée en
   double-background, reflet interne, ombre teintée), scindée en panneau fixe
   + corps défilant comme .em-fiche-panel/.em-fiche-panel-body. */
.em-tpl-modal {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
    background: rgba(255, 246, 240, .4);
    -webkit-backdrop-filter: blur(16px) saturate(170%);
            backdrop-filter: blur(16px) saturate(170%);
    animation: em-fade-up var(--em-dur) var(--em-ease) both;
}
.em-tpl-modal.hidden { display: none; }
.em-tpl-modal-panel {
    position: relative;
    width: 100%;
    max-width: 1360px;
    max-height: 92vh;
    display: flex;
    flex-direction: column;
    background:
        linear-gradient(160deg, rgba(255,255,255,.96) 0%, rgba(249,251,253,.92) 48%, rgba(255,240,234,.74) 100%) padding-box,
        linear-gradient(160deg, rgba(255,255,255,.95), rgba(242,122,84,.36)) border-box;
    border: 1px solid transparent;
    border-radius: var(--em-r-xl);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        0 34px 74px -22px color-mix(in srgb, var(--em-primary) 26%, transparent),
        0 14px 34px -16px rgba(14, 42, 61, .22);
}
/* Titre : même échelle typographique que .em-card-title (section 5). */
#tplModalTitre {
    font-family: var(--em-font-display);
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -.015em;
    color: var(--em-ink);
    margin: 0;
}
/* Fermeture visible : même recette que .em-fiche-modal-close (bouton rond en
   verre), posée en élément normal du bandeau d'en-tête plutôt qu'en overlay
   absolu — le bandeau (titre + bascule ordinateur/mobile + fermeture) est
   composé côté PHP avec les utilitaires flex habituels du fichier. */
#tplModalClose {
    flex: none;
    width: 2.1rem;
    height: 2.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--em-r-pill);
    border: 1px solid rgba(255, 255, 255, .7);
    background: rgba(255, 255, 255, .75);
    -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
    color: var(--em-ink-2);
    cursor: pointer;
    box-shadow: var(--em-sh-xs);
    transition: color var(--em-dur-fast) ease, transform var(--em-dur) var(--em-ease), box-shadow var(--em-dur) var(--em-ease);
}
#tplModalClose:hover { color: var(--em-primary-deep); transform: rotate(90deg); box-shadow: var(--em-sh-sm); }
#tplModalClose:focus-visible { outline: 2px solid var(--em-primary); outline-offset: 2px; }
.em-tpl-modal-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 1.5rem 1.5rem 1.75rem;
}
@media (min-width: 768px) { .em-tpl-modal-body { padding: 1.85rem 2rem 2.25rem; } }
/* La page hôte reste figée derrière la modale — même garde-fou que la fiche
   client (body.em-fiche-modal-open). */
body.em-tpl-modal-open { overflow: hidden; }

/* --- Liste des modèles (mode SMS) --------------------------------------------
   Pas de modale ici : chaque ligne pilote directement #tplSmsEditor /
   #tplSmsLiveText dans le même panneau (bulle iMessage .em-imsg-* de la
   section 13, qui s'écrit en direct pendant la frappe). */
.em-tpl-smsrow {
    display: flex;
    align-items: center;
    gap: .75rem;
    width: 100%;
    margin: 0;
    padding: .7rem .9rem;
    background: var(--em-surface);
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-sm);
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    transition: background-color var(--em-dur-fast) ease, border-color var(--em-dur-fast) ease, box-shadow var(--em-dur-fast) ease;
}
.em-tpl-smsrow + .em-tpl-smsrow { margin-top: .5rem; }
.em-tpl-smsrow:hover {
    background: var(--em-surface-2);
    border-color: var(--em-primary-light);
    border-color: color-mix(in srgb, var(--em-primary) 20%, #fff);
}
.em-tpl-smsrow:focus-visible { outline: 2px solid var(--em-primary); outline-offset: 2px; }
.em-tpl-smsrow.is-active {
    background: var(--em-primary-light);
    background: color-mix(in srgb, var(--em-primary) 8%, #fff);
    border-color: var(--em-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--em-primary) 14%, transparent);
}

/* --- Petits écrans (jusqu'à 360px) : la grille est déjà à 1 colonne (palier
   639px ci-dessus) ; on resserre ici les marges internes de la modale et de
   la carte pour ne jamais déborder horizontalement. --- */
@media (max-width: 380px) {
    .em-tpl-modal { padding: .6rem; }
    .em-tpl-modal-body { padding: 1.1rem 1rem 1.35rem; }
    .em-tpl-card-head { padding: .7rem .8rem .8rem; }
}

/* --- Mouvement réduit : le squelette de chargement ne défile plus, la carte
   ne s'élève plus au survol. Bloc dédié plutôt qu'ajout dans un bloc
   @media existant (règle de travail : aucune règle existante n'est modifiée). --- */
@media (prefers-reduced-motion: reduce) {
    .em-tpl-thumb.is-loading,
    .em-tpl-thumb[aria-busy="true"] { animation: none; }
    .em-tpl-card:hover,
    .em-tpl-card:focus-visible { transform: none; }
}

/* =============================================================================
   15. CONSENTEMENT DE CONTACT — attestation du courtier
   -----------------------------------------------------------------------------
   Bloc partagé par TOUS les points de capture (modale « Ajouter un client »,
   étape 3 de l'import CSV, modale d'envoi de devis, bandeau DER, modale de
   relance, modale d'invitation). Une seule apparence, un seul comportement :
   le courtier reconnaît l'attestation où qu'elle apparaisse.

   Deux tons volontaires :
     .em-consent            attente d'attestation (ambre, jamais alarmiste)
     .em-consent.is-granted rappel discret que le consentement est déjà acquis
     .em-consent.is-revoked refus ferme (désinscription) — non rattrapable
============================================================================= */
.em-consent {
    display: flex;
    gap: .65rem;
    align-items: flex-start;
    padding: .8rem .85rem;
    border-radius: var(--em-r-sm);
    background: color-mix(in srgb, var(--em-warn) 8%, #fff);
    border: 1px solid color-mix(in srgb, var(--em-warn) 26%, transparent);
    font-size: 12px;
    line-height: 1.45;
    color: var(--em-ink-2);
}
.em-consent-ico { color: var(--em-warn); margin-top: .1rem; flex: 0 0 auto; }
.em-consent-txt { flex: 1; min-width: 0; }
.em-consent-title { font-weight: 700; color: var(--em-ink); display: block; margin-bottom: .15rem; }
.em-consent-legal { color: var(--em-muted); font-size: 11.5px; }
.em-consent-label {
    display: flex;
    gap: .5rem;
    align-items: flex-start;
    cursor: pointer;
    margin: .5rem 0 0;
}
.em-consent-label input[type="checkbox"] {
    margin-top: .18rem;
    width: 15px; height: 15px;
    flex: 0 0 auto;
    accent-color: var(--em-primary);
    cursor: pointer;
}
.em-consent-count { font-weight: 700; color: var(--em-ink); }

.em-consent.is-granted {
    background: color-mix(in srgb, var(--em-success) 7%, #fff);
    border-color: color-mix(in srgb, var(--em-success) 24%, transparent);
}
.em-consent.is-granted .em-consent-ico { color: var(--em-success); }

.em-consent.is-revoked {
    background: color-mix(in srgb, var(--em-danger) 7%, #fff);
    border-color: color-mix(in srgb, var(--em-danger) 26%, transparent);
}
.em-consent.is-revoked .em-consent-ico { color: var(--em-danger); }

/* Variante compacte une-ligne : bandeau récap au-dessus de la file « À
   attester » (clients.php). Le détail juridique passe en tooltip plutôt
   qu'en pavé de texte imprimé — le paragraphe complet gênait plus qu'il
   n'aidait sur ce point d'entrée à fort trafic. */
.em-consent--bar {
    align-items: center;
    padding: .55rem .85rem;
    gap: .55rem;
    flex-wrap: wrap;
    cursor: default;
}
.em-consent--bar .em-consent-ico { margin-top: 0; }
.em-consent--bar .em-consent-title { margin-bottom: 0; font-weight: 700; }
.em-consent-bar-blocked {
    padding: .1rem .5rem;
    border-radius: var(--em-r-pill);
    background: color-mix(in srgb, var(--em-danger) 12%, #fff);
    color: color-mix(in srgb, var(--em-danger) 65%, black);
    font-size: 10.5px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.em-consent-bar-export {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    color: var(--em-ink-2);
    font-size: 11.5px;
    font-weight: 700;
    text-decoration: none;
    white-space: nowrap;
}
.em-consent-bar-export:hover { color: var(--em-primary-deep); }

/* Pastille de portefeuille (latte client, fiche) — jamais de rouge sur un
   simple « pas encore attesté » : l'absence de consentement est un travail à
   faire, pas une faute. */
.em-consent-dot {
    display: inline-flex;
    align-items: center;
    gap: .28rem;
    padding: .1rem .4rem;
    border-radius: var(--em-r-pill);
    font-size: 10px;
    font-weight: 700;
    line-height: 1.5;
    white-space: nowrap;
}
.em-consent-dot.is-missing { background: color-mix(in srgb, var(--em-warn) 13%, #fff); color: #92610A; }
.em-consent-dot.is-expired { background: color-mix(in srgb, var(--em-warn) 13%, #fff); color: #92610A; }
.em-consent-dot.is-revoked { background: color-mix(in srgb, var(--em-danger) 11%, #fff); color: #B91C1C; }
.em-consent-dot.is-granted { background: color-mix(in srgb, var(--em-success) 11%, #fff); color: #15803D; }

@media (prefers-reduced-motion: reduce) {
    .em-consent { transition: none; }
}

/* =============================================================================
   COCKPIT COMMISSIONS (extranet/courtier/commissions.php)
   Spec : docs/superpowers/specs/2026-08-30-commissions-cockpit-design.md

   Bloc entièrement scopé `.cm-*`. Il ne redéfinit AUCUN composant partagé :
   les cartes, KPI, onglets, pills, tables et graphiques viennent tels quels du
   design system ci-dessus. Ce qui suit ne couvre que ce qui n'existait pas —
   la chaîne de calcul pédagogique, la liste de bordereaux dépliable, le
   segmented des projections et les repères de couleur des trois natures de
   commission.
============================================================================= */

/* ── Exception au principe ci-dessus : les 4 onglets, sur mobile ──────────
   `.em-tabs` reste le composant partagé (défilement horizontal), mais mesuré
   en conditions réelles à 320-390px les 4 onglets pèsent ~477px pour un rail
   visible de 224-294px : plus d'un tiers du rail, dont l'onglet actif en cas
   de lien direct (`?tab=bordereaux`), reste hors champ SANS aucun indice
   visuel qu'il faille faire défiler. Aucune autre page à onglets du site n'a
   ce problème (moins d'onglets, ou libellés plus courts), donc l'override
   reste scopé à `data-em-scope="courtier-commissions"` plutôt que de changer
   le comportement de tous les groupes d'onglets. La pastille glissante
   (`.em-tab-indicator`) est repositionnée en JS par translation X seule : sur
   une grille 2×2 l'onglet actif de la 2e ligne se verrait surligné à la
   mauvaise hauteur, donc on la masque ici et .is-active porte son propre
   fond, statique. */
@media (max-width: 639px) {
    .em-tabs[data-em-scope="courtier-commissions"] {
        display: grid;
        grid-template-columns: repeat(2, 1fr);
        width: 100%;
        max-width: none;
        overflow: visible;
        border-radius: var(--em-r-lg);
    }
    .em-tabs[data-em-scope="courtier-commissions"] .em-tab-indicator { display: none; }
    .em-tabs[data-em-scope="courtier-commissions"] .em-tab {
        padding: .5rem .4rem;
        font-size: .74rem;
        text-align: center;
        white-space: nowrap;
    }
    .em-tabs[data-em-scope="courtier-commissions"] .em-tab.is-active {
        background: linear-gradient(180deg, #fff 0%, #FFFCFB 100%);
        box-shadow: var(--em-sh-xs);
    }
}

/* Les trois natures, une couleur chacune, tenue partout (badges, montants,
   graphiques). Le courtier doit reconnaître « première année » d'un coup d'œil
   sans lire l'étiquette. */
.cm-t-pre   { --cm-tint: var(--em-ink); }
.cm-t-lin   { --cm-tint: var(--em-primary); }
.cm-t-frais { --cm-tint: #B7892C; }

.cm-tag {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .55rem;
    border-radius: var(--em-r-pill);
    background: color-mix(in srgb, var(--cm-tint, var(--em-ink-3)) 11%, #fff);
    color: var(--cm-tint, var(--em-ink-2));
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .01em;
    white-space: nowrap;
}

/* Montants : chiffres tabulaires, sinon les colonnes de droite « dansent ». */
.cm-amount,
.cm-mono {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    white-space: nowrap;
}
.cm-amount { font-size: .82rem; color: var(--em-ink-2); }
.cm-amount--pre { color: var(--em-ink); }
.cm-amount--tot { color: var(--em-primary-deep); }
.cm-mono { font-size: .74rem; color: var(--em-ink-3); }

/* Cellule client : pastille d'initiale + nom + sous-ligne. */
.cm-cli { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.cm-cli-ini {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: var(--em-r-pill);
    background: linear-gradient(140deg, color-mix(in srgb, var(--em-primary) 22%, #fff), color-mix(in srgb, var(--em-primary) 55%, #fff));
    color: var(--em-primary-deep);
    font-size: .68rem;
    font-weight: 800;
}
.cm-cli-nom {
    margin: 0;
    font-size: .8rem;
    font-weight: 700;
    color: var(--em-ink);
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 12rem;
}
.cm-cli-sub {
    margin: 0;
    font-size: .68rem;
    color: var(--em-ink-3);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 12rem;
}

/* Hauteurs de canevas : fixées, sinon Chart.js et la grille CSS se renvoient la
   balle au premier paint et le graphique sort trop haut. */
.cm-canvas { position: relative; height: 260px; }
.cm-canvas--donut { height: 230px; }
.cm-canvas--compagnies { height: 240px; }
.cm-canvas--proj { height: 320px; }
@media (max-width: 639px) {
    .cm-canvas { height: 230px; }
    .cm-canvas--donut { height: 210px; }
    .cm-canvas--proj { height: 270px; }
}

/* ── Bandeau d'information neutre (onglet Pipeline) ─────────────────────── */
.cm-notice { display: flex; align-items: flex-start; gap: .8rem; }

/* ── Segmented de la projection ─────────────────────────────────────────── */
.cm-proj-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .85rem;
    margin-bottom: 1rem;
}
.cm-proj-ctrl { display: flex; flex-wrap: wrap; gap: .5rem; }
.cm-seg {
    display: inline-flex;
    gap: .15rem;
    padding: .2rem;
    border-radius: var(--em-r-pill);
    background: var(--em-surface-3);
}
.cm-seg-btn {
    padding: .34rem .72rem;
    border: 0;
    border-radius: var(--em-r-pill);
    background: transparent;
    color: var(--em-ink-2);
    font-family: var(--em-font-sans);
    font-size: .72rem;
    font-weight: 700;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--em-dur-fast) ease, background-color var(--em-dur-fast) ease,
                box-shadow var(--em-dur) var(--em-ease);
}
.cm-seg-btn:hover { color: var(--em-primary-deep); }
.cm-seg-btn.is-on {
    background: #fff;
    color: var(--em-ink);
    box-shadow: var(--em-sh-xs), 0 2px 6px -3px color-mix(in srgb, var(--em-primary) 40%, transparent);
}

/* ── Bordereaux : une ligne par mois, dépliable sur ses lignes ──────────── */
.cm-filters { display: flex; flex-wrap: wrap; align-items: center; gap: .85rem; }
.cm-brd-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }
.cm-brd-summary > div { min-width: 0; }
@media (max-width: 479px) { .cm-brd-summary { grid-template-columns: 1fr; gap: .9rem; } }

.cm-brd-list { display: grid; gap: .55rem; }
.cm-brd-block {
    border: 1px solid var(--em-line);
    border-radius: var(--em-r-lg);
    background: var(--em-surface);
    box-shadow: var(--em-sh-xs);
    overflow: hidden;
    transition: box-shadow var(--em-dur) var(--em-ease), border-color var(--em-dur-fast) ease;
}
.cm-brd-block:hover { box-shadow: var(--em-sh-sm); }

.cm-brd-month {
    display: grid;
    grid-template-columns: 1.1rem minmax(8rem, 1fr) auto auto auto;
    align-items: center;
    gap: .9rem;
    width: 100%;
    padding: .85rem 1rem;
    border: 0;
    background: transparent;
    text-align: left;
    cursor: pointer;
    font-family: var(--em-font-sans);
}
.cm-brd-month:hover { background: var(--em-surface-2); }
.cm-brd-chev { color: var(--em-ink-3); font-size: .7rem; transition: transform var(--em-dur) var(--em-ease); }
.cm-brd-month.is-open .cm-brd-chev { transform: rotate(90deg); color: var(--em-primary); }
.cm-brd-title { min-width: 0; }
.cm-brd-title strong { display: block; font-size: .85rem; font-weight: 800; color: var(--em-ink); }
.cm-brd-count { display: block; font-size: .68rem; color: var(--em-ink-3); }
.cm-brd-amounts { display: flex; gap: .4rem; }
.cm-brd-amt {
    padding: .15rem .45rem;
    border-radius: var(--em-r-sm);
    background: color-mix(in srgb, var(--cm-tint, var(--em-ink-3)) 10%, #fff);
    color: var(--cm-tint, var(--em-ink-2));
    font-size: .68rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.cm-brd-total {
    font-size: .92rem;
    font-weight: 800;
    color: var(--em-ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.cm-brd-statut {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .22rem .6rem;
    border-radius: var(--em-r-pill);
    font-size: .68rem;
    font-weight: 800;
    white-space: nowrap;
}
.cm-brd-statut.is-paye    { background: color-mix(in srgb, var(--em-success) 12%, #fff); color: #15803D; }
.cm-brd-statut.is-attente { background: color-mix(in srgb, var(--em-warn) 15%, #fff);    color: #92610A; }
.cm-brd-statut.is-partiel { background: color-mix(in srgb, var(--em-info) 12%, #fff);    color: #1D4ED8; }
.cm-brd-statut.is-neutre  { background: var(--em-surface-3); color: var(--em-ink-2); }

/* Le statut du MOIS (payé/à recevoir/partiel, cf. PHP) reste un agrégat
   honnête des vrais statuts de grossiste — la ligne de téléchargement direct
   vit donc à côté du bouton de dépliage, pas dedans : un <a> ne peut de toute
   façon pas s'imbriquer dans un <button>. */
.cm-brd-row { display: flex; align-items: stretch; }
.cm-brd-row .cm-brd-month { flex: 1 1 auto; min-width: 0; }
.cm-brd-dl {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: 0 .9rem;
    flex: 0 0 auto;
}
@media (max-width: 479px) { .cm-brd-dl { padding: 0 .6rem; gap: .3rem; } }

.cm-brd-lines { border-top: 1px solid var(--em-line); background: var(--em-surface-2); }
.cm-brd-lines:not(.hidden) { animation: em-tab-in .22s var(--em-ease) both; }
.cm-brd-none { margin: 1.5rem 0; text-align: center; font-size: .84rem; color: var(--em-ink-3); }

/* ── Sous-groupe grossiste, à l'intérieur d'un mois déplié ──────────────
   Un bordereau réel (1 compagnie, 1 statut de paiement) par bloc — c'est ICI
   que vit le badge payé/à recevoir individuel, jamais sur un contrat. */
.cm-brd-grossiste { background: var(--em-surface-3); }
.cm-brd-grossiste + .cm-brd-grossiste { border-top: 1px solid var(--em-line); }
.cm-brd-grossiste-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .7rem;
    padding: .85rem .95rem .6rem;
    border-bottom: 1px solid var(--em-line-2);
}
.cm-brd-grossiste-id { display: flex; flex-direction: column; gap: .1rem; margin-right: .2rem; }
.cm-brd-grossiste-eyebrow {
    font-size: 9px;
    font-weight: 800;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--em-ink-3);
}
.cm-brd-grossiste-nom {
    font-family: var(--em-font-display);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: .01em;
    color: var(--em-ink);
    line-height: 1.1;
}
.cm-brd-grossiste-head .em-btn-icon { width: 1.7rem; height: 1.7rem; font-size: .7rem; margin-left: auto; }
.cm-brd-grossiste-head .cm-brd-statut { margin-left: 0; }
.cm-brd-lines .cm-table-wrap { padding: 0 .95rem .9rem; background: transparent; }
.cm-brd-grossiste .cm-table tbody td { padding-top: .55rem; padding-bottom: .55rem; }

.cm-brd-figure {
    font-size: .84rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--em-ink-2);
}
.cm-brd-figure--empty { color: var(--em-ink-3); font-weight: 400; opacity: .5; }

/* Client + lien fiche, réutilisé dans la cellule "Client" du tableau contrats. */
.cm-brd-dos-id {
    display: flex;
    align-items: center;
    gap: .6rem;
    min-width: 0;
    text-decoration: none;
    color: inherit;
}
.cm-brd-dos-id:not(.cm-brd-dos-id--plain):hover .cm-cli-nom { color: var(--em-primary-deep); }

/* Mobile : la rangée de mois passe sur deux lignes plutôt que de déborder.
   Les trois sous-montants par nature disparaissent — le total et le statut
   suffisent, le détail est à un tap. */
@media (max-width: 767px) {
    .cm-brd-month {
        grid-template-columns: 1.1rem 1fr auto;
        grid-template-areas:
            "chev titre total"
            ".    statut statut";
        row-gap: .45rem;
        padding: .8rem .85rem;
    }
    .cm-brd-chev   { grid-area: chev; }
    .cm-brd-title  { grid-area: titre; }
    .cm-brd-total  { grid-area: total; }
    .cm-brd-statut { grid-area: statut; justify-self: start; }
    .cm-brd-amounts { display: none; }
}

/* ── Tables : sur très petit écran, empilement en cartes plutôt que scroll ──
   Une page de commissions qui défile latéralement est illisible au doigt. */
.cm-table-wrap { width: 100%; }
@media (max-width: 639px) {
    .cm-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .cm-table, .cm-table tbody, .cm-table tr, .cm-table td { display: block; width: 100%; }
    .cm-table tr {
        padding: .7rem .85rem;
        margin-bottom: .5rem;
        border: 1px solid var(--em-line);
        border-radius: var(--em-r);
        background: var(--em-surface);
    }
    .cm-table tr:hover { background: var(--em-surface); }
    .cm-table td {
        padding: .18rem 0 !important;
        border: 0 !important;
        text-align: left !important;
    }
    /* Les colonnes de montants gardent leur alignement à droite en s'appuyant
       sur le libellé injecté par `data-cm-lbl` là où il est utile. */
    .cm-table td.text-right { display: flex; justify-content: space-between; align-items: baseline; gap: .75rem; }
    .cm-table td.text-right::before {
        content: attr(data-cm-lbl);
        font-size: .66rem;
        font-weight: 800;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: var(--em-ink-3);
    }
    .cm-cli-nom, .cm-cli-sub { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
    .cm-brd-chev { transition: none; }
    .cm-brd-lines:not(.hidden) { animation: none; }
}

/* Filtres bordereaux : deux groupes distincts et étiquetés. Sans le libellé,
   « Toutes » et « Tous » côte à côte se lisaient comme un seul filtre. */
.cm-filter-grp { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.cm-filter-lbl {
    font-size: .64rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--em-ink-3);
}

/* Légende des natures de commission. */
.cm-legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem 1rem;
    margin-top: -.25rem;
    font-size: .72rem;
    color: var(--em-ink-2);
}
.cm-legend > span { display: inline-flex; align-items: center; gap: .35rem; }
.cm-legend-dot {
    width: .6rem;
    height: .6rem;
    border-radius: var(--em-r-pill);
    background: var(--cm-tint, var(--em-ink-3));
}
.cm-legend-hint { margin-left: auto; color: var(--em-ink-3); }
@media (max-width: 639px) { .cm-legend-hint { margin-left: 0; width: 100%; } }

/* ── Révision mobile 2026-08-30 (quinquies) — lisibilité à 390px ──────────
   Mesures prises sur les 4 onglets à 390px (cf. mission). Tout override d'un
   composant partagé `.em-*` est scopé `#cmPage` : on ne touche jamais aux
   règles génériques utilisées par dashboard.php et consorts. Les classes
   `.cm-*` (propres à cette page) sont modifiées/complétées directement. */
@media (max-width: 639px) {

    /* A. Chevauchement valeur / légende dans les colonnes KPI.
       `.em-stat-hint` est pensé pour un survol desktop (absolu, opacity:0) ;
       commissions.php le force visible en permanence (style inline
       opacity:1). En absolu, dès que `.em-stat-label` passe sur 2 lignes
       (« En cours · 1re année »), la valeur descend et vient percuter le
       hint qui, lui, ne bouge pas. On le remet dans le flux normal : le
       chevauchement devient structurellement impossible, plus seulement
       improbable. */
    #cmPage .em-kpi-col .em-stat-hint {
        position: static;
        display: block;
        margin-top: .3rem;
        right: auto;
        bottom: auto;
        text-align: left;
    }
    /* Le padding-bottom du dashboard réserve la place pour ce hint quand il
       est encore en position absolute ; ce n'est plus nécessaire puisqu'il
       est désormais dans le flux et pousse lui-même le bas de la carte. */
    #cmPage .em-kpi-col { padding-bottom: .85rem; }

    /* B. Valeurs KPI désalignées d'une colonne à l'autre.
       Les libellés font 1 ou 2 lignes selon leur longueur (« Récurrent
       annuel » vs « En attente de paiement ») : les montants qui suivent ne
       démarrent donc pas à la même hauteur d'une colonne à l'autre de la
       même rangée. On réserve systématiquement la hauteur de 2 lignes de
       libellé (2 × line-height 1.3) pour que tous les montants d'une rangée
       s'alignent, que leur propre libellé tienne sur 1 ou 2 lignes. */
    #cmPage .em-kpi-head .em-stat-label {
        line-height: 1.3;
        min-height: 2.6em;
    }

    /* C. En-têtes de carte comprimés.
       En `flex; justify-content:space-between`, titre et sous-titre se
       partagent une largeur déjà réduite à 390px et passent chacun sur 2
       lignes (« Par compagnie » / « montants reversés, toutes périodes »).
       En colonne, chacun dispose de toute la largeur de la carte. */
    #cmPage .em-card-head,
    #cmPage .em-chart-col-head {
        flex-direction: column;
        align-items: flex-start;
        gap: .15rem;
    }

    /* D. Lignes orphelines dans les cartes de tableau (Projection, Pipeline).
       En mode carte, `.cm-table td { display:block }` (spécificité 0,1,1)
       écrase l'utilitaire Tailwind `.hidden` (0,1,0) : des cellules marquées
       `hidden md:table-cell` / `hidden lg:table-cell` redeviennent visibles.
       Celles qui portent un `data-cm-lbl` restent lisibles (le libellé
       injecté en ::before explique le chiffre) ; celles qui n'en ont pas
       (Assiette HT, Grille…) n'ont aucun sens hors de leur colonne d'origine
       — on les masque. `.em-hide-xs` utilise déjà `!important`, donc n'a pas
       ce problème de spécificité ; pas de règle à dupliquer pour elle. */
    .cm-table td.hidden:not([data-cm-lbl]) { display: none; }

    /* E.2 Bordereaux — lignes « — » des cartes contrat.
       Les 3 natures (1re année / années suivantes / frais) sont toujours
       rendues côté PHP, même vides : sur mobile une ligne « — » prend
       autant de hauteur qu'un vrai montant sans rien apprendre — c'est la
       première cause du scroll interminable de l'onglet Bordereaux. Le
       <td> porte désormais `.cm-td-empty` quand son montant est nul
       (cf. commissions.php) ; on le masque uniquement ici, en carte : en
       tableau desktop la colonne doit rester pour que l'alignement tienne.
       La colonne Total, elle, n'a jamais cette classe : toujours visible. */
    .cm-table--ledger td.cm-td-empty { display: none; }

    /* F. Bordereaux — le nom du mois coupé en deux.
       À 767px (règle existante, tablette) le mois partage sa ligne avec le
       montant total dans une colonne "auto" : à 390px ça ne suffit plus,
       « Août 2026 » se coupe. On donne au titre toute la largeur de la
       ligne 1, et on redescend montant + statut ensemble en ligne 2. Cette
       règle vient APRÈS celle à 767px dans le fichier (même sélecteur,
       même spécificité) : à une largeur ≤639px les deux media queries sont
       vraies, et c'est l'ordre de la cascade qui tranche en faveur de
       celle-ci. Entre 640 et 767px, seule la règle tablette s'applique. */
    .cm-brd-month {
        grid-template-columns: 1.1rem 1fr auto;
        grid-template-areas:
            "chev  titre titre"
            ".     total statut";
    }
    /* Verrouille l'intention : quoi qu'il arrive, le mois ne se coupe plus. */
    .cm-brd-title strong { white-space: nowrap; }

    /* G. Bordereaux — en-tête grossiste sur 4 lignes.
       `flex-wrap` empile nom, "N contrats · montant", pastille de statut et
       bouton CSV chacun sur leur ligne. Une grille 2 colonnes × 2 lignes
       tient tout en 2 lignes : nom+CSV en haut, montant+statut en bas. */
    .cm-brd-grossiste-head {
        display: grid;
        grid-template-columns: 1fr auto;
        align-items: center;
        row-gap: .35rem;
        column-gap: .6rem;
    }
    .cm-brd-grossiste-head .cm-brd-grossiste-id { grid-column: 1; grid-row: 1; margin-right: 0; }
    .cm-brd-grossiste-head .em-btn-icon { grid-column: 2; grid-row: 1; margin-left: 0; }
    .cm-brd-grossiste-head .cm-cli-sub { grid-column: 1; grid-row: 2; }
    .cm-brd-grossiste-head .cm-brd-statut { grid-column: 2; grid-row: 2; justify-self: end; }
}

/* H. Bordereaux — bandeau de synthèse.
   Sous 479px, `.cm-brd-summary` passait à 1 seule colonne : trois grands
   nombres empilés qui mangent à eux seuls près de 370px avant même
   d'atteindre la liste des mois. Deux colonnes (À recevoir | Déjà payé)
   suffisent à contenir des montants de cette taille ; "Dernier paiement"
   (un libellé de mois, jamais un montant à 6 chiffres) s'étend sur les
   deux en dessous plutôt que de forcer une 3e colonne étroite. */
@media (max-width: 479px) {
    .cm-brd-summary { grid-template-columns: repeat(2, 1fr); gap: .6rem .5rem; }
    .cm-brd-summary > div:nth-child(3) { grid-column: 1 / -1; }
}

/* Filet à très petite largeur (iPhone SE 320px, hors mesure initiale à 390px
   mais couvert par la vérification finale). Deux défauts trouvés en testant
   ce palier, sans lien avec les 8 points ci-dessus :
   - le libellé le plus long d'une rangée KPI (« Frais de dossier <année> »)
     passe sur 3 lignes à 320px, pas 2 : la réserve de B (2.6em) ne suffit
     plus et les montants de la rangée se redésalignent (12px d'écart mesuré).
   - le segmented control de l'onglet Projection (5/10/20 ans) déborde de
     3px et pousse `scrollWidth` au-delà de `innerWidth`. */
@media (max-width: 359px) {
    #cmPage .em-kpi-head .em-stat-label { min-height: 3.9em; }
    .cm-seg-btn { padding: .3rem .5rem; font-size: .68rem; }
}
