/* ============================================================
   Events Manager — Frontend CSS
   ============================================================ */

:root {
  --em-color-primary:     #1a3a6b;
  --em-color-primary-rgb: 26, 58, 107;
  --em-color-secondary:   #e63946;
  --em-color-accent:      #457b9d;
  --em-color-bg:          #f8f9fa;
  --em-color-surface:     #ffffff;
  --em-color-border:      #dee2e6;
  --em-color-text:        #212529;
  --em-color-muted:       #4b4848;
  /* Semantic status tokens — badges, tags, filter checkmarks */
  --em-color-success:        #15803d;
  --em-color-success-bg:     #dcfce7;
  --em-color-warning:        #b45309;
  --em-color-warning-bg:     #fef3e2;
  --em-color-warning-border: #fde68a;
  --em-color-info-bg:        #e8f4fd;
  --em-color-info-border:    #bee5fd;
  --em-color-new:            #10b981;
  /* Attention a l'asymetrie : pour « En ligne » et « A la une », `--em-color-success` /
     `--em-color-warning` sont des couleurs de TEXTE posees sur un fond clair (-bg). Pour
     « Nouveau », `--em-color-new` est le FOND, et le texte etait code en dur en #fff — d'ou cette
     variable, sans quoi un fond clair rendait le badge illisible. */
  --em-color-new-text:       #ffffff;
  /* Surfaces et texte SUR FOND SOMBRE — utilises par les blocs a panneau sombre (carrousel
     horizontal, bandeau tuiles, onglets, chiffres cles). Ces quatre jetons remplacent 23 valeurs
     `#fff` / `rgba(255,255,255,…)` ecrites en dur : un client qui change de charte n'avait aucun
     point d'entree pour les ajuster, alors que tout le reste du plugin passe par des variables.
     Ils ne sont PAS des reglages back-office : quatre selecteurs de couleur de plus pour un gain
     marginal. Une feuille de style de theme les surcharge une fois pour les quatre blocs. */
  --em-on-dark:         #fff;
  --em-on-dark-surface: rgba(255, 255, 255, .07);
  --em-on-dark-hover:   rgba(255, 255, 255, .14);
  --em-on-dark-line:    rgba(255, 255, 255, .18);
  --em-radius:          8px;
  --em-shadow:          0 2px 8px rgba(0,0,0,.08);
  --em-transition:      200ms ease;
  --em-font:            system-ui, -apple-system, sans-serif;
  /* Vignettes : ratio + cadrage communs a TOUS les visuels d'evenement (cartes PHP et JS,
     agenda, « a la une »). Regles back-office thumb_ratio / thumb_fit. */
  --em-thumb-ratio:     16 / 9;
  --em-thumb-fit:       cover;
  /* Espacement des grilles d'evenements (.em-events-grid) — reglage back-office grid_gap. */
  --em-grid-gap:        1.25rem;
  /* Hauteur COMMUNE des controles de la barre de filtres (champ de recherche, dropdowns,
     bouton Reinitialiser). Jeton partage : .em-filters est en align-items:center, chaque
     controle garde donc SA hauteur intrinseque — sans hauteur commune, l'input de recherche
     (dimensionne par son padding + font-size, ~35px) etait plus bas que les dropdowns (42px). */
  --em-filter-height:   38px;
  /* Largeur de la vignette d'une ligne d'agenda — reglage back-office agenda_thumb_width.
     NB : sa HAUTEUR suit celle de la carte (align-self: stretch), c'est donc la largeur qui
     pilote son rendu sur l'agenda. Masquee sous 640px. */
  --em-agenda-thumb-width: 250px;
  /* Degrade de la zone « sans visuel » (reglages no_image_color_from / _to). Par defaut il suit la
     palette : primaire -> accent. */
  --em-noimg-from:      var(--em-color-primary);
  --em-noimg-to:        var(--em-color-accent);
}

/* ---- Common ---- */
/* Racines REELLES de tous les blocs — porteuses de la police (reglage font_family).
   Jusqu'en 2.5.179 la police etait posee sur `.em-agenda`, selecteur qui ne matche AUCUN element
   du markup : le reglage n'avait donc d'effet que sur `.em-upcoming` (seul autre consommateur de
   --em-font). Les vraies racines sont celles-ci, produites par templates/agenda.php (agenda +
   layout sidebar), events-list.php (aussi utilise par le bloc « cards »), hero-carousel.php et
   upcoming.php. Toute nouvelle racine de bloc doit etre ajoutee ici (garde : tests/test-css-roots.php). */
/* LES SEIZE RACINES, en UNE regle (2.5.347).

   La police etait declaree dans QUATORZE regles — cinq ici, et une dans le bloc propre de chaque autre
   racine. Deux d ntre elles, `.em-agenda-sidebar-wrap` et `.em-hero-carousel`, declaraient meme
   `font-family: inherit` APRES celle-ci : a specificite egale, elles gagnaient, et le reglage « Police »
   etait donc MORT sur ces deux blocs, sauf a cocher « forcer partout ».

   Cette liste doit correspondre EXACTEMENT a `em_font_roots()`, la table que le PHP utilise pour la regle
   `!important` de « forcer partout ». Deux listes de seize noms tenues a la main auraient diverge : une
   garde les compare desormais, donc une dix-septieme racine ajoutee au PHP fait echouer le test jusqu a
   ce qu elle soit ici aussi. */
.em-agenda-wrap,
.em-agenda-sidebar-wrap,
.em-list-wrap,
.em-hero-carousel,
.em-upcoming,
.em-dates-strip,
.em-city-list,
.em-contact,
.em-spotlight,
.em-posters,
.em-accordion,
.em-carousel,
.em-date-tiles,
.em-tabs,
.em-kpi-list,
.em-ticker,
.em-filtered-grid,
.em-event-banner,
.em-signup-list,
.em-campus-bar,
.em-event-row,
.em-photo-carousel,
.em-announce-ribbon,
.em-dates-panel,
.em-dates-card,
.em-titled-card {
  font-family: var(--em-font);
}
/* Conserve : `.em-agenda` ne matche rien dans le markup du plugin, mais peut etre cible par le CSS
   global d'un client ou un template personnalise. Ses declarations `color` / `box-sizing` etaient
   donc mortes elles aussi ; elles ne sont volontairement PAS reportees sur les racines ci-dessus
   (passer les blocs en border-box modifierait des paddings/largeurs existants). --em-color-text
   reste applique par les 9 autres regles qui le consomment : le reglage text_color fonctionne. */
.em-agenda {
  font-family: var(--em-font);
  color: var(--em-color-text);
  box-sizing: border-box;
}
*, *::before, *::after { box-sizing: inherit; }
/*
 * L'attribut `hidden` DOIT masquer, dans tout le plugin. (2.5.380)
 *
 * ⚠ POURQUOI CETTE RÈGLE EXISTE, ET CE QU'ELLE RÉPARE.
 *
 * `hidden` ne vaut `display: none` que dans la feuille du NAVIGATEUR : n'importe quel `display` d'auteur
 * le bat, à égalité de spécificité comme en dessous. Les six classes de flèches du plugin déclarent
 * `display: inline-flex`, donc `element.hidden = true` ne masquait RIEN.
 *
 * Conséquence mesurée : `wireSlides()` posait bien `b.hidden = count <= 1` — avec le commentaire « les
 * masquer plutôt que les désactiver » — et la ligne était **inerte**. Sur `spotlight` et `posters`, une
 * slide unique laissait donc ses deux flèches affichées, ce que le code disait explicitement vouloir
 * éviter. Le défaut était latent depuis l'écriture de ce helper.
 *
 * TROISIÈME occurrence de cette classe dans le dépôt. `admin.css` la traite génériquement depuis la
 * 2.5.370 (`.em-admin-wrap [hidden]`), après DEUX rustines ponctuelles ; `frontend.css` n'avait rien.
 * On applique donc directement le remède générique — une rustine par bloc laisserait le piège armé
 * pour le quatrième.
 *
 * ⚠ PORTÉE MESURÉE AVANT D'ÉCRIRE : dans toute la façade, **une seule ligne** utilise `hidden` — celle
 * qui est cassée. Aucun gabarit, aucun `render.php`, aucun autre point du JS. Cette règle ne peut donc
 * rien casser : elle ne peut que faire fonctionner la seule intention qui existe. C'est ce qui rend le
 * `!important` acceptable ici.
 *
 * Les racines sont celles de `em_font_roots()`, et une garde compare les deux listes : une dix-septième
 * racine ajoutée là-bas devra venir ici, sinon `hidden` redeviendrait silencieusement inerte dans ce
 * bloc-là. Scoper aux racines et non au document entier est délibéré — le plugin n'a pas à décider du
 * comportement de `hidden` dans le reste de la page.
 */
.em-filtered-grid [hidden],
.em-event-banner [hidden],
.em-signup-list [hidden],
.em-campus-bar [hidden],
.em-event-row [hidden],
.em-photo-carousel [hidden],
.em-announce-ribbon [hidden],
.em-dates-panel [hidden],
.em-dates-card [hidden],
.em-titled-card [hidden],
.em-agenda-wrap [hidden],
.em-agenda-sidebar-wrap [hidden],
.em-list-wrap [hidden],
.em-hero-carousel [hidden],
.em-upcoming [hidden],
.em-dates-strip [hidden],
.em-city-list [hidden],
.em-contact [hidden],
.em-spotlight [hidden],
.em-posters [hidden],
.em-accordion [hidden],
.em-carousel [hidden],
.em-date-tiles [hidden],
.em-tabs [hidden],
.em-kpi-list [hidden],
.em-ticker [hidden] {
  display: none !important;
}


/* ---- Filters ---- */
.em-agenda__filters {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: 1.5rem;
}
.em-filter {
  padding: .5rem .75rem;
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  background: var(--em-color-surface);
  font-size: .875rem;
  cursor: pointer;
  transition: border-color var(--em-transition);
}
.em-filter:focus { outline: none; border-color: var(--em-color-primary); }
.em-btn-reset {
  padding: .5rem 1rem;
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  background: transparent;
  font-size: .875rem;
  cursor: pointer;
  transition: background var(--em-transition);
}
.em-btn-reset:hover { background: var(--em-color-bg); }

/* ---- Count ---- */
.em-agenda__count {
  font-size: .875rem;
  color: var(--em-color-muted);
  margin-bottom: 1rem;
}

/* ---- Agenda rows ---- */
.em-event-card {
  background: var(--em-color-surface);
  border-radius: var(--em-radius);
  box-shadow: var(--em-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform var(--em-transition), box-shadow var(--em-transition);
}
.em-event-card:hover {
  box-shadow: 0 6px 20px rgba(0,0,0,.12);
}
.em-event-card__image {
  width: 100%;
  aspect-ratio: var(--em-thumb-ratio);
  object-fit: var(--em-thumb-fit);
}
.em-event-card__image-placeholder {
  width: 100%;
  /* Meme ratio que l'image reelle : sans ca, un evenement sans visuel decalerait la grille */
  aspect-ratio: var(--em-thumb-ratio);
  background: linear-gradient(135deg, var(--em-color-primary) 0%, var(--em-color-accent) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 2rem;
}
/* Card image container for JS-rendered cards */
.em-event-card__img {
  width: 100%;
  aspect-ratio: var(--em-thumb-ratio);
  overflow: hidden;
  background: var(--em-color-bg);
}
.em-event-card__img img {
  width: 100%;
  height: 100%;
  object-fit: var(--em-thumb-fit);
  display: block;
}
/* ---- Zone visuelle des evenements SANS image (reglage no_image_mode) ----
   Meme markup pour les 3 rendus (carte de grille, ligne d'agenda, carte « a la une ») : le
   conteneur parent porte deja le ratio (--em-thumb-ratio) ou la hauteur, d'ou height:100%. */
.em-noimg {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .125rem;
  background: linear-gradient(135deg, var(--em-noimg-from) 0%, var(--em-noimg-to) 100%);
  color: #fff;
  overflow: hidden;
}
.em-noimg__main {
  /* clamp : la meme zone sert une vignette d'agenda de 250px et une carte pleine largeur */
  font-size: clamp(1rem, 3.2vw, 2rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: center;
  padding: 0 .375rem;
  overflow-wrap: anywhere;
}
.em-noimg__sub {
  font-size: clamp(.625rem, 1.4vw, .75rem);
  font-weight: 700;
  letter-spacing: .12em;
  opacity: .85;
}

.em-event-card__body {
  padding: 1rem;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.em-event-card__meta {
  display: flex;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: .5rem;
}
.em-badge-tag {
  font-size: .75rem;
  padding: .25rem .5rem;
  border-radius: 999px;
  background: var(--em-color-bg);
  color: var(--em-color-muted);
  border: 1px solid var(--em-color-border);
}
.em-badge-tag--school { background: var(--em-color-info-bg); color: var(--em-color-primary); border-color: var(--em-color-info-border); }
.em-badge-tag--type   { background: var(--em-color-warning-bg); color: var(--em-color-warning); border-color: var(--em-color-warning-border); }
.em-event-card__title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 .5rem;
  line-height: 1.4;
  color: var(--em-color-primary);
}
.em-event-card__date {
  font-size: .875rem;
  color: var(--em-color-muted);
  margin-bottom: .5rem;
}
.em-event-card__address {
  font-size: .875rem;
  color: var(--em-color-muted);
  margin-bottom: .75rem;
}
.em-event-card__desc {
  font-size: .875rem;
  color: var(--em-color-muted);
  flex: 1;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: .75rem;
}
.em-event-card__actions {
  display: flex;
  gap: .5rem;
  margin-top: auto;
}
/* ==========================================================================================
   BOUTON D'ÉVÉNEMENT — base commune aux 7 blocs
   ==========================================================================================
   Le même objet existait en SEPT déclarations divergentes (13, 13, 10, 9, 8, 4 et 2 propriétés) :
   `.em-btn-cta`, `.em-agenda-item__cta`, `.em-fc__cta`, `.em-hc__event-cta`, `.em-upcoming__cta`,
   `.em-asb__cta-btn`, `.em-ds__cta`. C'est mécaniquement pour cette raison que les blocs avaient
   dérivé — l'icône globale n'atteignait que trois d'entre eux.

   Trois règles d'architecture, chacune nécessaire :

   1. La base est émise AVANT les sept règles historiques, qui sont conservées. À spécificité égale
      (0,1,0) la dernière déclarée gagne : chaque bloc garde donc exactement son apparence actuelle,
      et toute surcharge écrite côté thème ou client continue de s'appliquer. Zéro régression.

   2. Les déclinaisons (`--solid`, `--outline`, `--ghost`) ont leur classe DOUBLÉE, donc (0,2,0) :
      elles battent les règles de bloc. C'est indispensable — sinon choisir « solid » ne changerait
      rien là où le bloc déclare déjà un `background`. Même procédé que `.em-events-grid.em-events-grid`.

   3. La couleur passe par deux variables locales et jamais par `--em-color-primary` en dur. Le hero
      et la bande de dates posent leur CTA sur une image ou un fond coloré : sans le contexte
      `--on-dark`, demander « outline » y peindrait un bouton bleu marine sur fond sombre, donc
      illisible. Les déclinaisons sont ainsi ORTHOGONALES au contexte.
   ========================================================================================== */
.em-btn-event {
  --em-btn-c:  var(--em-color-primary);   /* couleur du bouton (bordure, texte, remplissage) */
  --em-btn-on: #fff;                      /* texte posé SUR --em-btn-c quand il remplit */
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  border-style: solid;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--em-transition), color var(--em-transition), opacity var(--em-transition);
}
/* Sur fond sombre ou coloré, les deux couleurs s'inversent. */
.em-btn-event--on-dark {
  --em-btn-c:  #fff;
  --em-btn-on: var(--em-color-primary);
  padding: .5rem 1rem;
  font-size: .75rem;
  border-width: 1px;
}
.em-btn-event .em-icon,
.em-btn-event > svg { flex-shrink: 0; transition: transform .15s; }
.em-btn-event:hover .em-icon { transform: translateX(3px); }

/* Déclinaisons — classe doublée pour primer sur les règles de bloc (cf. point 2 ci-dessus). */
.em-btn-event--outline.em-btn-event--outline {
  background: transparent;
  color: var(--em-btn-c);
  border-color: var(--em-btn-c);
}
.em-btn-event--outline.em-btn-event--outline:hover {
  background: var(--em-btn-c);
  color: var(--em-btn-on);
  opacity: 1;
}
.em-btn-event--solid.em-btn-event--solid {
  background: var(--em-btn-c);
  color: var(--em-btn-on);
  border-color: var(--em-btn-c);
}
.em-btn-event--solid.em-btn-event--solid:hover { opacity: .85; }
/* `ghost` : ni fond ni bordure. La bordure reste DÉCLARÉE en transparent et non retirée, sinon le
   bouton se décalerait de sa largeur au survol d'une autre déclinaison voisine. */
.em-btn-event--ghost.em-btn-event--ghost {
  background: transparent;
  color: var(--em-btn-c);
  border-color: transparent;
  padding-left: 0;
  padding-right: 0;
}
.em-btn-event--ghost.em-btn-event--ghost:hover { text-decoration: underline; opacity: 1; }

.em-btn-cta {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .5rem 1rem;
  background: transparent;
  color: var(--em-color-primary);
  border: 1.5px solid var(--em-color-primary);
  border-radius: var(--em-radius);
  font-size: .875rem;
  cursor: pointer;
  text-decoration: none;
  transition: background var(--em-transition), color var(--em-transition);
}
.em-btn-cta:hover { background: var(--em-color-primary); color: #fff; }
.em-btn-cta .em-icon { transition: transform .15s; flex-shrink: 0; }
.em-btn-cta:hover .em-icon { transform: translateX(3px); }
.em-btn-ics {
  padding: .5rem .75rem;
  background: transparent;
  color: var(--em-color-muted);
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  font-size: .875rem;
  cursor: pointer;
  text-decoration: none;
  transition: border-color var(--em-transition), color var(--em-transition);
}
.em-btn-ics:hover { border-color: var(--em-color-primary); color: var(--em-color-primary); }

/* ---- Event card ---- */
.em-agenda__list { display: flex; flex-direction: column; gap: 1rem; }
.em-agenda-month-group { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 1.5rem; }
.em-agenda-month-group:last-child { margin-bottom: 0; }
.em-agenda-month-title {
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--em-color-primary);
  margin: 0 0 .25rem;
  padding-bottom: .25rem;
  border-bottom: 2px solid var(--em-color-border);
  text-transform: capitalize;
}
.em-agenda-item-wrap {
  display: flex;
  flex-direction: column;
  gap: 0;
}
.em-agenda-item__campus-label {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  align-self: flex-start;
  background: var(--em-color-primary);
  color: #fff;
  font-size: .75rem;
  font-weight: 600;
  padding: .25rem .75rem;
  border-radius: var(--em-radius) var(--em-radius) 0 0;
  letter-spacing: .03em;
}
.em-agenda-item__campus-label .em-icon {
  opacity: 1;
}
.em-agenda-item {
  display: flex;
  background: var(--em-color-surface);
  border-radius: var(--em-radius);
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,.08);
  transition: transform .2s ease, box-shadow .2s ease;
  cursor: default;
}
.em-agenda-item-wrap:hover .em-agenda-item {
  box-shadow: 0 8px 24px rgba(0,0,0,.13);
}
/* Left column: date block + campus below */
.em-agenda-item__date-col {
  flex-shrink: 0;
  width: 100px;
  display: flex;
  flex-direction: column;
  align-self: stretch;
}
.em-agenda-item__date {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--em-color-primary);
  color: #fff;
  padding: 1rem .5rem .75rem;
  text-align: center;
  gap: .125rem;
}
/* Type label at the TOP of the date column, mirroring campus-below at the bottom */
.em-agenda-item__type-top {
  background: var(--em-color-primary);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: .25rem;
  padding: .375rem .25rem;
  font-size: .625rem;
  font-weight: 500;
  border-bottom: 1px solid rgba(255,255,255,.15);
  text-align: center;
  line-height: 1.5;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.em-agenda-item__type-top .em-icon { opacity: 1; flex-shrink: 0; }
.em-agenda-item__day {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.02em;
}
.em-agenda-item__mon {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  opacity: .85;
}

/* ---- Date range: two stacked date blocks ---- */
.em-agenda-item__date--range {
  flex-direction: column;
  justify-content: center;
  gap: 0;
  padding: .5rem .375rem;
}
.em-agenda-item__date-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
}
.em-agenda-item__date--range .em-agenda-item__day  { font-size: 2.25rem; }
.em-agenda-item__date--range .em-agenda-item__mon  { font-size: .75rem; }
.em-agenda-item__date-sep {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: .25rem 0;
  opacity: .55;
  flex-shrink: 0;
}
.em-date-arrow { display: block; }
/* Campus label below the date block, inside the left column */
.em-agenda-item__campus-below {
  background: var(--em-color-primary);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: .25rem;
  padding: .375rem .25rem;
  font-size: .75rem;
  font-weight: 500;
  border-top: 1px solid rgba(255,255,255,.15);
  text-align: center;
  line-height: 1.3;
}
.em-agenda-item__campus-below .em-icon { opacity: 1; flex-shrink: 0; }
/* School name at top of card body */
/*
 * ⚠ `secondary` ET NON `primary` DEPUIS LA 2.5.381, et c'est un changement de rendu assumé.
 *
 * Cette classe est rendue par la carte d'agenda ET par la carte de grille, où elle valait `primary` — la
 * MEME couleur que le titre situé juste en dessous, donc l'école s'y fondait. La carte « à la une » a sa
 * propre classe (`.em-fc__school`) et porte `secondary` depuis toujours : elle avait raison, et son choix
 * évite précisément ce fondu.
 *
 * Même donnée, deux couleurs selon le bloc, sur la même page : c'est l'incohérence qu'on ferme, pas une
 * préférence esthétique. Trouvée en mesurant les couleurs de texte des deux cartes, pas par une garde.
 *
 * Reste réglable : cette ligne appartient au groupe « métadonnées » des deux familles, donc
 * `agenda_meta_color` et `fc_meta_color` la surchargent.
 */
.em-event-school {
  font-size: .75rem;
  font-weight: 700;
  color: var(--em-color-secondary);
  text-transform: uppercase;
  letter-spacing: .06em;
  margin: 0;
}
/* Main wrapper (right of date block) */
.em-agenda-item__main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
/* Top bar — meta + badges, above thumb & body */
.em-agenda-item__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem;
  padding: .5rem 1rem;
  background: var(--em-color-bg);
  border-bottom: 1px solid var(--em-color-border);
  min-height: 34px;
}
/* Row — thumb left, body right */
.em-agenda-item__row {
  display: flex;
  flex: 1;
  align-items: stretch;
}
/* Body */
.em-agenda-item__body {
  flex: 1;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: .375rem;
  min-width: 0;
}
.em-agenda-item__title {
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0;
  line-height: 1.3;
  color: var(--em-color-primary);
}
.em-agenda-item__desc {
  font-size: .875rem;
  color: var(--em-color-muted);
  margin-bottom: .5rem;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.em-agenda-item__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem 0rem;
  font-size: .75rem;
  color: var(--em-color-muted);
}
.em-agenda-item__meta > span + span::before {
  content: "\00b7";
  margin: 0 .25rem;
  opacity: .5;
}
.em-agenda-item__address {
  font-size: .75rem;
  color: var(--em-color-muted);
  margin: 0;
}
.em-agenda-item__actions {
  margin-top: .5rem;
  /* Alignement du CTA configurable en back-office (Affichage > Theme) : gauche (defaut) / droite.
     .em-agenda-item__cta est inline-flex → text-align positionne le bouton horizontalement. */
  text-align: var(--em-agenda-cta-align, left);
}
.em-agenda-item__cta {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .5rem .75rem;
  border: 1.5px solid var(--em-color-primary);
  color: var(--em-color-primary);
  border-radius: calc(var(--em-radius) / 2);
  font-size: .75rem;
  font-weight: 600;
  text-decoration: none;
  transition: opacity .18s;
}
.em-agenda-item__cta:hover {
  opacity: .75;
}
.em-agenda-item__cta .em-icon { transition: transform .15s; flex-shrink: 0; }
.em-agenda-item__cta:hover .em-icon { transform: translateX(3px); }

/* Thumbnail — landscape, adapts to card height */
.em-agenda-item__thumb {
  flex-shrink: 0;
  width: var(--em-agenda-thumb-width);
  align-self: stretch;
  overflow: hidden;
  position: relative;
  aspect-ratio: var(--em-thumb-ratio);
}
.em-agenda-item__thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: var(--em-thumb-fit);
  display: block;
}
/* Featured badge inside agenda item */
.em-agenda-item__featured {
  align-self: flex-start;
  font-size: .625rem;
  padding: .125rem .5rem;
  border-radius: 999px;
}

/* ---- Load more ---- */
.em-load-more {
  display: block;
  margin: 2rem auto 0;
  padding: .75rem 2rem;
  background: transparent;
  border: 2px solid var(--em-color-primary);
  color: var(--em-color-primary);
  border-radius: var(--em-radius);
  font-size: 1rem;
  cursor: pointer;
  transition: background var(--em-transition), color var(--em-transition);
}
.em-load-more:hover { background: var(--em-color-primary); color: white; }

/* ---- Skeleton shimmer ----
   COMPOSABLE depuis la 2.5.389, et c'est un releve Lighthouse qui l'a designe (« Eviter les animations
   non composees : 1 element anime trouve »).

   L'animation portait sur `background-position`, que le compositeur ne sait pas prendre en charge : chaque
   image demandait donc un REPEINT sur le fil principal, en boucle INFINIE — et dans la zone ou l'image LCP
   doit justement se peindre. Mesure du meme releve : 2 390 ms de « delai d'affichage de l'element » sur un
   LCP de 2,9 s, la ressource elle-meme etant chargee en 160 ms.

   `transform` est composable : le balayage vit desormais dans un pseudo-element translate, et le fil
   principal n'a plus rien a repeindre.

   ⚠ L'EMPILEMENT est reproduit a l'identique, et c'est la seule subtilite. Les enfants `.em-skel-*`
   portent des fonds BLANCS SEMI-TRANSPARENTS (`rgba(255,255,255,.15)` a `.3`) qui se superposaient au
   degrade du parent. Un `::before` absolu paint dans la couche des descendants POSITIONNES, donc au-dessus
   des enfants en flux : sans le `position: relative` ci-dessous, le balayage passerait PAR-DESSUS eux et
   l'effet changerait d'aspect.

   `overflow: hidden` n'est pas repose : `.em-agenda-item` et `.em-event-card` le portent deja, et c'est
   verifie par une garde — le jour ou l'une des deux le perdrait, le balayage deborderait la carte. */
@keyframes em-shimmer {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}
.em-skeleton {
  position: relative;
  background: #e9ecef;
  border-radius: var(--em-radius);
}
.em-skeleton::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 0, #f8f9fa 50%, transparent 100%);
  animation: em-shimmer 1.4s infinite linear;
  pointer-events: none;
}
/* Les enfants REPASSENT au-dessus du balayage — cf. la note d'empilement ci-dessus. */
.em-skeleton > * { position: relative; }
.em-skeleton-card {
  height: 280px;
  border-radius: var(--em-radius);
}
.em-skeleton-line {
  height: 1em;
  margin-bottom: .5rem;
  border-radius: 4px;
}
.em-skeleton-line--short { width: 60%; }
.em-skeleton-line--med   { width: 80%; }

/* ---- Skeleton sub-elements (agenda + card) ---- */
.em-agenda-item.em-skeleton { min-height: 180px; }
.em-skel-date {
  width: 95px; flex-shrink: 0;
  background: rgba(255,255,255,.15);
}
.em-skel-body {
  flex: 1; padding: 1rem;
  display: flex; flex-direction: column;
  justify-content: center; gap: .5rem;
}
.em-skel-title {
  height: 1rem; border-radius: 4px;
  background: rgba(255,255,255,.3); width: 65%;
}
.em-skel-meta {
  height: .75rem; border-radius: 4px;
  background: rgba(255,255,255,.22); width: 45%;
}
.em-skel-meta.em-skel-meta--short { width: 30%; }
.em-event-card.em-skeleton { min-height: 280px; }
.em-skel-img {
  height: 160px; flex-shrink: 0;
  background: rgba(255,255,255,.2);
}
.em-skel-content {
  flex: 1; padding: 1rem;
  display: flex; flex-direction: column; gap: .5rem;
}

/* ---- Empty state ---- */
.em-empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--em-color-muted);
}
.em-empty-state svg, .em-empty-state .dashicons { font-size: 3rem; margin-bottom: 1rem; }

.em-empty-message {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .75rem;
  text-align: center;
  padding: .5rem;
  /*
   * LA COULEUR DU TEXTE DE SON BLOC, attenuee a 75 % (2.5.447). Elle valait `--em-color-muted`, UNE couleur
   * pour tous les blocs : sur les douze PANNEAUX SOMBRES, mesure, 1,25:1 avec la palette par defaut et 1:1 sur
   * un site dont le « Texte attenue » egalait la primaire — message invisible. Aucune couleur unique ne peut
   * convenir a un fond clair ET a un fond sombre ; l'HERITAGE, si : le message suit le texte du panneau qui le
   * porte, et la « Couleur du texte » reglee sur un bloc. Aucun reglage de plus, aucun bloc a lister.
   *
   * Deux declarations, a dessein : `inherit` est le REPLI des navigateurs sans `color-mix()` (avant Chrome 111,
   * Safari 16.2, Firefox 113), qui affichent alors la couleur pleine du texte — lisible. `currentColor` DANS la
   * propriete `color` designe la couleur HERITEE, d'ou l'attenuation du texte du parent. Pas d'`opacity` : elle
   * delaverait aussi le bouton « voir tous les evenements » rendu dans ce message.
   */
  color: inherit;
  color: color-mix(in srgb, currentColor 75%, transparent);
}
.em-empty-message__link {
  display: inline-block;
  padding: .5rem 1.25rem;
  background: var(--em-color-primary);
  color: #fff;
  border-radius: var(--em-radius);
  text-decoration: none;
  font-weight: 600;
  transition: opacity .15s;
}
.em-empty-message__link:hover { opacity: .85; color: #fff; }

/* ---- Responsive ---- */
@media (max-width: 640px) {
  /* On mobile: date-col (fixed 92px) left, main fills right column */
  .em-agenda-item {
    display: grid;
    grid-template-columns: 90px 1fr;
  }
  .em-agenda-item__date-col {
    grid-column: 1;
    grid-row: 1;
    width: auto;
    align-self: stretch;
  }
  .em-agenda-item__date {
    padding: .625rem .375rem .5rem;
    gap: .125rem;
  }
  .em-agenda-item__main {
    grid-column: 2;
    grid-row: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  .em-agenda-item__row {
    flex: 1;
    flex-direction: column;
  }
  .em-agenda-item__day { font-size: 1.5rem; }
  .em-agenda-item__date--range .em-agenda-item__day { font-size: 1.75rem; }
  .em-agenda-item__date--range { padding: .375rem .25rem; justify-content: flex-start; }
  .em-agenda-item__date--range .em-agenda-item__date-block { flex: 0 0 auto; }
  /* Hide thumbnail on mobile — keeps the card clean and readable at narrow widths */
  .em-agenda-item__thumb { display: none; }
  /*
   * CTA CENTRE et non pleine largeur (2.5.261). Il occupait toute la largeur de la carte : sur un
   * ecran de 360 px cela donne un bouton de plus de 300 px pour deux mots, qui pese plus lourd que
   * le titre de l'evenement et fait lire la carte a l'envers.
   *
   * `inline-flex` + `width: auto` pour la taille, `text-align: center` sur la rangee pour le placer :
   * le bouton contient une icone, donc il etablit son propre contexte de mise en page — mais il
   * reste inline-level, et c'est ce qui permet a `text-align` de le deplacer.
   *
   * Le padding passe de `.25rem` a `.5rem 1.125rem` : sans largeur imposee, c'est lui seul qui donne
   * au bouton sa surface tactile. En dessous de 44 px de haut, la cible devient difficile a viser au
   * doigt — seuil des recommandations d'accessibilite mobile.
   */
  .em-agenda-item__actions { width: 100%; margin-top: 0; padding: .375rem .75rem .625rem; text-align: center; }
  .em-agenda-item__cta {
    display: inline-flex; width: auto; justify-content: center;
    padding: .5rem 1.125rem;
    background: var(--em-color-primary); color: #fff;
    border-color: var(--em-color-primary);
  }
  .em-agenda-item__cta:hover { opacity: .85; color: #fff; }
}

/* CLS prevention: hold layout space while JS is fetching & rendering */
.em-agenda-list:empty { min-height: 5rem; }

/* ===================================================================================================
   RESERVATION DE HAUTEUR : ne pas pousser la page quand le JS remplit (2.5.343)

   Constat 08, reponse A. Mesure sur un releve reel : CLS 0,019 — sous le seuil de 0,1, donc PAS un
   probleme aujourd'hui. Ce qui change la donne est l'emplacement : un bloc en EN-TETE pousse tout ce qui
   suit, sur chaque page. On reserve donc, et on le fait avec des nombres qu'on peut justifier.

   ⚠ L'agenda etait le premier suspect : Lighthouse lui imputait 0,018 des 0,019. Mesure faite, sa liste
   reserve deja `max(500, par_page x 220)px` en style inline, et `.em-featured-zone--carousel` porte
   `min-height: 500px`. Les deux gros conteneurs etaient donc DEJA reserves — et la premiere idee (« il
   faut reserver la liste ») aurait ajoute une regle inutile en croyant corriger.

   Ce qui restait sans reservation dans ce bloc : les conteneurs de DEROULANTE. `templates/agenda.php`
   rend `.em-filter-select` vides et VISIBLES (trois d'entre eux sans `display:none`), donc a hauteur
   nulle, jusqu'a ce que `buildFilterDropdown()` y ecrive un bouton haut de `--em-filter-height`. La
   barre grandit alors, et tout le bloc avec.

   Cette reservation-la ne coute AUCUNE estimation : la hauteur est le jeton que les trois controles de
   la barre partagent deja (recherche, declencheur, bouton « reinitialiser »).

   `:not(:has(*))` et non `:empty` : le conteneur recoit des ELEMENTS, et `:empty` echoue des qu'il y a
   un blanc dans la source. La reservation se LIBERE donc d'elle-meme a l'arrivee du premier enfant, sans
   une ligne de JavaScript, sans etat a synchroniser, et sans risque de laisser un vide permanent.
   Sans `:has()` — Firefox anterieur a 121 — il n'y a simplement pas de reservation : le comportement
   d'avant, jamais un affichage casse.
   =================================================================================================== */
.em-filter-select:not(:has(*)) {
  min-height: var(--em-filter-height);
}

/* ---------------------------------------------------------------------------------------------------
   Les autres blocs : PROPHYLACTIQUE, et je le dis.

   Aucun n'a ete signale par le releve — la page testee les portait tous, et seul l'agenda a bouge. Mais
   aucun ne reserve quoi que ce soit, et c'est l'en-tete qui rend le defaut coûteux.

   DEUX nombres, pas onze, et chacun s'explique :

    · piste (`--em-track-reserve`, 260px) : une rangee de cartes. La carte la plus etroite du lot fait
      190-240px de large, son visuel suit `--em-thumb-ratio` (16/9 par defaut) donc ~110-135px de haut,
      et le corps — date, titre, ville — en ajoute ~120. Soit 230 a 255. On prend 260, legerement
      au-dessus : sur-reserver ne provoque aucun decalage, sous-reserver en laisse un ;
    · liste (`--em-list-reserve`, 180px) : environ quatre lignes. `.em-cl__item` porte `padding: .5rem 0`
      autour d'une ligne de texte, soit ~44px par ligne — 4 x 44 = 176.

   Les deux sont des VARIABLES : un site dont les cartes sont plus hautes les ajuste sans toucher au
   plugin. Et comme la reservation se libere a l'arrivee du contenu, une valeur imparfaite ne coûte au
   pire qu'un residu de decalage, jamais un vide durable.
   --------------------------------------------------------------------------------------------------- */
/* `.em-tk__track` n'est PAS dans ce groupe : la bande fait une ligne, sa reservation propre est
   quinze lignes plus bas. Il y figurait, et la seconde declaration le reprenait — une regle morte, que
   j'avais ecrite moi-meme dans la version qui a introduit ces reservations (2.5.343). */
.em-ds__track:not(:has(*)),
.em-dt__track:not(:has(*)),
.em-car__track:not(:has(*)),
.em-hc__track:not(:has(*)) {
  min-height: var(--em-track-reserve, 260px);
}

.em-cl__list:not(:has(*)),
.em-kpi__rows:not(:has(*)),
.em-ac__list:not(:has(*)),
.em-tb__grid:not(:has(*)),
.em-asb__events:not(:has(*)),
.em-upcoming__body:not(:has(*)) {
  min-height: var(--em-list-reserve, 180px);
}

/* Le ticker fait UNE ligne : lui appliquer les 260px d'une piste de cartes serait absurde. Sa hauteur
   reglable (`--em-tk-h`) vaut `auto` par defaut, donc ne reserve rien — on retombe sur une ligne. */
.em-tk__track:not(:has(*)) {
  min-height: var(--em-tk-h, 2.75rem);
}


/* ===================== SEARCH INPUT ===================== */
/* Classe DOUBLEE : un theme qui ecrit `input[type=search] { background: #FFF; padding: 0 1rem }` pese
   (0,1,1) et battait cette regle a (0,1,0). Deux degats, dont un tres visible : le `padding` gauche qui
   loge la loupe disparaissait, et le RACCOURCI `background` reinitialisait `repeat` / `position` / `size`
   — l'image survivant par ailleurs (elle vient d'une regle a 0,2,0), elle se TUILAIT sur toute la
   largeur. Signale depuis un site reel : une trentaine de loupes.

   On double le SELECTEUR plutot que de recopier la declaration : l'URI de donnees du SVG n'existe
   toujours qu'une fois. Meme idiome que `.em-events-grid.em-events-grid`, et meme raison.

   ⚠ La regle mobile plus bas est doublee AUSSI : sans cela elle passerait sous celle-ci et le champ ne
   prendrait plus toute la largeur sous 640 px. Corriger une fuite en creant un defaut n'est pas
   corriger. */
.em-search-input.em-search-input {
  flex: 1; min-width: 180px; max-width: 300px;
  /* Hauteur alignee sur les dropdowns via le jeton commun. box-sizing explicite : la regle
     globale `* { box-sizing: inherit }` est ancree sur .em-agenda, selecteur absent du markup,
     donc la valeur heritee depend du theme hote — sans ca, `height` s'ajouterait au padding. */
  box-sizing: border-box;
  height: var(--em-filter-height);
  padding: .5rem .75rem .5rem 2.25rem;
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  font-size: .875rem;
  background: var(--em-color-surface)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236c757d' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E")
    no-repeat .625rem center;
  background-size: 14px;
  transition: border-color var(--em-transition);
}
.em-search-input:focus { outline: none; border-color: var(--em-color-primary); box-shadow: 0 0 0 .25rem rgba(26,58,107,.18); }

/* ===================== AGENDA WRAP ===================== */
.em-agenda-wrap { outline: none; }
.em-agenda-wrap:focus,
.em-agenda-wrap:focus-visible { outline: none; }

/* ===================== AGENDA CONTAINER (desktop only) =====================
   RETOUR EN ARRIERE (2.5.259). La 2.5.256 avait pose un retrait lateral de 1 rem a TOUTES les largeurs,
   pour qu'un bloc en « Pleine largeur » ne colle pas son contenu aux bords. L'intention etait juste, la
   portee etait trop large : ce conteneur enveloppe AUSSI la liste d'evenements et la bande « a la une »,
   donc le retrait s'ajoutait a leurs propres marges et decalait tout l'agenda sur mobile — y compris la
   ou personne ne l'avait demande.

   Lecon : un conteneur partage par trois zones n'est pas le bon endroit pour corriger le cadrage d'UNE
   d'entre elles. Si le besoin revient, il faudra viser la zone concernee, pas leur ancetre commun.
   ========================================================================== */
/* ==========================================================================
   LARGEUR ET RETRAIT DU CONTENEUR — un seul point, deux racines (2.5.344)

   DEUX defauts signales depuis un site reel, et une seule cause : la contrainte de largeur n'existait
   qu'a UN endroit, et une racine sur deux ne la traversait pas.

   ① « la page agenda a un prb de container entre 13XXpx et 768px, prb de padding left et right »

   Exact, et la fenetre est precisement 768 px -> largeur maximale reglee. Au-dessus de 768 px la regle
   ne posait AUCUN retrait lateral, en s'appuyant sur `margin: auto` — mais `margin: auto` ne produit une
   marge que si la fenetre DEPASSE `max-width`. Entre les deux, le conteneur fait exactement la largeur de
   la fenetre, la marge automatique vaut zero, et le contenu touche les deux bords.

   La borne a 767 px avait une raison ecrite : « cumuler les deux rognerait la largeur maximale annoncee —
   un 1280 qui rendrait 1248 ». La raison etait juste ; c'est la conclusion qui l'etait moins. On garde la
   promesse en ELARGISSANT la boite de la valeur du retrait : `max-width` vaut la largeur reglee PLUS les
   deux retraits, donc au-dela le contenu mesure exactement la largeur reglee, et en dessous il respire.

     fenetre 1600 : boite 1312, retrait 2 x 16 -> contenu 1280  (identique a avant)
     fenetre 1000 : boite 1000, retrait 2 x 16 -> contenu  968  (avant : 1000, bord a bord)

   Rien ne bouge donc sur grand ecran, et la fenetre defectueuse est corrigee.

   ② « le bloc em-agenda-sidebar-wrap depasse le container par rapport aux autres blocs »

   Exact aussi, et pour une raison structurelle : la disposition « sidebar » ne contient AUCUN
   `.em-agenda-container`. Sa racine porte directement `.em-asb__inner`. Elle n'heritait donc ni de la
   largeur maximale ni du centrage — elle prenait toute la largeur de son parent quand tous les autres
   blocs s'arretaient a la largeur reglee.

   Les deux racines partagent maintenant la MEME declaration. Un troisieme gabarit qui oublierait le
   conteneur n'aurait qu'a s'ajouter a ce selecteur, au lieu de recopier quatre lignes qui divergeraient.

   ⚠ CONSEQUENCE ASSUMEE : entre 768 px et la largeur reglee, le panneau du widget est desormais en retrait
   de 1rem comme les cartes de l'agenda. C'est le but — il touchait les bords quand ses voisins ne les
   touchaient pas.

   Sous 768 px : rien ne change ici. Les deux regles de retrait mobile qui suivent restent seules
   responsables, et elles visent des zones precises pour la raison expliquee juste apres.
   ========================================================================== */
/*
   CLASSE DOUBLEE (2.5.346). Signale : « quand j'applique un nv container en back office ca ne fonctionne
   pas ».

   La chaine du reglage est intacte — mesure de bout en bout : 1440 est enregistre, assaini, et emis en
   `--em-container-max-width: 1440px`. Ce qui manquait est le POIDS de la regle qui la lit : a une seule
   classe, elle pese (0,1,0), le minimum possible. N'importe quelle regle de theme a deux classes, ou a une
   classe plus un type, la battait — et les feuilles de styles globaux d'un theme de blocs en sont pleines
   (`--wp--style--global--content-size` y vaut typiquement 1130 px).

   Troisieme fois que ce depot paie la specificite : la loupe de recherche (2.5.335), les puces de liste
   (2.5.341), la largeur du conteneur maintenant. La parade est la meme, et elle est deja eprouvee ici.

   ⚠ Ce n'est PAS forcement la cause chez le signalant, et il faut le dire : une largeur maximale ne peut
   rien montrer sur une fenetre plus etroite qu'elle. A 1280 px de fenetre, passer le reglage de 1280 a 1440
   ne change RIEN — la fenetre est la contrainte. L'indice du champ le dit desormais.

   Prix assume : un theme qui voudrait imposer sa propre largeur devra doubler sa classe a son tour. C'est
   le but — le reglage du plugin doit gagner, sinon il ne sert a rien.
   ========================================================================== */
@media (min-width: 768px) {
  .em-agenda-container.em-agenda-container,
  .em-agenda-sidebar-wrap.em-agenda-sidebar-wrap {
    /* `+ 2rem` = les deux retraits ci-dessous. C'est ce qui laisse le CONTENU a la largeur reglee. */
    max-width: calc(var(--em-container-max-width, 1280px) + 2rem);
    margin-left: auto;
    margin-right: auto;
    box-sizing: border-box;
    width: 100%;
    padding-left: 1rem;
    padding-right: 1rem;
  }
}
/*
 * Retrait lateral du conteneur DE LA BANDE DE FILTRES, sous 768 px uniquement.
 *
 * Deuxieme tentative, et la portee est le sujet. La 2.5.256 avait pose ce retrait sur
 * `.em-agenda-container` tout court : or ce conteneur enveloppe TROIS zones — la bande de filtres, la
 * bande « a la une » et la liste d'evenements. Le retrait s'ajoutait donc aux marges propres des deux
 * dernieres et decalait tout l'agenda. Annule en 2.5.259.
 *
 * Ici il ne vise que le conteneur INTERIEUR a la bande. Le besoin est reel et limite a elle : quand le
 * bloc est en « Pleine largeur », plus aucune colonne ne fournit de marge, et la barre de recherche
 * touche les deux bords de l'ecran. Les deux autres zones ont leurs propres marges et n'ont jamais eu
 * besoin de rien.
 *
 * Borne a 767 px : au-dela, le centrage par `max-width` + marges automatiques s'en charge, et cumuler
 * les deux rognerait la largeur maximale annoncee — un « 1280 » qui rendrait 1248.
 */
@media (max-width: 767px) {
  .em-filters-bar .em-agenda-container {
    box-sizing: border-box;
  }
}
/*
 * Retrait lateral des ZONES DE CONTENU sous 768 px — bande « a la une » et liste d'evenements.
 *
 * Le selecteur est un ENFANT DIRECT, et c'est ce qui le distingue de la 2.5.256. Le gabarit contient
 * DEUX `.em-agenda-container` :
 *   .em-agenda-wrap > .em-filters-bar > .em-agenda-container   (les filtres, traite plus haut)
 *   .em-agenda-wrap > .em-agenda-container                     (les cartes — celui-ci)
 * Viser la classe sans le `>` touchait les deux : les filtres recevaient donc un retrait EN PLUS du
 * leur, ce qui decalait la bande. Le `>` separe proprement les deux besoins.
 *
 * Borne a 767 px : au-dela, le centrage par `max-width` + marges automatiques s'en charge, et cumuler
 * les deux rognerait la largeur maximale annoncee.
 */
@media (max-width: 767px) {
  .em-agenda-wrap > .em-agenda-container {
    box-sizing: border-box;
    /* Ces deux lignes avaient DISPARU du fichier entre la 2.5.261 et la 2.5.262 : la regle ne contenait
       plus que `box-sizing`, donc les cartes touchaient les deux bords sur mobile. La garde du lot ne
       verifiait que la PRESENCE du selecteur, pas celle de ses declarations — c'est exactement la meme
       faute que « une cle presente ne dit pas qu'elle est remplie » (2.5.249). La garde exige desormais
       les declarations elles-memes. */
    padding-left: 1rem;
    padding-right: 1rem;
  }
}
/*
 * Neutralisation d'une astuce de pleine largeur DEVENUE FAUSSE quand le bloc est deja en pleine largeur.
 *
 * Un theme sort couramment cette bande de sa colonne en annulant le retrait racine de chaque cote :
 *
 *     .em-agenda-wrap .em-filters-bar {
 *       margin-left:  calc(var(--wp--style--root--padding-left)  * -1) !important;
 *       margin-right: calc(var(--wp--style--root--padding-right) * -1) !important;
 *       width: unset !important;
 *     }
 *
 * Le geste est juste tant qu'il y a un retrait a annuler. Quand le bloc est regle sur « Pleine largeur »,
 * son conteneur est DEJA bord a bord : il n'y a plus rien a annuler, et les marges negatives ne peuvent
 * plus que pousser la bande au-dela des deux bords. Mesure relevee chez le client : 390 px sur un ecran
 * de 360, soit exactement deux fois les 15 px de retrait racine — et la premiere liste deroulante etait
 * coupee.
 *
 * `!important` est necessaire ici, et c'est le seul endroit de la feuille ou il sert a contredire un
 * theme. La difference avec la tentative de la 2.5.257, qui avait ete annulee : celle-la forcait la
 * largeur et la position, donc elle retirait au theme des choix de PRESENTATION qui lui reviennent.
 * Celle-ci ne corrige qu'un calcul dont le resultat est demontrablement hors sujet — il n'y a pas de
 * retrait a annuler — et seulement sous `.alignfull`. Hors de ce cas, la regle du theme s'applique
 * intacte.
 *
 * DEUX selecteurs : la classe atterrit sur `.em-block-frame` (le conteneur emis par le plugin), mais un
 * theme peut aussi la poser sur un enveloppant a lui. En viser un seul laisserait la moitie des cas.
 */
.em-block-frame.alignfull .em-agenda-wrap .em-filters-bar,
.alignfull .em-agenda-wrap .em-filters-bar {
  margin-left: 0 !important;
  margin-right: 0 !important;
}
/* ===================== FILTERS BAR (full-width primary bg) ===================== */
.em-filters-bar {
  background: var(--em-filters-bar-bg, var(--em-color-primary));
  width: 100%;
  margin-bottom: 3rem;
}
/* Filtre désactivé en back-office — masqué même après re-render JS (inline style non-important perd face au !important). */
.em-filter-hidden { display: none !important; }
/* Filters inside the bar: transparent bg, white text, no bottom margin */
.em-filters-bar .em-filters {
  background: transparent;
  margin-bottom: 0;
  /* Retrait DESKTOP de la rangee de filtres, demande a 1.5rem en haut/bas et 0 sur les cotes : le
     conteneur `.em-agenda-container` fournit deja le retrait lateral, l'ajouter ici le doublerait.
     Le `!important` etait deja la — un theme repeint souvent ce genre de rangee. */
  padding: 1.5rem .25rem !important;
  color: #fff;
}
/* Search input */
.em-filters-bar .em-search-input {
  background-color: rgba(255,255,255,.15);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,.7)' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
  border-color: rgba(255,255,255,.3);
  color: #fff;
}
.em-filters-bar .em-search-input::placeholder { color: rgba(255,255,255,.65); }
.em-filters-bar .em-search-input:focus { border-color: rgba(255,255,255,.7); box-shadow: 0 0 0 .25rem rgba(255,255,255,.2); }
/* Filter dropdown triggers */
.em-filters-bar .em-filter-select .em-fsd__trigger,
.em-filters-bar .em-filter-select .em-fsd__trigger:hover,
.em-filters-bar .em-filter-select .em-fsd__trigger:focus,
.em-filters-bar .em-filter-select .em-fsd__trigger:active,
.em-filters-bar .em-filter-select.em-fsd--open .em-fsd__trigger {
  background: rgba(255,255,255,.15) !important;
  border-color: rgba(255,255,255,.25) !important;
  color: #fff !important;
  text-shadow: none !important;
}
.em-filters-bar .em-filter-select .em-fsd__trigger:hover,
.em-filters-bar .em-filter-select.em-fsd--open .em-fsd__trigger {
  background: rgba(255,255,255,.25) !important;
  border-color: rgba(255,255,255,.55) !important;
}
.em-filters-bar .em-filter-select.em-fsd--open .em-fsd__trigger {
  border-bottom-color: transparent !important;
}
/* Reset button */
.em-filters-bar .em-btn--reset {
  border-color: rgba(255,255,255,.4);
  color: rgba(255,255,255,.85);
  background: transparent;
}
.em-filters-bar .em-btn--reset:hover {
  background: rgba(255,255,255,.18);
  border-color: rgba(255,255,255,.65);
  color: #fff;
}
/* Per page label */
.em-filters-bar .em-per-page { color: rgba(255,255,255,.85); }
/* Focus ring inside bar */
.em-filters-bar .em-fsd__trigger:focus-visible,
.em-filters-bar .em-search-input:focus-visible {
  /* outline: 2px solid rgba(255,255,255,.7); */
  /* outline-offset: 2px; */
  box-shadow: none;
}

/* ===================== FILTERS ROW ===================== */
.em-filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin-bottom: 0;
  width: 100%; box-sizing: border-box;
  background: var(--em-color-surface);
}
.em-filters--stuck { box-shadow: 0 2px 8px rgba(0,0,0,.07); }
/* Quand c'est la barre parente (.em-filters-bar) qui est collée, l'ombre est plus marquée */
.em-filters-bar.em-filters--stuck { box-shadow: 0 3px 12px rgba(0,0,0,.18); }

/* ---- Reset filters button ----
   Meme jeton de hauteur que les autres controles de la barre : il apparait DES qu'un filtre est
   actif, a cote des dropdowns (il etait dimensionne par son seul padding, ~28px). */
.em-btn--reset {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  box-sizing: border-box;
  height: var(--em-filter-height);
  background: transparent;
  border: 0px solid var(--em-color-border);
  border-radius: 999px;
  color: var(--em-color-muted);
  font-size: .875rem;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1;
  transition: background .15s, border-color .15s, color .15s;
}
.em-btn--reset:hover { background: #fee2e2; border-color: #fca5a5; color: #dc2626; }
.em-per-page {
  display: flex; align-items: center; gap: .375rem;
  font-size: .875rem; color: var(--em-color-muted);
  margin-left: auto;
}
.em-per-page__label { white-space: nowrap; }
/* Per-page custom dropdown: compact, no need to be as wide as filter dropdowns */
.em-per-page .em-filter-select { font-size: .875rem; }

/* ---- Filters responsive ---- */
@media (max-width: 640px) {
  .em-filters {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: .375rem;
    align-items: start;
    padding: .5rem !important;
    width: 100%;
  }
  /*
   * MEME retrait, mais avec la specificite de `.em-filters-bar .em-filters` — et c'est indispensable.
   *
   * Entre deux declarations `!important`, c'est la SPECIFICITE qui tranche, pas l'ordre ni la media
   * query. La regle de base vaut (0,2,0) et celle-ci (0,1,0) : sans ce doublon, le retrait desktop de
   * `1.5rem 0rem` s'appliquerait AUSSI sur mobile, et le `.5rem` juste au-dessus n'aurait aucun effet.
   *
   * Le piege etait invisible jusqu'ici parce que les deux valeurs se ressemblaient (`.5rem .5rem` et
   * `.5rem`). Il apparait des que la valeur desktop s'en ecarte.
   */
  .em-filters-bar .em-filters { padding: .5rem !important; }
  /* Mobile: cancel JS-driven auto width, take full grid cell */
  .em-filter-select { --em-fsd-w: 100%; min-width: 0; width: 100%; display: block; }
  /* Doublee comme la regle de base : a specificite egale c'est l'ordre qui tranche, et celle-ci vient
     apres — donc elle gagne. A (0,1,0) elle aurait perdu, et le champ serait reste a 300 px. */
  .em-search-input.em-search-input { grid-column: 1 / -1; max-width: 100%; min-width: 0; }
  /* Trigger button shows ellipsis when label is wider than its 1/3 column */
  .em-fsd__trigger { overflow: hidden; }
  .em-fsd__label {
    flex: 1; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Dropdown panel: anchor to the RIGHT edge of the trigger on mobile so it never
     overflows the viewport (the rightmost column / "Mois" filter would otherwise
     extend off-screen with left:0). width/min-width already set in base styles. */
  .em-fsd__panel {
    left: auto;
    right: 0;
    max-width: min(90vw, 300px);
  }
  /*
   * « Par page » MASQUÉ sous 640 px.
   *
   * Il occupait une rangée entière de la barre de filtres — la barre étant collante, cette hauteur est
   * retirée à la lecture pendant tout le défilement. Et le choix qu'il offre (10, 15, 20) a peu de sens
   * sur un écran où l'on défile de toute façon : la valeur par défaut de l'agenda est 10 et le réglage
   * `mobile_pagination` permet désormais de basculer le mobile sur « Charger plus ».
   *
   * `display: none` et non un retrait du DOM : PHP ne connaît pas la largeur du viewport, la décision
   * est donc forcément côté client. Le menu continue d'être construit par le JS — inerte, puisque
   * `state.perPage` garde sa valeur — ce qui évite de toucher `buildFilterDropdown` et l'état pour un
   * gain nul. Une garde exige la règle ICI et son ABSENCE au-dessus du seuil : masquer partout serait
   * une régression sur ordinateur, où le choix est utile.
   */
  .em-per-page { display: none; }
  .em-btn--reset { grid-column: 1 / -1; }
}

/* Native select — Bootstrap 5 style (per-page) */
.em-select {
  -webkit-appearance: none; appearance: none;
  height: 38px;
  padding: .375rem 2rem .375rem .75rem;
  font-size: .875rem; color: var(--em-color-text);
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236c757d' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right .75rem center;
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  cursor: pointer;
  transition: border-color var(--em-transition), box-shadow var(--em-transition);
}
.em-select:focus {
  outline: none;
  border-color: var(--em-color-primary);
  box-shadow: 0 0 0 .25rem rgba(26,58,107,.18);
}

/* ===================== CUSTOM FILTER DROPDOWN ===================== */
.em-filter-select {
  position: relative;
  /* width: auto on inline-block = shrink-to-fit naturally, cheaper than max-content keyword
     (avoids intrinsic-size recalculation on every forced layout pass) */
  width: auto;
  display: inline-block;
  vertical-align: top;
}
/* Desktop: give the 4 filter dropdowns a comfortable, uniform min-width so the panel
   (locked to the trigger width) is wide enough for month labels like "Septembre 2026"
   without wrapping. Scoped to direct children of .em-filters so the compact per-page
   dropdown (.em-per-page .em-filter-select) is unaffected. Mobile (≤640px) keeps its
   own full-width grid rule above. */
@media (min-width: 641px) {
  .em-filters > .em-filter-select { min-width: 11rem; }
}

/* `margin` et `list-style` de cette liste : voir le bloc PARTAGE ci-dessous, qui traite les cinq listes
   du plugin. Cette regle-ci les reposait pour la seule deroulante, en double du bloc partage — deux points
   d emission pour la meme decision, donc deux occasions de diverger. Le `!important` n etait pas necessaire :
   le `ul, ol { margin: 30px 0 }` de Neve pese (0,0,1), un `.entry-content ul` (0,1,1), et la classe doublee
   du bloc partage (0,2,0) bat les deux. */
/* La PUCE d'un theme n'est presque jamais un `list-style` : c'est un `::before` avec du contenu genere,
   pose a cote d'un `list-style: none` que le theme ecrit lui-meme. `list-style: none !important` ne peut
   donc rien contre elle — il n'y a pas de marqueur a annuler, il y a du CONTENU a supprimer. Signale
   depuis un site reel : des points devant chaque option du menu.

   `padding` dans la meme regle (0,2,1) : le `.entry-content ul li { padding-left: .75em }` du theme pese
   (0,1,2) et battait `.em-fsd__opt` a (0,1,0), donc les options etaient decalees. Pas besoin
   d'`!important` ici — deux classes battent une classe, et un `!important` de plus rendrait ce padding
   impossible a ajuster par un theme enfant legitime. */
/* ===================================================================================================
   PUCES DE THEME : neutralisation sur les CINQ listes du plugin (2.5.341)

   Signale depuis un site reel, la regle exacte du theme :

       .entry-content ul li::before { content: ''; background-color: ...; border-radius: 50%;
                                      position: absolute; left: .5rem; width: 3px; height: 3px; }

   Corrige d'abord pour le SEUL menu deroulant de la barre de filtres (2.5.335), puis constate sur les
   autres blocs. C'est la meme faute que les filets manquants de la 2.5.340, dans l'autre sens : un
   correctif ecrit pour UN cas, la ou cinq listes partagent le probleme.

   Les CINQ listes du plugin, et rien d'autre. Le balayage ne dit PAS `.em-block-frame li` : une
   description d'evenement peut contenir une vraie liste a puces, et la puce y est le travail du theme.
   On neutralise les listes que le plugin CONSTRUIT, pas tout ce qui se trouve dans un bloc.

   CLASSE DOUBLEE et non un parent : `.em-fsd__panel.em-fsd__panel` pese (0,2,x) tout seul, alors que
   `.em-filter-select .em-fsd__panel` — la forme de 2.5.335 — depend d'un ancetre. Les cinq conteneurs de
   deroulante portent bien cette classe aujourd'hui, mais la prochaine instance posee ailleurs sortirait
   du filet en silence.

   `content: none` et non `content: ''` : il faut SUPPRIMER le pseudo-element, pas le vider. Un
   `::before` vide reste une boite positionnee, avec son `background-color` et ses 3 px.

   `!important` sur le seul `content` : (0,2,2) bat le (0,1,3) du theme signale, mais un theme un peu plus
   imbrique — `.entry-content .wp-block-group ul li::before`, (0,2,3) — reprendrait la main. Un plugin
   diffuse ne connait pas le theme qui l'accueille. Les autres proprietes s'en passent : deux classes
   battent une classe, et un `!important` de plus les rendrait inajustables par un theme enfant legitime.
   =================================================================================================== */
.em-fsd__panel.em-fsd__panel > li::before,
.em-fsd__panel.em-fsd__panel > li::after,
.em-cl__list.em-cl__list > li::before,
.em-cl__list.em-cl__list > li::after,
.em-ct__access-list.em-ct__access-list > li::before,
.em-ct__access-list.em-ct__access-list > li::after,
.em-kpi__rows.em-kpi__rows > li::before,
.em-kpi__rows.em-kpi__rows > li::after,
.em-asb__nav-list.em-asb__nav-list > li::before,
.em-asb__nav-list.em-asb__nav-list > li::after,
.em-fg__grid.em-fg__grid > li::before,
.em-fg__grid.em-fg__grid > li::after,
.em-eb__list.em-eb__list > li::before,
.em-eb__list.em-eb__list > li::after,
.em-sg__list.em-sg__list > li::before,
.em-sg__list.em-sg__list > li::after,
.em-cb__list.em-cb__list > li::before,
.em-cb__list.em-cb__list > li::after,
.em-rw__list.em-rw__list > li::before,
.em-rw__list.em-rw__list > li::after,
.em-rb__list.em-rb__list > li::before,
.em-rb__list.em-rb__list > li::after,
.em-dp__list.em-dp__list > li::before,
.em-dp__list.em-dp__list > li::after,
.em-dc__list.em-dc__list > li::before,
.em-dc__list.em-dc__list > li::after,
.em-tc__list.em-tc__list > li::before,
.em-tc__list.em-tc__list > li::after {
  content: none !important;
}

/* Marqueur, retrait et marges : ce qu'un theme pose sur `ul` et `li` et qu'aucune de ces cinq listes ne
   veut. `text-indent` est du lot parce qu'il ne se voit pas en relisant le CSS d'un theme : pose sur le
   `ul`, il decale la PREMIERE ligne de chaque element. */
.em-fsd__panel.em-fsd__panel,
.em-fsd__panel.em-fsd__panel > li,
.em-cl__list.em-cl__list,
.em-cl__list.em-cl__list > li,
.em-ct__access-list.em-ct__access-list,
.em-ct__access-list.em-ct__access-list > li,
.em-kpi__rows.em-kpi__rows,
.em-kpi__rows.em-kpi__rows > li,
.em-asb__nav-list.em-asb__nav-list,
.em-asb__nav-list.em-asb__nav-list > li {
  list-style: none;
  text-indent: 0;
  margin: 0;
}

/* PADDING GAUCHE : trois listes sur cinq n'en veulent aucun, et le `.entry-content ul li` du theme pese
   (0,1,2) contre le (0,1,0) de `.em-cl__item` — donc il gagnait, et les lignes etaient decalees.

   Les deux autres en veulent un : `.em-fsd__opt` (repose plus haut) et `.em-asb__nav-item`, dont les deux
   regles — bureau et mobile — sont doublees sur place. Un `padding-left: 0` global ici les aurait ecrasees,
   et c'est la raison de ne pas traiter les cinq d'un bloc. */
.em-cl__list.em-cl__list > li,
.em-ct__access-list.em-ct__access-list > li,
.em-kpi__rows.em-kpi__rows > li,
.em-eb__list.em-eb__list > li,
.em-sg__list.em-sg__list > li,
.em-cb__list.em-cb__list > li,
.em-rw__list.em-rw__list > li,
.em-rb__list.em-rb__list > li,
.em-dp__list.em-dp__list > li,
.em-dc__list.em-dc__list > li,
.em-tc__list.em-tc__list > li,
/* `.em-fg__grid` MANQUAIT depuis la 2.5.391 : le `.entry-content ul li` du theme pese (0,1,2) contre le
   (0,1,0) de `.em-fg__item`, donc il gagnait et decalait chaque carte vers la droite. Signale a l'ecran,
   invisible dans le CSS du plugin — la regle fautive n'y est pas. */
.em-fg__grid.em-fg__grid > li {
  padding-left: 0;
}
.em-filter-select .em-fsd__panel li {
  padding: .25rem .25rem;
}
.em-filter-select .em-fsd__trigger,
.em-filter-select .em-fsd__trigger:hover,
.em-filter-select .em-fsd__trigger:focus,
.em-filter-select .em-fsd__trigger:active,
.em-filter-select.em-fsd--open .em-fsd__trigger {
  color: var(--em-color-text) !important;
  text-shadow: none !important;
}
.em-filter-select .em-fsd__trigger:hover,
.em-filter-select.em-fsd--open .em-fsd__trigger {
  background: #f4f6fb !important;
}

.em-fsd__trigger {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; width: 100%;
  box-sizing: border-box;
  height: var(--em-filter-height);
  padding: 0 .5rem;
  background: #fff; border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  font-size: .875rem; color: var(--em-color-text);
  cursor: pointer; user-select: none; text-align: left;
  transition: border-color var(--em-transition), background var(--em-transition);
}
.em-fsd__trigger:hover {
  border-color: var(--em-color-primary);
  background: #f4f6fb;
}
.em-fsd--open .em-fsd__trigger {
  border-color: var(--em-color-primary);
  border-bottom-color: transparent;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  background: #f4f6fb;
  box-shadow: none;
}
.em-fsd__label {
  flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: .375rem;
}
.em-fsd__chevron { flex-shrink: 0; transition: transform .18s; }
.em-fsd--open .em-fsd__chevron { transform: rotate(180deg); }

/* Count badge shown inside trigger label in multi mode */
.em-fsd__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  background: var(--em-color-primary); color: #fff;
  border-radius: 9px; font-size: .75rem; font-weight: 700;
  line-height: 1; flex-shrink: 0;
}

.em-fsd__panel {
  position: absolute; top: 100%; left: 0;
  /* ⚠ `box-sizing` EXPLICITE, et ce n'est pas une precaution : la regle globale
     `*, *::before, *::after { box-sizing: inherit }` est ancree sur `.em-agenda`, un selecteur ABSENT
     du balisage (le commentaire de la barre de filtres le dit deja, 2.5.335). Le panneau restait donc
     en `content-box` : `width: 100%` PLUS `border: 1px` le rendait 2 px plus large que son
     declencheur, et il depassait visiblement a droite sur tous les blocs a deroulante. */
  box-sizing: border-box;
  /* Panel width is locked to the trigger width (never wider than the input) — options
     that don't fit wrap to the next line instead of pushing the panel past the trigger. */
  width: 100%;
  min-width: 100%;
  max-width: 100%;
  overflow-x: hidden;
  list-style: none; margin: 0; padding: .25rem 0;
  background: #fff;
  border: 1px solid var(--em-color-primary);
  border-top: none;
  border-radius: 0 0 var(--em-radius) var(--em-radius);
  box-shadow: 0 6px 18px rgba(26,58,107,.13);
  z-index: 1050; max-height: 260px; overflow-y: auto;
  display: none;
  animation: emFsdIn .12s ease;
}
@keyframes emFsdIn {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: translateY(0); }
}
.em-fsd--open .em-fsd__panel { display: block; }

.em-fsd__opt {
  display: flex; align-items: center; gap: .5rem;
  padding: .25rem .5rem; font-size: .875rem; color: var(--em-color-text);
  cursor: pointer; user-select: none;
  /* Wrap long option labels instead of clipping them — the panel is locked to the
     trigger width, so an option wider than the trigger flows onto a second line. */
  white-space: normal; overflow-wrap: anywhere; line-height: 1.35;
  transition: background .1s;
}
.em-fsd__opt:hover { background: rgba(var(--em-color-primary-rgb), .07); }
.em-fsd__opt--on { background: rgba(var(--em-color-primary-rgb), .05); color: var(--em-color-text); font-weight: 500; }
.em-fsd__opt--on:hover { background: rgba(var(--em-color-primary-rgb), .1); }

/* Checkbox tick box for multi-select mode */
.em-fsd__chk {
  width: 16px; height: 16px; flex-shrink: 0;
  border: 2px solid var(--em-color-primary); border-radius: 3px;
  background: var(--em-color-surface); display: flex; align-items: center; justify-content: center;
  transition: background .12s, border-color .12s;
}
.em-fsd__opt--on .em-fsd__chk {
  background: var(--em-color-primary); border-color: var(--em-color-primary); color: #fff;
}

/* ===================== RESULTS COUNTER ===================== */
.em-results-count {
  font-size: .875rem; font-weight: 600;
  color: var(--em-color-muted);
  margin-bottom: 1rem;
}

/* ===================== PAGINATION ===================== */
.em-pagination {
  display: flex; align-items: center; justify-content: center;
  gap: .25rem; padding: 1.75rem 0 .5rem; flex-wrap: wrap;
}
.em-page-btn {
  width: 38px; height: 38px;
  border-radius: var(--em-radius);
  border: 1px solid var(--em-color-border);
  background: var(--em-color-surface);
  color: var(--em-color-text);
  font-size: 1.25rem; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: all var(--em-transition);
}
.em-page-btn:disabled { opacity: .35; cursor: default; }
.em-page-btn:hover:not(:disabled) { border-color: var(--em-color-primary); color: var(--em-color-primary); }
.em-page-numbers { display: flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.em-page-num {
  min-width: 38px; height: 38px; padding: 0 .5rem;
  border-radius: var(--em-radius);
  border: 1px solid var(--em-color-border);
  background: var(--em-color-surface);
  color: var(--em-color-text);
  font-size: .875rem; cursor: pointer;
  transition: all var(--em-transition);
}
.em-page-num:hover { border-color: var(--em-color-primary); color: var(--em-color-primary); }
.em-page-num.active { background: var(--em-color-primary); border-color: var(--em-color-primary); color: white; font-weight: 600; }
.em-page-ellipsis { padding: 0 .25rem; color: var(--em-color-muted); font-size: .875rem; }

/* ===================== ACCESSIBILITY — focus-visible ===================== */
.em-event-card:focus-visible,
.em-agenda-item:focus-visible,
.em-filter:focus-visible,
.em-btn-reset:focus-visible,
.em-page-btn:focus-visible,
.em-page-num:focus-visible,
.em-fsd__trigger:focus-visible,
.em-fsd__opt:focus-visible,
.em-btn--share:focus-visible {
  outline: 2px solid var(--em-color-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(var(--em-color-primary-rgb), .15);
}
.em-fsd__opt:focus-visible { outline-offset: -2px; }
.em-agenda[tabindex]:focus { outline: none; }

/* ===================== SHARE BUTTON ===================== */
.em-btn--share {
  padding: .375rem .375rem;
  background: transparent;
  border: 1px solid var(--em-color-border);
  border-radius: var(--em-radius);
  color: var(--em-color-muted);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: all var(--em-transition);
}
.em-btn--share:hover { border-color: var(--em-color-accent); color: var(--em-color-accent); }

/* ===================== HIGHLIGHTED CARD ===================== */
.em-event-card--highlighted {
  outline: 2px solid var(--em-color-primary);
  outline-offset: 3px;
  animation: em-highlight-pulse 2s ease-out;
}
@keyframes em-highlight-pulse {
  0%, 100% { outline-color: var(--em-color-primary); }
  50%       { outline-color: var(--em-color-secondary); }
}

/* ===================== BADGES ===================== */
.em-event-card__badges,
.em-agenda-item__badges {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem;
  margin-bottom: .5rem;
}

.em-badge {
  display: inline-block;
  padding: .25rem .5rem;
  border-radius: 999px;
  font-size: .625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .025em;
  line-height: 1.3;
}

.em-badge--online {
  background: var(--em-color-success-bg);
  color: var(--em-color-success);
}

.em-badge--featured {
  background: var(--em-color-warning-bg);
  color: var(--em-color-warning);
}

.em-badge--new {
  background: var(--em-color-new);
  color: var(--em-color-new-text);
  font-weight: 700;
  letter-spacing: .04em;
}

/* ===================== ETAT « TERMINE » =====================
   Fenetre entre l'heure de fin et minuit : l'evenement reste visible mais signale comme termine.
   Passe cette fenetre, c'est le serveur qui le retire (EM_Rest::filter_past) — d'ou l'absence de
   regle « masquer » ici.

   L'effet est choisi en back-office (`ended_state_effect`). « Aucun » ne produit AUCUN modificateur :
   seul le badge distingue alors la carte. */
.em-badge--ended {
  background: var(--em-color-muted);
  color: var(--em-color-surface);
}

/* `opacity` cree un contexte de composition : les descendants NE PEUVENT PAS remonter au-dessus de
   l'opacite du parent — inutile d'esperer rendre le badge opaque depuis l'interieur. Le texte de la
   carte subit donc l'estompage, et c'est ce qui FIXE la valeur plancher.
   MESURE : sur #212529 / blanc, `.55` donne 3,60:1 — sous le minimum WCAG AA de 4,5:1 pour du texte
   courant. `.65` donne 4,88:1. La valeur n'est pas une preference esthetique, c'est un seuil. */
.em-is-ended--dim       { opacity: .65; }
.em-is-ended--grayscale { filter: grayscale(1); }
.em-is-ended--both      { opacity: .65; filter: grayscale(1); }

/* ===================== TIME ===================== */
.em-event-time {
  font-size: .75rem;
  color: var(--em-color-muted);
  /*
   * `margin: 0` — la seule ligne de donnée des cartes qui ne le déclarait pas (2.5.375).
   *
   * Elle est rendue dans un `<p>`, donc elle portait la marge de la FEUILLE DU NAVIGATEUR (~1 em). Le
   * corps de carte est en `display: flex; flex-direction: column; gap: .375rem` — et en flex les marges
   * ne se replient PAS : elles s'AJOUTENT au `gap`. L'écart sous l'horaire valait donc ~22 px là où
   * toutes les autres lignes en font 6.
   *
   * Le défaut existait avant l'échange horaire/adresse de la 2.5.372 — la marge tombait alors entre
   * l'horaire et le bouton, où `margin-top: .5rem` des actions la rendait moins lisible. L'échange l'a
   * placée ENTRE DEUX LIGNES DE DONNÉES, où elle se voit. Mesuré : 6 classes sur 49 portées par un `<p>`
   * ne réinitialisaient aucune marge ; les cinq autres sont des messages autonomes, où la marge du
   * navigateur est légitime.
   *
   * `.em-event-time` sert AUSSI sur un `<span>` (ligne de date des cartes de grille) : un `<span>` n'a
   * aucune marge par défaut, donc cette déclaration n'y change rien.
   */
  margin: 0;
}
/* Icon + text inline helper */
.em-ic-wrap {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
}
.em-ic-wrap svg, .em-icon { flex-shrink: 0; opacity: 1; vertical-align: middle; }
.em-ic-wrap svg { color: var(--em-color-secondary); }

/* ===================== FEATURED ZONE ===================== */
.em-featured-zone {
  margin-bottom: 2.5rem;
  /* Fond de la BANDE, distinct de celui des cartes. Les deux partageaient `--em-color-surface` :
     regler « Fond des cartes » repeignait donc tout le carrousel en meme temps que ses cartes, sans
     moyen de viser l'un sans l'autre. Le repli sur `--em-color-surface` garde le rendu identique
     tant qu'aucun reglage dedie n'est saisi. */
  background: var(--em-featured-zone-bg, var(--em-color-surface));
  border-radius: var(--em-radius, .75rem);
}
/* CLS prevention: reserve space from initial paint so content doesn't shift on reveal */
.em-featured-zone--carousel {
  opacity: 0;
  min-height: 500px;
  overflow: hidden;
  transition: opacity 0.2s ease;
}
.em-featured-zone--carousel.em-featured-zone--ready {
  opacity: 1;
  overflow: visible;
  min-height: 0;
}

.em-featured-zone__title {
  margin: 0 0 1.25rem;
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--em-color-primary);
  letter-spacing: .04em;
}

.em-featured-zone__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.25rem;
}

/* ----- Carousel variant ----- */
.em-featured-zone--carousel .em-featured-zone__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}
.em-featured-zone--carousel .em-featured-zone__title { margin: 0; }
.em-featured-zone--carousel .em-featured-zone__nav {
  display: flex;
  gap: .5rem;
}
.em-featured-zone--carousel .em-featured-zone__btn {
  /* Les TROIS declarations sont necessaires pour un cercle, et c'est la troisieme fois que ce piege
     se presente (`.em-car__arrow` et `.em-dt__arrow` en 2.5.240, ces deux-ci oubliees) :
      · `box-sizing: border-box` — la regle globale `*, *::before, *::after { box-sizing: inherit }`
        est ancree sur `.em-agenda`, un selecteur ABSENT du markup. Tout est donc en `content-box`,
        et la bordure de 1 px s'AJOUTE aux 36 px demandes ;
      · `padding: 0` — un `<button>` recoit un padding horizontal par DEFAUT du navigateur (de
        l'ordre de 6 px de chaque cote). En `content-box`, la boite reelle faisait donc ~50 x 38 px :
        un ovale, pas un cercle ;
      · `flex: 0 0 auto` — element flex, donc compressible ; il s'aplatirait sur un ecran etroit. */
  box-sizing: border-box;
  padding: 0;
  flex: 0 0 auto;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1px solid var(--em-color-border);
  background: #fff;
  color: var(--em-color-primary);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background .15s, border-color .15s, opacity .15s, transform .15s;
}
.em-featured-zone--carousel .em-featured-zone__btn:hover:not(:disabled) {
  background: var(--em-color-primary);
  color: #fff;
  border-color: var(--em-color-primary);
}
.em-featured-zone--carousel .em-featured-zone__btn:disabled {
  opacity: .35;
  cursor: not-allowed;
}
.em-featured-zone--carousel .em-featured-zone__btn:focus-visible {
  outline: 2px solid var(--em-color-primary);
  outline-offset: 2px;
}
.em-featured-zone--carousel .em-featured-zone__track {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.em-featured-zone--carousel .em-featured-zone__track::-webkit-scrollbar { display: none; }
.em-featured-zone--carousel .em-featured-zone__grid {
  display: flex;
  grid-template-columns: none;
  gap: 1rem;
  padding-bottom: .5rem;
}
.em-featured-zone--carousel .em-featured-zone__grid > .em-event-card,
.em-featured-zone--carousel .em-featured-zone__grid > .em-featured-card {
  flex: 0 0 clamp(240px, 30%, 320px);
  scroll-snap-align: start;
}
@media (max-width: 640px) {
  .em-featured-zone--carousel .em-featured-zone__grid > .em-event-card,
  .em-featured-zone--carousel .em-featured-zone__grid > .em-featured-card {
    flex-basis: 80%;
  }
}

/* ---- Carousel dot indicators ---- */
.em-featured-zone__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .375rem;
  margin-top: .875rem;
  min-height: 12px;
}
.em-featured-zone__dots:empty { display: none; }
.em-featured-zone__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--em-color-border);
  border: none; padding: 0;
  cursor: pointer; flex-shrink: 0;
  transition: background var(--em-transition);
}
.em-featured-zone__dot:hover { background: var(--em-color-accent); }
.em-featured-zone__dot--active {
  background: var(--em-color-primary);
  width: 18px;
  border-radius: 3px;
  height: 7px;
}

/* ===================== FEATURED CARD (carousel redesign) ===================== */
.em-featured-card {
  /* Fond de la CARTE seule (voir `.em-featured-zone` pour la bande). */
  background: var(--em-featured-card-bg, var(--em-color-surface));
  border-radius: var(--em-radius);
  box-shadow: var(--em-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform .18s, box-shadow .18s;
}
.em-featured-card:hover {
  box-shadow: 0 5px 12px rgba(0,0,0,.15);
}
.em-fc__media {
  position: relative;
  aspect-ratio: var(--em-thumb-ratio);
  overflow: hidden;
  background: var(--em-color-primary);
}
/*
 * ⚠ PORTÉE `img` ET NON LA CLASSE SEULE (2.5.375).
 *
 * `.em-fc__img` est posée sur DEUX éléments différents : la vraie `<img>` du visuel, et le `<div>` de
 * repli « sans visuel » que rend `noImgHtml(ev, "em-fc__img")` — lequel porte AUSSI `.em-noimg`.
 *
 * `.em-noimg` centre son contenu par `display: flex` + `align-items` + `justify-content`, et elle est
 * déclarée ligne ~192. Cette règle-ci, déclarée BIEN PLUS BAS avec la MÊME spécificité (0,1,0),
 * gagnait donc par simple ordre de source : `display: block` écrasait `display: flex`, les deux
 * propriétés d'alignement devenaient inertes, et le libellé (« ESME ») tombait en haut à gauche, en
 * `clamp(1rem, 3.2vw, 2rem)` — donc énorme et rogné par le pavé de date.
 *
 * Les trois déclarations n'ont de sens que pour une image : `object-fit` ne s'applique qu'au contenu
 * remplacé, et `display: block` sert à retirer l'espace de ligne de base sous une `<img>` inline. La
 * boîte du `<div>`, elle, est déjà dimensionnée par `.em-noimg` (`width/height: 100%`). Restreindre au
 * sélecteur d'élément est donc le correctif EXACT — pas une surcharge de spécificité qui laisserait le
 * conflit en place.
 *
 * QUATRIÈME fois que ce dépôt paie la spécificité, et la PREMIÈRE où c'est l'ORDRE à spécificité égale
 * (loupe de recherche 2.5.335, puces de liste 2.5.341, conteneur 2.5.346).
 */
img.em-fc__img {
  width: 100%; height: 100%;
  object-fit: var(--em-thumb-fit); display: block;
}
.em-fc__img--placeholder {
  width: 100%; height: 100%;
  background: linear-gradient(135deg, var(--em-color-primary) 0%, var(--em-color-accent) 100%);
}
/* gradient bas → transparent, donne de la lisibilité au badge date */
.em-fc__media::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,.45) 0%, transparent 55%);
  pointer-events: none;
}
/* Date stamp : carré coloré top-left */
.em-fc__date-stamp {
  position: absolute; top: .5rem; left: .5rem;
  background: var(--em-color-primary);
  color: #fff;
  border-radius: calc(var(--em-radius) / 2);
  padding: .25rem .5rem;
  text-align: center; line-height: 1.1; z-index: 1;
  box-shadow: 0 2px 6px rgba(0,0,0,.25);
  min-width: 36px; border: 1px solid #fff;opacity: .85;
}
.em-fc__date-stamp--range { padding: .25rem .5rem; }
.em-fc__stamp-chev { display: block; margin: .125rem auto; opacity: .7; }
.em-fc__day  { display: block; font-size: 1rem; font-weight: 800; }
.em-fc__mon  { display: block; font-size: .625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .9; }
/* Badges top-right */
.em-fc__badges {
  position: absolute; top: .625rem; right: .625rem;
  display: flex; flex-direction: column; align-items: flex-end; gap: .25rem; z-index: 1;
}
/* Body */
.em-fc__body {
  padding: .75rem;
  flex: 1; display: flex; flex-direction: column; gap: .375rem;
}
.em-fc__school {
  font-size: .75rem; font-weight: 700;
  color: var(--em-color-secondary);
  text-transform: uppercase; line-height: 1;
}
.em-fc__title {
  margin: 0; font-size: 1rem; font-weight: 700;
  line-height: 1.35; color: var(--em-color-primary);
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
.em-fc__meta {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .375rem; font-size: .75rem; color: var(--em-color-muted);
}
.em-fc__date-range { align-items: center; }
/*
 * Chevron des PLAGES de dates. Partagé par quatre blocs depuis la 2.5.287 (carte d'événement, carte « à la
 * une », hero, « Prochains événements »), d'où deux corrections :
 *
 *  · `currentColor` et non `--em-color-muted`. Il ne servait qu'aux cartes claires ; sur le panneau sombre
 *    de « Prochains événements » ou sur le pavé de date coloré de la carte « à la une », un gris atténué
 *    devient illisible. En héritant, il suit le texte qui l'entoure quel que soit le fond ;
 *  · une marge horizontale. L'ancienne flèche était un caractère entouré d'espaces (` → `) ; un `<svg>`
 *    n'en a pas, et les deux dates se collaient au chevron.
 */
.em-fc__date-chev { opacity: .55; flex-shrink: 0; color: currentColor; margin: 0 .25em; }
/* L'adresse est desormais un <span> DANS `.em-fc__meta` : taille et couleur sont heritees du
   conteneur, comme pour le campus et le type. Les declarations sont conservees pour rester
   correctes si un template personnalise la sort du bloc meta, et `margin: 0` pour neutraliser la
   marge de paragraphe du theme dans ce cas. */
.em-fc__address { font-size: inherit; color: inherit; margin: 0; }
.em-fc__tags { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .375rem; }
.em-fc__actions { margin-top: auto; padding-top: .5rem; }
.em-fc__cta {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .375rem .5rem;
  border: 1.5px solid var(--em-color-primary);
  color: var(--em-color-primary);
  border-radius: calc(var(--em-radius) / 2);
  font-size: .75rem; font-weight: 600;
  text-decoration: none;
  transition: opacity .15s;
}
.em-fc__cta:hover { opacity: .75; }
.em-fc__cta .em-icon { transition: transform .15s; }
.em-fc__cta:hover .em-icon { transform: translateX(3px); }
.em-fc__desc {
  margin: 0rem 0 .5rem 0px;
  font-size: .875rem;
  color: var(--em-color-muted);
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* `.em-featured-zone__count` RETIRE en 2.5.372 : la pastille de comptage de la zone « a la une »
   n'existe plus dans le gabarit. Garder son CSS aurait laisse une regle que rien ne lit — la
   « variable CSS morte » de la 4e garde meta, dans sa version selecteur. */
.em-featured-card:focus-visible { outline: 2px solid var(--em-color-primary); outline-offset: 2px; }

/* ===================== SUBSECTION TITLES (admin) ===================== */
.em-subsection-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--em-color-primary);
  margin: 1.5rem 0 .75rem;
  padding-bottom: .375rem;
  border-bottom: 1px solid var(--em-color-border);
}

.em-description--small {
  font-size: .75rem;
  color: var(--em-color-muted);
  margin-top: .25rem;
}

/* ===================== AGENDA SIDEBAR ===================== */
.em-agenda-sidebar-wrap {
  /* Chaîne : la variable du mécanisme PARTAGÉ, puis la primaire. Les huit lectures de `var(--em-accent)`
     de ce bloc restent inchangées — c'est un alias local, pas un second point d'émission (2.5.299). */
  --em-accent: var(--em-widget-accent, var(--em-color-primary, #1a3a6b));
  --em-accent-rgb: 26, 58, 107;
  /* `font-family: inherit` RETIRE en 2.5.347 : declare APRES la regle des cinq racines et a la meme
     specificite, il gagnait — donc le reglage « Police » etait MORT sur ce bloc, sauf a cocher
     « forcer partout ». Le retrait ne change rien quand aucune police n'est choisie : `--em-font`
     n'est alors pas definie, `var(--em-font)` est invalide a l'evaluation, et la propriete retombe
     sur l'heritage — exactement ce que `inherit` faisait. */
  /* `--em-widget-text` LU depuis la 2.5.311. Ce bloc lisait UNE des huit variables d'apparence partagees
     (l'accent) : les sept autres etaient emises et jamais lues, donc sept reglages de back-office qui ne
     faisaient rien. `inherit` en repli : le widget prenait la couleur du theme, et c'est ce qu'il doit
     continuer de faire sans reglage. */
  color: var(--em-widget-text, inherit);
}

.em-asb__inner {
  display: grid;
  /* DEUX colonnes depuis la 2.5.302 : la photo d'illustration a été retirée, et avec elle le
     modificateur `--no-photo` qui rendait cette mise en page. La grille était à trois colonnes
     (photo | campus | contenu) et deux déclarations décrivaient donc le même bloc selon un attribut.
     Largeur de la colonne des campus RÉGLABLE (2.5.299) ; le repli `220px` est la valeur d'origine :
     un site qui ne règle rien garde exactement sa mise en page. */
  grid-template-columns: var(--em-asb-nav-w, 220px) 1fr;
  /* Hauteur minimale REGLABLE depuis la 2.5.415, et sur GRAND ECRAN seulement : la requete de
     1024 px repose le plancher historique sans lire la variable. Le repli `320px` est la valeur
     d'origine, donc un site qui ne regle rien rend a l'identique — et une garde compare ce repli au
     nombre annonce par l'ecran ET au plancher de la requete media, les trois ne pouvant pas deriver. */
  min-height: var(--em-asb-panel-min-h, 320px);
  overflow: hidden;
  /* COQUILLE du bloc : c'est cet element qui est visible, pas la racine. Les quatre variables d'apparence
     partagees qui decrivent une enveloppe se lisent donc ici (2.5.311) — elles etaient emises et jamais
     lues. Tous les replis reproduisent le rendu d'avant : `transparent`, aucun rayon, aucune marge
     interieure, aucune ombre. Un site qui ne regle rien ne voit aucune difference. */
  background: var(--em-widget-bg, transparent);
  border-radius: var(--em-widget-radius, 0);
  padding: var(--em-widget-pad, 0);
  box-shadow: var(--em-widget-shadow, none);
}

/*
 * AUCUN CAMPUS A AFFICHER : la grille passe a UNE colonne. (2.5.385)
 *
 * Le JS pose cette classe en meme temps qu'il masque `.em-asb__navcol`. Les deux gestes ne se separent
 * pas : masquer la colonne sans reduire la piste laisserait un vide de 220 px et pousserait le contenu
 * dans la seconde colonne — un decalage pire que l'aplat bleu qu'on corrige.
 *
 * La largeur reglable (`--em-asb-nav-w`) reste intacte : elle reprend son effet des qu'un campus revient.
 */
.em-asb__inner.em-asb__inner--no-nav {
  grid-template-columns: 1fr;
}

/*
 * TITRE DU BLOC (2.5.402) : il coiffe TOUTES les colonnes.
 *
 * `1 / -1` et non `1 / 3` : la grille vaut une, deux ou trois colonnes selon la photo et la présence
 * de campus. Un nombre écrit laisserait le titre à cheval ou trop court dans deux cas sur trois.
 *
 * Aucune ligne n'est déclarée ici : le titre est le PREMIER enfant après la photo et il occupe toute
 * la largeur, donc le placement automatique le met seul sur la première ligne et pousse la colonne des
 * campus et le contenu sur la suivante. Rien à figer.
 */
.em-asb__title {
  grid-column: 1 / -1;
  margin: 2.5rem 0px 1.5rem;
  /* REGLABLE depuis la 2.5.406. Le repli `1.75rem` = 28 px = 14 crans de l'echelle de 0,125 rem,
     c'est-a-dire exactement ce que rendait la version precedente : un champ vide ne repeint rien. */
  font-size: var(--em-asb-title-size, 1.75rem);
  font-weight: 800;
  line-height: 1.2;
  /* `--em-widget-title` est la variable d'apparence PARTAGEE du bloc, emise par
     `em_block_appearance_style()` et pilotee par le reglage « Couleur du titre » qui existe deja.
     En inventer une ici aurait cree un second point d'emission pour une meme couleur — le defaut
     qui rendait morts deux controles de carrousel en 2.5.290. */
  color: var(--em-widget-title, var(--em-color-primary));
}

/*
 * PHOTO D'ILLUSTRATION (2.5.402). Trois colonnes, et la photo occupe TOUTE la hauteur.
 *
 * ⚠ LES LIGNES SONT EXPLICITES ICI, et il le faut : `grid-row: 1 / -1` ne désigne la dernière ligne
 * que si la grille en déclare. Sans `grid-template-rows`, `-1` retombe sur la ligne 1 et la photo ne
 * couvrirait que la hauteur du titre. Les quatre placements sont donc écrits, sinon le placement
 * automatique enverrait la colonne des campus sur la MÊME ligne que le titre.
 *
 * La ligne 1 est `auto` : sans titre, elle vaut zéro et rien ne bouge.
 */
.em-asb__inner.em-asb__inner--with-photo {
  grid-template-columns: var(--em-asb-photo-w, 38%) var(--em-asb-nav-w, 220px) 1fr;
  grid-template-rows: auto 1fr;
}
/*
 * ⚠ LA PHOTO DEBORDE LA MARGE DU PANNEAU (2.5.406), elle n'est pas dedans.
 *
 * `--em-widget-pad` est pose sur `.em-asb__inner`, donc il ecarte TOUS les elements de grille — la
 * photo comprise. Or une photo d'ambiance doit toucher les bords du panneau : avec la marge, elle
 * flottait au milieu d'un cadre blanc, ce qui n'est ni la maquette ni ce qu'on attend d'une image de
 * bord.
 *
 * Une marge NEGATIVE de la meme valeur l'annule sur trois cotes, et `margin-right: 0` la garde a
 * distance du contenu. Ca marche pour N'IMPORTE QUELLE valeur de marge, sans la recopier — un second
 * reglage « marge de la photo » aurait pu diverger du premier.
 *
 * `overflow: hidden` est deja sur `.em-asb__inner` : le debordement est donc rogne par l'arrondi du
 * panneau au lieu de le deborder, et la photo suit ses coins.
 *
 * ⚠ LA MARGE DROITE EST L'ECART PHOTO / CONTENU (2.5.407), et elle remplace un `0` qui collait les
 * deux. Ce n'est deliberement PAS un `column-gap` de grille : celui-ci ecarterait aussi la colonne
 * des campus du contenu, alors que ces deux-la doivent rester jointifs — le contenu porte le fond et
 * son propre `padding: 28px 32px`.
 *
 * Repli `24px` et non `0` : REPEINT DEMANDE, borne aux seuls blocs qui affichent une photo.
 */
.em-asb__inner--with-photo .em-asb__photo {
  grid-column: 1;
  grid-row: 1 / -1;
  margin: calc(-1 * var(--em-widget-pad, 0px));
  margin-right: var(--em-asb-photo-gap, 24px);
}
.em-asb__inner--with-photo .em-asb__title   { grid-column: 2 / -1; grid-row: 1; }
.em-asb__inner--with-photo .em-asb__navcol  { grid-column: 2; grid-row: 2; }
.em-asb__inner--with-photo .em-asb__content { grid-column: 3; grid-row: 2; }
/* Photo SANS colonne de campus : deux colonnes, et le contenu reprend la seconde. */
.em-asb__inner.em-asb__inner--with-photo.em-asb__inner--no-nav {
  grid-template-columns: var(--em-asb-photo-w, 38%) 1fr;
}
.em-asb__inner--with-photo.em-asb__inner--no-nav .em-asb__content { grid-column: 2; }
/* IMAGE DE FOND et non une balise `<img>` (2.5.405) — voir la raison dans `templates/agenda.php`.
   `cover` + `center` reproduisent exactement ce que faisait `object-fit: cover` sur l'image : le
   cadrage ne change pas, seule la technique change. Plus de règle `img` ici : une déclaration sans
   émetteur est du code que personne n'ose toucher. */
.em-asb__photo {
  min-width: 0;
  overflow: hidden;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Campus nav */
/*
 * ⚠ COLONNE CLAIRE PAR DEFAUT DEPUIS LA 2.5.406, et c'est un REPEINT DEMANDE.
 *
 * Elle etait un aplat de l'accent avec du texte blanc. Le defaut est desormais : AUCUN fond, noms de
 * campus dans la couleur primaire, et seul le campus ACTIF est peint. C'est ce que demande la maquette
 * de reference, ou la liste flotte sur le panneau et rien d'autre que l'actif n'est colore.
 *
 * ⚠ TOUS LES SITES DEJA EN PLACE CHANGENT D'APPARENCE. La doctrine « le defaut d'un reglage neuf
 * reproduit le rendu existant » protege contre les repeints ACCIDENTELS, pas contre un changement
 * demande (2.5.360). Les six reglages restent la : `Fond de la colonne` = l'ancienne couleur d'accent
 * et `Texte des campus` = blanc retablissent exactement le rendu d'avant.
 *
 * ⚠ LES SIX REPLIS CHANGENT ENSEMBLE, et il le faut : ils etaient tous calcules pour un fond SOMBRE.
 * N'en basculer qu'une partie donne du blanc sur blanc — le survol a `rgba(255,255,255,.08)` et le
 * fond de l'actif a `rgba(255,255,255,.12)` deviennent invisibles, et le filet de gauche blanc aussi.
 * C'est une PALETTE, pas six valeurs independantes.
 *
 * Le FOND passe sur le conteneur, pour qu'il couvre aussi le titre et que la colonne reste d'une
 * seule piece. Le `<nav>` garde le sien — inutile mais inoffensif, et le retirer casserait un gabarit
 * personnalise qui n'aurait pas le conteneur.
 */
.em-asb__navcol {
  background: var(--em-asb-nav-bg, transparent);
  min-width: 0;
}
.em-asb__nav-title {
  margin: 0;
  padding: 10px 5px 5px 10px;
  color: var(--em-asb-nav-title, var(--em-color-muted));
  font-size: .75rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.em-asb__nav {
  background: var(--em-asb-nav-bg, transparent);
  padding: 5px 0;
}
.em-asb__nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.em-asb__nav-item.em-asb__nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 15px;
  cursor: pointer;
  color: var(--em-asb-nav-text, var(--em-color-primary));
  font-size: 1.125rem;
  font-weight: 600;
  transition: color .15s, background .15s;
  border-left: 3px solid transparent;
  user-select: none;
}
/* Survol : une teinte de la PRIMAIRE et non un voile blanc — sur une colonne claire, un blanc a 8 %
   est strictement invisible. `--em-color-primary-rgb` existe pour exactement cet usage. */
.em-asb__nav-item:hover {
  background: rgba(var(--em-color-primary-rgb, 26, 58, 107), .08);
}
.em-asb__nav-item.active {
  color: var(--em-asb-nav-active, #fff);
  font-weight: 700;
  /* L'actif est le SEUL element peint de la colonne : c'est lui qui porte la lecture, pas un voile.
     Un `rgba(255,255,255,.12)` sur fond clair ne se verrait pas — d'ou la primaire pleine. */
  background: var(--em-asb-nav-active-bg, var(--em-color-primary));
  /* Le filet de gauche suit le FOND de l'actif et non son texte : sur un aplat plein il se fondait
     dans le texte blanc et ajoutait une bordure claire de 3 px sur le bord gauche de la pastille. */
  border-left-color: var(--em-asb-nav-active-bg, var(--em-color-primary));
}
/* Puce des campus : ronde par defaut, carree ou absente (2.5.299). Le `border-radius` est la SEULE
   difference entre les deux formes, et `display: none` retire la puce sans toucher a la grille — la
   `gap` du parent disparait avec l'element, donc l'alignement reste juste. */
.em-agenda-sidebar-wrap[data-bullet="square"] .em-asb__nav-bullet { border-radius: 1px; }
.em-agenda-sidebar-wrap[data-bullet="none"] .em-asb__nav-bullet   { display: none; }
.em-asb__nav-bullet {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
  transition: transform .15s;
}
.em-asb__nav-item.active .em-asb__nav-bullet {
  transform: scale(1.3);
}
/* Chevron du campus actif : masquable (2.5.299). `display: none` et non un retrait du DOM : il porte
   `aria-hidden`, donc rien n'est annonce dans les deux cas, et le CSS suffit. */
.em-agenda-sidebar-wrap[data-nav-arrow="0"] .em-asb__nav-arrow { display: none; }
.em-asb__nav-arrow {
  margin-left: auto;
  line-height: 0;
  display: inline-flex;
  align-items: center;
}

/* Content area */
.em-asb__content {
  display: flex;
  flex-direction: column;
  padding: 0 25px 25px;
  /* Chaine : reglage PROPRE de la zone, puis fond PARTAGE du bloc, puis le blanc d'origine (2.5.320).
     `--em-widget-bg` n'etait lu que sur `.em-asb__inner`, entierement recouvert par les deux colonnes
     opaques de la grille : le reglage « Couleur de fond » du bloc ne pouvait RIEN montrer, alors que ses
     huit maillons etaient corrects. « Lu par le CSS » n'est pas « visible ». */
  background: var(--em-asb-content-bg, var(--em-widget-bg, #fff));
}
.em-asb__events {
  flex: 1;
}

/* Event row */
.em-asb__event {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 14px 0;
  /* Le filet portait `#eef0f3` en dur, qui DOUBLAIT `--em-color-border` : regler la bordure du plugin
     ne le touchait pas. La teinte change tres legerement (les deux gris sont proches) et c'est
     l'intention — le jeton doit gagner (2.5.299). */
  /* Chaine : reglage propre du filet, puis bordure PARTAGEE, puis la charte. `--em-widget-border` etait
     emise et jamais lue (2.5.311) ; la brancher ICI plutot qu'en bordure de coquille evite de decaler la
     grille de tous les sites existants, et donne au reglage partage un sens exact — la couleur des traits
     du bloc, que le reglage propre affine. */
  border-bottom: 1px solid var(--em-asb-divider, var(--em-widget-border, var(--em-color-border)));
}
.em-asb__event:last-child {
  border-bottom: none;
}
/* `flex-start` : le defaut est « bouton sous le titre », ou le pave de date doit faire FACE au titre.
   Le mode `right` remet `center` par sa propre regle, plus bas. */
.em-asb__event-row {
  display: flex;
  align-items: flex-start;
  gap: 18px;
}
.em-asb__event-date {
  text-align: center;
  min-width: 38px;
  flex-shrink: 0;
  color: var(--em-asb-date, var(--em-accent));
}
.em-asb__day {
  display: block;
  font-size: var(--em-asb-day-size, 1.875rem);
  font-weight: 800;
  line-height: 1;
}
.em-asb__date-chev {
  display: block;
  font-size: .5rem;
  opacity: .6;
  line-height: 1;
  margin: 2px 0;
  color: var(--em-accent);
}
.em-asb__month {
  display: block;
  font-size: var(--em-asb-month-size, .75rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-top: 2px;
}
.em-asb__time {
  display: inline-flex;
    align-items: center;
    gap: .25rem;
}
.em-asb__type {
  display: inline-block;
  font-size: .625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--em-accent);
  opacity: .85;
  margin-bottom: 4px;
}
.em-asb__online {
  display: inline-block;
  font-size: .625rem;
  font-weight: 700;
  /* `#16a34a` en dur DOUBLAIT `--em-color-success` (#15803d), reglable en back-office : deux verts pour
     le meme sens sur une meme page, et le reglage n'atteignait pas ce badge (2.5.299). */
  color: var(--em-color-success);
  margin-bottom: 4px;
}
.em-asb__thumb {
  flex-shrink: 0;
  width: 75px;
  height: 75px;
  border-radius: 0px;
  overflow: hidden;
  line-height: 0;
}
.em-asb__thumb img {
  width: 75px;
  height: 75px;
  object-fit: cover;
  border-radius: 0px;
}
.em-asb__event-desc {
  margin: 3px 0 0;
  font-size: .75rem;
  line-height: 1.45;
  color: var(--em-color-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.em-asb__campus {
  display: block;
  font-size: .75rem;
  /* `--em-asb-city-op` n est emis QU AVEC la couleur : sans lui, la teinte choisie resterait delavee
     par l opacite de .7 et l utilisateur verrait une couleur qu il n a pas demandee (2.5.299). */
  color: var(--em-asb-city, inherit);
  opacity: var(--em-asb-city-op, .7);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/*
 * TRAIT DU PAVE DE DATE — `below` est le DEFAUT depuis la 2.5.407, donc c'est la regle de BASE qui
 * le decrit : le pave est SOULIGNE et le filet vertical n'existe pas.
 *
 * ⚠ LE SENS S'EST INVERSE AVEC LE DEFAUT, exactement comme pour la position du bouton. Une regle de
 * base qui decrirait la forme NON choisie par defaut obligerait chaque site a surcharger pour
 * obtenir le rendu standard — et une garde exige justement que toute valeur non-defaut d'une liste
 * fermee porte sa PROPRE regle. C'est elle qui a signale l'oubli.
 *
 * ⚠ LES DEUX FORMES S'EXCLUENT : un pave souligne QUI PORTE AUSSI son filet vertical dessine un L.
 * Chaque regle annule donc l'autre trait, elle ne fait pas qu'ajouter le sien.
 *
 * Les deux traits lisent la MEME variable : `widget_divider_color` continue de peindre, quelle que
 * soit la forme choisie — changer de forme ne doit pas faire perdre une couleur deja reglee.
 */
.em-asb__event-date {
  padding-bottom: .625rem;
  border-bottom: 3px solid var(--em-asb-divider, var(--em-accent));
}
/* Le filet vertical : masque par defaut, le mode `vertical` le retablit ci-dessous. */
.em-asb__divider {
  width: 2px;
  align-self: stretch;
  background: var(--em-accent);
  flex-shrink: 0;
  border-radius: 2px;
  opacity: .35;
  display: none;
}
.em-agenda-sidebar-wrap[data-date-divider="vertical"] .em-asb__divider {
  display: block;
}
/* `vertical` et `aucun` retirent tous deux le soulignement de base. */
.em-agenda-sidebar-wrap[data-date-divider="vertical"] .em-asb__event-date,
.em-agenda-sidebar-wrap[data-date-divider="none"]     .em-asb__event-date {
  padding-bottom: 0;
  border-bottom: none;
}
.em-asb__event-body {
  flex: 1;
  min-width: 0;
}
/*
 * BOUTON SOUS LE TITRE — le DEFAUT depuis la 2.5.406, donc c'est la regle de BASE qui le decrit.
 *
 * ⚠ LE SENS S'EST INVERSE AVEC LE DEFAUT, et il le fallait : une regle de base qui decrirait l'option
 * NON choisie par defaut obligerait chaque site a surcharger pour obtenir le rendu standard — et une
 * garde exige justement qu'une valeur non-defaut porte sa propre regle.
 *
 * Le JS place `.em-asb__event-cta` DANS `.em-asb__event-body` en mode `below`, et en fait un FRERE en
 * mode `right`. La marge ci-dessous cible donc l'enfant direct du corps : elle ne s'applique que dans
 * le premier cas, sans avoir besoin du selecteur d'attribut.
 *
 * ⚠ `align-items` n'est PAS cosmetique. En `flex-start` (below) le pave de date fait face au titre ;
 * en `center` (right) il se centre sur une ligne d'une seule hauteur. Garder `center` en mode below
 * recentrerait le pave sur un corps devenu deux fois plus haut. Le filet garde son `align-self:
 * stretch`, donc il suit la hauteur dans les deux cas.
 */
.em-asb__event-body > .em-asb__event-cta {
  margin-top: .625rem;
}
.em-agenda-sidebar-wrap[data-event-cta-pos="right"] .em-asb__event-row {
  align-items: center;
}
.em-asb__event-cta {
  flex-shrink: 0;
  align-self: center;
}
.em-asb__event-title {
  margin: 0 0 7px;
  /* Repli `.875rem` = 14 px : la taille d'avant la 2.5.406, donc un champ vide rend a l'identique. */
  font-size: var(--em-asb-ev-title-size, .875rem);
  font-weight: 700;
  line-height: 1;
  /* Variable PROPRE avec repli en chaine sur l accent (2.5.299) : le titre le suivait, donc regler
     l un repeignait l autre — impossible de les distinguer. */
  /* Meme chaine que le filet ci-dessus : reglage propre, puis titre PARTAGE, puis l'accent local. */
  color: var(--em-asb-ev-title, var(--em-widget-title, var(--em-accent)));
  display: -webkit-box;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.em-asb__cta-btn {
  display: inline-block;
  border: 1px solid var(--em-accent);
  color: var(--em-accent);
  padding: 4px 14px;
  font-size: .75rem;
  font-weight: 500;
  text-decoration: none;
  letter-spacing: .025em;
  transition: background .2s, color .2s;
  border-radius: 2px;
}
.em-asb__cta-btn:hover {
  background: var(--em-accent);
  color: #fff;
}

/* Footer buttons */
.em-asb__footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--em-color-border);
  text-align: center;
}
.em-asb__footer-title {
  margin: 0;
  font-size: .875rem;
  font-weight: 500;
  color: var(--em-color-muted);
}
.em-asb__footer-btns {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}
/* Boutons EMPILES (2.5.299) : sur une colonne etroite, deux libelles longs se compriment cote a cote.
   `align-items: stretch` leur donne la meme largeur, sans quoi chacun prendrait celle de son texte. */
.em-agenda-sidebar-wrap[data-footer-layout="stack"] .em-asb__footer-btns {
  flex-direction: column;
  align-items: stretch;
}
/*
 * ALIGNEMENT DU PIED (2.5.406). `center` est le defaut et reste dans la regle de base ci-dessus ;
 * seules les deux autres valeurs portent une regle, comme partout ailleurs dans ce fichier.
 *
 * ⚠ TROIS declarations par valeur, et il faut les trois : `align-items` place le TITRE du pied,
 * `justify-content` place les BOUTONS (ils vivent dans un flex a part), et `text-align` rattrape le
 * texte du titre s'il passe sur deux lignes. N'en poser qu'une laisse le pied a moitie aligne.
 */
.em-agenda-sidebar-wrap[data-footer-align="left"]  .em-asb__footer { align-items: flex-start; text-align: left; }
.em-agenda-sidebar-wrap[data-footer-align="left"]  .em-asb__footer-btns { justify-content: flex-start; }
.em-agenda-sidebar-wrap[data-footer-align="right"] .em-asb__footer { align-items: flex-end; text-align: right; }
.em-agenda-sidebar-wrap[data-footer-align="right"] .em-asb__footer-btns { justify-content: flex-end; }
.em-asb__footer-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 22px;
  font-size: 1.125rem;
  font-weight: 700;
  text-decoration: none;
  transition: all .2s;
  border-radius: 2px;
}
.em-asb__footer-btn--outline {
  border: 1px solid var(--em-accent);
  color: var(--em-accent);
  background: transparent;
}
.em-asb__footer-btn--outline:hover {
  background: var(--em-accent);
  color: #fff;
}
.em-asb__footer-btn--filled {
  background: var(--em-accent);
  color: #fff;
  border: 2px solid var(--em-accent);
}
.em-asb__footer-btn--filled:hover {
  opacity: .88;
}

/* Responsive */
@media (max-width: 1024px) {
  /*
   * LA PHOTO S'EMPILE SOUS 1024 px (2.5.409) — elle etait MASQUEE depuis la 2.5.402.
   *
   * L'ancien choix se justifiait ainsi : « une image, une colonne de campus et la liste ne tiennent pas
   * cote a cote sur une tablette ». C'etait vrai de la mise en page A TROIS COLONNES, pas de la photo.
   * Empilee, elle ne dispute plus aucune largeur : elle coiffe le bloc et la composition reprend dessous.
   * Retrait DEMANDE, et le cout est nomme — la liste descend de la hauteur de la photo, reglable.
   *
   * ⚠ ELLE DOIT RECEVOIR UNE HAUTEUR, sans quoi elle reste INVISIBLE sans qu'une regle manque : c'est un
   * `<div>` a IMAGE DE FOND (2.5.405), et un fond ne participe pas au flux. Sur sa propre rangee la boite
   * mesure donc ZERO. Sur ordinateur la question ne se pose pas, `grid-row: 1 / -1` lui donnant la
   * hauteur de la composition.
   *
   * ⚠ LES RANGEES SONT EXPLICITES : sans elles le placement automatique remettrait la colonne des campus
   * sur la rangee de la photo. Et le `--no-nav` ci-dessous vaut (0,3,0), donc il faut le reprendre ici —
   * sinon un bloc sans campus garderait la piste `photo-w` et rendrait une colonne vide de 38 %.
   *
   * ⚠ LA SPECIFICITE EST DOUBLEE A DESSEIN : la regle a trois colonnes vaut (0,2,0). Ecrite en
   * `.em-asb__inner` seul (0,1,0), celle-ci PERDRAIT, quelle que soit sa position dans le fichier —
   * c'est la troisieme fois que ce depot paie ce point (loupe de recherche, puces de liste, conteneur).
   */
  .em-asb__inner.em-asb__inner--with-photo,
  .em-asb__inner.em-asb__inner--with-photo.em-asb__inner--no-nav {
    grid-template-columns: var(--em-asb-nav-w, 200px) 1fr;
    grid-template-rows: auto auto 1fr;
  }
  .em-asb__inner--with-photo .em-asb__photo {
    grid-column: 1 / -1;
    grid-row: 1;
    height: var(--em-asb-photo-height, 220px);
    /* La marge negative fait toujours deborder le retrait du panneau, mais sur TROIS cotes differents :
       en haut et sur les cotes ici, la ou c'etaient les cotes et le bas sur ordinateur. L'ecart, lui,
       passe sous la photo — c'est le meme reglage, tourne de 90 degres avec la mise en page. */
    margin: calc(-1 * var(--em-widget-pad, 0px));
    margin-bottom: var(--em-asb-photo-gap, 24px);
  }
  .em-asb__inner--with-photo .em-asb__title   { grid-column: 1 / -1; grid-row: 2; }
  .em-asb__inner--with-photo .em-asb__navcol  { grid-column: 1; grid-row: 3; }
  .em-asb__inner--with-photo .em-asb__content { grid-column: 2; grid-row: 3; }
  .em-asb__inner--with-photo.em-asb__inner--no-nav .em-asb__content { grid-column: 1 / -1; }
  /*
   * ⚠ LE PLANCHER REDEVIENT FIXE SOUS 1024 px (2.5.415), et il ne lit PAS la variable.
   *
   * La hauteur minimale se regle pour la composition en colonnes. Une fois le bloc empile, elle ne
   * pourrait produire qu'un vide sous la liste — c'est pourquoi le reglage est annonce
   * « desktop seulement », et pourquoi cette ligne existe.
   *
   * `320px` est le plancher HISTORIQUE, identique au repli de la regle de base : un site qui ne regle
   * rien rend a l'identique a toutes les largeurs. Une garde tient les deux nombres ensemble.
   */
  .em-asb__inner {
    min-height: 320px;
  }
  .em-asb__inner {
    /* `var()` et non `200px` en dur (2.5.302) : cette règle est postérieure à celle de la largeur
       réglable et de même spécificité, donc elle GAGNAIT — le réglage « largeur de la colonne des
       campus » n'avait aucun effet sous 1024 px, silencieusement. Le repli vaut 200 px, la valeur
       qui était écrite ici : un site qui ne règle rien rend à l'identique. */
    grid-template-columns: var(--em-asb-nav-w, 200px) 1fr;
  }
}
@media (max-width: 640px) {
  .em-asb__inner {
    grid-template-columns: 1fr;
  }
  /* Meme raison de specificite qu'a 1024 px : sans la classe doublee, la regle a trois colonnes
     l'emporterait ici aussi et le telephone rendrait deux colonnes. */
  .em-asb__inner.em-asb__inner--with-photo,
  .em-asb__inner.em-asb__inner--with-photo.em-asb__inner--no-nav {
    grid-template-columns: 1fr;
  }
  /*
   * ⚠ ANNULER LES PLACEMENTS DE 1024 px, ET NON LES REECRIRE (2.5.409).
   *
   * Les deux requetes media s'appliquent TOUTES LES DEUX sur un telephone. La colonne des campus y est
   * posee en `grid-row: 3` et le contenu AUSSI : sur une grille a une seule colonne, les deux se
   * superposeraient exactement — la liste des evenements par-dessus les campus.
   *
   * Un `auto` rend la main au placement automatique, donc a l'ORDRE DU DOM : photo, titre, campus,
   * evenements. C'est exactement l'empilement voulu, et il n'y a rien a renumeroter.
   *
   * ⚠ LA SPECIFICITE EST LE FOND DU SUJET, ET LE PREMIER JET L'A RATEE (corrige en 2.5.410).
   *
   * Ecrite `.em-asb__inner--with-photo > *`, cette regle pese (0,1,0) — une classe et un universel, qui
   * ne compte pour rien. Elle affrontait `.em-asb__inner--with-photo .em-asb__navcol` en (0,2,0) et
   * `…--no-nav .em-asb__content` en (0,3,0) : elle PERDAIT, et le telephone gardait les placements de
   * la tablette. Mesure du rendu en production : la colonne des campus poussee dans une piste implicite
   * de largeur nulle — donc INVISIBLE — et les evenements dans une seconde colonne rognee par
   * l'`overflow: hidden` du conteneur.
   *
   * QUATRIEME fois que ce depot paie ce point (loupe de recherche 2.5.335, puces de liste 2.5.341,
   * conteneur 2.5.346, ici). D'ou (0,4,0) — strictement superieur a tout ce qui place ces enfants, donc
   * l'ORDRE DES REGLES ne rentre plus en jeu. Une garde compare desormais les specificites CALCULEES au
   * lieu de constater qu'un texte est present : la regle etait bien la, elle ne s'appliquait pas.
   */
  .em-agenda-sidebar-wrap .em-asb__inner.em-asb__inner--with-photo.em-asb__inner--with-photo > * {
    grid-column: 1 / -1;
    grid-row: auto;
  }
  /* `min-width: 0` sur les items de la grille — LA cause des trois symptômes mobiles signalés.
     Un item de grille garde `min-width: auto` par défaut : la colonne `1fr` ne peut donc pas descendre
     sous la largeur de son contenu le plus large. Ici c'est la barre d'onglets, en `white-space:
     nowrap`, qui fixait le plancher : la grille devenait plus large que l'écran et l'`overflow: hidden`
     de `.em-asb__inner` CLIPPAIT la droite au lieu de laisser défiler.
     D'où, d'un seul coup : les onglets de campus coupés sans pouvoir glisser (leur `overflow-x: auto`
     ne s'active jamais faute de largeur contrainte), le bouton d'inscription des lignes hors champ, et
     le second bouton de pied tronqué. */
  .em-asb__inner > * { min-width: 0; }
  .em-asb__nav {
    padding: 0;
  }
  /* Le titre reste, resserre : sous 640 px la liste devient une barre d'onglets a defilement, et 12 px
     de marge au-dessus d'elle consommeraient une hauteur qui manque deja. */
  .em-asb__nav-title { padding: 10px 12px 4px; font-size: .625rem; }
  /* Le bouton d'inscription porte `flex-shrink: 0` : il ne peut pas se comprimer, il doit donc pouvoir
     passer à la ligne plutôt que d'être repoussé hors du cadre. Pleine largeur ensuite, comme la ligne
     d'agenda le fait déjà sous 640 px. */
  .em-asb__event-row {
    flex-wrap: wrap;
    gap: 12px;
  }
  .em-asb__event-cta {
    flex: 1 0 100%;
    align-self: stretch;
  }
  .em-asb__event-cta .em-asb__cta-btn {
    display: block;
    text-align: center;
  }
  /*
   * ⚠ CETTE BARRE ETAIT ECRITE EN BLANC, ET ELLE EST DEVENUE INVISIBLE EN 2.5.406 (corrige en 2.5.409).
   *
   * Elle portait CINQ valeurs blanches en dur — texte inactif a 60 %, texte actif, underline de l'actif,
   * ligne de base, survol — toutes calculees pour la colonne SOMBRE d'origine. La 2.5.406 a rendu cette
   * colonne transparente sur un panneau clair : blanc sur blanc. Le titre « FILTRER PAR VILLE » restait
   * lisible (il lit `--em-color-muted`), donc l'ecran montrait un intitule surmontant le VIDE.
   *
   * C'est le defaut que le journal de la 2.5.406 decrivait lui-meme — « n'en basculer qu'une partie
   * donne du blanc sur blanc » — sur les six replis de la colonne. Ces cinq-la etaient dans une REQUETE
   * MEDIA, et la garde de palette ne lisait que la regle de base : elle ne pouvait pas les voir.
   *
   * ⚠ ON NE LIT PAS `--em-asb-nav-active` ICI, alors que c'est la couleur du texte actif sur ordinateur.
   * Son repli est `#fff`, juste sur une pastille pleine et faux partout ailleurs : ici il n'y a AUCUNE
   * pastille, l'actif se signale par la graisse et l'underline. Reprendre cette variable reintroduirait
   * exactement le blanc sur blanc qu'on corrige.
   *
   * Tab-bar : une seule ligne defilable, labels en MAJUSCULES, ligne de base + underline sous l'onglet
   * actif (pas de pills). Scrollbar masquee ; ne wrappe jamais.
   */
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-list {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    overflow-x: auto;
    padding: 0 8px;
    border-bottom: 1px solid var(--em-asb-divider, var(--em-widget-border, var(--em-color-border)));
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;              /* Firefox */
    overscroll-behavior-x: contain;     /* pas de "pull" horizontal parasite de la page */
  }
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-list::-webkit-scrollbar { display: none; }  /* WebKit */
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-item.em-asb__nav-item {
    flex: 0 0 auto;
    gap: 0;
    padding: 14px 14px 12px;
    border-left: none;
    border-radius: 0;
    background: transparent;
    /* Meme variable que sur ordinateur : un site qui a regle la couleur de ses campus la retrouve
       sur telephone. Aucune opacite — une teinte choisie en back-office ne doit pas revenir delavee
       (lecon des trois couleurs du ticker, 2.5.298). L'actif se distingue par la graisse et le trait. */
    color: var(--em-asb-nav-text, var(--em-color-primary));
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    white-space: nowrap;
    border-bottom: 3px solid transparent;   /* emplacement de l'indicateur */
    margin-bottom: -1px;                     /* chevauche la ligne de base */
    transition: color .15s, border-color .15s;
  }
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-item:hover {
    /* Meme teinte de primaire que sur ordinateur : un voile blanc a 8 % ne se voit pas sur clair. */
    background: rgba(var(--em-color-primary-rgb, 26, 58, 107), .08);
    color: var(--em-asb-nav-text, var(--em-color-primary));
  }
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-item.active {
    background: transparent;
    font-weight: 800;
    /* L'underline reprend la couleur qui PEINT l'actif sur ordinateur : un site qui l'a reglee garde
       le meme reperage d'une taille d'ecran a l'autre, sans un second reglage a tenir. */
    border-bottom-color: var(--em-asb-nav-active-bg, var(--em-color-primary));
  }
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-bullet { display: none; }     /* pas de puce dans le style onglets */
  .em-agenda-sidebar-wrap:not([data-mobile-nav="select"]):not([data-mobile-nav="list"]) .em-asb__nav-arrow { display: none; }
  .em-asb__content {
    padding: 18px 16px;
  }

  /* ---- Mode « menu deroulant » (2.5.419) ----
     Le `<nav>` disparait ENTIEREMENT : il ne porte que les onglets, et un conteneur vide garderait
     son retrait sous la liste deroulante (lecon de 2.5.385). */
  .em-agenda-sidebar-wrap[data-mobile-nav="select"] .em-asb__nav { display: none; }
  .em-agenda-sidebar-wrap[data-mobile-nav="select"] .em-asb__nav-select {
    display: block;
    /* `border-box` OBLIGATOIRE : la boite porte un retrait ET une bordure par-dessus une largeur en
       pourcentage, donc sans lui elle deborde sa colonne de trois pixels de chaque cote (garde du
       depot, et elle a crie sur ce bloc-ci). */
    box-sizing: border-box;
    margin: 12px 16px;
    width: calc(100% - 32px);
    padding: .625rem .75rem;
    border: 1px solid var(--em-asb-divider, var(--em-widget-border, var(--em-color-border)));
    border-radius: var(--em-radius);
    background: var(--em-asb-nav-bg, transparent);
    color: var(--em-asb-nav-text, var(--em-color-primary));
    font: inherit;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .07em;
  }

  /* ---- Mode « liste sur deux colonnes » (2.5.419) ----
     ⚠ C'EST LA VUE BUREAU, remise sur deux colonnes — pas une troisieme mise en forme. Les puces et le
     chevron de l'actif sont ceux du widget depuis toujours ; seul le mode ONGLETS les masquait, parce
     qu'une barre d'onglets n'a pas de puces. Les reglages `widget_bullet` et « Chevron du campus
     actif » retrouvent donc leur effet sur telephone, sans une ligne de plus. */
  .em-agenda-sidebar-wrap[data-mobile-nav="list"] .em-asb__nav-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 12px;
    padding: 10px 16px;
    overflow: visible;
    border-bottom: 1px solid var(--em-asb-divider, var(--em-widget-border, var(--em-color-border)));
  }
  .em-agenda-sidebar-wrap[data-mobile-nav="list"] .em-asb__nav-item {
    /* `min-width: 0` : un nom long doit se tronquer dans sa colonne au lieu de l'elargir et de
       desaligner la seconde. Sans lui, `text-overflow` ne s'applique jamais (lecon de 2.5.271). */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ===================== EVENTS LIST GRID ===================== */
.em-events-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--em-grid-gap);
}
/* Nombre de colonnes forcé (réglage back-office) — desktop seulement, mobile reste 1 col. */
@media (min-width: 768px) {
  .em-events-grid.em-cols-2 { grid-template-columns: repeat(2, 1fr); }
  .em-events-grid.em-cols-3 { grid-template-columns: repeat(3, 1fr); }
  .em-events-grid.em-cols-4 { grid-template-columns: repeat(4, 1fr); }
}
.em-loadmore-wrap {
  text-align: center;
  padding: 1.5rem 0 .5rem;
}
/* Bouton « Charger plus » (mode pagination loadmore de l'agenda) */
.em-loadmore-btn {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .625rem 1.625rem;
  background: var(--em-color-primary);
  color: #fff;
  border: none;
  border-radius: 999px;
  font-size: .875rem;
  font-weight: 600;
  cursor: pointer;
  transition: opacity .15s;
}
.em-loadmore-btn:hover { opacity: .88; }
/* Progression « 10 sur 55 » et message de fin de liste (2.5.317). Discrets : ils accompagnent le bouton,
   ils ne le concurrencent pas. */
.em-loadmore-count,
.em-loadmore-done {
  margin: .5rem 0 0;
  font-size: .75rem;
  color: var(--em-color-muted);
  text-align: center;
}
.em-loadmore-count:empty { display: none; }   /* rempli par le JS : vide, il ne doit pas prendre de place */
.em-loadmore-done { padding: 1.5rem 0 .5rem; margin: 0; }

/* Sous 640 px : le bouton prend TOUTE la largeur, avec une cible de 48 px.
   `inline-flex` centre par le parent laissait une pilule de ~40 px de haut au milieu de l'ecran — sous les
   44 px recommandes pour une cible tactile (WCAG 2.5.5), et facile a rater au pouce. */
@media (max-width: 640px) {
  .em-loadmore-btn {
    display: flex;
    width: 100%;
    box-sizing: border-box;
    min-height: 48px;
    justify-content: center;
    font-size: 1rem;
    font-weight: 500;
  }
}

/* ===================== HERO CAROUSEL ===================== */
.em-hero-carousel {
  background: var(--em-ds-bg, var(--em-color-primary));
  color: #fff;
  padding: 1.75rem 1.75rem 1.5rem;
  width: 100%;
  box-sizing: border-box;
  /* `font-family: inherit` RETIRE en 2.5.347 : declare APRES la regle des cinq racines et a la meme
     specificite, il gagnait — donc le reglage « Police » etait MORT sur ce bloc, sauf a cocher
     « forcer partout ». Le retrait ne change rien quand aucune police n'est choisie : `--em-font`
     n'est alors pas definie, `var(--em-font)` est invalide a l'evaluation, et la propriete retombe
     sur l'heritage — exactement ce que `inherit` faisait. */
  user-select: none;
}
.em-hc__title {
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: .04em;
  margin: 0 0 1.25rem;
  line-height: 1.3;
}
/* viewport clips the sliding track */
.em-hc__viewport {
  overflow: hidden;
  width: 100%;
  min-height: 80px;
}
.em-hc__track {
  display: flex;
  transition: transform .38s cubic-bezier(.4, 0, .2, 1);
  will-change: transform;
}
/* one "page" of events — events stacked vertically */
.em-hc__slide {
  flex: 0 0 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: .375rem;
  box-sizing: border-box;
}
/* single event card */
.em-hc__event {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  font-size: .875rem;
  min-width: 0;
  padding: .375rem .5rem;
  border-radius: 6px;
  background: rgba(255,255,255,.1);
}
.em-hc__event-title {
  font-weight: 700;
  font-size: .875rem;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.em-hc__event-meta {
  display: flex;
  align-items: center;
  gap: .375rem;
  opacity: .75;
  font-size: .75rem;
}
.em-hc__event-city {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.em-hc__event-date {
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: .25rem;
  flex-shrink: 0;
}
.em-hc__event-date svg, .em-hc__event-meta svg { flex-shrink: 0; opacity: .8; }
.em-hc__event-type {
  font-size: .625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  opacity: .65;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.em-hc__event-time {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  flex-shrink: 0;
}
.em-hc__event-online {
  font-size: .625rem;
  font-weight: 700;
  color: #4ade80;
  text-transform: uppercase;
  letter-spacing: .05em;
}
.em-hc__event-cta {
  display: inline-block;
  margin-top: .25rem;
  padding: .25rem .75rem;
  font-size: .75rem;
  font-weight: 700;
  color: #fff;
  background: rgba(255,255,255,.18);
  border: 1px solid rgba(255,255,255,.35);
  border-radius: 4px;
  text-decoration: none;
  white-space: nowrap;
  transition: background .15s;
  align-self: flex-start;
}
.em-hc__event-cta:hover {
  background: rgba(255,255,255,.3);
  color: #fff;
}
/* dots */
.em-hc__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .25rem;
  margin: .875rem 0 1.125rem;
}
.em-hc__dot {
  height: 8px;
  width: 8px;
  border-radius: 4px;
  background: rgba(255, 255, 255, .3);
  border: none;
  cursor: pointer;
  padding: 0;
  transition: background .2s, width .25s cubic-bezier(.4, 0, .2, 1);
  flex-shrink: 0;
}
.em-hc__dot.active {
  background: #fff;
  width: 24px;
}
/* CTA */
/* Le pied est un FLEX pour que l'alignement soit une seule propriete a basculer. `text-align`
   n'aurait pas suffi : le CTA est un `inline-flex` (il contient une icone), et `text-align` ne
   deplace pas un enfant qui etablit son propre contexte de mise en page. */
/* Pied en flex plutot qu'un `text-align` sur le conteneur : le bouton est inline-level, donc
   `text-align` le deplacerait aussi — mais il DESCENDRAIT en meme temps dans le bouton et
   recentrerait son propre texte, et il cesserait d'agir le jour ou le bouton passe en `display:
   block` (ce que fait la regle pleine largeur en dessous de 640 px sur ses cousins). Le couple
   flex + `justify-content` ne depend ni du `display` de l'enfant ni de l'heritage. */
.em-hc__footer { margin-top: .25rem; display: flex; }
.em-hc__footer--left   { justify-content: flex-start; }
.em-hc__footer--center { justify-content: center; }
.em-hc__footer--right  { justify-content: flex-end; }
/* L'icone heritee du mecanisme commun : meme animation au survol que les autres boutons. */
.em-hc__cta .em-icon { flex-shrink: 0; transition: transform .15s; }
.em-hc__cta:hover .em-icon { transform: translateX(3px); }
.em-hc__cta {
  /* `inline-flex` + `gap` : ce bouton etait en `inline-block`, ce qui suffisait tant qu'il ne
     contenait que du texte. Depuis qu'il porte l'icone commune (2.5.242), l'icone etait un enfant
     EN LIGNE — alignee sur la ligne de base donc decalee du texte, sans espacement, et le
     `flex-shrink: 0` pose sur `.em-icon` etait inerte faute de conteneur flex. */
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem 1.5rem;
  border: 2px solid #fff;
  color: #fff;
  font-size: .875rem;
  font-weight: 700;
  text-transform: uppercase;
  text-decoration: none;
  letter-spacing: .06em;
  transition: background .18s;
}
.em-hc__cta:hover { background: rgba(255, 255, 255, .12); color: #fff; }
/* empty/loading states */
.em-hc__loading, .em-hc__empty {
  font-size: .875rem;
  opacity: .75;
  padding: 1rem;
  text-align: center;
  width: 100%;
}

/* ===================== UPCOMING EVENTS WIDGET ===================== */
.em-upcoming {
  --em-up-accent: var(--em-color-primary, #1a3a6b);
  background: var(--em-color-surface);
  border-radius: var(--em-radius);
  box-shadow: var(--em-shadow);
  padding: 1.5rem;
  box-sizing: border-box;
}
.em-upcoming__title {
  font-size: 1.25rem;
  font-weight: 800;
  color: var(--em-up-accent);
  line-height: 1.25;
  margin: 0 0 1.125rem;
  text-transform: uppercase;
  letter-spacing: .04em;
}
/* one event row */
.em-upcoming__event {
  padding: .875rem 0;
}
/* date + location line */
.em-upcoming__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .375rem 1rem;
  font-size: .75rem;
  color: var(--em-color-muted);
  margin-bottom: .375rem;
}
.em-upcoming__date,
.em-upcoming__loc {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
}
.em-upcoming__date svg,
.em-upcoming__loc svg { flex-shrink: 0; }
.em-upcoming__school {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  font-weight: 600;
  color: var(--em-up-accent);
}
/* type badge — matches the agenda item type-top label */
.em-upcoming__type {
  display: inline-block;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .07em;
  padding: .25rem .5rem;
  border-radius: 999px;
  background: var(--em-color-warning-bg);
  color: var(--em-color-warning);
  border: 1px solid var(--em-color-warning-border);
  margin-bottom: .25rem;
}
.em-upcoming__online {
  display: inline-block;
  font-size: .625rem;
  font-weight: 700;
  padding: .25rem .5rem;
  border-radius: 999px;
  background: var(--em-color-success-bg);
  color: var(--em-color-success);
  margin-bottom: .125rem;
}
.em-upcoming__tags {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin: .375rem 0 .125rem;
}
.em-upcoming__tag {
  font-size: .75rem;
  font-weight: 600;
  padding: .125rem .5rem;
  border-radius: 999px;
  background: var(--em-color-bg);
  color: var(--em-color-muted);
  border: 1px solid var(--em-color-border);
}
/* event title */
.em-upcoming__event-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--em-up-accent);
  margin: 0 0 .5rem;
  line-height: 1.3;
}
/* optional description */
.em-upcoming__desc {
  font-size: .875rem;
  color: var(--em-color-muted);
  margin: .125rem 0 .625rem;
  line-height: 1.45;
}
/* CTA button — aligned with .em-agenda-item__cta */
.em-upcoming__cta {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .5rem 1rem;
  background: transparent;
  color: var(--em-up-accent);
  border: 1.5px solid var(--em-up-accent);
  border-radius: calc(var(--em-radius) / 2);
  font-size: .75rem;
  font-weight: 600;
  text-decoration: none;
  transition: background var(--em-transition), color var(--em-transition);
  white-space: nowrap;
}
.em-upcoming__cta:hover {
  background: var(--em-up-accent);
  color: #fff;
  transform: none;
  opacity: 1;
}
.em-upcoming__cta svg { flex-shrink: 0; }
/* separator between events */
.em-upcoming__sep {
  border: none;
  border-top: 1px solid var(--em-color-border);
  margin: 0;
}
/* empty / loading */
.em-upcoming__empty,
.em-upcoming__loading {
  font-size: .875rem;
  color: var(--em-color-muted);
  padding: .75rem 0;
}

/* ============================================================================
   APPARENCE PAR INSTANCE DE BLOC (2.5.205)

   Chaque variable est posee en `style` sur la RACINE du bloc par `em_block_appearance_style()`, et
   consommee ici avec un repli sur le design system. Consequence voulue : un reglage laisse vide
   n'emet aucune variable, donc le repli s'applique et l'instance herite de la charte — sans aucune
   condition ni regle supplementaire cote PHP.

   Emises en variables et non en regles `<style>` par instance : une regle exigerait un selecteur
   unique par bloc (donc un id, donc un risque de collision) et pourrait fuir hors du bloc. Une
   variable posee sur la racine ne descend que dans ce bloc.

   Ces declarations sont volontairement placees en FIN de fichier : a specificite egale, la derniere
   declaree gagne, donc elles surchargent les regles d'origine sans avoir a les modifier.
   ============================================================================ */

/* ---- Prochains evenements ---- */
.em-upcoming {
  background:    var(--em-up-bg,     var(--em-color-surface));
  border-radius: var(--em-up-radius, var(--em-radius));
  box-shadow:    var(--em-up-shadow, var(--em-shadow));
  /* Le repli porte les DEUX valeurs d'origine (vertical horizontal) : une variable peut contenir
     une liste, donc le padding asymetrique est preserve tant que rien n'est regle. */
  padding:       var(--em-up-pad,    1.5rem 1.75rem);
}
.em-upcoming__title       { color: var(--em-up-title, var(--em-color-primary)); }
.em-upcoming__event-title { color: var(--em-up-text,  var(--em-color-text)); }
.em-upcoming__event       { border-color: var(--em-up-border, var(--em-color-border)); }
.em-up--compact           { padding: var(--em-up-pad, 1rem 1.125rem); }
.em-up--compact .em-upcoming__event { padding-top: .5rem; padding-bottom: .5rem; }

/* ---- Hero carousel ---- */
.em-hero-carousel {
  border-radius: var(--em-hc-radius, 0);
  box-shadow:    var(--em-hc-shadow, none);
  padding:       var(--em-hc-pad,    1.75rem 1.75rem 1.5rem);
  border:        1px solid var(--em-hc-border, transparent);
}
.em-hc__title       { color: var(--em-hc-title, var(--em-hc-accent, #fff)); }
.em-hc__event-title { color: var(--em-hc-text,  #fff); }
.em-hc--compact     { padding: var(--em-hc-pad, 1rem 1rem .875rem); }

/* ---- Grille personnalisee ---- */
.em-list-wrap {
  background:    var(--em-cards-bg,     transparent);
  border-radius: var(--em-cards-radius, 0);
  box-shadow:    var(--em-cards-shadow, none);
  padding:       var(--em-cards-pad,    0);
}
.em-list-wrap .em-event-card {
  background:    var(--em-cards-bg,     var(--em-color-surface));
  border-color:  var(--em-cards-border, var(--em-color-border));
}
/* Chaine : titre propre, puis accent PARTAGE, puis la charte (2.5.313). */
.em-list-wrap .em-event-card__title { color: var(--em-cards-title, var(--em-cards-accent, var(--em-color-primary))); }
.em-list-wrap .em-event-card__desc  { color: var(--em-cards-text,  var(--em-color-muted)); }
.em-cards--compact .em-event-card__body { padding: .625rem .875rem; }

/* ============================================================================
   BANDE DE DATES (bloc dates-strip)

   En-tete en grille : titre a gauche, bouton + fleches a droite en desktop ; les trois elements
   s'empilent sous 640 px. La piste defile horizontalement avec `scroll-snap`, ce qui donne un
   alignement propre au doigt sur mobile sans une ligne de JS supplementaire.

   Toutes les couleurs passent par des variables `--em-ds-*` avec repli sur le design system : un
   reglage laisse vide herite de la charte (cf. em_block_appearance_style).
   ============================================================================ */
.em-dates-strip {
  background:    var(--em-ds-bg, var(--em-color-primary));
  color:         var(--em-ds-text, #fff);
  border-radius: var(--em-ds-radius, 0);
  box-shadow:    var(--em-ds-shadow, none);
  padding:       var(--em-ds-pad, 1.5rem 1.5rem);
  box-sizing: border-box;
}
.em-ds__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.125rem;
}
.em-ds__title {
  margin: 0; font-size: 1.25rem; font-weight: 700; line-height: 1.2;
  color: var(--em-ds-title, inherit);
}
.em-ds__actions { display: flex; align-items: stretch; gap: .625rem; }
.em-ds__cta {
  display: inline-flex; align-items: center; padding: .5rem 1.125rem;
  border: 2px solid currentColor; color: inherit; text-decoration: none;
  font-weight: 700; font-size: .875rem; white-space: nowrap;
  transition: background var(--em-transition, .2s ease), color var(--em-transition, .2s ease);
}
.em-ds__cta:hover { background: #fff; color: var(--em-ds-bg, var(--em-color-primary)); }
.em-ds__nav { display: flex; }
.em-ds__arrow {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; border: 0; cursor: pointer; color: inherit;
  /* Fleche INACTIVE en fond semi-transparent, ACTIVE en blanc : c'est la difference visible sur
     les captures entre la fleche gauche (debut de piste) et la fleche droite. */
  background: rgba(255,255,255,.18);
  transition: background var(--em-transition, .2s ease);
}
.em-ds__arrow:last-child { background: #fff; color: var(--em-ds-bg, var(--em-color-primary)); }
.em-ds__arrow:hover:not(:disabled) { background: rgba(255,255,255,.32); }
.em-ds__arrow:last-child:hover:not(:disabled) { background: #f0f0f0; }
.em-ds__arrow:disabled { opacity: .45; cursor: default; }
.em-ds__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.em-ds__viewport { overflow: hidden; }
.em-ds__track {
  display: flex; gap: 12px; overflow-x: auto; scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  /* Barre masquee : la navigation se fait aux fleches ou au doigt. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.em-ds__track::-webkit-scrollbar { display: none; }

.em-ds__card {
  display: flex; align-items: stretch; flex: 0 0 auto;
  /* 3 cartes visibles par defaut, gouttiere comprise. Les bandes tablette/mobile sont dans les
     media queries ci-dessous — le JS ne fait que la pagination, pas la largeur. */
  /* Meme correctif (2.5.296). Repli `3` = ancien nombre code = defaut de `ds_per_view`. */
  /* `border-box` (2.5.315) : sans lui la bordure de 1 px des quatre cotes s'ajoute a la largeur calculee.
     Debordement plus discret que les deux autres pistes — 2 px par carte — mais reel. */
  box-sizing: border-box;
  width: calc((100% - (var(--em-ds-per, 3) - 1) * 12px) / var(--em-ds-per, 3));
  min-width: 190px;
  background: var(--em-ds-city-bg, #fff);
  color: var(--em-ds-city-color, var(--em-color-primary));
  text-decoration: none; scroll-snap-align: start;
  border: 1px solid var(--em-ds-border, transparent);
  overflow: hidden;
}
.em-ds__date {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  flex: 0 0 auto; min-width: 66px; padding: .75rem .5rem;
  color: var(--em-ds-date-color, var(--em-ds-accent, var(--em-color-primary)));
}
.em-ds__day   { font-size: 1.5rem; font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.em-ds__month { font-size: .625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
.em-ds__place {
  display: flex; align-items: center; gap: .375rem;
  padding: .75rem .875rem; font-size: 1rem; font-weight: 600;
}
.em-ds__place .em-icon { flex-shrink: 0; }
/* Champs visibles (2.5.442) : titre et horaire OFFERTS. Presents, le texte passe en colonne ; le lieu y perd
   son propre retrait, porte par la colonne. `min-width: 0` : sans lui un titre long elargit la carte au lieu
   de passer a la ligne. */
.em-ds__body {
  display: flex; flex-direction: column; justify-content: center; gap: .25rem;
  min-width: 0; padding: .75rem .875rem;
}
.em-ds__body .em-ds__place { padding: 0; }
.em-ds__title { font-size: .875rem; font-weight: 700; line-height: 1.25; }
.em-ds__time  { font-size: .75rem; font-weight: 500; }

/* ------------------------------------------------------------------------------------------
   Affordance de la carte cliquable.
   ------------------------------------------------------------------------------------------
   Le lien a toujours fonctionné, mais RIEN ne le disait : aucune règle `:hover`, aucun style de
   focus, aucune transition. La bande se lisait donc comme une simple liste de dates, et un
   utilisateur n'essayait pas de cliquer. Un défaut d'affordance ne produit aucune erreur — il
   produit un composant qu'on n'utilise pas.

   Le sélecteur est `a.em-ds__card` et non `.em-ds__card` : quand la source ne fournit aucune URL,
   le JS rend un `<div>` (un `<a>` sans `href` n'est pas focalisable et mentirait au lecteur
   d'écran). Ce `<div>` ne doit surtout PAS réagir au survol, sinon il promet un clic qui n'arrive
   jamais — la distinction visuelle entre les deux devient au contraire une information utile.
   ------------------------------------------------------------------------------------------ */
a.em-ds__card {
  cursor: pointer;
  transition: transform var(--em-transition), box-shadow var(--em-transition);
}
a.em-ds__card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, .16);
}
/* Le lieu se souligne au survol : sur un fond coloré, l'ombre portée est peu lisible, alors que le
   soulignement dit « lien » sans dépendre du contraste. */
a.em-ds__card:hover .em-ds__place > span:last-child { text-decoration: underline; }

/* `outline-offset` NÉGATIF, donc tracé à l'intérieur de la carte : `.em-ds__track` est en
   `overflow-x: auto`, et un contour dessiné à l'extérieur serait rogné pour la première et la
   dernière carte de la piste — précisément celles qu'on atteint au clavier en premier. */
a.em-ds__card:focus-visible {
  outline: 3px solid var(--em-ds-city-color, var(--em-color-primary));
  outline-offset: -3px;
}
.em-ds__empty { margin: 0; font-size: .875rem; opacity: .85; }

.em-ds--compact { padding: var(--em-ds-pad, 1rem 1.125rem); }
.em-ds--compact .em-ds__place { padding: .5rem .75rem; }

@media (max-width: 1023px) {
  .em-ds__card { width: calc((100% - (var(--em-ds-per-tab, 2) - 1) * 12px) / var(--em-ds-per-tab, 2)); }
}
@media (max-width: 640px) {
  /* En-tete deplie : titre, puis bouton, puis fleches alignees a droite — comme la capture mobile. */
  .em-ds__head    { display: block; }
  .em-ds__title   { margin-bottom: .875rem; }
  .em-ds__actions { justify-content: space-between; }
  .em-ds__card    { width: calc((100% - (var(--em-ds-per-mob, 1) - 1) * 12px) / var(--em-ds-per-mob, 1)); }
  /* `min-width: 0` : la regle de base impose un plancher en PIXELS pour garder des cartes lisibles
     sur grand ecran. Sous 640 px ce plancher devient plus large que la place disponible : la carte
     ne peut plus se comprimer, et le viewport en `overflow: hidden` la ROGNE au lieu de l'adapter —
     titre coupe en deux, pave de date colle au bord. La largeur `100%` ci-dessus ne suffit pas :
     `min-width` gagne toujours contre `width`. */
  .em-ds__card    { min-width: 0; }
}

/* ===================== BOUTON DE PAUSE DES CARROUSELS =====================
   Exigence WCAG 2.2.2 (niveau A) : un contenu qui s'anime seul plus de 5 s doit pouvoir etre arrete.
   La pause au survol et au FOCUS est toujours active ; ce bouton est le controle EXPLICITE, le seul
   qu'on voit sans avoir a le deviner. Reglage `carousel_pause_button`.

   PLACE DANS LE GROUPE DE CONTROLES de chaque carrousel, pas en absolu sur sa racine : il s'y
   superposait aux fleches de navigation, qui occupent le meme coin superieur droit. Dans leur groupe il
   herite de leur alignement, et aucun recouvrement n'est possible par construction. Le repli
   `--floating` ci-dessous ne sert qu'aux gabarits personnalises depourvus de ce conteneur. */
.em-featured-zone,
.em-hero-carousel { position: relative; }

.em-autoplay-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* `box-sizing` : sans lui la bordure de 1 px s'ajoute aux 28 px (cf. le bouton de navigation
     ci-dessus), et le bouton fait 30 px la ou ses voisins en font 36 — une rangee de ronds de
     diametres differents. `flex: 0 0 auto` : il ne doit pas s'aplatir sur un ecran etroit. */
  box-sizing: border-box;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--em-color-border);
  border-radius: 50%;
  background: var(--em-color-surface);
  color: var(--em-color-text);
  cursor: pointer;
  /* Discret au repos, franc a l'interaction : le bouton ne doit pas concurrencer le contenu, mais
     rester trouvable. Jamais sous 3:1 face au fond, seuil AA des elements d'interface. */
  opacity: .65;
  transition: opacity var(--em-transition, .2s), box-shadow var(--em-transition, .2s);
}
.em-autoplay-toggle:hover,
.em-autoplay-toggle:focus-visible { opacity: 1; }
.em-autoplay-toggle:focus-visible {
  outline: 2px solid var(--em-color-primary);
  outline-offset: 2px;
}
/* Le hero a un fond sombre : le bouton y reprend les couleurs claires du bandeau. */
.em-hero-carousel .em-autoplay-toggle {
  background: rgba(255, 255, 255, .15);
  border-color: rgba(255, 255, 255, .45);
  color: #fff;
}

/* Dans le groupe des fleches « a la une » : meme diametre qu'elles, pour une rangee homogene. */
.em-featured-zone--carousel .em-featured-zone__nav .em-autoplay-toggle {
  width: 36px;
  height: 36px;
}

/* Rangee de controles du hero : les points au centre, le bouton a leur droite. `justify-content` sur le
   conteneur et non une marge sur le bouton — les points restent ainsi centres sur le carrousel meme
   quand le bouton est absent (reglage decoche ou mouvement reduit). */
/* Repli pour un gabarit personnalise sans groupe de controles : le bouton flotte, comme avant. Mieux
   vaut un controle mal place qu'un carrousel qu'on ne peut pas arreter. */
.em-autoplay-toggle--floating {
  position: absolute;
  top: .5rem;
  right: .5rem;
  z-index: 3;
}

/* ===================== MOUVEMENT REDUIT =====================
   `prefers-reduced-motion` : preference SYSTEME, exprimee par des personnes sujettes au mal des
   transports, aux migraines ou aux troubles vestibulaires. Le defilement automatique est deja coupe
   cote JS (il ne demarre pas) ; ici on neutralise transitions et defilements animes.
   `scroll-behavior: auto` est indispensable : les carrousels utilisent `scrollTo({behavior:"smooth"})`,
   que seule cette propriete peut ramener a un saut instantane. */
@media (prefers-reduced-motion: reduce) {
  .em-agenda-wrap *,
  .em-agenda-sidebar-wrap *,
  .em-list-wrap *,
  .em-hero-carousel *,
  .em-upcoming *,
  .em-dates-strip * {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .em-featured-zone__track,
  .em-hc__track,
  .em-ds__viewport { scroll-behavior: auto !important; }
  /*
   * ⚠ `*` NE MATCHE PAS LES PSEUDO-ELEMENTS, et c'est ce qui aurait ete perdu en 2.5.389.
   *
   * Le balayage du squelette vivait sur `.em-skeleton` lui-meme, donc la regle `*` ci-dessus le
   * neutralisait. Depuis qu'il vit dans un `::before` — pour devenir composable — il echappe a cette
   * regle : une animation INFINIE serait revenue chez les personnes qui demandent explicitement de ne
   * pas en voir. Rendre une animation plus performante ne dispense pas de la rendre coupable.
   */
  .em-skeleton::before {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* Contenu reserve aux lecteurs d'ecran : visible pour eux, invisible a l'ecran.
   `clip-path` plutot que `display:none` ou `visibility:hidden`, qui retirent l'element de l'arbre
   d'accessibilite — une region live ainsi masquee n'annoncerait plus rien. */
.em-sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================================
   AGENDA — MODÈLES DE MISE EN PAGE (2.5.228)
   ==========================================================================================
   Deux modèles mobiles et deux modèles desktop, choisis en back-office. Le modèle est posé en
   attribut `data-*` sur `.em-agenda-wrap` par PHP : aucun JavaScript n'intervient, donc le choix
   survit à une page mise en cache.

   Pourquoi des attributs et non des classes utilitaires : la valeur vient d'un réglage à liste
   fermée, et un attribut se lit tel quel dans l'inspecteur du navigateur — on voit immédiatement
   quel modèle s'applique. Spécificité `[attr] .classe` = (0,2,0), donc ces règles battent les
   règles de base (0,1,0) sans avoir besoin de `!important` ni de dépendre de l'ordre du fichier.

   Le CSS ne peut pas DÉPLACER un nœud : le type et le campus existent donc à deux endroits dans le
   DOM (colonne de date / surimpression de la vignette) et chaque modèle n'en montre qu'un. Le
   masquage se fait en `display: none`, seule propriété qui retire réellement l'élément de l'arbre
   d'accessibilité — sinon un lecteur d'écran annoncerait deux fois le type de chaque événement.
   ========================================================================================== */

/* Surimpressions : invisibles par DÉFAUT. Ajouter des nœuds au DOM ne doit repeindre aucun site qui
   n'a pas choisi de modèle — c'est le CSS qui les révèle, jamais le JS qui les omet. */
.em-agenda-item__ov-date,
.em-agenda-item__ov-type,
.em-agenda-item__ov-place,
.em-agenda-item__type-line { display: none; }

.em-agenda-item__ov-date,
.em-agenda-item__ov-type,
.em-agenda-item__ov-place {
  position: absolute;
  z-index: 2;
  align-items: center;
  /* La vignette peut devenir cliquable : les pastilles ne doivent pas intercepter le clic. */
  pointer-events: none;
}

/* Le pavé de date NE DÉCLARE AUCUN style propre : il porte aussi `.em-fc__date-stamp`, le tampon du
   carrousel « à la une », et hérite donc de sa taille, de son arrondi, de son ombre portée et de son
   opacité depuis une déclaration UNIQUE. Un style « qui ressemble » aurait fait deux pavés à
   maintenir. Seule exception : le positionnement, propre à chaque contexte, réglé par le modèle. */

/* Pastille de type : collée au bord droit, arrondie du seul côté visible. */
.em-agenda-item__ov-type {
  padding: .25rem .75rem;
  background: var(--em-color-primary);
  color: #fff;
  border-radius: var(--em-radius) 0 0 var(--em-radius);
  font-size: .75rem;
  font-weight: 700;
}

/* Pastille de lieu : fond translucide sombre, lisible sur n'importe quelle photo sans connaître ses
   couleurs — un fond opaque de la charte deviendrait invisible sur une image de même teinte. */
.em-agenda-item__ov-place {
  gap: .25rem;
  padding: .25rem .625rem;
  background: rgba(0, 0, 0, .62);
  color: #fff;
  border-radius: 999px;
  font-size: .75rem;
  font-weight: 600;
}
.em-agenda-item__ov-place .em-icon { flex-shrink: 0; }

/* Ligne de type dans le corps (modèle « date à côté de l'image »). */
.em-agenda-item__type-line {
  margin: 0 0 .25rem;
  color: var(--em-agenda-type-color, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}

/* ---------------------------------- MOBILE ---------------------------------- */
@media (max-width: 640px) {

  /* ⚠ LES MODELES AVEC IMAGE NE VISENT QUE LES CARTES QUI EN ONT UNE (2.5.435).
     `.em-agenda-item--thumb` est posee par le JS quand une vignette est rendue. Sans elle, les modeles
     « date a cote » et « pleine largeur » organisaient la carte autour d'une image ABSENTE : un blanc de
     140 px a cote du pave, le campus perdu (il n'etait que sur l'image), et en « pleine largeur » la DATE
     ELLE-MEME disparaissait — la colonne masquee, et le tampon qui la remplacait vivant dans la vignette.
     Une carte sans la classe retombe sur le rendu mobile de base, puis sur `agenda_mobile_noimg`.
     La condition porte sur la CARTE et non sur le reglage : un evenement sans visuel au milieu d'une
     liste illustree est traite pareil (meme regle que `.em-ph__slide--flat`, 2.5.422). */

  /* ===== Modèle A — « date à côté de l'image » =====
     Pavé de date à gauche, vignette à droite, texte en dessous sur toute la largeur.
     `display: contents` sur les deux conteneurs intermédiaires : c'est le seul moyen, sans toucher
     au DOM, de promouvoir vignette et corps comme enfants directs de la grille de la carte. Ces
     deux `div` n'ont ni fond, ni bordure, ni rôle — les faire disparaître est sans conséquence. */
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb {
    display: grid;
    grid-template-columns: var(--em-agenda-mobile-date-w, 88px) 1fr;
    /* La première rangée a une hauteur MINIMALE : sans elle, une date simple (bloc court) écraserait
       l'image en un bandeau de 90 px, alors qu'une plage de dates la laisserait à 150 px — le même
       modèle rendrait deux résultats très différents selon la donnée. */
    grid-template-rows: minmax(var(--em-agenda-mobile-media-h, 140px), auto) auto;
  }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__main,
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__row { display: contents; }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__date-col {
    grid-area: 1 / 1;
    width: auto;
    align-self: stretch;
  }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__thumb {
    display: block;
    grid-area: 1 / 2;
    width: auto;
    align-self: stretch;
    /* La rangée impose déjà la hauteur ; garder le ratio la contredirait. */
    aspect-ratio: auto;
  }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__body { grid-area: 2 / 1 / 3 / 3; }
  /* Le type et le campus quittent la colonne de date : sur 108 px un libellé comme
     « Portes ouvertes » se couperait en trois lignes. Ils reparaissent dans le corps et sur l'image. */
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__type-top,
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__campus-below { display: none; }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__type-line { display: block; }
  [data-agenda-mobile="date-side"] .em-agenda-item--thumb .em-agenda-item__ov-place {
    display: flex;
    right: .5rem;
    bottom: .5rem;
  }

  /* ===== Modèle B — « image pleine largeur » =====
     Vignette en bandeau, pavé de date et type en surimpression. La colonne de date disparaît
     entièrement : sa hauteur étirée était justement ce qui déformait la carte. */
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb {
    display: flex;
    flex-direction: column;
  }
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__date-col { display: none; }
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__main { display: block; }
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__row { display: block; }
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__thumb {
    display: block;
    width: 100%;
    align-self: auto;
    aspect-ratio: var(--em-thumb-ratio);
  }
  /* `top`/`left` viennent de `.em-fc__date-stamp` : ne rien redéclarer ici, sinon le pavé de
     l'agenda se décalerait du tampon du carrousel au premier ajustement de ce dernier. */
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__ov-date { display: block; }
  [data-agenda-mobile="overlay"] .em-agenda-item--thumb .em-agenda-item__ov-type {
    display: inline-flex;
    right: 0;
    bottom: 0;
  }

  /* ===== Modèle D — « vignette compacte à droite » (2.5.435) =====
     Date, texte et une petite vignette CARRÉE sur une seule rangée : environ trois cartes par écran au
     lieu d'une. La description est masquée — c'est le prix de la compacité, et le dire à l'écran. Sans
     image la carte retombe sur le rendu de base, qui est CETTE disposition moins la vignette. */
  [data-agenda-mobile="compact"] .em-agenda-item--thumb {
    display: grid;
    /* Colonne de date plus etroite que dans les autres modeles : c'est ce qui rend la place au texte. */
    grid-template-columns: var(--em-agenda-compact-date-w, 64px) minmax(0, 1fr) var(--em-agenda-compact-thumb, 72px);
    align-items: start;
  }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__main,
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__row { display: contents; }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__date-col {
    grid-column: 1;
    grid-row: 1;
    width: auto;
    align-self: stretch;
  }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__body { grid-column: 2; grid-row: 1; }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__thumb {
    display: block;
    grid-column: 3;
    grid-row: 1;
    width: var(--em-agenda-compact-thumb, 72px);
    aspect-ratio: 1;
    /* `start` : la regle de base l'etire (`stretch`), et un element etire ignore son aspect-ratio. */
    align-self: start;
    margin: .625rem .625rem 0 0;
    border-radius: calc(var(--em-radius) / 2);
  }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__desc,
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__type-top { display: none; }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__type-line { display: block; }
  /* Titre et pave reduits : sans eux la carte garde la hauteur d'un titre sur trois lignes et
     « compact » ne tient pas sa promesse. */
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__body { padding: .5rem .75rem; gap: .25rem; }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__title { font-size: 1rem; }
  [data-agenda-mobile="compact"] .em-agenda-item--thumb .em-agenda-item__day { font-size: 1.25rem; }

  /* ===== Modèle E — « image en bandeau, pavé qui chevauche » (2.5.435) =====
     L'image sur toute la largeur, au ratio de la charte ; le VRAI pavé de date (la colonne, pas le
     tampon en surimpression) posé dans la MEME cellule, calé en bas, et qui déborde de 1,75 rem sur le
     corps. Une marge NÉGATIVE et non une translation : la marge est ce qui laisse la cellule mesurer
     l'image seule, une translation déborderait sans que le corps le sache. */
  [data-agenda-mobile="banner"] .em-agenda-item--thumb {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__main,
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__row { display: contents; }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__thumb {
    display: block;
    grid-area: 1 / 1;
    width: 100%;
    align-self: auto;
    aspect-ratio: var(--em-thumb-ratio);
  }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__date-col {
    grid-area: 1 / 1;
    position: relative;
    z-index: 3;
    align-self: end;
    justify-self: start;
    width: var(--em-agenda-mobile-date-w, 88px);
    margin: 0 0 -1.75rem .75rem;
    border-radius: calc(var(--em-radius) / 2);
    overflow: hidden;
    box-shadow: 0 4px 12px rgba(0, 0, 0, .25);
  }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__body { grid-area: 2 / 1; padding-top: 2.25rem; }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__type-top,
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__campus-below { display: none; }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__ov-type {
    display: inline-flex;
    right: 0;
    bottom: .75rem;
  }
  [data-agenda-mobile="banner"] .em-agenda-item--thumb .em-agenda-item__ov-place {
    display: flex;
    top: .5rem;
    right: .5rem;
  }

  /* ===== SANS IMAGE — `agenda_mobile_noimg` (2.5.435) =====
     `side` n'a AUCUNE règle : c'est le rendu mobile de base (date à gauche, texte à droite), donc le
     modèle « Pas d'image » d'avant ne bouge pas d'un pixel. Les deux autres ne font que RÉARRANGER la
     colonne de date — le type, la date et le campus y sont déjà, dans cet ordre du DOM. */

  /* Bandeau : la colonne devient une bande horizontale en tête de carte. */
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb),
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item {
    display: flex;
    flex-direction: column;
  }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-col,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-col {
    flex-direction: row;
    align-items: center;
    /* Un type long (« Journée portes ouvertes ») passe a la ligne ENTIER plutot que de s'empiler
       sur trois lignes dans une pastille. */
    flex-wrap: wrap;
    gap: .25rem .5rem;
    width: auto;
    padding-right: .75rem;
    background: var(--em-color-primary);
    color: #fff;
  }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: .375rem;
    padding: .5rem .75rem;
  }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-block,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-block { flex: 0 0 auto; flex-direction: row; align-items: baseline; gap: .25rem; }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-sep,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-sep { padding: 0 .125rem; transform: rotate(-90deg); }
  /* Le chevron de plage mesure 30 x 26 : a l'horizontale il ecarterait les deux dates d'autant. */
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-date-arrow,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-date-arrow { width: 14px; height: 12px; }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__campus-below,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__campus-below {
    order: 1;
    padding: 0;
    border-top: 0;
    justify-content: flex-start;
    white-space: nowrap;
  }
  [data-agenda-noimg="strip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__type-top,
  [data-agenda-noimg="strip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__type-top {
    order: 2;
    margin-left: auto;
    white-space: nowrap;
    padding: .125rem .5rem;
    border-bottom: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, .16);
  }

  /* Pastille : plus de colonne pleine, la date devient une étiquette en tête du texte ; un filet de la
     couleur principale garde le repère que portait le pavé. */
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb),
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item {
    display: block;
    border-left: 4px solid var(--em-color-primary);
  }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-col,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-col {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: .375rem .5rem;
    width: auto;
    padding: .75rem .75rem 0;
  }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: center;
    gap: .25rem;
    padding: .25rem .5rem;
    border-radius: calc(var(--em-radius) / 2);
  }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__day,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__day { font-size: 1rem; }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-block,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-block { flex: 0 0 auto; flex-direction: row; align-items: baseline; gap: .25rem; }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__date-sep,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__date-sep { padding: 0 .125rem; transform: rotate(-90deg); }
  /* Le chevron de plage mesure 30 x 26 : a l'horizontale il ecarterait les deux dates d'autant. */
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-date-arrow,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-date-arrow { width: 14px; height: 12px; }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__type-top,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__type-top,
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__campus-below,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__campus-below {
    padding: 0;
    border: 0;
    background: transparent;
    justify-content: flex-start;
    font-weight: 700;
  }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__type-top,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__type-top { order: 1; color: var(--em-agenda-type-color, var(--em-color-secondary)); }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__campus-below,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__campus-below { order: 2; color: var(--em-color-muted); }
  [data-agenda-noimg="chip"] .em-agenda-item:not(.em-agenda-item--thumb) .em-agenda-item__body,
  [data-agenda-noimg="chip"][data-agenda-mobile="hidden"] .em-agenda-item .em-agenda-item__body { padding-top: .5rem; }
}

/* --------------------------------- DESKTOP --------------------------------- */
@media (min-width: 641px) {

  /* Vignette à DROITE du texte, détachée du bord. Par défaut elle est à gauche, collée à la colonne
     de date et étirée sur toute la hauteur ; ici elle est centrée verticalement et respecte le
     ratio, ce qui exige `align-self: center` — avec `stretch`, `aspect-ratio` est ignoré. */
  [data-agenda-desktop="thumb-right"] .em-agenda-item__thumb {
    order: 2;
    align-self: center;
    margin: 1rem 1rem 1rem 0;
    border-radius: var(--em-radius);
    aspect-ratio: var(--em-thumb-ratio);
  }
}

/* ===== Deux colonnes au-dessus de 1024 px — `agenda_desktop_columns` (2.5.435) =====
   La liste ET chaque groupe de mois deviennent la grille : sans regroupement les cartes sont enfants
   directs de la liste, avec regroupement elles sont enfants du groupe, et le titre du mois doit
   couvrir les deux colonnes. Tout enfant qui n'est pas une carte (message vide, chargement, carte
   d'un gabarit personnalise) couvre aussi la ligne : un message coupe en demi-largeur se lit comme
   une erreur de mise en page. Sous 1024 px, deux cartes avec vignette de 250 px ne tiennent pas. */
@media (min-width: 1024px) {
  [data-agenda-cols="2"] .em-agenda-list,
  [data-agenda-cols="2"] .em-agenda-month-group {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
  }
  [data-agenda-cols="2"] .em-agenda-list > :not(.em-agenda-item-wrap),
  [data-agenda-cols="2"] .em-agenda-month-group > :not(.em-agenda-item-wrap) { grid-column: 1 / -1; }
  /* Deux cartes d'une meme rangee ont la meme hauteur : la carte remplit sa cellule. */
  [data-agenda-cols="2"] .em-agenda-item-wrap > .em-agenda-item { flex: 1; }
  /* Une demi-largeur ne porte pas une vignette de 250 px ET un texte lisible. */
  [data-agenda-cols="2"] .em-agenda-item__thumb { max-width: 40%; }
}

/* ==========================================================================================
   BLOC « LISTE VILLE + DATE » (2.5.233)
   ==========================================================================================
   Aucune couleur en dur : tout passe par les jetons du design system, et par les variables
   d'instance émises par `em_block_appearance_style()` (préfixe `cl`) qui retombent dessus quand le
   réglage est vide. C'est ce qui rend le bloc utilisable tel quel sur une charte différente.
   ========================================================================================== */
.em-city-list {
  background: var(--em-cl-bg, var(--em-color-surface));
  color: var(--em-cl-text, var(--em-color-text));
  border-radius: var(--em-cl-radius, var(--em-radius));
  border: 1px solid var(--em-cl-border, transparent);
  box-shadow: var(--em-cl-shadow, none);
  /* Repli `0` : ce bloc n avait aucune marge interieure de racine (2.5.313). */
  padding: var(--em-cl-pad, 0);
}
.em-cl__eyebrow {
  margin: 0 0 .25rem;
  color: var(--em-cl-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.em-cl__title {
  margin: 0 0 1rem;
  color: var(--em-cl-title, var(--em-color-primary));
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.3;
}
.em-cl__list { margin: 0; padding: 0; list-style: none; }

/* La ligne est une GRILLE `1fr auto` et non un flex avec `justify-content: space-between` : la
   flèche reste alignée d'une ligne à l'autre même quand un nom de ville est deux fois plus long.
   Avec `space-between`, chaque flèche se caserait à une abscisse différente. */
.em-cl__item {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: .75rem;
  padding: .5rem 0;
}
.em-cl__item + .em-cl__item { border-top: 1px solid var(--em-cl-border, var(--em-color-border)); }

.em-cl__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .25rem 1rem;
  font-size: .875rem;
  /* `min-width: 0` : sans lui, un libellé long empêche la grille de se comprimer et pousse la flèche
     hors du bloc. Même cause que les trois symptômes mobiles de la 2.5.219. */
  min-width: 0;
}
.em-cl__place strong { color: var(--em-cl-title, var(--em-color-primary)); font-weight: 700; }
/* Couleur des DEUX icônes de la ligne, réglable indépendamment (2.5.271).
   Le repli en CHAÎNE garde exactement le rendu antérieur quand le réglage est vide : icône -> accent du
   bloc -> couleur secondaire. Une variable propre était nécessaire — l'accent sert aussi au contour de
   focus du bouton, donc le régler pour les icônes repeignait autre chose. */
.em-cl__place .em-icon,
.em-cl__date .em-icon { color: var(--em-cl-icon, var(--em-cl-accent, var(--em-color-secondary))); }
/* Type d'evenement : pastille discrete, PAS un badge.
   Elle reprend `--em-color-info-bg` / `--em-color-info-border` — les jetons du tag d'ecole, deja
   utilises pour cet usage exact ailleurs. Detourner un jeton de badge (`warning`, celui du « A la
   une ») repeindrait ce bloc en ajustant un badge : la faute corrigee en 2.5.240 sur les quatre
   blocs sombres. */
.em-cl__type {
  align-self: flex-start;
  padding: .125rem .5rem;
  border: 1px solid var(--em-color-info-border);
  border-radius: calc(var(--em-radius) / 2);
  background: var(--em-color-info-bg);
  color: var(--em-color-primary);
  font-size: .75rem;
  font-weight: 600;
  line-height: 1.5;
}
.em-cl__date { color: var(--em-cl-text, var(--em-color-text)); }

/* Le bouton flèche : carré, contour, il ne remplit qu'au survol. C'est un `<a>` — donc curseur,
   focus et clavier viennent du navigateur ; on ajoute seulement l'affordance visuelle, la leçon de
   la 2.5.227. */
.em-cl__go {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  color: var(--em-cl-title, var(--em-color-primary));
  border: 1px solid currentColor;
  border-radius: calc(var(--em-radius) / 3);
  text-decoration: none;
  transition: background var(--em-transition), color var(--em-transition);
}
/* L'icone est BORNEE a la boite du bouton (2.5.270).
   `sanitize_btn_icon_svg()` accepte une taille jusqu'a 48 px, et ce bouton mesure 30 px : un SVG
   personnalise plus grand deborderait de son cadre. Les attributs `width`/`height` du SVG etant
   surchargeables par le CSS, une regle suffit — et elle ne touche que ce bouton-la. */
.em-cl__go .em-icon { width: 14px; height: 14px; }
/* SURVOL REGLABLE depuis la 2.5.448 (`cl_go_hover`). « Le carre se remplit » reste le DEFAUT et vaut aussi
   quand l'attribut manque (gabarit personnalise, page en cache) : `:not([data-go-hover="arrow"])`.
   ⚠ `:where()` garde la SPECIFICITE d'avant, (0,2,0) : un CSS de site qui surchargeait `.em-cl__go:hover`
   continue de gagner. Sans lui, la regle monterait a (0,4,0) et repeindrait en silence ces sites-la. */
:where(.em-city-list:not([data-go-hover="arrow"])) .em-cl__go:hover {
  background: var(--em-cl-title, var(--em-color-primary));
  color: var(--em-color-surface);
}
/* « La fleche sort en diagonale et revient » : elle quitte le carre par le coin haut droit, reapparait par le
   coin bas gauche et revient au centre. `overflow: hidden` SEULEMENT dans ce mode — c'est lui qui rend la
   sortie invisible — pour ne rien changer au bouton des sites restes sur « remplissage ». Clavier compris
   (`:focus-visible`), et rien pour qui a demande moins d'animations. */
:where(.em-city-list[data-go-hover="arrow"], .em-city-list[data-go-hover="both"]) .em-cl__go { overflow: hidden; }
:where(.em-city-list[data-go-hover="arrow"], .em-city-list[data-go-hover="both"]) .em-cl__go:hover .em-icon,
:where(.em-city-list[data-go-hover="arrow"], .em-city-list[data-go-hover="both"]) .em-cl__go:focus-visible .em-icon {
  animation: em-cl-go-loop .35s ease-in-out;
}
@keyframes em-cl-go-loop {
  0%   { transform: translate(0, 0); }
  45%  { transform: translate(180%, -180%); }
  46%  { transform: translate(-180%, 180%); }
  100% { transform: translate(0, 0); }
}
/* Même trajet pour l'icône des boutons CTA (« Survol de l'icône » = « Sortie en diagonale et retour »,
   2.5.449). La règle qui la déclenche est émise par `em_appearance_css()` dans ce seul mode. Ici le bouton
   ne masque PAS ce qui déborde : c'est l'opacité qui fait disparaître la flèche, sa boîte reste intacte. */
@keyframes em-btn-icon-loop {
  0%   { transform: translate(0, 0);         opacity: 1; }
  40%  { transform: translate(.5em, -.5em);  opacity: 0; }
  41%  { transform: translate(-.5em, .5em);  opacity: 0; }
  100% { transform: translate(0, 0);         opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  :where(.em-city-list[data-go-hover="arrow"], .em-city-list[data-go-hover="both"]) .em-cl__go:hover .em-icon,
  :where(.em-city-list[data-go-hover="arrow"], .em-city-list[data-go-hover="both"]) .em-cl__go:focus-visible .em-icon {
    animation: none;
  }
}
.em-cl__go:focus-visible {
  outline: 2px solid var(--em-cl-accent, var(--em-color-secondary));
  outline-offset: 2px;
}

@media (max-width: 480px) {
  /*
   * LIGNE UNIQUE sur mobile : lieu, date et flèche côte à côte (2.5.271, à la demande).
   *
   * C'est l'inverse du choix précédent — la flèche passait SOUS le texte, au motif que sur 320 px une
   * ville, une date et un bouton de 30 px ne tiennent pas sans césure. Le motif était juste : tenir sur
   * une ligne a donc un prix, et il est payé ici explicitement.
   *
   * 1. `nowrap` sur la rangée de méta, sinon `flex-wrap: wrap` la casse en deux dès que ça serre.
   * 2. Le LIEU se tronque avec des points de suspension — c'est le seul élément de longueur imprévisible,
   *    et le seul moyen de garantir une ligne unique sans césure au milieu d'un mot. La date a une
   *    largeur bornée par son format, la flèche est fixe.
   * 3. `min-width: 0` sur le lieu : sans lui, `text-overflow` ne s'applique jamais — un élément flex a
   *    une largeur minimale automatique égale à son contenu, donc il refuse de rétrécir et déborde au
   *    lieu de tronquer. Même piège que les trois symptômes mobiles de la 2.5.219.
   * 4. La pastille de TYPE est masquée : ce serait un quatrième élément, et quatre sur 320 px rendent
   *    les trois autres illisibles. L'information reste sur tablette et ordinateur.
   *
   * ⚠ Un format de date LONG (« 16 septembre 2026 ») ne laisse presque rien au nom de ville. Le réglage
   * « Format de date » du bloc existe pour ça — `medium` ou `day-month` sur mobile.
   */
  .em-cl__item { grid-template-columns: 1fr auto; }
  .em-cl__meta {
    flex-wrap: nowrap;
    gap: .5rem;
    font-size: .875rem;
  }
  .em-cl__place {
    min-width: 0;
    overflow: hidden;
  }
  .em-cl__place strong {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .em-cl__date { white-space: nowrap; }
  .em-cl__type { display: none; }
}

/* ==========================================================================================
   BLOC « TICKER DÉFILANT » (2.5.247)
   ==========================================================================================
   Une seule ligne, un défilement continu. Le mouvement est en CSS — `@keyframes` sur la piste — et
   non en JavaScript : le navigateur l'anime alors sur le compositeur, et surtout la MISE EN PAUSE au
   survol et au focus devient une seule déclaration (`animation-play-state`) au lieu d'un minuteur à
   arrêter et redémarrer. Le JS ne calcule qu'une durée et duplique le contenu.

   TROIS mécanismes de pause, aucun décoratif :
     · survol, pour lire un libellé qui passe ;
     · FOCUS clavier — `:focus-within`, sans quoi une personne qui tabule ne peut rien arrêter ;
     · bouton explicite, requis par le critère WCAG 2.2.2 pour un mouvement automatique de plus de
       cinq secondes. Ni le survol ni le focus ne sont disponibles au doigt sur un écran tactile.
   ========================================================================================== */
.em-ticker {
  display: flex;
  align-items: center;
  gap: .875rem;
  box-sizing: border-box;
  padding: var(--em-tk-pad, .5rem .875rem);
  background: var(--em-tk-bg, var(--em-color-surface));
  color: var(--em-tk-text, var(--em-color-text));
  border-top: 1px solid var(--em-tk-border, var(--em-color-border));
  border-bottom: 1px solid var(--em-tk-border, var(--em-color-border));
  border-radius: var(--em-tk-radius, 0);
  box-shadow: var(--em-tk-shadow, none);
  /* Hauteur et corps réglables (2.5.298). `auto` et `inherit` en repli : un champ vidé rend la main à la
     feuille de style, il ne produit pas une bande de zéro pixel. */
  min-height: var(--em-tk-h, auto);
  font-size: var(--em-tk-fs, inherit);
  /* La piste est plus large que la bande par construction : sans ce masquage, elle déborderait la page
     au lieu de défiler derrière une fenêtre. */
  overflow: hidden;
}

/* ---------- Bords de la bande (2.5.298) ----------
   La règle de base pose HAUT + BAS, ce qui correspond au réglage `y` — le défaut, et le rendu d'origine.
   Les quatre autres valeurs ne font que s'en écarter, donc un site qui ne règle rien n'est pas repeint. */
.em-ticker[data-border-sides="all"] {
  border-left: 1px solid var(--em-tk-border, var(--em-color-border));
  border-right: 1px solid var(--em-tk-border, var(--em-color-border));
}
.em-ticker[data-border-sides="top"]    { border-bottom: 0; }
.em-ticker[data-border-sides="bottom"] { border-top: 0; }
.em-ticker[data-border-sides="none"]   { border-top: 0; border-bottom: 0; }

/* ---------- Bande COLLANTE (2.5.298) ----------
   `sticky` et non `fixed` : un élément fixe sort du flux et se superpose au contenu, alors qu'une bande
   collante garde sa place tant qu'elle est visible puis s'arrête au bord. C'est aussi ce qui la fait
   cohabiter avec le conteneur Gutenberg sans calcul de hauteur.

   ⚠ LIMITE INHÉRENTE : `position: sticky` est annulé dès qu'un ANCÊTRE porte `overflow: hidden`, ce que
   beaucoup de thèmes appliquent à un conteneur de contenu. Le plugin ne peut pas le contourner — un
   `position: fixed` « de secours » remplacerait un réglage sans effet par un chevauchement, ce qui est
   pire. L'indice du réglage le dit à l'utilisateur.

   Le `z-index` reste MODESTE (20) : au-dessus du contenu, mais sous la barre de filtres collante de
   l'agenda (qui monte plus haut) et sous toute modale de thème. Une bande décorative qui passerait
   devant une fenêtre de dialogue serait un défaut d'accessibilité, pas une fonctionnalité. */
.em-ticker[data-sticky="top"],
.em-ticker[data-sticky="bottom"] {
  position: sticky;
  z-index: 20;
}
.em-ticker[data-sticky="top"]    { top: var(--em-tk-sticky-top, 0px); }
.em-ticker[data-sticky="bottom"] { bottom: 0; }

/* Barre d'administration de WordPress : elle est `position: fixed` en haut, donc une bande collée à
   `top: 0` passerait DESSOUS. WordPress pose la classe `admin-bar` sur `<body>` quand elle est présente,
   ce qui permet de décaler sans aucun réglage — et sans décaler les visiteurs, qui ne l'ont pas. */
body.admin-bar .em-ticker[data-sticky="top"] { top: calc(32px + var(--em-tk-sticky-top, 0px)); }
@media screen and (max-width: 782px) {
  /* La barre d'admin passe à 46 px sous ce seuil, et devient elle-même non fixe : le décalage ne vaut
     que tant qu'elle est fixe, d'où le seuil aligné sur celui de WordPress. */
  body.admin-bar .em-ticker[data-sticky="top"] { top: calc(46px + var(--em-tk-sticky-top, 0px)); }
}

@media (max-width: 640px) {
  /* Sous 640 px la bande NE COLLE PAS par défaut : elle mangerait définitivement de la hauteur sur un
     écran où chaque ligne compte. `data-sticky-mobile="1"` rétablit le comportement pour qui le veut. */
  .em-ticker[data-sticky][data-sticky-mobile="0"] { position: static; }
}

/* Libellé de section : FIXE, hors de la piste. Dans la piste il disparaîtrait puis reviendrait, alors
   que sa fonction est de rester lisible en permanence. */
.em-tk__label {
  flex: 0 0 auto;
  margin: 0;
  /* Couleur PROPRE depuis la 2.5.298, repli en CHAINE sur l accent : le libelle et la date
     partageaient l accent, donc les distinguer etait impossible. */
  color: var(--em-tk-eyebrow, var(--em-tk-accent, var(--em-color-secondary)));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
}

.em-tk__viewport {
  flex: 1 1 auto;
  /* `min-width: 0` : élément flex, donc `min-width: auto` par défaut — il refuserait de descendre sous
     la largeur de la piste, qui est justement énorme. La bande deviendrait plus large que l'écran. */
  min-width: 0;
  overflow: hidden;
}

.em-tk__track {
  display: flex;
  align-items: center;
  /* `max-content` : la piste prend la largeur de son contenu, sans jamais se comprimer ni passer à la
     ligne. C'est ce qui rend le calcul de durée du JS exact. */
  width: max-content;
  animation: em-tk-scroll var(--em-tk-dur, 40s) linear infinite;
}
.em-tk__half { display: flex; align-items: center; }

/* `-50%` de la piste, et la piste vaut exactement DEUX moitiés identiques : au terme du cycle, la
   seconde moitié occupe la place qu'occupait la première, donc la reprise est invisible. C'est tout le
   principe, et c'est pourquoi le JS duplique le contenu au lieu de le faire tourner. */
@keyframes em-tk-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
/* Sens inverse : `animation-direction` plutôt qu'un second jeu d'images-clés — une seule définition à
   maintenir, et la durée reste celle calculée. */
.em-ticker[data-direction="right"] .em-tk__track { animation-direction: reverse; }

/* `em-tk--paused` a ete retire avec le bouton (2.5.248) : plus rien ne posait cette classe, et une
   regle qui ne peut plus etre declenchee est du CSS mort — meme reproche que les reglages fantomes. */
.em-ticker:hover .em-tk__track,
.em-ticker:focus-within .em-tk__track { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  /* Préférence SYSTÈME : elle est exprimée par des personnes que le mouvement rend malades, et un
     défilement continu est le pire motif possible pour elles. L'animation est SUPPRIMÉE, pas ralentie,
     et la bande devient une liste qu'on fait défiler à la main — le contenu reste entièrement
     atteignable. */
  .em-tk__track { animation: none; }
  .em-tk__viewport { overflow-x: auto; }
}

.em-tk__item {
  display: inline-flex;
  align-items: baseline;
  gap: .75rem;
  padding: 0 .5rem;
  /* Aucun retour à la ligne : une entrée coupée en deux dans une bande d'une ligne serait illisible. */
  white-space: nowrap;
  color: inherit;
  text-decoration: none;
}
/* Survol et focus ciblent `a.em-tk__item` et JAMAIS la classe seule : sans URL l'entrée est un
   `<span>`, et lui donner un état de survol promettrait un clic qui n'existe pas — la faute corrigée
   sur la carte de la bande de dates en 2.5.227. */
a.em-tk__item:hover .em-tk__name { text-decoration: underline; }
a.em-tk__item:focus-visible {
  outline: 2px solid var(--em-tk-accent, var(--em-color-secondary));
  outline-offset: -2px;
}
.em-tk__date {
  color: var(--em-tk-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  /* Chiffres à largeur fixe : sans eux, « 05 Nov. » et « 22 Oct. » n'ont pas la même largeur et la
     bande se dilate imperceptiblement pendant le défilement. */
  font-variant-numeric: tabular-nums;
}
/* `--em-tk-title` LU depuis la 2.5.311 : le titre de l'evenement est ce que designe le reglage de titre de
   ce bloc, et il n'etait lu nulle part — donc le reglage ne faisait rien. `inherit` en repli : la bande
   porte deja sa couleur de texte, et forcer une valeur ici la contredirait. */
.em-tk__name { font-size: .875rem; font-weight: 600; color: var(--em-tk-title, inherit); }
/* `--em-tk-city-op` n est emis QUE si une couleur est fournie : sans lui, la couleur choisie resterait
   delavee par l opacite de .7 et l utilisateur verrait une teinte qu il n a pas demandee. */
.em-tk__city { font-size: .75rem; color: var(--em-tk-city, inherit); opacity: var(--em-tk-city-op, .7); }

/* Séparateur en contenu généré : trois formes pour un seul élément, donc aucun balisage conditionnel
   côté JS. */
.em-tk__sep { flex: 0 0 auto; display: flex; align-items: center; }
.em-ticker[data-separator="dot"] .em-tk__sep::before {
  content: "·";
  color: var(--em-tk-sep, inherit);
  opacity: var(--em-tk-sep-op, .45);
}
.em-ticker[data-separator="bar"] .em-tk__sep::before {
  content: "";
  width: 1px;
  height: .9em;
  background: currentColor;
  opacity: .25;
}
.em-ticker[data-separator="none"] .em-tk__sep { display: none; }


@media (max-width: 640px) {
  /* Le libellé passe à la ligne plutôt que de comprimer la bande : sur 320 px, un libellé et une piste
     sur la même ligne ne laissent plus rien à lire. */
  .em-ticker { flex-wrap: wrap; gap: .375rem .875rem; }
  .em-tk__viewport { flex: 1 1 100%; }
}
/* ==========================================================================================
   CONTENEUR DE BLOC GUTENBERG (2.5.244)
   ==========================================================================================
   Emis par `em_block_frame_open()` autour de la racine `.em-*` de chaque bloc. Il porte ce que
   Gutenberg calcule — alignement, classe personnalisee, ancre, marges, espacements, hauteur
   minimale — sans jamais entrer en conflit avec les `id` et `data-*` du plugin.
   ========================================================================================== */
.em-block-frame {
  /* `grid` et non `block` : l'enfant unique remplit la largeur ET la hauteur du conteneur, donc une
     hauteur minimale reglee dans Gutenberg est reellement occupee au lieu d'etre un vide sous le
     bloc. Une seule declaration pour les deux axes.
     Les marges automatiques d'un enfant continuent de le centrer : une marge `auto` prend le pas sur
     l'etirement, c'est prevu par l'alignement de boites — le centrage de `.em-agenda-container` n'est
     donc pas perdu. */
  display: grid;
  /* `minmax(0, 1fr)` et surtout PAS la colonne implicite `auto` : le maximum d'une piste `auto` est
     le `max-content` de son contenu, donc la grille DEBORDE son conteneur des qu'un enfant est plus
     large que la place disponible. La mise en page en bloc ne faisait jamais cela — un enfant en
     `width: auto` prend la largeur du parent et c'est son contenu qui deborde, sans changer la boite.
     Constate sur l'agenda (2.5.245) : `.em-filters` est un `flex` dont le `max-content` est la somme
     des controles DEPLIES (recherche + 3 listes + « par page »), soit ~1400 px. Le bloc mesurait donc
     cette largeur au lieu de celle de la colonne du theme, et la barre de filtres collee recopiait ce
     chiffre dans son `position: fixed` — elle depassait a droite jusqu'au bord de l'ecran.
     `min-width: 0` ci-dessous ne peut pas y suppleer : il abaisse le minimum de l'ELEMENT, pas le
     maximum de la PISTE. Les deux sont necessaires. */
  grid-template-columns: minmax(0, 1fr);
}
.em-block-frame > * {
  /* Un element de grille vaut `min-width: auto`, donc il refuse de descendre sous la largeur
     minimale de son CONTENU. Quatre blocs contiennent une piste a defilement horizontal
     (`overflow-x: auto`) : sans ce `0`, la piste imposerait sa largeur totale au conteneur et
     deborderait la page au lieu de defiler. C'est le piege classique de grid, et il ne se voit
     qu'avec beaucoup d'evenements — donc jamais sur un jeu de test court. */
  min-width: 0;
}
/* ==========================================================================================
   BLOC « ACCORDÉON » (2.5.233)
   ==========================================================================================
   Bâti sur `<details>`/`<summary>` natif : le clavier, l'état annoncé aux lecteurs d'écran, la
   recherche dans la page qui déplie la section trouvée et l'impression complète sont acquis. Le CSS
   ne fait que masquer le marqueur par défaut et dessiner le signe +/×.
   ========================================================================================== */
.em-accordion {
  /* Necessaire pour animer `block-size: 0` -> `auto` ci-dessous. Pose sur la RACINE DU BLOC et non
     sur `:root` : la propriete est heritee, et l'activer globalement rendrait animables toutes les
     transitions vers `auto` de la page — y compris celles du theme, qui ne l'ont pas demande. */
  interpolate-size: allow-keywords;
  background: var(--em-ac-bg, var(--em-color-surface));
  color: var(--em-ac-text, var(--em-color-text));
  border: 1px solid var(--em-ac-border, var(--em-color-border));
  border-radius: var(--em-ac-radius, var(--em-radius));
  box-shadow: var(--em-ac-shadow, none);
  overflow: hidden;
}
.em-ac__title {
  margin: 0;
  padding: var(--em-ac-pad, .875rem 1.125rem) ;
  color: var(--em-ac-title, var(--em-color-primary));
  font-size: 1.125rem;
  font-weight: 700;
}
.em-ac__item + .em-ac__item { border-top: 1px solid var(--em-ac-border, var(--em-color-border)); }
.em-accordion[data-dividers="0"] .em-ac__item + .em-ac__item { border-top: none; }

/* `list-style: none` ET `::-webkit-details-marker` : le triangle par défaut se retire par deux
   mécanismes différents selon le moteur, et n'en traiter qu'un laisse un chevron parasite sur l'autre. */
.em-ac__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: baseline;
  gap: 1rem;
  padding: var(--em-ac-pad, .75rem 1rem);
  cursor: pointer;
  list-style: none;
}
.em-ac__row::-webkit-details-marker { display: none; }
.em-ac__row:hover { background: rgba(var(--em-color-primary-rgb), .04); }
/* Le focus se pose sur `<summary>`, pas sur un enfant : c'est lui que le navigateur rend focalisable. */
.em-ac__item > .em-ac__row:focus-visible {
  outline: 2px solid var(--em-ac-accent, var(--em-color-primary));
  outline-offset: -2px;
}

/* Chiffres tabulaires : sans eux, « 05.Nov » et « 22.Oct » n'ont pas la même largeur et la colonne
   des titres tremble d'une ligne à l'autre. */
.em-ac__date {
  flex-shrink: 0;
  color: var(--em-ac-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}
.em-ac__label {
  color: var(--em-ac-title,var(--em-color-primary));
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.35;
  min-width: 0;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Signe +/× dessiné en CSS et non en SVG : il doit basculer avec l'état `[open]`, ce qu'un contenu
   généré fait sans JavaScript. `aria-hidden` côté HTML — l'état est déjà annoncé par `<details>`. */
.em-ac__sign {
  position: relative;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  opacity: .55;
}
.em-ac__sign::before,
.em-ac__sign::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: 1.5px;
  background: currentColor;
  transition: transform var(--em-transition);
}
.em-ac__sign::after { transform: rotate(90deg); }
.em-ac__item[open] .em-ac__sign::before { transform: rotate(45deg); }
.em-ac__item[open] .em-ac__sign::after  { transform: rotate(-45deg); }

/* ------------------------------------------------------------------------------------------
   Ouverture PROGRESSIVE — en CSS seul, sans le moindre gestionnaire de clic.

   `<details>` ne s'animait pas : le contenu apparaissait d'un bloc. Le faire en JavaScript
   imposerait d'intercepter le clic pour retarder la fermeture (l'evenement `toggle` arrive APRES,
   quand le contenu n'est deja plus rendu — il n'y a donc plus rien a animer en sortie). Or ce bloc
   n'a AUCUN gestionnaire de clic, et un test l'interdit : c'est ce qui garantit que le clavier, la
   recherche du navigateur qui deplie une section trouvee et l'impression continuent de fonctionner
   sans code. `::details-content` fait le meme travail sans rien intercepter.

   Prise en charge : Chrome/Edge 131+, Safari 18.4+, Firefox 141+. Ailleurs, le depliage reste
   instantane — exactement le comportement d'avant. Une amelioration progressive, jamais une
   dependance : rien ne cesse de fonctionner faute d'animation.

   `transition-behavior: allow-discrete` est indispensable pour `content-visibility`, propriete
   discrete : sans lui le contenu redeviendrait visible d'un coup au premier pixel de la transition,
   et l'animation de hauteur se jouerait sur un contenu deja affiche.
   ------------------------------------------------------------------------------------------ */
.em-ac__item::details-content {
  block-size: 0;
  /* `clip` et non `hidden`, avec une marge : il faut masquer le contenu qui depasse pendant le
     depliage, mais `overflow: hidden` couperait aussi le contour de focus du bouton une fois le
     panneau ouvert — un contour rogne est un contour qu'on ne voit pas, et c'est le seul repere du
     visiteur au clavier. Les 4 px laissent passer un contour de 2 px et son decalage. */
  overflow: clip;
  overflow-clip-margin: 4px;
  transition: block-size var(--em-ac-anim, 260ms) ease, content-visibility var(--em-ac-anim, 260ms);
  transition-behavior: allow-discrete;
}
.em-ac__item[open]::details-content { block-size: auto; }

@media (prefers-reduced-motion: reduce) {
  /* Preference SYSTEME : ce n'est pas une question de gout, elle est exprimee par des personnes que
     le mouvement rend malades. Le depliage redevient instantane, le contenu reste accessible. */
  .em-ac__item::details-content { transition: none; }
}

/* Signe « chevron » — variante du +/× par defaut.
   Le `::after` (la barre verticale du +) est retire, et le `::before` devient un angle a deux
   bordures qui pivote. Redessiner un chevron en SVG serait revenu a le poser en HTML, donc a le
   rendre dependant du JS pour basculer avec `[open]` : ici, l'etat suffit. */
.em-accordion[data-sign="chevron"] .em-ac__sign::after { display: none; }
.em-accordion[data-sign="chevron"] .em-ac__sign::before {
  inset: 2px auto auto 1px;
  width: 8px;
  height: 8px;
  background: none;
  border: 2px solid currentColor;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.em-accordion[data-sign="chevron"] .em-ac__item[open] .em-ac__sign::before { transform: rotate(-135deg); }

/* Date SOUS le titre — deuxieme modele de ligne.
   Placement explicite plutot que des zones nommees : si le champ « date » est masque, aucune
   deuxieme rangee n'est reclamee, donc aucun interligne vide ne subsiste. Une grille en
   `grid-template-areas` aurait garde la rangee et son `row-gap`. */
.em-accordion[data-date-pos="under"] .em-ac__row {
  grid-template-columns: 1fr auto;
  align-items: center;
  row-gap: .25rem;
}
.em-accordion[data-date-pos="under"] .em-ac__label { grid-column: 1; grid-row: 1; }
.em-accordion[data-date-pos="under"] .em-ac__date  { grid-column: 1; grid-row: 2; }
.em-accordion[data-date-pos="under"] .em-ac__sign  { grid-column: 2; grid-row: 1 / -1; justify-self: end; }
/* Le panneau n'est plus decale : le retrait alignait le texte sur la colonne des titres, colonne qui
   n'existe plus dans ce modele. Le conserver ferait un retrait sans rien a quoi s'aligner. */
.em-accordion[data-date-pos="under"] .em-ac__panel { padding-left: 1.125rem; }
.em-ac__panel {
  padding: 0 1.5rem 1rem;
  /* Aligné sur la colonne des titres : le retrait fait lire le panneau comme la suite de sa ligne. */
  padding-left: calc(1.5rem + 3.5rem);
}
.em-ac__desc { margin: 0 0 .375rem; font-size: .875rem; line-height: 1.5; }
.em-ac__meta { margin: 0; font-size: .75rem; color: var(--em-color-muted); }
.em-ac__cta  { margin-top: .75rem; padding: .375rem .875rem; font-size: .75rem; border-width: 1.5px; }

@media (max-width: 480px) {
  /* Le retrait du panneau disparaît : 3,6 rem de marge sur 320 px ne laissent plus de place au texte. */
  .em-ac__panel { padding-left: 1.125rem; }
  .em-ac__row   { gap: .625rem; }
}

/* ==========================================================================================
   QUATRE BLOCS AJOUTÉS EN 2.5.239
   ==========================================================================================
   Aucune couleur en dur : tout passe par les jetons du design system et par les variables
   d'instance émises par `em_block_appearance_style()`, qui retombent dessus quand le réglage est
   vide. Un test le vérifie section par section.

   Les trois blocs à fond sombre (carrousel, tuiles, onglets) définissent leur fond via
   `--em-XX-bg` avec la primaire en repli — et NON une couleur figée : le client change de charte
   dans « Affichage › Thème », pas dans une feuille de style.
   ========================================================================================== */

/* ------------------------------ Piste horizontale, partagée ------------------------------ */
/* Barre de défilement masquée : la navigation se fait aux flèches ou au doigt. `scroll-snap` aligne
   la carte sur le bord, sinon un défilement au doigt laisse une carte coupée en deux. */
.em-car__track,
.em-dt__track {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.em-car__track::-webkit-scrollbar,
.em-dt__track::-webkit-scrollbar { display: none; }
.em-car__viewport,
.em-dt__viewport { overflow: hidden; }

/* ================================= CARROUSEL HORIZONTAL ================================= */
.em-carousel {
  background: var(--em-car-bg, var(--em-color-primary));
  color: var(--em-car-text, var(--em-on-dark));
  border-radius: var(--em-car-radius, var(--em-radius));
  padding: var(--em-car-pad, 1.5rem 1.5rem);
  box-shadow: var(--em-car-shadow, none);
}
.em-car__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.125rem;
}
.em-car__title {
  margin: 0;
  color: var(--em-car-title, var(--em-on-dark));
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.25;
}
.em-car__actions { display: flex; align-items: center; gap: .75rem; flex-shrink: 0; }
.em-car__all {
  color: var(--em-car-accent, var(--em-color-secondary));
  font-size: .875rem;
  font-weight: 600;
  text-decoration: none;
}
.em-car__all:hover { text-decoration: underline; }
.em-car__nav { display: flex; gap: .375rem; }
/* Chevrons SVG des commandes « precedent / suivant » — les CINQ boutons concernes (2.5.295).
   Ils portaient un chevron TEXTE (`&#8249;` / `&#8250;`), dont le trace depend de la police du
   visiteur : sur la meme page, deux carrousels ne montraient pas la meme fleche. Le dessin vient
   desormais de `em_chevron_svg()`, une source unique.

   Cette regle existe pour UNE raison : la plupart des themes portent un `img, svg { max-width: 100%;
   height: auto }`. Le `height: auto` recalculerait la hauteur depuis le ratio et, combine a un
   `flex-shrink` par defaut de 1, la fleche pourrait s'aplatir dans son bouton. `flex: 0 0 auto` et les
   dimensions explicites l'en empechent. `display: block` supprime en plus l'espace de ligne de base,
   qui decalait le glyphe de 1 a 2 px vers le haut dans un bouton circulaire. */
.em-car__arrow > svg,
.em-dt__arrow > svg,
.em-ds__arrow > svg,
.em-featured-zone__btn > svg,
.em-page-btn > svg {
  flex: 0 0 auto;
  display: block;
  width: auto;
  height: auto;
}

.em-car__arrow {
  /* `box-sizing` + `padding: 0` + `line-height: 1` : les TROIS sont necessaires pour un cercle.
     La regle globale `* { box-sizing: inherit }` est ancree sur `.em-agenda`, un selecteur ABSENT du
     markup — tous les blocs sont donc en `content-box`, et le padding par defaut du navigateur sur
     `<button>` (~1px 6px) s ajoutait a la largeur : 44x34 au lieu de 32x32, soit un ovale.
     `aspect-ratio` en dernier recours si une police heritee force encore la hauteur. */
  box-sizing: border-box;
  padding: 0;
  
  flex: 0 0 auto;
  aspect-ratio: 1;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  font-size: 1.125rem;
  line-height: 1;
  color: var(--em-car-text, var(--em-on-dark));
  background: var(--em-on-dark-hover);
  transition: background var(--em-transition);
}
.em-car__arrow:hover:not(:disabled) { background: var(--em-on-dark-line); }
.em-car__arrow:disabled { opacity: .35; cursor: default; }

.em-car__card {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  flex: 0 0 auto;
  /* Largeur en `calc` sur le nombre de cartes visibles : la piste ne connaît pas ce nombre, c'est
     le JS qui l'utilise pour son pas — les deux doivent s'accorder, d'où la même arithmétique. */
  /* Meme correctif que `.em-dt__item` (2.5.296) : le nombre etait code en dur, donc
     « Cartes visibles » ne changeait que le pas de defilement. Repli `4` = ancien nombre = defaut
     de `car_per_view`. */
  /* Même correctif que `.em-dt__item` (2.5.315) : sans `border-box`, le padding de 33,6 px s'ajoute à la
     largeur calculée. C'est le débordement le plus fort des trois pistes. */
  box-sizing: border-box;
  width: calc((100% - (var(--em-car-per, 4) - 1) * 12px) / var(--em-car-per, 4));
  min-width: 210px;
  padding: 1rem 1rem;
  background: var(--em-on-dark-surface);
  border-radius: calc(var(--em-radius) / 1.5);
  /* Bordure en ombre INTERNE (2.5.313) et non en `border` : ces cartes sont en `content-box`, donc un
     `border` de 1px retrecirait leur contenu de 2px sur tout site deja en production. Repli transparent,
     donc aucun changement visuel ni de mise en page tant que le reglage est vide. */
  box-shadow: inset 0 0 0 1px var(--em-car-border, transparent);
  scroll-snap-align: start;
  text-decoration: none;
  color: inherit;
  transition: background var(--em-transition), transform var(--em-transition);
}
/* Affordance sur l'ANCRE seulement : sans URL la carte est un `<div>`, et l'animer promettrait un
   clic inexistant (leçon de la bande de dates, 2.5.227). */
a.em-car__card:hover { background: var(--em-on-dark-hover); transform: translateY(-2px); }
a.em-car__card:focus-visible { outline: 2px solid var(--em-car-accent, var(--em-color-secondary)); outline-offset: -2px; }

.em-car__stamp { display: flex; align-items: baseline; gap: .25rem; }
.em-car__day {
  color: var(--em-car-accent, var(--em-color-secondary));
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.02em;
}
.em-car__mon {
  color: var(--em-car-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
}
.em-car__label { font-size: 1rem; font-weight: 700; line-height: 1.35; }
.em-car__desc {
  font-size: .75rem;
  line-height: 1.45;
  opacity: .75;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.em-car__city {
  margin-top: auto;
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  opacity: .6;
}

@media (max-width: 1023px) { .em-car__card { width: calc((100% - (var(--em-car-per-tab, 2) - 1) * 12px) / var(--em-car-per-tab, 2)); } }
@media (max-width: 640px) {
  .em-car__card { width: calc((100% - (var(--em-car-per-mob, 1) - 1) * 12px) / var(--em-car-per-mob, 1)); }
  /* `min-width: 0` : la regle de base impose un plancher en PIXELS pour garder des cartes lisibles
     sur grand ecran. Sous 640 px ce plancher devient plus large que la place disponible : la carte
     ne peut plus se comprimer, et le viewport en `overflow: hidden` la ROGNE au lieu de l'adapter —
     titre coupe en deux, pave de date colle au bord. La largeur `100%` ci-dessus ne suffit pas :
     `min-width` gagne toujours contre `width`. */
  .em-car__card { min-width: 0; }
  .em-car__head { flex-direction: column; align-items: flex-start; }
}

/* ================================ BANDEAU TUILES DATE ================================ */
.em-date-tiles {
  display: flex;
  align-items: center;
  gap: 1.375rem;
  background: var(--em-dt-bg, var(--em-color-primary));
  color: var(--em-dt-text, var(--em-on-dark));
  padding: var(--em-dt-pad, 1rem 1.5rem);
  border-radius: var(--em-dt-radius, 0);
  box-shadow: var(--em-dt-shadow, none);
}
.em-dt__label {
  margin: 0;
  flex-shrink: 0;
  max-width: 9rem;
  color: var(--em-dt-title, var(--em-dt-accent, var(--em-color-secondary)));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  line-height: 1.35;
}
/* `min-width: 0` sur le viewport : sans lui, un titre long empêche le flex de se comprimer et la
   piste déborde de la bande au lieu de défiler. Même cause que les symptômes mobiles de 2.5.219. */
.em-dt__viewport { flex: 1; min-width: 0; }
.em-dt__item {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex: 0 0 auto;
  /* Nombre de tuiles visibles PILOTÉ par le réglage (2.5.296). Il était codé en dur (`/ 4`), donc
     « Tuiles visibles » ne changeait que le pas de défilement : un réglage accepté et sans effet.
     `n` tuiles laissent `n - 1` intervalles de 12 px, le `gap` de `.em-dt__track`. Le repli `4` est le
     nombre codé auparavant ET le défaut de `dt_per_view` : un site qui n'a rien réglé rend à l'identique.
     `min-width` reste un PLANCHER de lisibilité : au-delà d'environ six tuiles la largeur calculée
     descend sous 200 px, la tuile garde son plancher et la piste défile — c'est le comportement voulu
     pour une piste, mais cela borne en pratique le nombre réellement visible. */
  /*
   * `box-sizing: border-box` INDISPENSABLE avec une largeur en `calc` (2.5.315).
   *
   * Signalé : « les cartes sont un peu coupées » sur mobile. La cause est écrite dans ce fichier depuis
   * toujours — la règle globale `*, *::before, *::after { box-sizing: inherit }` est ancrée sur
   * `.em-agenda`, un sélecteur que son propre commentaire dit ABSENT du markup. Tous les blocs héritent
   * donc du défaut du navigateur, `content-box` : le `padding-right` et le `border-right` s'AJOUTENT à la
   * largeur calculée.
   *
   * Mesuré ici : 14,4 px de padding + 1 px de bordure par tuile. Avec quatre tuiles sur ordinateur, la
   * piste dépasse de 62 px — visible mais discret. Avec UNE tuile sur mobile, la carte fait 100 % de la
   * piste PLUS 15 px : elle dépasse forcément, et c'est la coupe signalée.
   */
  box-sizing: border-box;
  width: calc((100% - (var(--em-dt-per, 4) - 1) * 12px) / var(--em-dt-per, 4));
  min-width: 200px;
  padding-right: .875rem;
  /* Filet séparateur porté par la carte et non par un élément dédié : un `<hr>` entre chaque tuile
     serait annoncé par les lecteurs d'écran comme une séparation de contenu, ce qu'il n'est pas. */
  border-right: 1px solid var(--em-dt-border, var(--em-on-dark-line));
  scroll-snap-align: start;
  text-decoration: none;
  color: inherit;
}
.em-dt__item:last-child { border-right: 0; }
a.em-dt__item:hover .em-dt__name { text-decoration: underline; }
a.em-dt__item:focus-visible { outline: 2px solid var(--em-dt-accent, var(--em-color-secondary)); outline-offset: 2px; }

.em-dt__tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 35px;
  padding: .5rem .5rem;
  background: var(--em-on-dark-surface);
  border-radius: calc(var(--em-radius) / 2);
  line-height: 1.25;
}
.em-dt__day { font-size: 1.125rem; font-weight: 800; }
.em-dt__mon { font-size: .625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; opacity: .8; }
.em-dt__body { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }
.em-dt__name {
  font-size: .75rem;
  font-weight: 700;
  line-height: 1.5;
  /* Une seule ligne coupée : la bande a une hauteur fixe, un titre sur trois lignes la déformerait. */
  display: -webkit-box;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.em-dt__city { font-size: .75rem; opacity: .65; }
.em-dt__nav { display: flex; gap: .25rem; flex-shrink: 0; }
.em-dt__arrow {
  /* Meme raison que `.em-car__arrow` : les blocs sont en `content-box`, le padding par defaut du
     bouton elargissait le cercle en ovale. */
  box-sizing: border-box;
  padding: 0;
  line-height: 1;
  flex: 0 0 auto;
  aspect-ratio: 1;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  cursor: pointer;
  color: inherit;
  background: var(--em-on-dark-hover);
  border-radius: 50%;
  transition: background var(--em-transition);
}
.em-dt__arrow:hover:not(:disabled) { background: var(--em-on-dark-line); }
.em-dt__arrow:disabled { opacity: .35; cursor: default; }

@media (max-width: 1023px) { .em-dt__item { width: calc((100% - (var(--em-dt-per-tab, 2) - 1) * 12px) / var(--em-dt-per-tab, 2)); } }
@media (max-width: 640px) {
  .em-date-tiles { flex-wrap: wrap; gap: .625rem 1rem; }
  /* Le libelle prend SA PROPRE LIGNE (`flex-basis: 100%`), la piste et les fleches la suivante.
     `flex-wrap: wrap` seul ne suffisait pas : le viewport est en `flex: 1` avec `min-width: 0`, il se
     comprime donc indefiniment plutot que de provoquer le retour a la ligne. Resultat, les trois
     elements restaient sur une rangee et la piste n'avait plus que ~200 px pour une carte qui en
     exigeait 200 — d'ou le titre coupe. */
  .em-dt__label  { max-width: none; flex: 1 1 100%; }
  .em-dt__viewport { flex: 1 1 auto; }
  /* `100%` etait « une seule tuile », code en dur : `dt_per_mobile` n avait donc aucun effet. */
  .em-dt__item   { width: calc((100% - (var(--em-dt-per-mob, 1) - 1) * 12px) / var(--em-dt-per-mob, 1)); border-right: 0; }
  /* `min-width: 0` : la regle de base impose un plancher en PIXELS pour garder des cartes lisibles
     sur grand ecran. Sous 640 px ce plancher devient plus large que la place disponible : la carte
     ne peut plus se comprimer, et le viewport en `overflow: hidden` la ROGNE au lieu de l'adapter —
     titre coupe en deux, pave de date colle au bord. La largeur `100%` ci-dessus ne suffit pas :
     `min-width` gagne toujours contre `width`. */
  .em-dt__item   { min-width: 0; }
}

/* ============================== ONGLETS PAR CATÉGORIE ============================== */
.em-tabs {
  background: var(--em-tb-bg, var(--em-color-primary));
  color: var(--em-tb-text, var(--em-on-dark));
  border-radius: var(--em-tb-radius, var(--em-radius));
  padding: var(--em-tb-pad, 1.5rem 1.5rem);
  box-shadow: var(--em-tb-shadow, none);
}
.em-tb__title {
  margin: 0 0 1rem;
  color: var(--em-tb-title, var(--em-on-dark));
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.2;
}
.em-tb__bar { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.125rem; }
.em-tb__pill {
  padding: .375rem 1rem;
  border: 1px solid var(--em-tb-border, var(--em-on-dark-line));
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font-size: .875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--em-transition), color var(--em-transition);
}
.em-tb__pill:hover { background: var(--em-on-dark-surface);color: var(--em-kpi-accent, var(--em-color-secondary)); }
.em-tb__pill--on {
  background: var(--em-tb-accent, var(--em-color-secondary));
  border-color: var(--em-tb-accent, var(--em-color-secondary));
  color: var(--em-color-primary);
}
.em-tb__pill:focus-visible { outline: 2px solid var(--em-tb-accent, var(--em-color-secondary)); outline-offset: 2px; }

.em-tb__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: .875rem;
}
.em-tb__card {
  display: flex;
  flex-direction: column;
  gap: .375rem;
  padding: 1rem 1rem;
  background: var(--em-on-dark-surface);
  border-radius: calc(var(--em-radius) / 1.5);
  text-decoration: none;
  color: inherit;
  transition: background var(--em-transition);
}
a.em-tb__card:hover { background: var(--em-on-dark-hover); }
a.em-tb__card:focus-visible { outline: 2px solid var(--em-tb-accent, var(--em-color-secondary)); outline-offset: -2px; }
.em-tb__when  { color: var(--em-tb-accent, var(--em-color-secondary)); font-size: .75rem; font-weight: 600; }
.em-tb__name  { font-size: 1rem; font-weight: 700; line-height: 1.35; }
.em-tb__where { font-size: .75rem; opacity: .65; }

/* ============================== CHIFFRES CLÉS + LISTE ============================== */
.em-kpi-list {
  display: grid;
  /* `minmax(0, …)` et non `1fr` : sans lui, un titre long empêche la colonne de se comprimer et la
     grille déborde. `1fr` a pour minimum la taille du contenu. */
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
  background: var(--em-kpi-bg, var(--em-color-primary));
  color: var(--em-kpi-text, var(--em-on-dark));
  border-radius: var(--em-kpi-radius, var(--em-radius));
  padding: var(--em-kpi-pad, 1.75rem 1.75rem);
  box-shadow: var(--em-kpi-shadow, none);
}
.em-kpi__eyebrow {
  margin: 0 0 .5rem;
  color: var(--em-kpi-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
}
.em-kpi__title {
  margin: 0 0 .625rem;
  color: var(--em-kpi-title, var(--em-on-dark));
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.15;
}
.em-kpi__lead { margin: 0 0 1.25rem; font-size: .875rem; line-height: 1.5; opacity: .8; }
.em-kpi__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  padding-top: 1.125rem;
}
.em-kpi__stat { display: flex; flex-direction: column; gap: .25rem; }
.em-kpi__num {
  color: var(--em-kpi-num-color, var(--em-kpi-accent, var(--em-color-secondary)));
  font-size: var(--em-kpi-num-size, 2rem);
  font-weight: 800;
  line-height: 1;
  /* Chiffres a largeur FIXE. Pendant le comptage la valeur change a chaque image : avec des chiffres
     proportionnels, un « 1 » remplace par un « 8 » elargit le nombre et fait vibrer le libelle centre
     en dessous. C'est la moitie du remede — l'autre est la largeur reservee en `ch` posee par le JS,
     qui traite le passage de 1 a 2 chiffres. Aucune des deux ne remplace l'autre. */
  font-variant-numeric: tabular-nums;
}
.em-kpi__label {
  font-size: var(--em-kpi-label-size, .75rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
  /* Libelle attenue par DEFAUT, mais l'opacite est une variable : une couleur choisie a la main est
     emise avec `--em-kpi-label-opacity:1`, sinon tout reglage de couleur ressortirait delave et
     personne ne devinerait pourquoi. */
  color: var(--em-kpi-label-color, inherit);
  opacity: var(--em-kpi-label-opacity, .7);
}

/* Alignement VERTICAL du groupe de compteurs.
   Strictement ADDITIF : `start` (le defaut) n'emet aucune regle, donc le rendu existant est
   inchange. Les deux autres valeurs ont besoin de DEUX choses que `start` n'exige pas :
   · la cellule de gauche doit s'ETIRER a la hauteur de la rangee — la grille est en
     `align-items: start`, donc sans cela la colonne fait la hauteur de son contenu et il n'y a
     aucun espace libre a l'interieur pour y deplacer quoi que ce soit ;
   · l'intro devient une colonne flex, seule facon de pousser un enfant avec une marge `auto`.
   Sous 640 px la grille passe a une colonne : les hauteurs s'egalisent, les marges `auto` n'ont
   plus de place et le reglage devient sans effet — ce qui est le comportement correct, pas un oubli. */
.em-kpi-list[data-stat-valign="center"],
.em-kpi-list[data-stat-valign="end"] { align-items: stretch; }
.em-kpi-list[data-stat-valign="center"] .em-kpi__intro,
.em-kpi-list[data-stat-valign="end"] .em-kpi__intro {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.em-kpi-list[data-stat-valign="center"] .em-kpi__stats { margin-top: auto; margin-bottom: auto; }
.em-kpi-list[data-stat-valign="end"] .em-kpi__stats { margin-top: auto; }

/* Alignement des compteurs — UN reglage, DEUX axes qu'il pilote ensemble :
   · la position du GROUPE dans la bande (`justify-content`) — c'est ce qui repond au cas « deux
     compteurs sur quatre », ou un groupe cale a gauche laisse un vide qui se lit comme une donnee
     manquante plutot que comme un choix ;
   · l'alignement du chiffre et de son libelle l'un sous l'autre (`align-items` + `text-align`).
   Les DEUX proprietes sont necessaires : le slot est un conteneur flex en colonne, donc `text-align`
   seul laisserait ses deux enfants etires sur toute la largeur. */
.em-kpi-list[data-stat-align="center"] .em-kpi__stats { justify-content: center; }
.em-kpi-list[data-stat-align="center"] .em-kpi__stat  { align-items: center; text-align: center; }
.em-kpi-list[data-stat-align="right"]  .em-kpi__stats { justify-content: flex-end; }
.em-kpi-list[data-stat-align="right"]  .em-kpi__stat  { align-items: flex-end; text-align: right; }
.em-kpi-list[data-stat-align="left"]   .em-kpi__stats { justify-content: flex-start; }
.em-kpi-list[data-stat-align="left"]   .em-kpi__stat  { align-items: flex-start; text-align: left; }

.em-kpi__rows { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: .5rem; }
.em-kpi__row {
  display: flex;
  align-items: baseline;
  gap: .875rem;
  padding: .75rem 1rem;
  background: var(--em-on-dark-surface);
  border-radius: calc(var(--em-radius) / 1.5);
  /* Meme raison que la carte du carrousel horizontal : ombre interne, jamais un `border`. */
  box-shadow: inset 0 0 0 1px var(--em-kpi-border, transparent);
  text-decoration: none;
  color: inherit;
  transition: background var(--em-transition);
}
a.em-kpi__row:hover { background: var(--em-on-dark-hover); }
a.em-kpi__row:focus-visible { outline: 2px solid var(--em-kpi-accent, var(--em-color-secondary)); outline-offset: -2px; }
/* Chiffres tabulaires : sans eux « 05.Nov » et « 22.Oct » n'ont pas la même largeur et la colonne
   des titres tremble d'une ligne à l'autre. */
.em-kpi__date {
  flex-shrink: 0;
  color: var(--em-kpi-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.em-kpi__name {
    font-size: .875rem;
    font-weight: 600;
    line-height: 1.35;
    min-width: 0;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

@media (max-width: 860px) {
  .em-kpi-list { grid-template-columns: 1fr; gap: 1.25rem; }
  .em-kpi__title { font-size: 1.5rem; }
}

/* ===================== CONTACT & ACCES (2.5.281) =====================
 *
 * Deux colonnes egales : infos pratiques et carte. AUCUNE couleur en dur — tout passe par les jetons
 * du design system, avec les variables `--em-ct-*` par instance en surcharge.
 *
 * `minmax(0, 1fr)` et non `1fr` : une piste implicite vaut `max-content` au MAXIMUM, donc une adresse
 * longue sans espace ferait deborder la grille au lieu de se replier. Meme piege que le conteneur de
 * bloc Gutenberg (2.5.245), et invisible sur un jeu de test court.
 */
.em-contact {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0;
  background: var(--em-ct-bg, var(--em-color-surface));
  color: var(--em-ct-text, var(--em-color-text));
  border: 1px solid var(--em-ct-border, var(--em-color-border));
  border-radius: var(--em-ct-radius, var(--em-radius));
  box-shadow: var(--em-ct-shadow, var(--em-shadow));
  overflow: hidden;
}
/* Chaque element de grille doit pouvoir RETRECIR : `min-width` vaut `auto` par defaut, donc la colonne
   ne descend pas sous la largeur de son contenu et la carte deborderait la page. */
.em-contact > * { min-width: 0; }

/* Carte a GAUCHE : on inverse l'ordre visuel, pas l'ordre du DOM. Le lecteur d'ecran et le parcours
   clavier gardent « infos puis carte », qui est l'ordre de lecture utile. */
.em-ct--map-left .em-ct__panel  { order: 2; }
.em-ct--map-left .em-ct__mapcol { order: 1; }

.em-ct__panel { padding: var(--em-ct-pad, 2rem); }
.em-ct__eyebrow {
  margin: 0 0 .5rem;
  color: var(--em-ct-accent, var(--em-color-secondary));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
}
.em-ct__title {
  margin: 0 0 1.5rem;
  color: var(--em-ct-title, var(--em-color-primary));
  font-size: 1.25rem;
  line-height: 1.25;
  font-weight: 700;
}
.em-ct__infos { margin: 0; display: grid; gap: 1rem; }
.em-ct__row { display: flex; align-items: flex-start; gap: .75rem; }
/* Pastille d'icone : fond derive de la primaire par son triplet RGB, jamais une couleur en dur — un
   theme qui change la primaire emporte la pastille avec lui. */
.em-ct__ico {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: rgba(var(--em-color-primary-rgb), .08);
  color: var(--em-ct-icon, var(--em-color-accent, var(--em-color-secondary)));
}
.em-ct__pair { min-width: 0; }
.em-ct__label {
  margin: 0;
  /* Repli en CHAINE vers l'ancienne variable partagee : un site qui ne remplit rien rend a l'identique. */
  color: var(--em-ct-label, var(--em-color-muted));
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.em-ct__value {
  margin: .125rem 0 0;
  color: var(--em-ct-title, var(--em-color-primary));
  font-weight: 500;
  overflow-wrap: anywhere;
  font-size: 1rem;
}
/*
 * Panneau d'acces : quatre couleurs et deux dimensions PROPRES depuis la 2.5.282.
 *
 * Il empruntait `--em-color-bg` et `--em-color-muted`, donc l'eclaircir imposait de repeindre le fond de
 * page de tout le plugin. Chaque variable retombe EN CHAINE sur celle qu'elle remplace : tant que rien
 * n'est saisi, le rendu est identique au pixel.
 */
.em-ct__access {
  margin-top: 1.75rem;
  padding: var(--em-ct-access-pad, 1.125rem 1.25rem);
  background: var(--em-ct-access-bg, var(--em-color-bg));
  border-radius: var(--em-ct-access-radius, var(--em-radius));
}
.em-ct__access-title {
  margin: 0 0 .75rem;
  color: var(--em-ct-access-title, var(--em-color-muted));
  font-size: .625rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.em-ct__access-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .625rem; }
.em-ct__access-item {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  font-size: .875rem;
  line-height: 1.45;
}
.em-ct__access-item .em-ct__ico {
  width: 20px; height: 20px; background: none;
  align-self: center;
}
/* Les libelles en gras suivent les titres du bloc par defaut : ils s'alignent ainsi sur le titre de
   l'evenement juste au-dessus. Surchargeables separement si ce n'est pas voulu. */
.em-ct__access-item strong {
  color: var(--em-ct-access-strong, var(--em-ct-title, var(--em-color-primary)));
  font-weight: 600;
}
/*
 * Le point median suit le TEXTE du panneau, atenue par l'opacite — et non `--em-color-border`.
 *
 * Un separateur de texte n'est pas une bordure : lie aux bordures, il disparaissait des qu'on les
 * eclaircissait, alors que rien dans l'ecran ne laissait deviner le lien.
 */
.em-ct__dot { color: var(--em-ct-access-text, var(--em-color-muted)); opacity: .45; }
.em-ct__access-text { color: var(--em-ct-access-text, var(--em-color-muted)); min-width: 0; }

/* Colonne carte. Le fond gris tient la place AVANT le chargement de l'iframe : sans lui, un bloc blanc
   de la hauteur du panneau clignote au premier affichage. */
.em-ct__mapcol {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--em-color-bg);
  min-height: var(--em-ct-map-h, 100%);
}
.em-ct__map {
  flex: 1 1 auto;
  width: 100%;
  min-height: var(--em-ct-map-h, 320px);
  border: 0;
  display: block;
}
.em-ct__itin { position: absolute; right: 1rem; bottom: 1rem; }
/* Apercu de consentement : aux couleurs du plugin, jamais une capture de carte — montrer une fausse
   carte pour eviter d'en charger une vraie serait un contresens. */
.em-ct__consent {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  padding: 2rem 1.5rem;
  text-align: center;
  min-height: var(--em-ct-map-h, 320px);
  background: linear-gradient(135deg, var(--em-noimg-from, var(--em-color-primary)), var(--em-noimg-to, var(--em-color-accent)));
  color: var(--em-on-dark);
}
.em-ct__consent .em-ct__consent-ico { background: var(--em-on-dark-hover); color: var(--em-on-dark); }
.em-ct__consent-text { margin: 0; font-size: .875rem; line-height: 1.5; max-width: 34ch; }

@media (max-width: 900px) {
  /*
   * Une seule colonne, et la carte APRES les infos quel que soit `ct_map_side` : une carte de 640 px
   * poussee en premier repousserait le contenu utile hors de l'ecran. La maquette place la carte a
   * droite sur grand ecran, ce qui ne dit rien de l'ordre sur mobile.
   */
  .em-contact { grid-template-columns: minmax(0, 1fr); }
  .em-ct--map-left .em-ct__panel,
  .em-ct--map-left .em-ct__mapcol { order: 0; }
  .em-ct__panel { padding: var(--em-ct-pad, 1.5rem); }
  .em-ct__title { font-size: 1rem; }
  .em-ct__map, .em-ct__consent { min-height: var(--em-ct-map-h, 260px); }
}

/* ===================== CARROUSEL VEDETTE (spotlight, 2.5.288) =====================
 *
 * Panneau sombre, UN evenement par slide. Aucune couleur en dur : le fond vient de `--em-sp-bg` avec repli
 * sur la primaire, et tout le texte pose dessus passe par les jetons `--em-on-dark*` — les memes que les
 * quatre autres blocs a panneau sombre, donc un theme les surcharge une fois pour tous.
 */
.em-spotlight {
  background: var(--em-sp-bg, var(--em-color-primary));
  /* `--em-sp-text` LU depuis la 2.5.311 : ce reglage existait en back-office et n'etait lu nulle part,
     donc il ne faisait rien. Repli sur le jeton des panneaux sombres, valeur d'origine. */
  color: var(--em-sp-text, var(--em-on-dark));
  border-radius: var(--em-sp-radius, var(--em-radius));
  padding: var(--em-sp-pad, 2rem);
  box-shadow: var(--em-sp-shadow, none);
}
.em-sp__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
}
.em-sp__panel-title {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--em-sp-title, var(--em-on-dark));
}
/*
 * La fenetre CACHE le debordement, la piste fait N fois sa largeur et se deplace par `translateX`.
 *
 * `min-width: 0` sur la fenetre : c'est un element de grille dans le conteneur Gutenberg, et sans lui la
 * piste imposerait sa largeur totale au bloc, qui deborderait la page au lieu de masquer.
 */
.em-sp__viewport { overflow: hidden; min-width: 0; }
.em-sp__track {
  display: flex;
  /* Transition sur `transform` UNIQUEMENT : animer `left` ou une marge declencherait une remise en page a
     chaque image, la ou `transform` reste sur le compositeur. */
  transition: transform .45s ease;
  will-change: transform;
}
/* Chaque slide occupe exactement la fenetre. La largeur est posee en ligne par le JS (100/N %) : elle
   depend du nombre d'evenements, que le CSS ne connait pas. */
.em-sp__slide {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 1.75rem;
}
/* Pave de date : surface claire translucide sur fond sombre, jeton partage par les blocs sombres. */
.em-sp__tile {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 110px;
  height: 110px;
  border-radius: var(--em-radius);
  background: var(--em-sp-tile, var(--em-on-dark-surface));
}
.em-sp__day   { font-size: 2.125rem; font-weight: 800; line-height: 1; }
.em-sp__month {
  margin-top: .25rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--em-sp-eyebrow, var(--em-sp-accent, var(--em-color-secondary)));
}
.em-sp__body { min-width: 0; }
.em-sp__eyebrow {
  margin: 0 0 .375rem;
  font-size: .75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--em-sp-eyebrow, var(--em-sp-accent, var(--em-color-secondary)));
}
.em-sp__title {
  margin: 0 0 .5rem;
  font-size: 1.5rem;
  line-height: 1.2;
  font-weight: 700;
  color: var(--em-sp-title, var(--em-on-dark));
}
.em-sp__desc {
  margin: 0 0 .875rem;
  font-size: .875rem;
  line-height: 1.5;
  opacity: .85;
  max-width: 100ch;
}
.em-sp__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .375rem 1.375rem;
  font-size: .875rem;
  opacity: .9;
}
.em-sp__meta-item { display: inline-flex; align-items: center; gap: .375rem; min-width: 0; }
.em-sp__cta { margin-top: .75rem; padding: .5rem 1rem; font-size: .75rem; border-width: 1px; }
/* Pied : pastilles a gauche, lien d'agenda et fleches a droite — l'ordre de la maquette. */
.em-sp__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  /* `--em-sp-border` LU depuis la 2.5.311 : ce filet est le SEUL trait du bloc, donc c'est lui que designe
     le reglage de bordure. La poser sur la racine dessinerait un cadre que ce bloc n'a jamais eu. */
  border-top: 1px solid var(--em-sp-border, var(--em-on-dark-line));
}
.em-sp__foot-right { display: flex; align-items: center; gap: .75rem; }
.em-sp__dots { display: flex; gap: .25rem; }
.em-sp__dot {
  width: 10px;
  height: 5px;
  padding: 0;
  border: 0;
  border-radius: 3px;
  background: var(--em-on-dark-hover);
  cursor: pointer;
  transition: background .2s ease, width .2s ease;
}
.em-sp__dot.active { width: 22px; background: var(--em-sp-eyebrow, var(--em-color-secondary)); }
.em-sp__arrows { display: flex; gap: .5rem; }
.em-sp__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--em-on-dark-line);
  background: transparent;
  color: var(--em-on-dark);
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.em-sp__arrow:hover:not(:disabled) { background: var(--em-on-dark-hover); }
/*
 * Fleche DESACTIVEE en bout de course : elle reste visible mais inerte, et le dit. `pointer-events: none`
 * seul ne suffirait pas — un bouton `disabled` est aussi retire du parcours clavier et annonce son etat.
 */
.em-sp__arrow:disabled { opacity: .35; cursor: default; }

@media (max-width: 780px) {
  /*
   * Le pave de date passe AU-DESSUS du texte plutot que de disparaitre : c'est le repere visuel du bloc, et
   * la date complete de la ligne de meta ne le remplace pas — elle se lit, elle ne se repere pas.
   */
  .em-spotlight { padding: var(--em-sp-pad, 1.25rem); }
  .em-sp__slide { flex-direction: column; gap: 1rem; }
  .em-sp__tile { width: 84px; height: 84px; }
  .em-sp__day { font-size: 1.75rem; }
  .em-sp__title { font-size: 1.25rem; }
  /* Le pied s'empile : trois groupes cote a cote sur 320 px rendraient les fleches inatteignables. */
  .em-sp__foot { flex-direction: column; align-items: stretch; gap: .875rem; }
  .em-sp__foot-right { justify-content: space-between; }
}

/* ===================== AFFICHES (posters, 2.5.289) =====================
 *
 * Meme coquille que le carrousel vedette — panneau sombre, fenetre, piste, pastilles, fleches — et une carte
 * dont le visuel occupe le haut. Aucune couleur en dur : jetons `--em-on-dark*` partages par les blocs sombres.
 */
.em-posters {
  background: var(--em-po-bg, var(--em-color-primary));
  /* `--em-po-text` LU depuis la 2.5.311 — meme correctif que le carrousel vedette : le reglage existait
     et ne faisait rien. */
  color: var(--em-po-text, var(--em-on-dark));
  border-radius: var(--em-po-radius, var(--em-radius));
  padding: var(--em-po-pad, 2rem);
  box-shadow: var(--em-po-shadow, none);
}
.em-po__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.em-po__panel-title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--em-po-title, var(--em-on-dark));
}
.em-po__viewport { overflow: hidden; min-width: 0; }
.em-po__track { display: flex; transition: transform .45s ease; will-change: transform; }
/*
 * Une slide = une grille de N colonnes EGALES. `--em-po-n` est pose en ligne par le JS : la derniere slide
 * peut contenir moins de cartes, et sans ce nombre ses cartes s'etireraient pour remplir la largeur.
 *
 * `minmax(0, 1fr)` et non `1fr` : une piste implicite vaut `max-content` au MAXIMUM, donc un titre long sans
 * espace ferait deborder la grille. Meme piege qu'en 2.5.245.
 */
.em-po__slide {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(var(--em-po-n, 4), minmax(0, 1fr));
  gap: 1.125rem;
}
.em-po__card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border-radius: var(--em-radius);
  background: var(--em-po-card, var(--em-on-dark-surface));
  color: inherit;
  text-decoration: none;
  transition: background .2s ease, transform .2s ease;
}
/* Survol et focus ciblent `a.em-po__card` et JAMAIS la classe seule : sans URL la carte est un `<div>`, et
   l'animer promettrait un clic inexistant. Meme regle que la bande de dates (2.5.227). */
a.em-po__card:hover { background: var(--em-on-dark-hover); }
a.em-po__card:focus-visible { outline: 2px solid var(--em-color-secondary); outline-offset: 2px; }
/*
 * Zone du visuel. Le ratio vient du jeton PARTAGE `--em-thumb-ratio` : le reglage global des vignettes vaut
 * ici comme ailleurs. `--em-po-media-h` le remplace quand une hauteur fixe est demandee.
 */
.em-po__media {
  position: relative;
  aspect-ratio: var(--em-thumb-ratio, 16 / 9);
  height: var(--em-po-media-h, auto);
  background: var(--em-on-dark-hover);
}
.em-po__img {
  width: 100%;
  height: 100%;
  object-fit: var(--em-thumb-fit, cover);
  display: block;
  position: absolute;
  inset: 0;
}
.em-po__noimg { width: 100%; height: 100%; }
/* Sur-titre POSE sur le visuel, en haut a gauche, comme la maquette. */
.em-po__eyebrow {
  position: absolute;
  top: .5rem;
  left: .5rem;
  background: var(--em-color-primary);
  /* Chaine : reglage propre, puis accent PARTAGE, puis le jeton des panneaux sombres. C'est le motif que
     le ticker utilisait deja pour son accent — les deux blocs sombres ne le suivaient pas, donc leur
     reglage d'accent partage etait mort (2.5.311). */
  color: var(--em-po-eyebrow, var(--em-po-accent, var(--em-on-dark)));
  border-radius: calc(var(--em-radius) / 2);
  padding: .25rem .5rem;
  text-align: center;
  line-height: 1;
  z-index: 1;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .28);
  min-width: 36px;
  /* `var(--em-on-dark)` et non `#fff` : ce sur-titre est posé sur le visuel, donc dans la famille
     « sur fond sombre », et le jeton vaut `#fff` par défaut — le rendu est identique. Un blanc en dur
     ici est refusé par la garde des blocs à panneau sombre, et pour une raison concrète : un thème qui
     surcharge `--em-on-dark` verrait la bordure rester blanche alors que le texte a changé. */
  /* `--em-po-border` LU depuis la 2.5.311 : ce contour est le seul trait du bloc, donc c'est lui que
     designe le reglage de bordure. Repli inchange sur le jeton des panneaux sombres. */
  border: 1px solid var(--em-po-border, var(--em-on-dark));
  opacity: 0.8;
  font-size: .75rem;
  font-weight: 500;
  text-transform: uppercase;
}
.em-po__body {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  padding: 1rem;
  /* La carte s'etire, le corps pousse vers le bas : les dates s'alignent d'une carte a l'autre meme quand
     les titres font une ou deux lignes. */
  margin-top: auto;
}
.em-po__day   { font-size: 1.75rem; font-weight: 800; line-height: 1; }
.em-po__month {
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .12em;
  opacity: .8;
  margin-bottom: .5rem;
}
.em-po__name { font-size: .875rem; font-weight: 700; line-height: 1.35; }
.em-po__city { font-size: .75rem; opacity: .7; min-width: 0; }
/* Pied et commandes : memes formes que le carrousel vedette, jetons partages. */
.em-po__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.375rem;
}
.em-po__foot-right { display: flex; align-items: center; gap: .75rem; }
.em-po__dots { display: flex; gap: .25rem; }
.em-po__dot {
  width: 12px;
  height: 5px;
  padding: 0;
  border: 0;
  border-radius: 3px;
  background: var(--em-on-dark-hover);
  cursor: pointer;
  transition: background .2s ease, width .2s ease;
}
.em-po__dot.active { width: 22px; background: var(--em-po-eyebrow, var(--em-color-secondary)); }
.em-po__arrows { display: flex; gap: .5rem; }
.em-po__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--em-on-dark-line);
  background: transparent;
  color: var(--em-on-dark);
  cursor: pointer;
  transition: background .2s ease;
}
.em-po__arrow:hover:not(:disabled) { background: var(--em-on-dark-hover); }
.em-po__arrow:disabled { opacity: .35; cursor: default; }

@media (max-width: 780px) {
  .em-posters { padding: var(--em-po-pad, 1.25rem); }
  .em-po__panel-title { font-size: 1.25rem; }
  /* L'en-tete s'empile : un titre et deux fleches sur 320 px se chevaucheraient. */
  .em-po__head { flex-direction: column; align-items: stretch; gap: .75rem; }
  .em-po__foot { flex-direction: column; align-items: stretch; gap: .875rem; }
  .em-po__foot-right { justify-content: space-between; }
  .em-po__day { font-size: 1.5rem; }
}

/* ===================== GRILLE FILTREE (2.5.391) =====================
   Une grille compacte : titre, intro, barre de filtres REDUITE, cartes a un titre et une ligne de meta,
   bouton « voir tout » en pied.

   Les onze variables emises par `render.php` sont TOUTES lues ici — les huit d'apparence partagees
   (`em_block_appearance_style`) et les trois nombres de colonnes. Une variable emise que le CSS ne lit
   jamais est un reglage sans effet, et c'est une garde meta qui l'interdit (2.5.311). */
/* ⚠ PANNEAU SOMBRE PAR DEFAUT depuis la 2.5.397, et c'est un REPEINT demande.
   Le bloc etait transparent sur le fond de page. Les quatre couleurs ci-dessous changent donc le rendu
   des sites deja en production — ce que la doctrine reserve a un changement DEMANDE (2.5.360), et
   c'en est un. Chacune reste surchargeable par ses reglages `fg_bg`, `fg_text`, `fg_title_color`,
   `fg_accent`, en back-office comme par instance. */
.em-filtered-grid {
  background: var(--em-fg-bg, var(--em-color-primary));
  color: var(--em-fg-text, var(--em-on-dark));
  /* `--em-fg-border` est une COULEUR, comme `--em-sp-border` : le support Gutenberg `border` porte deja
     la LARGEUR et le style. Le trait du conteneur n'existe donc que si une couleur est reglee. */
  border-color: var(--em-fg-border, transparent);
  border-radius: var(--em-fg-radius, var(--em-radius));
  /* 2rem, comme la liste d'inscriptions : un panneau colore sans retrait colle son texte au bord. */
  padding: var(--em-fg-pad, 2rem);
  box-shadow: var(--em-fg-shadow, none);
}
.em-fg__title {
  color: var(--em-fg-title, var(--em-on-dark));
  font-size: 1.75rem;
  font-weight: 700;
  margin: 0 0 .75rem;
}
.em-fg__intro {
  color: var(--em-fg-text, var(--em-on-dark));
  margin: 0 0 1.5rem;
  max-width: 60ch;
}

/* ---- Barre de filtres reduite ---- */
.em-fg__filters { margin-bottom: 1.5rem; }
.em-fg__filters-legend {
  color: var(--em-fg-text, var(--em-color-muted));
  font-size: .875rem;
  margin: 0 0 .375rem;
}
.em-fg__filters-row {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}
/* Les deroulantes sont celles de l'agenda (`buildFilterDropdown`) : elles heritent donc de
   `--em-filter-height` et de tout le style de `.em-fsd__*`. On ne fixe ici que leur LARGEUR, la barre
   de l'agenda etant en grille et celle-ci en flex. */
.em-fg__filter { flex: 1 1 220px; min-width: 0; max-width: 320px; }
.em-fg__reset {
  display: inline-block;
  margin-top: .5rem;
  padding: 0;
  background: none;
  border: 0;
  color: var(--em-fg-accent, var(--em-color-secondary));
  font: inherit;
  font-size: .875rem;
  text-decoration: underline;
  cursor: pointer;
}
.em-fg__reset:hover { opacity: .8; }

/* ---- Grille ----
   `repeat(var(--em-fg-cols), 1fr)` et non une classe par valeur : trois bandes x six valeurs auraient
   fait dix-huit regles pour trois reglages. */
.em-fg__grid {
  display: grid;
  grid-template-columns: repeat(var(--em-fg-cols, 3), 1fr);
  gap: var(--em-grid-gap, 1.25rem) 2rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.em-fg__item {
  /* `min-width: 0` OBLIGATOIRE dans une grille : sans lui un titre long elargit sa colonne au lieu de
     passer a la ligne, et les trois colonnes cessent d'etre egales. */
  min-width: 0;
  padding-bottom: .75rem;
  /* Le filet sous chaque carte est le SEUL trait que ce bloc dessine de lui-meme : c'est donc lui que
     designe le reglage « Bordure », et une garde exige que cette phrase existe (2.5.345).
     ⚠ TRANSPARENT par defaut depuis la 2.5.397, a la demande : le trait n'existe plus que si une
     couleur est choisie. Le retirer tout court aurait laisse le reglage « Bordure » sans cible, donc
     un reglage mort — pire qu'un trait de trop (2.5.289). */
  border-bottom: 1px solid var(--em-fg-border, transparent);
}
.em-fg__item-title { margin: 0 0 .5rem; font-size: 1rem; font-weight: 600; }
.em-fg__link {
  color: var(--em-fg-accent, var(--em-color-secondary));
  text-decoration: underline;
}
.em-fg__link:hover { opacity: .8; }
/* LA LIGNE DE META EST UN FLEX QUI ENVELOPPE, ET SES MORCEAUX SONT INSECABLES.

   Elle portait `.em-ic-wrap` — donc `inline-flex` SANS `flex-wrap` — et ses noeuds de texte devenaient
   des elements de flex ANONYMES : « 28 septembre 2026 » se coupait a l'interieur de son propre morceau
   pendant que les separateurs, centres sur la hauteur des DEUX lignes, pendaient au milieu du vide.

   C'est le MORCEAU qu'il faut rendre insecable, jamais la ligne : un `nowrap` sur la ligne entiere la
   ferait deborder de sa colonne, et la grille cesserait d'avoir des colonnes egales (2.5.392).

   `gap` et non une marge laterale sur le separateur : une marge survit au passage a la ligne et decale
   le premier morceau de la seconde ligne d'une valeur que rien n'explique. */
.em-fg__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .125rem .375rem;
  margin: 0;
  color: var(--em-fg-text, var(--em-on-dark));
  font-size: .875rem;
}
.em-fg__part { white-space: nowrap; }
.em-fg__sep { opacity: .5; }

/* ---- Pied ---- */
.em-fg__footer { margin-top: 1.5rem; display: flex; justify-content: flex-end; }

/* DENSITE « compacte ». `em_block_density_class()` emet `em-fg--compact` depuis la 2.5.391 et
   AUCUNE regle ne la lisait : le reglage traversait ses huit maillons sans rien peindre. Corrige en
   2.5.393, en meme temps que l'ecriture de la regle jumelle du bandeau — c'est en ecrivant la seconde
   qu'on voit que la premiere manque. */
.em-fg--compact .em-fg__grid { gap: .75rem 1.25rem; }
.em-fg--compact .em-fg__item { padding-bottom: .5rem; }
.em-fg--compact .em-fg__filters { margin-bottom: 1rem; }

@media (max-width: 1023px) {
  .em-fg__grid { grid-template-columns: repeat(var(--em-fg-cols-tab, 2), 1fr); }
}
@media (max-width: 640px) {
  .em-fg__grid { grid-template-columns: repeat(var(--em-fg-cols-mob, 1), 1fr); }
  .em-fg__filter { flex: 1 1 100%; max-width: none; }
  .em-fg__footer { justify-content: stretch; }
  .em-fg__all { width: 100%; text-align: center; }
}

/* ===================== BLOC « BANDEAU EVENEMENT » (2.5.393) =====================
   Un titre de section, puis une ligne par evenement en DEUX colonnes separees par un filet vertical :
   pave de date et lieu a gauche, titre / description / horaire / lien a droite.

   Les huit variables d'apparence partagees (`em_block_appearance_style`) sont TOUTES lues ici. Une
   variable emise que le CSS ne lit jamais est un reglage sans effet, et c'est une garde meta qui
   l'interdit (2.5.311). */
.em-event-banner {
  background: var(--em-eb-bg, transparent);
  color: var(--em-eb-text, var(--em-color-text));
  /* `--em-eb-border` est une COULEUR, comme `--em-sp-border` : le support Gutenberg `border` porte deja
     la LARGEUR et le style. Le trait du conteneur n'existe donc que si une couleur est reglee. */
  border-color: var(--em-eb-border, transparent);
  border-radius: var(--em-eb-radius, var(--em-radius));
  padding: var(--em-eb-pad, 0);
  box-shadow: var(--em-eb-shadow, none);
}
.em-eb__title {
  color: var(--em-eb-title, var(--em-color-primary));
  font-size: 2rem;
  font-weight: 800;
  text-transform: uppercase;
  margin: 0 0 1.5rem;
}

.em-eb__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 2rem;
}
.em-eb__item { display: flex; align-items: stretch; gap: 1.5rem; }

/* ---- Colonne de gauche : le pave de date ----
   Il n'est PAS colore, a l'inverse des pastilles des blocs a piste : la maquette le pose en texte sur
   le fond de page. C'est aussi pourquoi son mois est rendu en casse courante (« Sept. ») et non en
   capitales — cf. le style `title` de `monthLabel()`. */
.em-eb__date {
  flex: 0 0 7rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  color: var(--em-eb-title, var(--em-color-primary));
}
.em-eb__day   { font-size: 2.25rem; font-weight: 800; line-height: 1; }
.em-eb__month { font-size: 1rem; margin-top: .25rem; }
.em-eb__place { font-weight: 700; margin-top: .75rem; }

/* ---- Colonne de droite, et LE FILET ----
   `border-left` ici et nulle part ailleurs : c'est le SEUL trait que ce bloc dessine de lui-meme, donc
   c'est lui que designe le reglage « Bordure », et une garde exige que cette phrase existe (2.5.345).
   `min-width: 0` est OBLIGATOIRE dans un flex : sans lui un titre long elargit la colonne au lieu de
   passer a la ligne, et le pave de date se fait ecraser. */
.em-eb__body {
  flex: 1 1 auto;
  min-width: 0;
  border-left: 1px solid var(--em-eb-border, var(--em-color-border));
  padding-left: 1.5rem;
}
.em-eb__name {
  color: var(--em-eb-title, var(--em-color-primary));
  font-size: 1.25rem;
  font-weight: 700;
  margin: 0 0 .5rem;
}
.em-eb__desc { margin: 0 0 .75rem; max-width: 60ch; }
.em-eb__time { margin: 0 0 .5rem; }

/* Le lien de l'evenement et celui du pied partagent leur habillage : ce sont deux liens de meme nature,
   et leur donner deux regles les ferait diverger a la premiere retouche. */
.em-eb__cta,
.em-eb__all {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  color: var(--em-eb-accent, var(--em-color-primary));
  font-weight: 700;
  text-decoration: underline;
}
.em-eb__cta:hover,
.em-eb__all:hover { opacity: .8; }
.em-eb__footer { margin-top: 1.5rem; }

/* DENSITE « compacte ». `em_block_density_class()` emet `em-eb--compact` : sans regle qui la lise, le
   reglage serait branche de bout en bout et n'afficherait rien — un reglage sans effet est pire qu'un
   reglage absent (2.5.289). */
.em-eb--compact .em-eb__list { gap: 1.25rem; }
.em-eb--compact .em-eb__title { margin-bottom: 1rem; }
.em-eb--compact .em-eb__desc { margin-bottom: .5rem; }

@media (max-width: 640px) {
  .em-eb__title { font-size: 1.5rem; }
  .em-eb__item  { gap: 1rem; }
  .em-eb__date  { flex-basis: 4.5rem; }
  .em-eb__day   { font-size: 1.75rem; }
  .em-eb__body  { padding-left: 1rem; }
}

/* ===================== BLOC « LISTE D'INSCRIPTIONS » (2.5.394) =====================
   Panneau sombre : un titre, puis une ligne par evenement — une phrase composee a gauche, un bouton
   d'inscription a droite. La liste DEFILE au-dela de `--em-sg-max-h`.

   Les huit variables d'apparence partagees sont TOUTES lues ici. Une variable emise que le CSS ne lit
   jamais est un reglage sans effet, et c'est une garde meta qui l'interdit (2.5.311). */
.em-signup-list {
  /* PANNEAU SOMBRE par defaut, comme `spotlight`, `posters`, `carousel` et `kpi-list` : le fond retombe
     sur la primaire de la charte, et les textes sur les jetons `--em-on-dark` partages par ces blocs. */
  background: var(--em-sg-bg, var(--em-color-primary));
  color: var(--em-sg-text, var(--em-on-dark));
  border-color: var(--em-sg-border, transparent);
  border-radius: var(--em-sg-radius, var(--em-radius));
  padding: var(--em-sg-pad, 2rem);
  box-shadow: var(--em-sg-shadow, none);
}
.em-sg__title {
  color: var(--em-sg-title, var(--em-on-dark));
  font-size: 1.375rem;
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1.25;
  margin: 0 0 1.5rem;
}

/* ---- La liste, et sa zone de defilement ----
   `--em-sg-max-h` n'est emise que si le reglage est > 0, donc SANS repli ici : `max-height: var(--x)`
   sur une variable absente rend la declaration invalide, et la liste s'affiche entiere — exactement ce
   que « 0 = aucune limite » doit produire. C'est le repli le plus simple : l'absence de regle. */
.em-sg__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: var(--em-sg-max-h);
  overflow-y: auto;
  /* La barre de defilement suit l'accent du bloc plutot que le gris du systeme : sur un panneau sombre,
     une barre claire par defaut est le seul element qui ne soit pas a la charte.
     Repli sur la « Couleur d'accent » du theme (`--em-color-accent`) et non plus sur la secondaire
     (2.5.439, demande) : la barre est un element d'interface discret, pas un element a faire ressortir.
     L'accent PROPRE au bloc, s'il est regle, reste prioritaire. */
  scrollbar-width: thin;
  scrollbar-color: var(--em-sg-accent, var(--em-color-accent)) transparent;
}
/* La zone est focalisable (`tabindex="0"` dans le gabarit) : sans contour visible, un visiteur au clavier
   ne saurait pas qu'il vient d'atteindre la liste. */
.em-sg__list:focus-visible {
  outline: 2px solid var(--em-sg-accent, var(--em-color-secondary));
  outline-offset: 2px;
}
.em-sg__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .625rem 1rem;
}
.em-sg__item:last-child { border-bottom: 0; }
/* `min-width: 0` OBLIGATOIRE dans un flex : sans lui une phrase longue pousse le bouton hors de la boite
   au lieu de passer a la ligne. */
.em-sg__line { margin: 0; min-width: 0; font-size: 1rem; }
.em-sg__val  { font-weight: 700; }

/* ---- Le bouton d'inscription ----
   Contour et non aplat : c'est ce que montre la maquette, et sur un panneau sombre un bouton plein
   attirerait l'oeil avant le titre. `flex-shrink: 0` pour qu'il ne se comprime jamais — un bouton
   d'inscription tronque est un bouton qu'on n'ose pas cliquer. */
.em-sg__cta {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .5rem 1rem;
  border: 1px solid var(--em-sg-accent, var(--em-color-secondary));
  border-radius: var(--em-radius);
  color: var(--em-sg-accent, var(--em-color-secondary));
  background: transparent;
  font-size: .875rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .03em;
  text-decoration: none;
  transition: background var(--em-transition), color var(--em-transition);
}
.em-sg__cta:hover,
.em-sg__cta:focus-visible {
  background: var(--em-sg-accent, var(--em-color-secondary));
  color: var(--em-sg-bg, var(--em-color-primary));
}

/* DENSITE « compacte ». `em_block_density_class()` emet `em-sg--compact` : sans regle qui la lise, le
   reglage serait branche de bout en bout et n'afficherait rien (2.5.393). */
.em-sg--compact { padding: var(--em-sg-pad, 1.25rem); }
.em-sg--compact .em-sg__item { padding: .625rem .25rem; }
.em-sg--compact .em-sg__title { margin-bottom: 1rem; }

@media (max-width: 640px) {
  .em-sg__title { font-size: 1.25rem; }
  /* ⚠ DEFILEMENT DESACTIVE SOUS 640 px, et c'est un choix. Une zone defilante imbriquee dans le
     defilement de la page est un piege tactile : le doigt fait defiler la liste au lieu de la page, ou
     l'inverse, sans que rien ne dise lequel des deux repondra. La liste s'affiche donc entiere. */
  .em-sg__list { max-height: none; overflow-y: visible; }
  .em-sg__item {
    flex-direction: column;
    align-items: flex-start;
    gap: .75rem;
  }
  .em-sg__cta { width: 100%; justify-content: center; }
}

/* ==========================================================================
   BLOC « BARRE DE CAMPUS + LISTE » (campus-bar, prefixe cb) — 2.5.417
   ==========================================================================

   Une barre d'onglets pleine largeur, puis une ligne par evenement. Le bloc n'est PAS un panneau : il
   se pose sur le fond de la page, et ses trois couleurs propres nomment ce qu'elles peignent — la date,
   l'onglet actif, les filets. Chacune retombe EN CHAINE sur l'accent du bloc puis sur la charte, donc un
   site qui ne remplit rien rend a l'identique (2.5.319).

   Les huit variables `--em-cb-*` du panneau d'apparence PARTAGE (fond, accent, titre, texte, bordure,
   rayon, retrait, ombre) restent lues ici : elles viennent d'`em_block_appearance_style()`, et ce
   fichier est leur SEUL lecteur. */
.em-campus-bar {
  background: var(--em-cb-bg, transparent);
  color: var(--em-cb-text, var(--em-color-text));
  border-color: var(--em-cb-border, transparent);
  border-radius: var(--em-cb-radius, 0);
  padding: var(--em-cb-pad, 0);
  box-shadow: var(--em-cb-shadow, none);
}
.em-cb__title-block {
  color: var(--em-cb-title, var(--em-color-primary));
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.25;
  margin: 0 0 1rem;
}

/* ---- La barre d'onglets ----
   Le filet du bas est celui qui court sous TOUTE la barre ; l'onglet actif pose le sien par-dessus, plus
   epais et colore. Deux filets superposes et non un seul deplace : sans le filet continu, la barre se
   deforme quand l'onglet actif change de largeur.

   `overflow-x: auto` des le bureau : quinze campus ne tiennent sur aucune largeur, et une barre qui
   passe a la ligne ne se lit plus comme une barre. */
/* ⚠ LE FILET VIT SUR LE CONTENEUR et non sur la barre (2.5.419) : les fleches sont des sœurs de la
   barre, donc un filet porte par elle seule s'arreterait a ses bords et laisserait deux trous sous les
   commandes. */
.em-cb__barwrap {
  display: flex;
  align-items: stretch;
  gap: .25rem;
  border-bottom: 1px solid var(--em-cb-line, var(--em-color-border));
  margin: 0 0 .25rem;
}
/* `flex: 1 1 auto` AVEC `min-width: 0` : sans le second, une barre de quinze campus impose sa largeur
   intrinseque au conteneur flex, qui deborde la page au lieu de laisser la barre defiler. C'est la meme
   regle que `.em-block-frame > *` (2.5.244), pour la meme raison. */
.em-cb__bar {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: stretch;
  gap: 1.5rem;
  overflow-x: auto;
  scrollbar-width: none;
}
/* Les fleches empruntent le trace PARTAGE (`em_chevron_svg()`, 2.5.295) et la couleur de l'onglet actif :
   ce sont deux commandes de la meme barre, les separer demanderait un reglage de plus pour rien.

   Elles portent `hidden` a l'arrivee et `wireHScroll()` les revele — voir son 7e argument. Une fleche
   DESACTIVEE dirait « momentanement indisponible » ; ici il n'y a rien a gauche tant qu'on n'a pas
   defile, donc elle est ABSENTE (2.5.380). */
.em-cb__arrow {
  flex: 0 0 auto;
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0 .25rem;
  display: inline-flex;
  align-items: center;
  color: var(--em-cb-tab, var(--em-cb-accent, var(--em-color-secondary)));
  cursor: pointer;
  transition: opacity var(--em-transition);
}
.em-cb__arrow:hover { opacity: .7; }
.em-cb__arrow:focus-visible {
  outline: 2px solid var(--em-color-accent);
  outline-offset: 2px;
}

/* ---- Le menu deroulant des campus ----
   Rendu SUR TOUTES LES TAILLES par le gabarit, masque par defaut : c'est la requete media qui le fait
   paraitre, en mode `select` et sous 640 px seulement. `display: none` et non un masquage visuel : une
   commande invisible mais annoncee par un lecteur d'ecran ferait deux fois le meme choix dans le meme
   bloc. */
.em-cb__select { display: none; }
/* Meme regle pour le widget sidebar : rendu par PHP sur toutes les tailles, montre par la seule
   requete media du mode `select`. */
.em-asb__nav-select { display: none; }
.em-cb__bar::-webkit-scrollbar { display: none; }
/* RESERVATION DE HAUTEUR (2.5.343) : la barre est rendue VIDE puis remplie par le JS. Sans hauteur, tout
   ce qui suit remonte puis redescend — un decalage de mise en page sur chaque chargement. `:not(:has(*))`
   et non `:empty`, qui echoue au premier blanc ; la reservation se LIBERE d'elle-meme a l'arrivee du
   premier onglet, et sans `:has()` on retrouve simplement le comportement d'avant. */
.em-cb__bar:not(:has(*)) { min-height: var(--em-filter-height, 42px); }
.em-cb__tab {
  flex: 0 0 auto;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: .25rem solid transparent;
  padding: .625rem .125rem;
  margin: 0;
  font: inherit;
  font-size: .875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--em-color-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--em-transition), border-color var(--em-transition);
}
.em-cb__tab:hover { color: var(--em-cb-tab, var(--em-cb-accent, var(--em-color-secondary))); }
.em-cb__tab:focus-visible {
  outline: 2px solid var(--em-color-accent);
  outline-offset: 2px;
}
.em-cb__tab--on {
  color: var(--em-cb-tab, var(--em-cb-accent, var(--em-color-secondary)));
  border-bottom-color: var(--em-cb-tab, var(--em-cb-accent, var(--em-color-secondary)));
}

/* ---- Les lignes ----
   Le filet est en `border-bottom` et la DERNIERE ligne le retire : un filet sous la derniere ligne
   fermerait la liste comme une boite, alors que le bloc se pose sur le fond de la page. */
/* `--em-cb-list-max` n'est emise que si le reglage est > 0, donc SANS repli ici : `max-width: var(--x)`
   sur une variable absente rend la declaration invalide, et la liste occupe toute la largeur — exactement
   ce que « 0 = pleine largeur » doit produire. C'est le repli le plus simple : l'absence de regle.

   `margin-inline: auto` est inoffensif sans largeur maximale (une boite deja pleine largeur n'a rien a
   centrer) et indispensable avec : c'est lui qui pose la liste sous le MILIEU de la barre, comme le
   modele de reference, et non collee a gauche. */
.em-cb__list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: var(--em-cb-list-max);
  margin-inline: auto;
}
/* Meme reservation que la barre, et le meme raisonnement : la liste est la plus grande des deux zones,
   donc c'est elle qui deplacerait le plus. Quatre lignes a 68 px, valeur PROPHYLACTIQUE et variable —
   aucun releve ne l'a signalee, elle est ajustable sans toucher au plugin. */
.em-cb__list:not(:has(*)) { min-height: var(--em-cb-reserve, 272px); }
.em-cb__row {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  padding: 1.25rem .125rem;
  border-bottom: 1px solid var(--em-cb-line, var(--em-color-border));
}
.em-cb__row:last-child { border-bottom: 0; }
/* DATE ET TITRE EMPILES, le bouton a droite. `min-width: 0` OBLIGATOIRE dans un flex : sans lui un titre
   long pousse le bouton hors de la boite au lieu de passer a la ligne. */
.em-cb__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .5rem;
}
/* PASTILLE, et non un simple texte colore : c'est un pave pose sur la ligne, donc il lui faut un fond et
   un retrait. `align-items: flex-start` sur le conteneur l'empeche de s'etirer sur toute la largeur — un
   fond qui court jusqu'au bouton ne se lirait plus comme une etiquette.

   ⚠ COINS CARRES, et c'est la maquette : `--em-radius` arrondirait la pastille comme une carte. Le nom
   « pastille » ne decide de rien, le dessin si.

   `white-space: nowrap` : une date coupee en deux lignes doublerait la hauteur de la pastille. */
.em-cb__date {
  display: inline-block;
  padding: .375rem .625rem;
  background: var(--em-cb-date-bg, var(--em-color-bg));
  color: var(--em-cb-date, var(--em-cb-accent, var(--em-color-secondary)));
  font-size: .875rem;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}
.em-cb__title {
  margin: 0;
  font-size: var(--em-cb-title-size, 1.5rem);
  font-weight: 800;
  line-height: 1.25;
  color: inherit;
}
/* Champs visibles (2.5.442) : horaire et campus sous le titre. Chaque morceau garde sa ligne entiere
   (`nowrap`), l'ensemble enveloppe ; le point median ne vit qu'ENTRE deux morceaux. */
.em-cb__meta {
  margin: 0;
  display: flex; flex-wrap: wrap; gap: .25rem .5rem;
  font-size: .875rem; color: var(--em-color-muted);
}
.em-cb__part { white-space: nowrap; }
.em-cb__part + .em-cb__part::before { content: "·"; margin-right: .5rem; }
/* `flex-shrink: 0` : un bouton d'inscription tronque est un bouton qu'on n'ose pas cliquer.
   La FORME du bouton vient entierement du systeme PARTAGE (`.em-btn-event` et ses declinaisons) : ce bloc
   n'en redefinit ni le remplissage, ni la bordure, ni la graisse.

   ⚠ IL REPOSE UN SEUL JETON, `--em-btn-c` (2.5.419), et c'est exactement ce pour quoi ce jeton existe :
   « locales a `.em-btn-event`, jamais la primaire en dur — c'est ce qui rend les declinaisons
   ORTHOGONALES au contexte ». La declinaison choisie en back-office decide toujours de la forme, ce
   reglage seulement de la couleur.

   CLASSE DOUBLEE : `.em-btn-event` pose `--em-btn-c` a (0,1,0). A specificite egale c'est l'ordre du
   fichier qui tranche — une regle qui ne gagne que parce qu'elle est ecrite plus bas cesse de gagner au
   premier deplacement. (0,2,0) le rend independant de l'ordre.

   ⚠ LE DEFAUT EST L'ACCENTUATION ET NON LA PRIMAIRE, et c'est un REPEINT DEMANDE : le bouton rendait
   `--em-color-primary` comme tous les autres blocs. La doctrine « le defaut d'un reglage neuf reproduit
   le rendu existant » protege des repeints ACCIDENTELS, pas d'un changement demande (2.5.360). */
.em-cb__cta.em-cb__cta {
  flex-shrink: 0;
  --em-btn-c: var(--em-cb-cta, var(--em-color-secondary));
}

/* DENSITE « compacte » : `em_block_density_class()` emet `em-cb--compact`. Elle resserre les lignes et
   la barre, et ne touche a aucune couleur — la densite est une question d'espacement. */
.em-cb--compact .em-cb__row { padding: .75rem .125rem; }
.em-cb--compact .em-cb__bar { gap: 1rem; }
.em-cb--compact .em-cb__title-block { margin-bottom: .75rem; }

/* ---- Sous 640 px ----
   Le bouton passe SOUS le titre et prend la pleine largeur, la date reste sur sa ligne. Un titre de deux
   ou trois lignes face a un bouton aligne a droite laisse une colonne vide au milieu ; empile, chaque
   element garde sa largeur utile. La barre, elle, defile deja horizontalement — c'est la forme qu'elle a
   des le bureau, donc rien a changer. */
/* ==========================================================================
   LES TROIS MODES DE NAVIGATION MOBILE (2.5.419)
   ==========================================================================

   Table PARTAGEE avec le widget sidebar : `em_campus_nav_modes()`. Le mode ne change RIEN au-dessus de
   640 px — les onglets y tiennent toujours — donc tout vit dans la requete media ci-dessous.

   ⚠ LE MODE « ONGLETS » N'EST PAS ECRIT : il est ce qui reste quand aucun des deux autres ne s'applique.
   Ecrit en `[data-mobile-nav="tabs"]`, il aurait disparu sur tout HTML servi depuis un cache de page
   anterieur a cet attribut — la barre y aurait perdu toute sa mise en forme d'un coup. L'ABSENCE vaut
   « onglets », comme le defaut du reglage (2.5.412). */
@media (max-width: 640px) {
  /* ---- Mode « menu deroulant » ---- */
  .em-campus-bar[data-mobile-nav="select"] .em-cb__bar,
  .em-campus-bar[data-mobile-nav="select"] .em-cb__arrow { display: none; }
  .em-campus-bar[data-mobile-nav="select"] .em-cb__barwrap { border-bottom: 0; }
  .em-campus-bar[data-mobile-nav="select"] .em-cb__select {
    display: block;
    flex: 1 1 auto;
    min-width: 0;
    padding: .625rem .75rem;
    border: 1px solid var(--em-cb-line, var(--em-color-border));
    border-radius: var(--em-radius);
    background: var(--em-color-surface);
    color: inherit;
    font: inherit;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
  }

  /* ---- Mode « liste sur deux colonnes » ----
     La barre cesse d'etre une piste : plus de defilement, donc plus de fleches, et les onglets
     s'alignent a gauche sur deux colonnes. Le filet du bas disparait — il soulignait une barre, il n'a
     rien a souligner sous une liste. */
  .em-campus-bar[data-mobile-nav="list"] .em-cb__arrow { display: none; }
  .em-campus-bar[data-mobile-nav="list"] .em-cb__barwrap { border-bottom: 0; }
  .em-campus-bar[data-mobile-nav="list"] .em-cb__bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .25rem 1rem;
    overflow: visible;
  }
  .em-campus-bar[data-mobile-nav="list"] .em-cb__tab {
    justify-content: flex-start;
    text-align: left;
    padding: .375rem 0;
    border-bottom: 0;
    white-space: normal;
  }
  /* La puce remplace le trait : sur une liste, l'actif se repere par un marqueur en tete de ligne et non
     par un soulignement, qui se lirait comme un lien. */
  .em-campus-bar[data-mobile-nav="list"] .em-cb__tab::before {
    content: "";
    display: inline-block;
    width: .375rem;
    height: .375rem;
    margin-right: .5rem;
    border-radius: 50%;
    background: currentColor;
    opacity: .35;
  }
  .em-campus-bar[data-mobile-nav="list"] .em-cb__tab--on::before { opacity: 1; }

  .em-cb__row {
    flex-wrap: wrap;
    gap: .75rem 1rem;
    padding: 1rem .125rem;
  }
  /* Le conteneur prend la ligne entiere et le bouton passe dessous, en pleine largeur : un titre de deux
     ou trois lignes face a un bouton aligne a droite laisse une colonne vide au milieu. */
  .em-cb__main  { flex: 1 1 100%; }
  .em-cb__title { font-size: var(--em-cb-title-size, 1.25rem); }
  .em-cb__cta   { width: 100%; justify-content: center; }
  .em-cb__title-block { font-size: 1.25rem; }
}

/* ==========================================================================
   BLOC « ENCART EVENEMENT » (event-row, prefixe rw) — 2.5.420
   ==========================================================================

   Un encart ENCADRE par evenement : pave de date plein a gauche, texte au centre, bouton en pastille a
   droite. Le bloc lui-meme ne peint rien — il se pose sur le fond de la page, et ce sont les ENCARTS qui
   portent le cadre. C'est ce qui rend les huit variables d'apparence partagees non ambigues ici : elles
   decrivent toutes la meme boite, l'encart.

   ⚠ LE PAVE A SES DEUX COULEURS PROPRES (`--em-rw-pill-bg` / `--em-rw-pill-color`) parce que le « fond »
   des huit partagees designe l'ENCART. Deux surfaces dans une meme boite ne peuvent pas partager un
   reglage nomme `bg` — le critere de 2.5.319, applique a l'endroit plutot que subi. */
.em-event-row {
  color: var(--em-rw-text, var(--em-color-text));
}
.em-rw__title-block {
  color: var(--em-rw-title, var(--em-color-primary));
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.25;
  margin: 0 0 1rem;
}
.em-rw__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .75rem;
}

/* ---- L'encart ----
   `align-items: stretch` : c'est ce qui fait monter le pave de date sur TOUTE la hauteur de l'encart,
   comme la maquette. En `center`, il flotterait au milieu d'un cadre plus haut que lui des que la
   description passe sur trois lignes.

   `overflow: hidden` : le pave porte le rayon de l'encart par heritage de decoupe. Sans lui, ses coins
   carres depasseraient des coins arrondis du cadre — visible au pixel, et seulement sur certains rayons. */
.em-rw__item {
  display: flex;
  align-items: stretch;
  gap: 1.25rem;
  background: var(--em-rw-bg, var(--em-color-surface));
  border: 1px solid var(--em-rw-border, var(--em-color-border));
  border-radius: var(--em-rw-radius, var(--em-radius));
  box-shadow: var(--em-rw-shadow, none);
  padding: var(--em-rw-pad, 0);
  overflow: hidden;
}

/* ---- Le pave de date ----
   `flex: 0 0 auto` et un plancher de largeur : deux encarts voisins doivent aligner leur texte, or « 3
   mai » et « 12 decembre » n'ont pas la meme largeur. Sans plancher, chaque encart placerait son texte a
   un endroit different et la pile perdrait sa colonne. */
.em-rw__pill {
  flex: 0 0 auto;
  min-width: 9.375rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .125rem;
  padding: 1.125rem 1.25rem;
  background: var(--em-rw-pill-bg, var(--em-color-primary));
  color: var(--em-rw-pill-color, var(--em-on-dark));
  text-align: center;
}
.em-rw__day  { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
.em-rw__time { font-size: .875rem; opacity: .9; }

/* ---- Le corps ----
   `min-width: 0` OBLIGATOIRE dans un flex : sans lui une description longue pousse le bouton hors du
   cadre au lieu de passer a la ligne. */
.em-rw__body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .25rem;
  padding: 1rem 0;
}
.em-rw__text { margin: 0; font-size: 1rem; line-height: 1.5; }
/* Couleur PRIMAIRE depuis la 2.5.445 (demande), par la MEME chaine que le titre du bloc : le reglage « Couleur
   des titres » de l'encart, puis la primaire de la charte. Elle suivait l'accent, qui colore desormais le
   bouton — titre et bouton de la meme couleur se seraient confondus. */
.em-rw__name { font-weight: 800; color: var(--em-rw-title, var(--em-color-primary)); font-size: 1.125rem;}
.em-rw__meta {
  margin: 0;
  font-size: .875rem;
  color: var(--em-color-muted);
}

/* ---- Le bouton ----
   MEME ALLURE QUE « Liste d'inscriptions » (`.em-sg__cta`) depuis la 2.5.444, a la demande : contour et
   texte en couleur d'accent, capitales, aplat au survol. Toutes les valeurs passent par des variables —
   `--em-rw-accent` (reglage « Couleur d'accent » du bloc) puis la secondaire de la charte.

   ⚠ C'est l'APPARENCE PROPRE du bloc, celle qui s'applique quand « Declinaison des boutons » est vide.
   Une declinaison choisie en back-office (`em-btn-event--solid` / `--outline` / `--ghost`, doublees et
   placees apres) la remplace : le systeme de boutons PARTAGE garde la main.
   ⚠ Le bouton est en CONTEXTE CLAIR : `.em-btn-event` seul n'y pose ni epaisseur ni couleur de bordure
   (lecon de 2.5.426), donc la regle les ecrit TOUTES.

   `align-self: center` et `margin-right` : il flotte dans l'encart, il ne le remplit pas. */
.em-rw__cta {
  flex: 0 0 auto;
  align-self: center;
  margin-right: .625rem;
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .5rem 1rem;
  border: 1px solid var(--em-rw-accent, var(--em-color-secondary));
  border-radius: var(--em-radius);
  color: var(--em-rw-accent, var(--em-color-secondary));
  background: transparent;
  font-size: .875rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .03em;
  text-decoration: none;
  transition: background var(--em-transition), color var(--em-transition);
}
.em-rw__cta:hover,
.em-rw__cta:focus-visible {
  background: var(--em-rw-accent, var(--em-color-secondary));
  color: var(--em-rw-bg, var(--em-color-surface));
}

/* DENSITE « compacte » : `em_block_density_class()` emet `em-rw--compact`. Elle resserre, et ne touche a
   aucune couleur — la densite est une question d'espacement. */
.em-rw--compact .em-rw__list { gap: .5rem; }
.em-rw--compact .em-rw__pill { padding: .75rem 1rem; }
.em-rw--compact .em-rw__body { padding: .75rem 0; }

/* ---- Sous 640 px ----
   L'encart s'empile : le pave prend toute la largeur en tete, le texte dessous, le bouton en pleine
   largeur en pied. Garder les trois colonnes reduirait le texte a une gouttiere de quelques mots. */
@media (max-width: 640px) {
  .em-rw__item {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  .em-rw__pill {
    min-width: 0;
    flex-direction: row;
    justify-content: flex-start;
    gap: .5rem;
    padding: .75rem 1rem;
  }
  .em-rw__body { padding: 1rem; }
  .em-rw__cta {
    align-self: stretch;
    justify-content: center;
    margin: 0 1rem 1rem;
  }
}

/* ==========================================================================
   BLOC « CARROUSEL SUR IMAGE » (photo-carousel, prefixe ph) — 2.5.420
   ==========================================================================

   UN evenement par slide, dont le VISUEL occupe tout le cadre ; la carte flotte par-dessus. C'est la seule
   composition du plugin ou l'image est le DECOR et non un contenu — `posters` la met dans la carte,
   `spotlight` et `hero-carousel` sont des panneaux de couleur pleine.

   La coquille (`templates/carousel-slides.php`) et la mecanique (`wireSlides()`) sont PARTAGEES avec les
   deux autres blocs a slides : seule la mise en page differe, et elle vit ici. */
.em-photo-carousel {
  position: relative;
  /* La bordure est LUE ici : emise sans etre lue, elle serait un reglage sans aucun effet — et la
     garde meta des variables mortes l'a dit des le premier rendu. */
  border: 1px solid var(--em-ph-border, transparent);
  background: var(--em-ph-bg, transparent);
  border-radius: var(--em-ph-radius, var(--em-radius));
  box-shadow: var(--em-ph-shadow, none);
  padding: var(--em-ph-pad, 0);
  color: var(--em-ph-text, var(--em-color-text));
}
.em-ph__panel-title {
  color: var(--em-ph-title, var(--em-color-primary));
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.25;
  margin: 0 0 1rem;
}

/* ---- La fenetre ----
   `overflow: hidden` PLUS un rayon : c'est lui qui fait que les coins de l'image suivent ceux du bloc. La
   piste, elle, est translatee en pixels par `wireSlides()` — elle ne doit donc jamais porter de rayon,
   sinon on verrait un coin arrondi glisser au milieu du cadre. */
.em-ph__viewport {
  position: relative;
  overflow: hidden;
  border-radius: var(--em-ph-radius, var(--em-radius));
}
.em-ph__track {
  display: flex;
  transition: transform .45s ease;
}
/* `flex: 0 0 100%` : une slide occupe EXACTEMENT la fenetre. `wireSlides()` translate d'un multiple de
   `offsetWidth`, donc toute autre largeur decalerait progressivement la piste (defaut de 2.5.230). */
.em-ph__slide {
  position: relative;
  flex: 0 0 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--em-ph-h, 380px);
  padding: 2rem;
  box-sizing: border-box;
  background-color: var(--em-color-primary);
  background-size: cover;
  background-position: center;
}
/* ---- Le voile ----
   Un NŒUD a part et non une ombre portee sur la carte : il doit couvrir l'IMAGE, pas la carte, et un
   `box-shadow` de la carte ne peut pas assombrir ce qui est derriere elle a cette distance.

   `--em-ph-veil` est TOUJOURS emise, y compris a 0 — le repli du CSS vaut .35, donc l'omettre a zero
   rendrait « aucun voile » inexprimable. C'est la distinction que `fg_title_max` a du faire en 2.5.397. */
.em-ph__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, .15), rgba(0, 0, 0, .55));
  opacity: var(--em-ph-veil, .35);
  pointer-events: none;
}

/* ---- La carte ----
   `position: relative` : sans lui elle passerait SOUS le voile, qui est absolu et vient apres elle dans
   l'ordre de peinture. Le defaut serait une carte grisee dont personne ne comprend la teinte. */
.em-ph__card {
  position: relative;
  z-index: 1;
  max-width: 26rem;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  padding: 2rem 1.75rem;
  background: var(--em-ph-card-bg, var(--em-color-surface));
  border-radius: var(--em-radius);
  box-shadow: 0 1.25rem 3rem rgba(0, 0, 0, .25);
  text-align: center;
}
.em-ph__type {
  margin: 0;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--em-ph-accent, var(--em-color-secondary));
}
.em-ph__name  { margin: 0; font-size: 1.25rem; font-weight: 800; line-height: 1.25; }
/* Date et horaire sur DEUX lignes depuis la 2.5.421 : meme traitement, donc UNE regle — deux blocs de
   declarations identiques sont exactement ce que la garde des doublons cherche (2.5.347). La carte etant
   une colonne flex a `gap: .5rem`, aucune marge n'est necessaire pour les separer. */
.em-ph__when,
.em-ph__time  { margin: 0; font-size: 1rem; color: var(--em-color-muted); }
.em-ph__place { margin: 0; font-size: .875rem; color: var(--em-color-muted); }
.em-ph__cta   { margin-top: .5rem; border-radius: 999px; padding: .625rem 1.5rem; }

/* ---- La carte se place, elle ne s'aligne pas ----
   `justify-content` sur la SLIDE et non une marge sur la carte : une marge automatique se comporterait
   differemment selon la largeur restante, et la carte ne tiendrait pas la meme position d'une taille
   d'ecran a l'autre. */
.em-photo-carousel[data-card-pos="left"]  .em-ph__slide { justify-content: flex-start; }
.em-photo-carousel[data-card-pos="right"] .em-ph__slide { justify-content: flex-end; }

/* ---- Les fleches, POSEES SUR L'IMAGE ----
   Le conteneur couvre toute la fenetre et ne prend AUCUN clic (`pointer-events: none`) : sans cela, il
   masquerait la slide entiere et le lien de la carte deviendrait incliquable. Les boutons, eux, les
   reprennent. C'est le seul moyen de les ecarter aux deux bords sans les sortir du flux. */
.em-ph__arrows {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1rem;
  pointer-events: none;
}
.em-ph__arrow {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--em-color-surface);
  color: var(--em-color-primary);
  box-shadow: 0 .25rem 1rem rgba(0, 0, 0, .25);
  cursor: pointer;
  transition: opacity var(--em-transition), transform var(--em-transition);
}
.em-ph__arrow:hover { transform: scale(1.06); }
.em-ph__arrow:focus-visible {
  outline: 2px solid var(--em-color-accent);
  outline-offset: 2px;
}
/* DESACTIVEE et non masquee : en bout de course il y a bien une commande, momentanement indisponible —
   c'est ce qui dit qu'on a tout vu. Masquer serait le choix d'une barre d'onglets (2.5.419). */
.em-ph__arrow[disabled] { opacity: .4; cursor: default; transform: none; }

/* ---- Le pied ---- */
/* ⚠ CENTRE et non `space-between` (2.5.421, demande) : les pastilles sont la seule commande du pied de
   ce bloc — ses fleches sont sur l'image — donc les pousser a gauche les laissait seules a un bord. Les
   deux autres blocs a slides gardent `space-between`, leur pied portant DEUX groupes.

   Le conteneur de droite n'est plus rendu quand il est vide (cf. `carousel-slides.php`) : sans cela son
   `gap` decalerait ce centrage d'un demi-ecart. */
.em-ph__foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 1rem;
}
.em-ph__dots { display: flex; align-items: center; gap: .5rem; }
.em-ph__dot {
  width: .625rem;
  height: .625rem;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--em-color-border);
  cursor: pointer;
  transition: background var(--em-transition), width var(--em-transition);
}
.em-ph__dot.active {
  width: 1.5rem;
  background: var(--em-ph-accent, var(--em-color-secondary));
}
.em-ph__dot:focus-visible { outline: 2px solid var(--em-color-accent); outline-offset: 2px; }

/* DENSITE « compacte » : `em_block_density_class()` emet `em-ph--compact`. */
.em-ph--compact .em-ph__slide { min-height: var(--em-ph-h, 260px); padding: 1.25rem; }
.em-ph--compact .em-ph__card  { padding: 1.25rem 1rem; }

/* ---- Sous 640 px ----
   Les fleches passent DANS le flux, sous l'image : posees dessus, deux boutons de 44 px mangent la moitie
   d'une carte de telephone, et le doigt qui veut faire defiler la page en touche forcement un. */
@media (max-width: 640px) {
  .em-ph__slide { min-height: var(--em-ph-h, 300px); padding: 1.25rem; }
  .em-ph__card  { padding: 1.5rem 1.25rem; max-width: none; }
  .em-ph__arrows {
    position: static;
    justify-content: center;
    gap: .75rem;
    padding: 0;
    margin-top: .75rem;
  }
}

/* ---- SANS IMAGE : la slide n'est plus une surface (2.5.422) ----

   L'aplat et les 380 px etaient la place de l'IMAGE, pas celle de la carte. Sans visuel, ils laissaient un
   panneau bleu marine que personne n'avait choisi, bien plus haut que son contenu — c'est ce qui a ete
   signale. La classe est posee par le JS sur la presence du FOND, donc elle couvre aussi un evenement sans
   vignette au milieu d'evenements qui en ont.

   ⚠ CLASSE DOUBLEE, et c'est necessaire : `.em-ph--compact .em-ph__slide` pese (0,2,0) et la regle mobile,
   a poids egal, vient PLUS BAS dans le fichier. A une seule classe cette regle perdrait les deux fois —
   sur telephone et en densite compacte, c'est-a-dire precisement la ou l'aplat se remarque le plus.

   `min-height` garde la variable : une hauteur REGLEE reste une demande explicite, et son repli devient
   simplement « la hauteur du contenu » au lieu de « la place d'une image ». Meme convention qu'ailleurs —
   `0` veut dire « le CSS decide », et ici le CSS decide autre chose. */
.em-ph__slide--flat.em-ph__slide--flat {
  background-color: transparent;
  min-height: var(--em-ph-h, 0);
}

/* =========================================================================
   RUBAN D'ANNONCE (2.5.423) — bloc `announce-ribbon`, prefixe `rb`
   -------------------------------------------------------------------------
   DEUX surfaces cote a cote : un encadre clair qui porte le libelle fixe, et une bande coloree qui
   porte une ligne par evenement. C'est cette DUALITE qui explique pourquoi le bloc n'est pas declare
   dans `em_block_appearance_blocks()` — « fond » et « bordure » n'y designeraient pas une seule chose
   (le critere de 2.5.319) — et pourquoi il a six couleurs NOMMEES a la place.
   ========================================================================= */
.em-announce-ribbon {
  display: flex;
  align-items: stretch;
  /* Les HUIT variables `--em-rb-*` du panneau d'apparence PARTAGE. Sans ces lectures elles seraient
     emises et mortes — le defaut de 2.5.311, quatorze reglages sans aucun effet. Le fond ne se voit
     qu'avec un retrait, les deux surfaces recouvrant sinon la racine exactement : l'indice le dit. */
  background: var(--em-rb-bg, transparent);
  color: var(--em-rb-text, var(--em-color-text));
  border: 0 solid var(--em-rb-border, transparent);
  border-radius: var(--em-rb-radius, 0);
  padding: var(--em-rb-pad, 0);
  box-shadow: var(--em-rb-shadow, none);
}
/* ---- L'encadre du libelle ----
   `flex: 0 0 auto` : il se dimensionne sur son texte, la bande prend le reste. Un pourcentage figerait
   sa largeur alors que « Prochaines rentrees » et « Nos JPO » n'ont pas la meme longueur. */
.em-rb__label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: .75rem 1.5rem;
  box-sizing: border-box;
  background: var(--em-rb-label-bg, var(--em-color-surface));
  border: 1px solid var(--em-rb-label-line, var(--em-color-border));
}
.em-rb__label-text {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 800;
  line-height: 1.25;
  color: var(--em-rb-label, var(--em-color-primary));
  white-space: nowrap;
}
/* ---- La bande ----
   `min-width: 0` INDISPENSABLE : sans lui un titre long ferait deborder le ruban au lieu de le laisser
   revenir a la ligne — un element de flex vaut `min-width: auto` (meme piege qu'au conteneur de bloc). */
.em-rb__band {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: .75rem 1.5rem;
  box-sizing: border-box;
  background: var(--em-rb-band-bg, var(--em-color-primary));
  color: var(--em-rb-band-text, var(--em-rb-text, var(--em-on-dark)));
}
.em-rb__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .375rem;
}
/* RESERVATION DE HAUTEUR (2.5.343) : la liste est rendue VIDE puis remplie par le JS, donc la bande
   s'ecraserait sur ses seuls retraits avant de se redeployer. `:not(:has(*))` et non `:empty`, qui
   echoue au premier blanc — et la reservation se LIBERE d'elle-meme a l'arrivee du premier enfant. */
.em-rb__list:not(:has(*)) { min-height: 1.5rem; }
.em-rb__item {
  font-size: 1rem;
  line-height: 1.375;
  text-align: center;
}
.em-rb__name { font-weight: 800; }
/* La casse est une MISE EN FORME : le texte reste intact dans le DOM, donc un lecteur d'ecran et un
   copier-coller rendent la casse d'origine. */
.em-announce-ribbon[data-uppercase="1"] .em-rb__name { text-transform: uppercase; }
.em-rb__sep  { font-weight: 800; }
.em-rb__date { font-weight: 800; color: var(--em-rb-date, var(--em-color-secondary)); }
/* La ligne entiere est cliquable quand la source fournit une URL. `color: inherit` : sans lui le bleu
   de lien du theme se poserait sur une bande deja sombre. */
.em-rb__link { color: inherit; text-decoration: none; }
.em-rb__link:hover,
.em-rb__link:focus-visible { text-decoration: underline; }
.em-rb__link:focus-visible { outline: 2px solid var(--em-color-accent); outline-offset: 2px; }

/* DENSITE « compacte » : `em_block_density_class()` emet `em-rb--compact`. */
.em-rb--compact .em-rb__label,
.em-rb--compact .em-rb__band { padding: .5rem 1rem; }

/* ---- « Toujours empile » (2.5.424) ----
   La MEME composition dans l'autre sens, et non un second bloc : memes deux surfaces, meme ligne, memes
   couleurs. La regle mobile ci-dessous fait la meme chose, mais sur un SEUIL DE FENETRE — elle ne peut
   donc rien pour un ruban pose dans une colonne etroite d'une page large. Poids (0,2,0) contre le (0,1,0)
   de la regle de base : elle gagne a toute largeur. */
.em-announce-ribbon[data-layout="stacked"] { flex-direction: column; }
.em-announce-ribbon[data-layout="stacked"] .em-rb__label { justify-content: center; }
.em-announce-ribbon[data-layout="stacked"] .em-rb__label-text { white-space: normal; text-align: center; }

/* ---- Sous 768 px : le libelle passe AU-DESSUS ----
   C'est la seconde maquette. Un `flex-wrap` aurait fait basculer a une largeur imprevisible, qui depend
   de la longueur du libelle : un seuil nomme se verifie et se documente. */
@media (max-width: 768px) {
  .em-announce-ribbon { flex-direction: column; }
  .em-rb__label { justify-content: center; }
  .em-rb__label-text { white-space: normal; text-align: center; }
}

/* =========================================================================
   PANNEAU DE DATES (2.5.425) — bloc `dates-panel`, prefixe `dp`
   -------------------------------------------------------------------------
   UNE surface, donc `dp` EST declare dans `em_block_appearance_blocks()` : « fond », « bordure »,
   « arrondi » et « ombre » y designent le panneau, sans ambiguite (le critere de 2.5.319).
   ========================================================================= */
.em-dates-panel {
  position: relative;
  background: var(--em-dp-bg, var(--em-color-primary));
  color: var(--em-dp-text, var(--em-on-dark));
  border-radius: var(--em-dp-radius, var(--em-radius));
  box-shadow: var(--em-dp-shadow, none);
  padding: var(--em-dp-pad, 2rem);
  box-sizing: border-box;
}
/* ---- Le filet, INSET et non sur le bord ----
   C'est ce que montre la maquette : le fond deborde du cadre de tous cotes. Un `border` ordinaire se
   pose sur l'arete, donc il ne peut pas rendre cette composition — d'ou le pseudo-element.

   ⚠ LA VARIABLE NE CHANGE PAS. C'est toujours `--em-dp-border`, celle du reglage « Bordure » du
   panneau d'apparence, et `em_appearance_border_target()` annonce deja qu'elle designe « le cadre du
   panneau entier ». Deplacer le TRAIT sans deplacer la VARIABLE est ce qui garde le reglage branche.

   Son repli est `--em-on-dark`, le JETON du texte pose sur panneau sombre, et NON
   `--em-on-dark-line` : celui-ci vaut .18 et sert a separer deux lignes, pas a dessiner un cadre
   — a cette opacite le filet ne se voyait pas. Une garde interdit toute couleur EN DUR sur un
   bloc sombre, et elle a raison : un cinquieme jeton `on-dark` se serait ajoute aux quatre pour
   un seul usage.

   `pointer-events: none` : le cadre couvre toute la surface, donc sans lui il intercepterait le clic
   du bouton de pied. */
.em-dates-panel::before {
  content: "";
  position: absolute;
  inset: .75rem;
  border: 1px solid var(--em-dp-border, var(--em-on-dark));
  border-radius: calc(var(--em-dp-radius, var(--em-radius)) / 2);
  pointer-events: none;
}
.em-dp__title {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.25;
  text-align: center;
  color: var(--em-dp-title, var(--em-on-dark));
}
.em-dp__subtitle {
  margin: .25rem 0 0;
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: center;
  color: var(--em-dp-subtitle, var(--em-dp-text, var(--em-on-dark)));
}
.em-dp__list {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.em-dp__item {
  display: flex;
  align-items: center;
  gap: .75rem;
  min-width: 0;
}
.em-dp__date {
  flex: 0 0 auto;
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.125;
  color: var(--em-dp-date, var(--em-dp-accent, var(--em-on-dark)));
}
/* ⚠ LE FILET EST POSE PAR UN SELECTEUR DE FRERE ADJACENT, et c'est ce qui le fait disparaitre de
   lui-meme quand la case « Date » est decochee : sans `.em-dp__date` avant lui, la regle ne s'applique
   pas. Le calculer en JS aurait demande une condition de plus, donc une occasion de plus de laisser un
   filet orphelin (2.5.391). */
.em-dp__text {
  min-width: 0;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  line-height: 1.375;
}
.em-dp__date + .em-dp__text {
  border-left: 1px solid var(--em-dp-line, var(--em-on-dark-line));
  padding-left: .75rem;
}
.em-dp__cta { margin-top: 1.5rem; }

/* DENSITE « compacte » : `em_block_density_class()` emet `em-dp--compact`. */
.em-dp--compact { padding: var(--em-dp-pad, 1.25rem); }
.em-dp--compact .em-dp__list { gap: .625rem; margin-top: 1rem; }

@media (max-width: 640px) {
  .em-dp__title { font-size: 1.375rem; }
  .em-dp__subtitle { font-size: 1rem; }
  .em-dp__date { font-size: 1.25rem; }
  .em-dp__text { font-size: .875rem; }
}

/* =========================================================================
   CARTE DE DATES (2.5.425) — bloc `dates-card`, prefixe `dc`
   ========================================================================= */
.em-dates-card {
  background: var(--em-dc-bg, var(--em-color-surface));
  color: var(--em-dc-text, var(--em-color-text));
  border: 1px solid var(--em-dc-border, transparent);
  border-radius: var(--em-dc-radius, var(--em-radius));
  box-shadow: var(--em-dc-shadow, none);
  padding: var(--em-dc-pad, 1.5rem);
  box-sizing: border-box;
}
.em-dc__title {
  margin: 0 0 1rem;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--em-dc-title, var(--em-color-primary));
}
.em-dc__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .02rem;
}
.em-dc__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .375rem;
  font-size: 1rem;
  line-height: 1.5;
  min-width: 0;
}
.em-dc__place { font-weight: 800; color: var(--em-dc-place, var(--em-dc-accent, inherit)); }
.em-dc__date  { color: var(--em-dc-date, inherit); }
/* Le lien reprend la composition de la ligne : il est le SEUL enfant de `.em-dc__item` quand l'URL
   existe, donc sans ce flex le lieu et la date se colleraient sans `gap`. */
.em-dc__link {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .375rem;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
.em-dc__link:hover,
.em-dc__link:focus-visible { text-decoration: underline; }
.em-dc__link:focus-visible { outline: 2px solid var(--em-color-accent); outline-offset: 2px; }
/* ⚠ HABILLAGE PROPRE, et il est OBLIGATOIRE sur un bouton en contexte CLAIR. La base
   `.em-btn-event` ne pose ni retrait ni epaisseur de bordure — `--on-dark` le fait pour les blocs
   sombres, rien ne le fait ici. Sans cette regle, et avec le defaut `btn_variant = ''` (« apparence
   d'origine de chaque bloc »), il ne restait qu'un `border-style: solid` sans largeur ni couleur.

   Les trois declinaisons sont DOUBLEES dans leur selecteur, donc elles priment sur celle-ci des que le
   back-office en choisit une : cette regle est bien l'apparence « d'origine », pas une surcharge. */
.em-dc__cta {
  margin-top: 1.25rem;
  padding: .625rem 1.25rem;
  border-width: 1px;
  border-color: transparent;
  border-radius: var(--em-radius);
  background: var(--em-dc-accent, var(--em-color-primary));
  color: var(--em-on-dark);
  font-size: .875rem;
  font-weight: 700;
}
.em-dc__cta:hover { opacity: .85; }

.em-dc--compact { padding: var(--em-dc-pad, 1rem); }

/* =========================================================================
   CARTE A INTITULE (2.5.425) — bloc `titled-card`, prefixe `tc`
   -------------------------------------------------------------------------
   DEUX surfaces dans une meme boite — le panneau et l'encadre de l'intitule — exactement comme l'encart
   evenement et son pave de date (2.5.420) : le « fond » des huit reglages partages designe le PANNEAU,
   l'encadre a le sien (`tc_label_bg`). Deux surfaces ne peuvent pas partager un reglage nomme `bg`.
   ========================================================================= */
.em-titled-card {
  background: var(--em-tc-bg, var(--em-color-primary));
  color: var(--em-tc-text, var(--em-on-dark));
  border: 1px solid var(--em-tc-border, var(--em-on-dark-line));
  border-radius: var(--em-tc-radius, 0);
  box-shadow: var(--em-tc-shadow, none);
  padding: var(--em-tc-pad, 1.5rem);
  box-sizing: border-box;
  text-align: center;
}
.em-tc__label {
  background: var(--em-tc-label-bg, var(--em-color-surface));
  padding: .5rem 1rem;
}
.em-tc__label-text {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 800;
  line-height: 1.25;
  color: var(--em-tc-title, var(--em-color-primary));
}
.em-tc__list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .375rem;
}
.em-tc__item {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  align-items: center;
}
.em-tc__date { font-size: 1rem; font-weight: 800; color: var(--em-tc-date, var(--em-tc-accent, inherit)); }
.em-tc__meta { font-size: .875rem; opacity: .85; }
.em-tc__cta  { margin-top: 1rem; }

.em-tc--compact { padding: var(--em-tc-pad, 1rem); }

/* =========================================================================
   MASQUER UN BLOC SOUS UN SEUIL (2.5.426)
   -------------------------------------------------------------------------
   Six regles pour les six seuils partages (`em_breakpoints()`), ecrites UNE fois. Une media query ne
   peut pas lire une variable CSS : un seuil quelconque imposerait une balise `<style>` par INSTANCE
   dans le HTML de chaque page. Une garde compare cette liste a la table PHP.

   Ecrites EN FIN DE FEUILLE et sans `!important` : a specificite egale (0,1,0) avec la regle de base du
   bloc, c'est l'ORDRE qui tranche — et un `!important` empecherait un theme de reprendre la main
   legitimement.
   ========================================================================= */
@media (max-width: 439px)  { .em-hide-below-440  { display: none; } }
@media (max-width: 767px)  { .em-hide-below-768  { display: none; } }
@media (max-width: 1023px) { .em-hide-below-1024 { display: none; } }
@media (max-width: 1279px) { .em-hide-below-1280 { display: none; } }
@media (max-width: 1439px) { .em-hide-below-1440 { display: none; } }
@media (max-width: 1919px) { .em-hide-below-1920 { display: none; } }
