/* =========================================================
   UI ORGANISMES
   Habillage des sections pleine page, posé sur les composants
   Bootstrap (navbar, offcanvas, dropdown, row/col). Les fonds
   décoratifs (filigranes, dégradés) n'ont pas
   d'équivalent Bootstrap et restent ici purement custom.
   ========================================================= */

/* ---- Gouttières de grille ----
   Bootstrap .row n'applique un espacement vertical entre lignes
   qui wrappent que si --bs-gutter-y est défini (0 par défaut) ;
   l'ancien système (CSS Grid gap) espaçait uniformément les deux
   axes. On repose donc --bs-gutter-x ET --bs-gutter-y à chaque
   fois pour retrouver le même espacement une fois les cartes
   empilées en mobile. */
.ui-grid-row {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 1.5rem;
}

/* ---- Barre utilitaire ---- */
/* Rembourrage 0.5625rem et non 0.75 : 9px de part et d'autre au lieu de 12,
   soit 6px de moins sur la hauteur totale de la barre. La hauteur n'est pas
   déclarée — elle se déduit du rembourrage et du contenu — c'est donc là
   qu'on la règle. --utility-bar-h suit dans tokens.css, et ui-main.js la
   remesure de toute façon au pixel près une fois la page chargée. */
.ui-utility-bar {
  position: relative;
  z-index: 40;
  /* Le pétrole DENSE des titres de hero, pas celui des titres courants : la
     barre coiffe la page, elle gagne à être le point le plus sombre du haut. */
  background: var(--color-titre-hero);
  color: var(--white);
  padding: var(--space-2) 0;
}

/* LE FILET — la barre vidée de son contenu, réduite à 6px. Hauteur EXPLICITE
   et rembourrage à zéro : la barre pleine tire sa hauteur de son rembourrage et
   de son texte, or il n'y a plus ni l'un ni l'autre, et sans hauteur déclarée
   elle se serait effondrée à zéro.
   Elle coiffe l'espace membre, où la barre du site n'a rien à dire : son
   accroche, ses badges et son sélecteur de langue s'adressent à un visiteur qui
   n'est pas encore entré. Reste le trait, dans le même pétrole dense — ce qui
   se reconnaît d'un espace à l'autre est le bandeau sombre en tête de page, pas
   ce qu'il portait. */
.ui-utility-bar--filet {
  height: 6px;
  padding: 0;
}

.ui-utility-inner {
  position: relative;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
  display: flex;
}

/* Le libellé est en BLANC comme le reste de la barre, et non en --on-dark-soft :
   ce jeton est un blanc cassé prévu pour l'encre, et il tombait à 5,6:1 sur le
   nouveau fond. Le blanc pur y donne 12,01:1. */
.ui-utility-text {
  text-align: left;
  white-space: nowrap;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--white);
  /* +14px sur la marge d'origine (20 posés, puis 6 repris) : le libellé
     s'écarte de la gouttière pour tomber SOUS LE LOGO plutôt que sur le bord
     de l'écran. À 30px de marge il commence à 114px, exactement où commence
     le mot-symbole — les deux lignes de l'en-tête partent du même bord. */
  margin: 0 0 0 calc(var(--space-3) + 14px);
  font-size: var(--fs-caption);
}

.ui-utility-actions {
  align-items: center;
  gap: var(--space-2);
  display: flex;
}

@media (width <= 767px) {
  .ui-utility-bar {
    height: 3px;                     /* la moitié : 6px pesaient trop (30 sept. 2026) */
    padding: 0;
    overflow: hidden;
  }
  .ui-utility-inner { display: none; }
}/* ---- En-tête / Navbar ---- */
.ui-site-header {
  z-index: 30;
  min-height: var(--header-h);
  position: relative;
  background: var(--white);
  padding: 0;
}

.ui-header-inner {
  position: relative;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--header-h);
  padding-right: 13.875rem; /* réserve la place des actions (right: 2rem) : 190 + 32 pour éviter le chevauchement avec la nav */
  display: flex;
}

.ui-brand {
  position: absolute;
  z-index: 2;
  left: calc(max(32px, calc((100% - 80rem) / 2)) + 27px);
  top: 1.25rem;
  width: 198px;                         /* logo Vibe2Meet, plus allongé (3 oct. 2026) */
  display: block;
}

.ui-brand img { width: 100%; height: auto; display: block; }

/* Variante horizontale du logo. Le logo vertical par défaut est plus haut que
   la barre et déborde sous elle (position:absolute, top fixe) ; l'horizontal
   n'a pas besoin de cette place et se cale au centre — exactement le
   traitement que .ui-brand reçoit déjà sous 1200px. Le modificateur ne fait
   donc que remonter ce comportement à toutes les largeurs.
   Placé AVANT les media queries de repli : celles-ci doivent continuer à
   l'emporter en dessous de 1200px, où les deux variantes se confondent. */
.ui-brand--horizontal {
  top: 50%;
  /* Le palier a d'abord été agrandi de 20 % (180 → 216, 150 → 180, 132 → 158),
     puis réduit de 10 % : 216 → 194, 180 → 162, 158 → 142. Il reste donc à
     +8 % de sa valeur d'origine. Les trois valeurs bougent ENSEMBLE — elles
     forment une échelle, et n'en toucher qu'une ferait sauter le logo d'une
     largeur d'écran à l'autre.

     Le quatrième palier, 132px sous 560px, ne suit PAS : ce n'est pas une
     marche de l'échelle mais un plancher de collision (voir plus bas), fixé
     par la place qui reste entre la gouttière et « Créer mon profil ». Le
     réduire encore n'aurait rien résolu et aurait seulement rendu la marque
     illisible là où elle est déjà la plus petite.

     PUIS −15 % SUR TOUTE L'ÉCHELLE, plancher compris : 194 → 165, 162 → 138,
     142 → 121, 132 → 112. Le plancher suit cette fois, et ce n'est pas une
     contradiction avec ce qui précède : laissé à 132, il serait devenu PLUS
     LARGE que la marche de 767px au-dessus de lui (121), et le logo aurait
     grandi en passant sous 560px. Réduire un plancher de collision ne fait
     qu'augmenter le jeu avec « Créer mon profil ».
     LOGO VIBE2MEET (3 oct. 2026) : deux fois plus allongé que l'ancien ;
     ×1,45 sur toute l'échelle pour garder sa hauteur à l'œil, puis −10 %. */
  width: 216px;
  transform: translateY(-50%);
}

/* Deux classes pour battre ui-charte.css, chargée après cette feuille, qui
   décale la marque de -15px : ce rattrapage vise le logo VERTICAL et son
   blanc de gauche. Le lockup horizontal n'en a pas besoin — il sortirait de
   la grille du même écart.

   Il part au contraire de 8px vers la DROITE. Une marge, et non un ajout au
   « left » : celui-ci est recalculé à quatre paliers différents, et il aurait
   fallu porter les 8px dans les quatre calculs puis les y tenir accordés. La
   marge s'ajoute à ce que « left » trouve, quel qu'il soit. */
.ui-brand.ui-brand--horizontal {
  margin-left: var(--space-2);
}

/* xxl : le logo est décalé de 30px supplémentaires vers la droite
   (27 + 30 = 57px après la gouttière du conteneur). */
@media (width >= 1400px) {
  .ui-brand { left: calc(max(2rem, calc((100% - 1280px) / 2)) + var(--space-6)); }
}

.ui-nav {
  align-items: center;
  gap: var(--space-4);
  margin-left: auto;
  display: flex;
}

/* Variante : nav calée à gauche, sur le même repère que le reste du
   contenu (.ui-container). Utilisée par le header du /ds, qui n'a
   pas de panier/sélecteur de langue à droite pour justifier le
   margin-left:auto du header du site principal.
   .ui-brand est en position absolute (elle déborde volontairement
   sur le hero) et démarre quasiment au bord du conteneur (left:
   ...+27px, alors que le contenu de .ui-header-inner commence à
   +32px) : avec margin-left:0 la nav se retrouverait sous le logo.
   Le décalage ci-dessous la fait démarrer juste après (logo 148px
   + un espacement cohérent avec le gap de .ui-header-inner). */
.ui-nav.ui-nav--left { margin-left: 11.25rem; }

.ui-nav-link {
  white-space: nowrap;
  /* 16px : les entrées de menu sont la navigation principale, elles ne
     doivent pas se lire plus petit que le texte courant. */
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  position: relative;
  padding: 0;
  color: inherit;
}

.ui-nav-link:after {
  content: '';
  background: var(--color-primary);
  height: 1px;
  transition: all 0.2s;
  position: absolute;
  bottom: -9px;
  left: 0;
  right: 100%;
}

.ui-nav-link:hover { color: var(--color-primary); }
.ui-nav-link:hover:after { right: 0; }

.ui-nav-link.is-current { color: var(--color-primary); }
.ui-nav-link.is-current:after { right: 0; }

.ui-header-actions {
  position: absolute;
  right: 2rem; /* aligné sur la gouttière de .ui-container (padding-inline) → reste dans la grille */
  top: 50%;
  transform: translateY(-50%);
  align-items: center;
  gap: var(--space-3);
  display: flex;
}

.ui-menu-button {
  font-size: var(--icon-lg);   /* le bouton ne porte qu'un picto : l'échelle des pictos */
  line-height: 1;
  padding: 0;
  border: 0;
  box-shadow: none;
}

.ui-menu-button .material-symbols-outlined { font-size: inherit; vertical-align: middle; }

/* Bouton de fermeture de l'offcanvas : remplace le SVG natif de
   Bootstrap (--bs-btn-close-bg) par Material Symbols, via la
   technique de ligature (content + font-family font d'icônes).
   Boîte carrée et glyphe centré : .btn-close est en content-box chez
   Bootstrap (1em + padding), donc sa hauteur suivait la taille de police
   héritée et non celle du glyphe — le titre et la croix ne tombaient pas
   sur la même ligne optique. */
.ui-nav-offcanvas .btn-close {
  background-image: none;
  box-sizing: border-box;
  justify-content: center;
  align-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  opacity: 1;
  display: flex;
}

.ui-nav-offcanvas .btn-close::before {
  content: 'close';
  font-family: 'Material Symbols Outlined';
  font-size: var(--icon-md);
  line-height: 1;
  color: var(--text);
}

@media (width <= 1100px) {
  .ui-site-header { min-height: 62px; background: var(--white); }
  .ui-header-inner { min-height: 62px; gap: var(--space-3); padding-right: 0; }
  .ui-brand { width: 176px; top: 50%; left: 1.25rem; transform: translateY(-50%); }
  .ui-header-actions { right: 1.25rem; } /* gouttière .ui-container = 20px sous 1100px */
}
/* ---- La carte du texte ----
   Elle remplace le voile dégradé qui délavait la moitié gauche de la photo :
   le texte ne repose plus sur l'image, il repose sur un aplat opaque, et la
   lisibilité cesse de dépendre de ce qu'il y a dessous — une photo change,
   un contraste tenu au jugé se perd au premier remplacement.

   Mêmes valeurs que .ui-filtres, la carte de la jonction : même rayon, même
   fond, même ombre. Les deux se lisent alors comme une seule famille, l'une
   au-dessus de l'autre sur la même photo, et non comme deux cartouches
   dessinés séparément.

   La carte occupe TOUTE sa colonne — 6 sur 12 — sans borne de largeur en
   plus : la colonne est déjà la borne, et une seconde mesure aurait fait
   dériver la carte de la grille sur laquelle tout le reste de la page
   s'aligne. La moitié droite revient à la photo, groupe de personnes
   compris — c'est ce qu'elle a de parlant.

   Le rembourrage est celui de .ui-hero-communaute-copy, la MÊME carte posée
   sur une MÊME photo, une page plus loin : --space-5 sur les quatre côtés,
   --space-4 sous 560px. Deux cartouches qui font le même travail — rendre un
   texte lisible sur une image — n'ont pas à se rembourrer différemment ; le
   visiteur passe de la liste à la fiche d'une communauté, et rien ne doit
   bouger entre les deux.

   Le rembourrage est posé sur la CARTE et non sur la colonne, exactement
   comme là-bas : la colonne garde sa demi-gouttière, et c'est ce qui aligne
   le BORD de la carte sur la marge du conteneur — donc sur le bord de la
   carte de filtres juste dessous. Le TEXTE, lui, est en retrait de son
   rembourrage : 24px de plus vers la droite que le libellé « Rechercher »,
   qui n'a que --space-4 dans .ui-filtres. Les bords s'alignent, les textes
   non — c'est le prix de deux cartouches de rembourrages différents, et
   celui de communaute.html est la référence retenue.

   Lui donner ce fond sur la COLONNE plutôt que sur la carte ferait déborder
   l'aplat de la mesure du texte : la colonne porte déjà le décalage de
   .ui-hero--recherche .ui-hero-copy-primary. */
/* Fond et ombre sont ceux de .ui-hero-communaute-copy, au pixel : l'aplat
   blanc opaque des cartes, et --shadow-photo, l'ombre des cartes posées sur
   une image. C'est la MÊME carte, sur la MÊME sorte de photo, à une page de
   distance — le visiteur passe de la liste des évènements à la fiche d'une
   communauté, et rien ne doit changer entre les deux.

   Quatre autres états ont été essayés avant de revenir ici, et chacun a dit
   pourquoi celui-ci tient :
     · un blanc translucide à 60 % — les silhouettes du groupe traversaient
       les lettres, le texte se lisait sur du bruit ;
     · un noir translucide — lisible, mais seul objet sombre d'un site tout
       en cartes claires : une exception, pas un accent ;
     · un verre dépoli — le flou réglait le bruit, mais rien d'autre sur le
       site n'a cette matière ;
     · pas de carte du tout, texte à même la photo sous un dégradé — élégant,
       et suspendu à ce que montre l'image : la remplacer casse tout.
   L'aplat opaque est le seul qui ne dépende de rien. Une photo change ; un
   contraste tenu au jugé se perd au premier remplacement.

   Un fondu vers la transparence a été essayé sur le bord droit, du côté de la
   photo. Mesuré plutôt qu'apprécié à l'oeil : le titre s'arrête à 80 % de la
   largeur de la carte et le chapô à 91 %, alors que l'aplat commençait à
   s'éteindre dès 62 %. Les deux se terminaient donc sur du transparent — le
   défaut même que la carte est là pour supprimer, réintroduit par sa droite.
   Un fond plein n'a pas ce problème, quelle que soit la longueur du texte. */
.ui-hero-carte {
  padding: var(--space-5);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
}

/* Le texte garde ses teintes de PAGE, et donc aucune règle : sur un aplat
   blanc, l'encre du titre et le brun du chapô sont les bonnes, telles que le
   hero les pose déjà — et telles que la carte de communaute.html les porte.
   Seul le sur-titre a la sienne : le corail, comme l'accent du titre. Les
   deux marques rouges de la carte se répondent, en haut et au milieu. */
.ui-hero-carte .ui-eyebrow { color: var(--color-brand-pink-ink); }

.ui-hero-carte .ui-eyebrow { color: var(--color-brand-pink-ink); }

/* Le dernier bloc de la carte ne laisse pas de blanc sous lui : le chapô et
   la ligne d'actions portent leur marge basse pour l'espacement du hero, où
   rien ne les fermait par le bas. Dans un cartouche, cette marge devient un
   fond de carte vide, et le rembourrage paraît deux fois plus grand en bas
   qu'en haut. */
.ui-hero-carte > :last-child {
  margin-bottom: 0;
}

/* Et son palier aussi : sous 560px, .ui-hero-communaute-copy descend à
   --space-4 — 48px de chaque côté y prendraient un quart de la largeur, et
   la mesure du titre display tomberait à quelques mots par ligne. */
@media (width <= 560px) {
  .ui-hero-carte {
    padding: var(--space-4);
  }
}

@media (max-width: 1100px) and (min-width: 768px) {
  .ui-brand { width: 198px; left: 2rem; transform: translateY(calc(-50% + 20px)); }
  .ui-header-actions { transform: translateY(calc(-50% + 20px)); } /* centre vertical aligné sur le logo */
}

@media (width <= 767px) {
  .ui-brand { width: 158px; transform: translateY(calc(-50% + 10px)); }
  .ui-header-actions { transform: translateY(calc(-50% + 10px)); } /* centre vertical aligné sur le logo */
}

/* ---- Logo horizontal : sur la ligne du menu, à toutes les largeurs ----
   Les décalages ci-dessus (+20px, +10px) rattrapent l'optique du logo
   VERTICAL, dont le mot-symbole tombe sous le personnage : ils le descendent
   pour que son centre visuel retombe sur celui de la barre, et c'est ce
   débord qui lui donnait sa signature. Le lockup horizontal n'a rien à
   rattraper — appliqués à lui, ils le font simplement sortir sous la barre,
   c'est-à-dire réintroduire le débord qu'on cherche à supprimer.

   Les actions suivent : leur propre décalage n'existait que pour rester sur
   le centre du logo. « :has » les rend à leur centrage dès que la marque est
   horizontale, sans toucher au cas du logo vertical.

   Ces règles viennent APRÈS les media queries de repli, contrairement à
   .ui-brand--horizontal (déclarée plus haut, avant elles, pour que le repli
   l'emporte sur le logo vertical). L'ordre dit lequel gagne, et ici c'est
   la variante qui doit gagner. */
/* Calé sur la LIGNE DE CONTENU de .ui-container, puis décalé de 30px vers la
   droite. Le conteneur est centré sous 1320px de large et retient 24px de
   gouttière — on refait le même calcul, et le +30px se lit alors pour ce
   qu'il est : un retrait VOULU par rapport à la grille, et non le reste d'un
   calcul approximatif. Le calage historique, lui, mêlait les deux : il
   partait d'un conteneur de 1280 et ajoutait 27px, ce qui donnait 20px de
   retrait ici et 28 au-delà de 1400px, sans que rien ne dise lequel était
   l'intention.
   Ces deux règles viennent après le décalage xxl de .ui-brand, déclaré plus
   haut : à spécificité égale, c'est l'ordre qui tranche. */
@media (width >= 1101px) {
  .ui-brand--horizontal {
    left: calc(max(0px, (100% - 1320px) / 2) + var(--space-4) + 30px);
  }
}

@media (width <= 1100px) {
  /* La gouttière du conteneur tombe à 16px sous 1100px : le logo suit. */
  .ui-brand--horizontal {
    width: 180px;                       /* logo Vibe2Meet (3 oct. 2026), −10 % */
    left: calc(max(0px, (100% - 1320px) / 2) + var(--space-3) + 30px);
    transform: translateY(-50%);
  }

  .ui-site-header:has(.ui-brand--horizontal) .ui-header-actions {
    transform: translateY(-50%);
  }
}

@media (width <= 767px) {
  .ui-brand--horizontal { width: 158px; }
}

/* Sous 560px, le logo revient sur la gouttière ET renonce aux +20 % : le
   décalage de 30px le faisait passer SOUS le bouton « Créer mon profil » —
   mesuré à 21px de recouvrement sur un écran de 375. Le bloc d'actions est
   ancré à droite, c'est donc la largeur de l'écran qui décide, et à cette
   taille il n'y a plus de place à donner.

   Le seuil était à 480px et l'agrandissement l'a fait remonter à 560 : à
   519px de large, le logo à 158 recouvrait le bouton de 29px. La mesure a
   d'ailleurs montré que l'ancien logo y mordait DÉJÀ de 3px — le défaut
   existait, l'agrandissement l'a seulement rendu visible.

   Ici la largeur retombe à 132px, la valeur d'avant l'agrandissement. Ce
   n'est pas un oubli : à 388px il reste 141px entre la gouttière et le
   bouton, et 158 n'y entre pas. Un logo qui déborde sur l'action principale
   coûte plus qu'il ne rapporte. */
@media (width <= 560px) {
  .ui-brand--horizontal {
    width: 126px;                       /* Vibe2Meet, −10 % */
    left: calc(max(0px, (100% - 1320px) / 2) + var(--space-3));
  }

  /* Le décalage de 8px vers la droite tombe ici aussi. C'est le même palier
     que la largeur : sous 560px, il ne reste plus rien à donner — mesuré, les
     8px transformaient un jeu de 2px en un recouvrement de 6 sur « Créer mon
     profil » à 388px de large. Deux classes, pour battre la règle générale
     que ui-charte.css charge après cette feuille. */
  .ui-brand.ui-brand--horizontal {
    margin-left: 0;
  }
}

/* ---- Le lien de texte de l'en-tête, sur les écrans étroits ----
   Le bloc d'actions est ancré à DROITE et grandit vers la gauche : sous
   480px, « Se connecter » finit sur le logo — les deux se lisaient l'un
   par-dessus l'autre, ce qui donnait au mot-symbole son air dédoublé. Le
   défaut est antérieur au logo horizontal ; il se voyait déjà avec le
   vertical.

   Le bouton, lui, reste : c'est l'action que la page cherche à obtenir.
   Attention, conséquence à connaître : sous 480px, « Se connecter » n'a
   plus d'entrée dans l'en-tête, et le panneau mobile ne le porte pas
   davantage — il faudrait l'ajouter à « panelActions » dans les données. */
@media (width <= 479.98px) {
  .ui-header-link { display: none; }
}

/* Deux navigations distinctes : la barre horizontale en desktop, le panneau
   Offcanvas (ouvert à droite) en dessous de la bascule. Bootstrap masque déjà
   le bouton burger au-delà via .navbar-expand-xl ; reste à masquer la barre
   horizontale en deçà. */
@media (width < 1200px) {
  .ui-nav { display: none; }
}

/* Panneau de navigation mobile : le menu vertical y respire davantage que
   dans une colonne de documentation. */
/* Titre et croix sur la même ligne optique : .offcanvas-header centre déjà ses
   enfants, il reste à annuler la marge basse du titre. Sa taille reste celle du
   h3 des fondations — aucune surcharge typographique ici. */
.ui-nav-offcanvas .offcanvas-header {
  align-items: center;
}

.ui-nav-offcanvas .offcanvas-title {
  margin: 0;
}

.ui-nav-offcanvas .ui-side-nav .nav-link {
  padding: var(--space-3) var(--space-3);
  font-size: var(--fs-base);
}

/* Toute la carte du panneau est sur blanc — en-tête comprise. Seule la seconde
   section porte le fond gris. La colonne permet de la caler en bas du panneau. */
.ui-nav-offcanvas,
.ui-nav-offcanvas .offcanvas-header,
.ui-nav-offcanvas .offcanvas-body {
  background: var(--white);
}

.ui-nav-offcanvas .offcanvas-body {
  flex-direction: column;
  display: flex;
}

/* Seconde section du panneau : sous 768px la barre utilitaire se replie à 6px
   (ses entrées sont masquées). Le panneau les reprend — zone de livraison,
   compte, langue — sur un fond gris, derrière un filet séparateur. Au-dessus,
   la barre utilitaire est visible : la section ferait doublon.
   Marges négatives : la section rejoint les bords du panneau (l'.offcanvas-body
   porte le padding de Bootstrap) tout en réalignant son contenu dessus. */
.ui-nav-panel-utility {
  display: none;
  background: var(--surface-douce);
  margin: auto calc(-1 * var(--bs-offcanvas-padding-x, 1rem)) calc(-1 * var(--bs-offcanvas-padding-y, 1rem)); /* auto en haut : calée en bas du panneau */
  padding: 0 var(--bs-offcanvas-padding-x, 1rem) var(--space-4);
}

@media (width <= 767px) {
  .ui-nav-panel-utility { display: block; }
}

.ui-nav-panel-divider {
  border: 0;
  border-top: 1px solid var(--border);
  opacity: 1;
  /* pleine largeur : annule le padding latéral de la section */
  margin: 0 calc(-1 * var(--bs-offcanvas-padding-x, 1rem)) var(--space-3);
}/* « Votre langue : » + sélecteur, en pied de panneau, alignés à droite. */
.ui-nav-panel-language {
  justify-content: flex-end;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  padding: 0 var(--space-3); /* aligné sur le retrait des .nav-link du panneau */
  font-size: var(--fs-sm);
  color: var(--text);
  display: flex;
}

/* ---- Hero ---- */
.ui-hero-scroll-zone { position: relative; }

/* Dégradé du fond de page vers le second niveau de retrait. Il court sur
   TOUTE la zone — hero et barre de recherche — et non sur la seule <section> :
   la carte est hors de celle-ci, un fond arrêté au bas de la section poserait
   une couture en travers de la page, au ras de la carte, là où elle ne couvre
   pas la largeur.

   Deux teintes pleines, sans « transparent » en point d'arrivée : transparent
   est un NOIR transparent, et voilerait de gris un dégradé parti du crème. */
/* Du BLANC pur en haut — qui prolonge l'en-tête sans couture — à la teinte
   froide de la charte en bas (--section-teinte-7, #e1eced, le pétrole à 12 %).
   La page s'ouvre donc sur la couleur qui la scande ensuite, la même servant
   de fond aux sections FAQ, et non sur une teinte de plus.

   ESSAI FAIT ET ÉCARTÉ : ce pied a été passé au rose de la marque
   (--section-teinte-1, #ffe8ee) pour réchauffer le premier écran — les six
   bandes teintées du site public sont toutes ce même bleu, et le logo porte
   pourtant deux couleurs. Techniquement l'essai tenait : force du dégradé
   conservée (1,17 contre 1,21), aucun échec de contraste.
   Il a été refusé pour une raison qui n'est pas colorimétrique : sur un site
   de rencontre, un premier écran rose GENRE le produit. Il le fait lire comme
   s'adressant aux femmes, alors qu'il s'adresse à tout le monde. Le rose
   reste donc un accent — CTA, smiley du logo, chiffre du titre — et ne
   devient jamais une surface.
   Le besoin de chaleur, lui, reste entier : c'est du côté de l'ivoire
   (--section-papier, #faf8f4) qu'il faudra le chercher, une teinte qui
   réchauffe sans genrer. */
.ui-hero-scroll-zone--degrade {
  background: linear-gradient(to bottom, var(--white), var(--section-teinte-7));
  /* Le dégradé s'arrête à MI-CARTE et non au bas de la zone : c'est là que la
     limite entre le hero et la première section doit se lire, la carte de
     recherche la traversant par le milieu.

     On raccourcit la PEINTURE, pas la boîte. Remonter la section suivante
     d'une marge négative aurait marché aussi, mais elle n'a pas toujours de
     fond — sur l'accueil elle est transparente — et la limite se serait alors
     lue au même endroit qu'avant. Ce qui se voit ici, c'est où le dégradé
     s'arrête ; c'est donc lui qu'on règle.

     --search-bar-h est mesuré par ui-main.js. À 0px (sans JavaScript), le
     dégradé reprend toute la hauteur : la limite retombe au bas de la carte,
     comme avant. Dégradé, pas cassé. */
  background-size: 100% calc(100% - var(--search-bar-h, 0px) / 2);
  background-repeat: no-repeat;
}

/* Et la section doit LAISSER PASSER ce fond. ui-charte.css déclare
   « .ui-hero { background: var(--white) } » — un aplat opaque, posé
   là pour que le hero porte la couleur du papier au lieu d'en hériter. Il
   recouvrait les 642px hauts de la zone : le dégradé n'apparaissait plus que
   dans la bande de la barre de recherche, sous la section, et le hero
   paraissait uni.

   Sélecteur à deux classes, comme pour .ui-hero.ui-hero--recherche : la
   charte est chargée après cette feuille, une seule classe perdrait
   l'arbitrage. Et la portée est bien le hero À FOND PROPRE — partout
   ailleurs, l'aplat de la charte reste ce qu'il doit être. */
.ui-hero-scroll-zone--degrade .ui-hero {
  background: transparent;
}

.ui-hero {
  /* Deux colonnes centrées : le texte à gauche, le visuel à droite. La charte
     ne met PAS d'image en fond de hero — pas d'image pleine largeur, donc pas
     de voile pour rattraper la lisibilité du texte par-dessus. Le hero repose
     sur le fond de page comme n'importe quelle section. */
  position: relative;
  min-height: 680px;
  margin-top: var(--space-4);              /* 24px */
  padding-block: var(--space-6);
  display: flex;
  align-items: center;
}

@keyframes ui-hero-in { to { opacity: 1; } }

/* ---- Image de fond du hero ----
   La charte ne met PAS d'image en fond de hero : c'est l'exception d'une page
   de liste, où la photo dit le sujet plus vite qu'un visuel de côté. Elle se
   paie d'un voile — sans lui, le texte dépend de ce qu'il y a sous lui, et
   une photo change.

   Une <img> et non un background-image : le build ne pose ses ?v= que sur
   « src » et « srcset ». Une URL enfermée dans un style en ligne échapperait
   au cache-busting, et un remplacement de photo resterait invisible chez ceux
   qui ont déjà vu la page.

   Deux classes pour le fond transparent : ui-charte.css est chargée après
   cette feuille et y pose « .ui-hero { background: var(--white) } », un
   aplat opaque qui recouvrirait la photo. Le sélecteur « :has » monte à deux
   classes et tranche — la portée reste le seul hero À IMAGE. */
/* Le hero d'une page de LISTE prend la hauteur de celui de l'accueil, et non
   une hauteur à lui. C'est une contrainte de PARCOURS, pas d'esthétique : on
   passe de l'accueil aux évènements par la navigation, et deux bandes de tête
   inégales font sauter la page d'un écran à l'autre — la barre change de
   place, la première rangée de résultats aussi.

   Il a longtemps porté un calcul de sa façon — une part de la hauteur de
   fenêtre, moins l'en-tête, moins la barre, plus une série de retouches. Le
   calcul suivait le viewport, l'accueil suivait son contenu, et les deux ne
   se rejoignaient à aucune taille d'écran. Une hauteur imposée des deux côtés
   est le seul accord possible.

   Le rembourrage est ÉGAL en haut et en bas : c'est lui qui centre la carte
   du texte sur la hauteur, la rangée étant en align-items:center. Il passe
   par la même règle à trois classes que le min-height, parce que
   « .ui-hero.ui-hero--recherche { padding-bottom: 0 } » attend plus bas dans
   cette feuille et l'emporterait autrement. */
.ui-hero.ui-hero--recherche:has(.ui-hero-fond) {
  min-height: var(--hero-tete-h);
  padding-block: var(--space-6);
}
/* Et sa BARRE prend la hauteur de celle de l'accueil, pour la même raison :
   sans cela, les deux heros seraient d'accord et les deux barres non — la
   ligne de flottaison entre la bande de tête et le contenu se déplacerait
   encore. La carte de filtres porte moins de champs qu'une barre de
   recherche ; c'est un plancher, pas une hauteur fixe, et ses commandes se
   centrent dedans. */
.ui-hero-recherche .ui-filtres {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: var(--hero-tete-barre-h);
}

.ui-hero:has(.ui-hero-fond) {
  background: transparent;
  /* Et la marge de 30px qui sépare le hero de l'en-tête disparaît : elle est
     faite pour un hero posé sur le fond de page, où elle donne de l'air. Avec
     une photo, elle laisse au contraire une bande claire sous la barre de
     navigation — et l'en-tête paraît plus haut que sur les autres pages,
     alors qu'il mesure exactement pareil (145px partout). La photo part donc
     du ras de l'en-tête. */
  margin-top: 0;
}

/* z-index POSITIF, et c'est le point délicat : en négatif, l'image passait
   derrière le fond de .ui-hero-scroll-zone — la zone est en position relative
   sans z-index, elle ne crée donc pas de contexte d'empilement, et un enfant
   négatif remonte le peindre en dessous. Elle était invisible. En positif,
   elle se pose au-dessus du fond de la zone et sous .ui-hero-content, qui est
   déjà en z-index 2. */
.ui-hero-fond {
  position: absolute;
  z-index: 0;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 55%;
}

/* La photo DESCEND SOUS LA SECTION, de la demi-hauteur de la barre — c'est ce
   qui fait que la carte de filtres se pose à moitié sur elle et à moitié sur
   la section suivante. Sans ce débord, l'image s'arrêtait au bas de la
   section, c'est-à-dire au HAUT de la carte : la barre paraissait posée sous
   le hero, pas à cheval.

   Le débord tombe exactement là où commence la section suivante — elle est
   remontée de la même demi-hauteur — donc rien ne se recouvre : les deux
   fonds se rencontrent au milieu de la carte. --search-bar-h est mesuré par
   ui-main.js ; à 0px (sans JavaScript), l'image s'arrête au bas de la section
   et la carte retombe dessous. Dégradé, pas cassé.

   C'est la HAUTEUR qu'on augmente, et non « bottom » : sur un élément
   remplacé — une image — top et bottom ne suffisent pas à décrire la boîte,
   le navigateur retombe sur les dimensions intrinsèques du fichier. La photo
   dépassait alors de 250px au lieu de 63. */
.ui-hero--recherche .ui-hero-fond {
  height: calc(100% + var(--search-bar-h, 0px) / 2);
}

/* Le voile part du blanc à gauche, où vit le texte, et s'efface vers la
   droite pour laisser voir la scène. Le point d'arrivée garde la TEINTE du
   token à opacité nulle : « transparent » y mettrait un noir transparent, et
   le fondu virerait au gris — mesuré ailleurs dans cette feuille à 194
   niveaux d'écart par canal. */
.ui-hero-voile {
  position: absolute;
  z-index: 1;
  inset: 0;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--white) 94%, transparent) 0%,
    color-mix(in srgb, var(--white) 88%, transparent) 40%,
    color-mix(in srgb, var(--white) 45%, transparent) 68%,
    rgb(from var(--white) r g b / 0) 92%);
}

/* Sous 992px le texte prend toute la largeur : le voile n'a plus de côté vers
   lequel s'effacer, il passe donc à la verticale et se ferme sur le fond de
   page — la photo reste lisible en haut, le texte sur un aplat en bas. */
@media (width <= 991.98px) {
  .ui-hero-voile {
    background: linear-gradient(180deg,
      rgb(from var(--white) r g b / 0) 0%,
      color-mix(in srgb, var(--white) 70%, transparent) 22%,
      color-mix(in srgb, var(--white) 94%, transparent) 42%,
      color-mix(in srgb, var(--white) 97%, transparent) 100%);
  }
}

@media (max-width: 1100px) and (min-width: 768px) {
  .ui-hero-image { object-position: calc(50% - 100px) bottom; }
}

@media (width <= 820px) {
  .ui-hero-image { object-position: 60% bottom; }
}

@media (width <= 820px) {
  .ui-hero-overlay { background: linear-gradient(90deg, color-mix(in srgb, var(--white) 90.1961%, transparent), color-mix(in srgb, var(--white) 45.098%, transparent) 70%, transparent); }
}

@media (width <= 767px) {
  .ui-hero-overlay { background: linear-gradient(90deg, var(--white) 0%, color-mix(in srgb, var(--white) 65.098%, transparent) 70%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .ui-hero { opacity: 1; animation: none; }
  .ui-hero-bulle { animation: none; }
}

.ui-hero-visual {
  /* Le visuel occupe sa colonne, sans déborder ni imposer de hauteur : c'est
     le texte qui commande la hauteur du hero. */
  position: relative;   /* repère de la bulle, qui se pose PAR-DESSUS le visuel */
  display: flex;
  justify-content: center;
}

/* Enfant DIRECT : le hero peut contenir une scène (variante « sélection »)
   dont les images ont leurs propres dimensions. Un sélecteur descendant les
   atteignait — à spécificité égale, la feuille des organismes est chargée
   après celle des molécules et l'emportait sur .ui-profil-mini > img. */
.ui-hero-visual > img {
  width: 100%;
  max-width: 560px;
  height: auto;
  border-radius: var(--radius);
}

/* Visuel détouré : un PNG à fond transparent se pose SUR la page, il n'est
   pas dans un cartouche. Le rayon de carte n'y arrondit rien — il n'y a pas
   de fond — mais il rogne le sujet : un personnage qui touche le bas de son
   cadre s'y fait trancher aux deux coins bas. */
/* Aplat organique posé DERRIÈRE le détouré. Rayons dissymétriques et rotation
   de −6° repris du .ui-hero-board::before de concept.html : c'est le même
   ornement, il doit se reconnaître d'une page à l'autre plutôt que se
   ressembler à peu près.

   Mêmes valeurs que lui — 40 % de --surface-appuyee : la couleur fait partie
   de l'identification au même titre que la forme.

   Il suit le décalage horizontal de l'image (voir la requête média plus bas) :
   un ::before ne reçoit pas la transform de son frère, et laisser l'aplat
   centré pendant que le couple glisse de 60px les désolidariserait. */
.ui-hero-visual--detoure::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  /* Jamais plus large que sa colonne : à 620px fixes, l'aplat débordait sous
     1200px et faisait scroller la page (56px mesurés à 1000). Le plafond
     garde la taille voulue en grand écran, le 100 % la borne ailleurs, et le
     ratio tient la forme d'un bout à l'autre. */
  width: 100%;
  max-width: 620px;
  aspect-ratio: 620 / 580;
  border-radius: 46% 54% 58% 42% / 47% 43% 57% 53%;
  background: color-mix(in srgb, var(--surface-appuyee) 40%, transparent);
  transform: translate(-50%, -50%) rotate(-6deg);
}

/* Le sujet passe devant l'aplat. */
.ui-hero-visual--detoure > img {
  position: relative;
  z-index: 1;
}

/* Posé sur le MODIFICATEUR et non sur .ui-hero-visual > img, qui sert aussi
   au hero à photo encadrée de la fiche du DS : un détouré n'a pas de cadre,
   il peut prendre plus de place. */
.ui-hero-visual--detoure > img {
  max-width: 38.5rem;              /* 616px : +10 % sur les 560 du socle */
  border-radius: 0;
}

/* +15 % de plus, soit 708px. À cette taille l'image DÉBORDE de sa colonne —
   col-lg-6 ne vaut que 648px à 1400. C'est voulu et c'est la seule façon d'y
   arriver : un max-width plus large seul ne servirait à rien, width:100% le
   plafonnerait à la colonne. Le débord est symétrique (la colonne centre son
   contenu) et reste sans conséquence : le sujet garde 25px de marge
   transparente à gauche et 19px à droite, il ne touche donc ni le texte ni le
   bord de l'écran.

   Le décalage de 30px vers la gauche n'est pas qu'un cadrage : c'est LUI qui
   rend le débord tenable. Centré, le visuel dépassait le bord droit de
   l'écran entre 992 et ~1340px — la page gagnait une barre de défilement
   horizontale de 18 à 21px. Le décaler ramène ce dépassement dans l'écran à
   toutes les largeurs. Les deux valeurs se tiennent : élargir sans décaler
   ferait revenir le défilement.

   translate et non margin : le décalage ne doit pas rentrer dans le calcul de
   la colonne, sinon le centrage du flex le rattraperait pour moitié.

   Bornés à ≥992px tous les deux : en dessous les colonnes s'empilent, le
   visuel prend déjà toute la largeur, et 115 % comme le décalage le
   sortiraient de l'écran. */
@media (width >= 992px) {
  .ui-hero-visual--detoure > img {
    width: 115%;
    max-width: 44.25rem;
    transform: translateX(-1.875rem);   /* 30px */
  }

  .ui-hero-visual--detoure::before {
    transform: translate(-50%, -50%) translateX(-1.875rem) rotate(-6deg);
  }
}

/* Les 30px suivants, à partir de 1100px seulement. Plus bas, les colonnes
   sont trop serrées : à 1000px le sujet mordait le titre de 26px. Le seuil
   n'est pas choisi au hasard, c'est celui que la feuille emploie déjà pour
   recaler la colonne de texte du hero. Mesuré à 60px : 22px d'écart au titre
   à 1100, 62 à 1200, 125 à 1400. */
@media (width >= 1100px) {
  .ui-hero-visual--detoure > img {
    transform: translateX(-3.75rem);    /* 60px : 30 + 30 */
  }

  .ui-hero-visual--detoure::before {
    transform: translate(-50%, -50%) translateX(-3.75rem) rotate(-6deg);
  }
}

.ui-hero-content {
  position: relative;
  z-index: 2;
  align-content: stretch;
  /* PAS de « margin: 0 » ici. Une rangée Bootstrap porte des marges négatives
     d'une demi-gouttière, qui compensent exactement le rembourrage de ses
     colonnes : c'est ce qui fait tomber le contenu sur la ligne de grille.
     Les annuler décalait tout le hero de 12px vers la droite, visible dès
     qu'on superposait la grille. */
}

.ui-hero-copy { align-self: center; }

.ui-hero-copy-primary { align-self: center; }

@media (width <= 1100px) {
  .ui-hero-copy-primary { margin-top: 11.875rem; }

  /* Même exception que pour les décalages de 20px, et pour la même raison :
     ces 190px descendent le texte sous le visuel de l'accueil, qui passe
     au-dessus de lui dès que la grille se casse. Un hero à PHOTO n'a pas de
     visuel empilé — l'image est derrière, pleine section — et cette marge n'y
     descend rien : elle décentre la carte, 282px d'air au-dessus contre 92 en
     dessous à 375px de large. */
  .ui-hero--recherche:has(.ui-hero-fond) .ui-hero-copy-primary {
    margin-top: 0;
  }
}

@media (width <= 767px) {
  .ui-hero-copy { align-self: center; }
}

.ui-hero .ui-hero-copy-primary .ui-display-title {
  top: 0;
  opacity: 0;
  transform: translateY(26px);
  animation: ui-hero-copy-in 800ms cubic-bezier(0.16, 1, 0.3, 1) 280ms forwards;
}

.ui-hero .ui-display-title {
  color: var(--color-titre-hero);
  margin-bottom: var(--space-4);
  position: relative;
  top: 1.25rem;
}

/* Toute la colonne de texte de l'accueil remonte de 20px : titre, chapô et
   bouton d'un bloc. Portée par .ui-hero--recherche — concept.html partage
   .ui-hero-copy-primary et ne doit pas bouger.

   Sur la COLONNE et non sur le titre seul : ce dernier avait d'abord reçu son
   propre top:-20px, et rehausser en plus la colonne l'aurait remonté deux
   fois. Un seul décalage, posé sur le parent, garde les trois éléments à
   leurs distances d'origine. */
.ui-hero--recherche .ui-hero-copy-primary {
  position: relative;
  /* −3.75rem : les −1.25rem d'origine (l'ajustement optique décrit ci-dessus)
     plus les 40px demandés. Une seule valeur plutôt qu'un second décalage
     empilé — deux « top » sur la même colonne se seraient annulés, et un
     décalage posé ailleurs aurait à nouveau séparé le titre de son chapô. */
  top: -3.75rem;
}

/* Sauf quand le texte est dans une CARTE, sur une photo : ces deux décalages
   de 20px sont ceux de l'accueil, où le texte flotte sur le fond de page et
   où l'œil les lit comme un ajustement optique. Dans un cartouche, ils se
   lisent comme une erreur de rembourrage — la colonne remontée décentrait la
   carte de 40px sur la hauteur du hero (20 de moins en haut, 20 de plus en
   bas), et le titre descendu de 20 collait au chapô en s'éloignant du
   sur-titre. Le cartouche donne au texte le repère que le fond de page ne
   lui donnait pas : ses propres bords. */
.ui-hero--recherche:has(.ui-hero-fond) .ui-hero-copy-primary,
.ui-hero--recherche:has(.ui-hero-fond) .ui-display-title {
  top: 0;
}

.ui-hero .ui-display-title .ui-accent { color: var(--color-brand-pink); }

@media (width <= 767px) {
  .ui-hero .ui-display-title { margin-bottom: var(--space-4); }
}

@media (width <= 560px) {
  .ui-hero .ui-display-title { margin-bottom: var(--space-6); }
}

.ui-hero-lead {
  margin-bottom: var(--space-4);
  font-size: var(--fs-lead);
  line-height: var(--lh-base);
  max-width: 460px;
}

@media (width <= 767px) { .ui-hero-lead { margin-inline: auto; } }

@media (width <= 560px) {
  .ui-hero-lead { max-width: 290px; font-size: var(--fs-lead); margin-bottom: var(--space-6); }
}

.ui-hero-copy-primary .ui-hero-lead {
  opacity: 0;
  transform: translateY(20px);
  animation: ui-hero-copy-in 720ms cubic-bezier(0.16, 1, 0.3, 1) 520ms forwards;
}

@keyframes ui-hero-copy-in { to { opacity: 1; transform: translateY(0); } }

/* ---- Bulle posée sur le visuel du hero ----
   Une accroche de bande dessinée, décorative (aria-hidden dans le snippet).
   Position absolue : elle doit MORDRE sur le visuel, pas le pousser — la
   mettre dans le flux rétrécirait l'image de sa hauteur.

   Le contour est une ELLIPSE PLEINE : « 50% » dans les deux sens. Le rayon
   elliptique précédent (50% / 42%) laissait 16 % de la hauteur en segment
   DROIT sur chaque flanc — 20px bien visibles — parce que les rayons
   verticaux des deux coins d'un même côté ne totalisaient que 84 % de la
   hauteur. Il leur faut 100 % pour se rejoindre, donc 50 % chacun.

   Les deux petites bulles qui la relient au personnage sont les
   pseudo-éléments. Un SVG rendrait un contour de nuage exact, mais ne
   suivrait plus la hauteur du texte : la bulle doit pouvoir accueillir trois
   lignes comme une seule. ---- */
.ui-hero-bulle {
  position: absolute;
  z-index: 2;
  inset-block-start: calc(8% - 20px);
  inset-inline-start: -1.875rem;   /* 30px : elle déborde à gauche du visuel */
  max-width: 13rem;
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border: 2px solid var(--color-primary);
  border-radius: 50%;
  background: var(--white);
  color: var(--color-dark);
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  line-height: var(--lh-heading);
  text-align: center;
  animation: ui-hero-bulle-flotte 2.016s ease-in-out infinite alternate;  /* 3.2s − 10 % puis − 30 % */
}

/* Elle flotte doucement. « alternate » évite d'écrire deux fois la position
   haute : l'aller-retour suffit, et la courbe s'applique dans les deux sens.
   Les deux petites bulles sont ses enfants, elles montent avec elle — c'est
   toute la grappe qui respire, pas la bulle seule. */
@keyframes ui-hero-bulle-flotte {
  from { transform: translateY(0); }
  to   { transform: translateY(-0.9rem); }    /* 14,4px : 12px + 20 % */
}

.ui-hero-bulle::before,
.ui-hero-bulle::after {
  content: "";
  position: absolute;
  border: 2px solid var(--color-primary);
  border-radius: 50%;
  background: var(--white);
}

.ui-hero-bulle::before {
  inset-block-end: -1.125rem;
  inset-inline-end: 2.25rem;
  width: 1.25rem;
  height: 1.25rem;
}

.ui-hero-bulle::after {
  inset-block-end: -2.125rem;
  inset-inline-end: 1.25rem;
  width: 0.75rem;
  height: 0.75rem;
}

/* Les 100px suivants, à partir de 1200px seulement — le palier xl, celui où
   la navigation se déplie déjà. Plus bas, la colonne de texte est trop large
   et la bulle chevauchait le titre : mesuré à −42px d'écart à 1100 et −92px à
   1000, contre 0 à 1200 et +60 à 1400. */
@media (width >= 1200px) {
  .ui-hero-bulle { inset-inline-start: -8.125rem; }   /* 130px : 30 + 100 */
}

/* Sous 992px les colonnes s'empilent et le visuel prend toute la largeur : le
   débord de 30px à gauche n'a plus de marge où déborder, il sortait de l'écran
   et rognait le texte de la bulle. On la recale dans la colonne. */
@media (width <= 991px) {
  .ui-hero-bulle {
    inset-inline-start: 0;
    max-width: 10rem;
    padding: var(--space-3) var(--space-4);
    font-size: var(--fs-base);
  }
}

/* ---- Barre de recherche, sous le visuel du hero ----
   Rendue APRÈS la <section>, dans la zone de défilement. Elle vient se caler
   BORD À BORD sous l'image : le personnage repose dessus, il ne la recouvre
   pas.

   Aucun calage chiffré, et c'est le point. Une marge négative accordée à la
   main aurait supposé connue la hauteur de l'image — elle change avec le
   fichier, avec la largeur, avec le ratio. On supprime plutôt tout ce qui
   sépare les deux :

     · padding-bottom: 0  — le bas du hero devient le bas de sa rangée,
     · min-height: 0      — sinon le hero garderait ses 680px de plancher et
                            recentrerait la rangée, rouvrant un jeu au bas,
     · aucune marge sur la carte, qui suit donc immédiatement.

   Le bas de l'image et le haut de la carte tombent alors au même pixel, quelle
   que soit l'image. Les deux règles vont ensemble : remettre un min-height
   sans remettre le padding rouvrirait l'écart en silence. ---- */
/* Sélecteur DOUBLÉ, à dessein : ui-charte.css est chargé après cette feuille
   et y repose « .ui-section { padding-block: 5.5rem } » sous 1100px. À
   spécificité égale (une classe chacun), c'est la charte qui l'emportait —
   d'où 88px de jeu réapparus sous le visuel en mobile, alors que le desktop
   était bord à bord. Deux classes suffisent à trancher, quel que soit
   l'ordre des feuilles. */
.ui-hero.ui-hero--recherche {
  min-height: 0;
  padding-bottom: 0;
}

/* La carte descend sous la zone de sa MOITIÉ basse : la limite entre le fond
   du hero et la section suivante tombe alors en son milieu, et la carte la
   traverse. La marge négative retire cette moitié de la hauteur de la zone —
   c'est donc le dégradé lui-même qui s'arrête au milieu de la carte, sans
   qu'aucune couleur soit répétée d'un bloc à l'autre.

   --search-bar-h est mesuré par ui-main.js (voir son commentaire) : sa moitié
   n'est pas calculable en CSS. Le repli à 0px n'est pas un accident, c'est le
   comportement sans JavaScript — la carte reste dans le flux, la limite
   retombe sous elle.

   z-index : la carte doit passer DEVANT la section suivante, qu'elle recouvre
   maintenant. Positionnée contre non positionnée, elle gagne déjà ; on
   l'écrit tout de même, parce que c'est une intention, pas un effet de bord. */
.ui-hero-recherche {
  position: relative;
  z-index: 2;
  margin-bottom: calc(-1 * var(--search-bar-h, 0px) / 2);
}

/* Et la section suivante creuse son haut d'autant : sans cela, son sur-titre
   et son titre passeraient DERRIÈRE la carte — invisible en desktop où la
   moitié ne fait que 87px, franchement gênant en mobile où elle en fait 350.
   :has() borne la règle aux zones qui portent une barre ; les autres pages
   gardent le rythme de la charte.

   position: relative n'est pas décoratif ici, c'est CE QUI REND LA JONCTION
   VISIBLE. La zone du hero est positionnée (elle sert de repère à la bulle) :
   son fond est donc peint dans la couche des éléments positionnés, APRÈS les
   fonds des blocs statiques. Une section statique qui suit voyait son fond
   recouvert par le dégradé sur toute la bande de chevauchement — la limite
   se lisait au bas de la carte au lieu de son milieu, alors même que les
   boîtes, elles, étaient au bon endroit. La positionner la remet dans la même
   couche que la zone, où l'ordre du document tranche : elle vient après, elle
   passe devant. (.ui-showcase y échappait déjà, étant positionnée pour ses
   propres besoins — d'où une bande sombre qui, elle, tombait juste.) */
.ui-hero-scroll-zone:has(.ui-hero-recherche) + .ui-section {
  position: relative;
  padding-top: calc(var(--space-7) + var(--search-bar-h, 0px) / 2);
}

.ui-hero-load-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  opacity: 0;
  transform: translateY(16px);
  animation: ui-hero-actions-in 650ms cubic-bezier(0.16, 1, 0.3, 1) 800ms forwards;
}

/* Les boutons du hero sont les boutons standard : la charte n'y fait pas
   d'exception, et un appel principal rapetissé serait contradictoire.
   (Le socle les réduisait à 48px et au corps caption.) */

@media (width <= 767px) { .ui-hero-load-actions { align-items: center; } }

@media (width <= 560px) {
  .ui-hero-load-actions { flex-direction: column; align-items: stretch; }
}

@keyframes ui-hero-actions-in { to { opacity: 1; transform: translateY(0); } }

@media (prefers-reduced-motion: reduce) {
  .ui-hero-load-actions,
  .ui-hero .ui-hero-copy-primary .ui-display-title,
  .ui-hero-copy-primary .ui-hero-lead {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* ---- Section engagement (quality) ---- */
.ui-cards-section {
  position: relative;
  overflow: hidden;
  padding-top: var(--space-7);
  padding-bottom: var(--space-7);
  background: linear-gradient(color-mix(in srgb, var(--white) 10.1961%, transparent), color-mix(in srgb, var(--white) 10.1961%, transparent)), url('../../images/fond.svg');
  background-size: cover;
  background-position: center calc(50% + var(--commitment-bg-y, 0px));
  background-repeat: no-repeat;
}

.ui-cards-section > .ui-container { position: relative; z-index: 1; }

/* ---- Sections collection (dark / white) ---- */
.ui-showcase {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background-image:
    linear-gradient(115deg, color-mix(in srgb, var(--color-dark) 74.1176%, transparent) 0%, color-mix(in srgb, var(--color-dark-soft) 72.1569%, transparent) 52%, color-mix(in srgb, var(--color-dark) 78.0392%, transparent) 100%),
    url('../../images/fond.svg');
  background-size: cover, cover;
  background-position: center, center calc(50% + var(--collection-bg-y, 0px));
  background-repeat: no-repeat;
  padding-top: var(--space-7);
  padding-bottom: var(--space-7);
}

.ui-showcase > .ui-container { position: relative; z-index: 1; }

.ui-showcase .ui-section-heading {
  align-items: center;
  margin-bottom: var(--space-6);
}

/* Le filet décoratif se pose SUR la ligne de grille (d'où le left égal au demi
   -gutter de la colonne, qui ramène au bord du contenu) ; le sur-titre et le
   titre s'en écartent de 16px. Padding = demi-gutter + cet écart. */
.ui-showcase .ui-section-heading > div:first-child {
  position: relative;
  padding-left: calc(var(--bs-gutter-x, 1.5rem) * 0.5 + var(--space-3));
}

.ui-showcase .ui-section-heading > div:first-child::before,

.ui-showcase .ui-eyebrow--accent { color: var(--on-dark-muted); }

.ui-showcase .ui-section-title {
  color: var(--surface-douce);
  text-shadow: 0 2px 18px color-mix(in srgb, var(--shadow-color) 40%, transparent);
}

.ui-showcase .ui-section-title .ui-accent { color: var(--accent-sur-sombre); }

/* Chapô de la vitrine sombre : couleur et plafond de largeur propres
   à ce contexte. */
.ui-showcase .ui-showcase-lead {
  color: var(--on-dark-muted);
  max-width: 440px;
  margin-bottom: 0;
}

/* Lien au fil du chapeau (leadHtml) : reprend la taille du paragraphe plutôt
   que le 14px de l'atome .ui-text-link, sinon il décroche de la ligne. */
.ui-showcase--dark .ui-showcase-lead { color: var(--on-dark-muted); }
.ui-showcase--dark .ui-eyebrow--accent { color: var(--on-dark-muted); }

@media (width <= 820px) {
  .ui-showcase--dark .ui-grid-row { max-width: 680px; margin-inline: auto; }
}

/* ---- Aperçu des communautés ----
   Un en-tête centré, une rangée de cartes, un bouton dessous. Version courte
   de .ui-communaute-section, qui, elle, raconte le principe sur deux colonnes.

   Préfixe .ui-groupes- au PLURIEL — la section qui en dispose plusieurs ;
   .ui-groupe- au singulier est la carte (couche molécules). ---- */
/* .ui-groupes-section : pas de fond, blanc de la page. */

.ui-groupes-grille {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 1.5rem;
}

/* Hauteur égale posée ICI et non par la donnée de chaque carte : c'est une
   contrainte de RANGÉE. Deux cartes voisines dont les accroches n'ont pas la
   même longueur doivent garder leurs pieds sur la même ligne — la carte, elle,
   ne sait rien de ses voisines. */
.ui-groupes-grille > .ui-grid-item { display: flex; }
.ui-groupes-grille .ui-groupe-carte { width: 100%; }

.ui-groupes-action {
  margin-top: var(--space-6);
  text-align: center;
}

/* ---- Section de contenu (pages de contenu simple) ----
   Trois gabarits posés sur la grille 12 de Bootstrap, via des col-lg-* et des
   offset-lg-* dans le HTML (voir organismes/section-contenu) : ici seuls le fond,
   le rythme vertical et le rendu de l'image sont propres au composant. ---- */
/* Pas de fond : blanc de la page. */

/* Le titre coiffe la section : hors de la rangée, il occupe toute la largeur du
   conteneur et reste centré quel que soit le gabarit. */
.ui-content-heading {
  text-align: center;
  margin-bottom: var(--space-5);
}

/* Colonnes calées en haut : le texte est souvent bien plus haut que l'image,
   qui flotterait au milieu du bloc si la rangée restait centrée. */
.ui-content-row { align-items: start; }
/* La photo calée sur le HAUT du texte (3 oct. 2026, « À propos » des
   communautés), par le champ « modifier » de la section. !important : le
   align-items-center de Bootstrap, posé sur la rangée, en porte un. */
.ui-content-section--cale-haut > .ui-container > .ui-content-row { align-items: flex-start !important; }

/* Enfant DIRECT : le média peut contenir une pile de vignettes, dont les
   photos ont leur propre hauteur. Un sélecteur descendant les atteignait — à
   spécificité égale, la feuille des organismes est chargée après celle des
   molécules et l'emportait sur « .ui-profil-mini > img ». Même piège que
   « .ui-hero-visual img », corrigé de la même façon. */
/* « À PROPOS » D'UNE COMMUNAUTÉ (3 oct. 2026) : sous la photo, ses
   activités puis ses questions en accordéon. Rendus par le serveur
   (serveur/communautes.js), chacun absent quand il est vide. */
.ui-apropos-activites { margin-top: calc(var(--space-4) + var(--space-2)); }   /* 32px, le cran le plus proche des 30 demandés */
.ui-apropos-activites > .ui-eyebrow { margin-bottom: var(--space-2); }
.ui-apropos-activites > ul {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ui-apropos-activites li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.ui-apropos-activites li > .material-symbols-outlined {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--surface-appuyee);
  color: var(--color-primary);
  font-size: var(--icon-md);
}
.ui-apropos-activites li > span:last-child {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.ui-apropos-activites strong { color: var(--color-titre); }

/* Les questions : même dessin que les activités — le sur-titre au-dessus,
   une carte par question, qui s'ouvre sur sa réponse. */
.ui-apropos-questions {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.ui-apropos-questions > .ui-eyebrow { margin-bottom: 0; }
.ui-apropos-questions details {
  padding: 0 var(--space-3);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--surface-card);
}
.ui-apropos-questions summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: 600;
  list-style: none;
  cursor: pointer;
}
.ui-apropos-questions summary::-webkit-details-marker { display: none; }
.ui-apropos-questions summary > .material-symbols-outlined { transition: transform 0.2s ease; }
.ui-apropos-questions details[open] summary > .material-symbols-outlined { transform: rotate(180deg); }
.ui-apropos-questions details > p { margin: 0 0 var(--space-3); color: var(--text-muted); font-size: var(--fs-sm); }

.ui-content-media > img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  display: block;
}

.ui-content-body > p {
  color: var(--text-muted);
  line-height: var(--lh-base);
}

.ui-content-body > p + p { margin-top: var(--space-4); }

.ui-content-body > ul {
  color: var(--text-muted);
  margin: var(--space-4) 0 0;
  padding-left: var(--space-4);
  line-height: var(--lh-base);
}

/* Empilé sous lg : l'image reprend la tête du bloc, avec de l'air sous elle. */
@media (width <= 991.98px) {
  /* Fond propre à la section. Le blanc n'est PAS le fond de page : celui-ci est
   le « papier » #fffdfb. L'écart est d'un cheveu, mais c'est lui qui détache
   la section de ses voisines sans employer une teinte de retrait. */
.ui-content-section--blanc {
  background: var(--white);
}

.ui-content-media { margin-bottom: var(--space-4); }
  .ui-content-media + .ui-content-body { margin-top: 0; }
  .ui-content-body + .ui-content-media { margin-top: var(--space-4); margin-bottom: 0; }
} /* ---- CTA entreprises ---- */
.ui-cta-band {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding: var(--space-7) 0;
  color: var(--surface-douce);
  background-image:
    linear-gradient(115deg, color-mix(in srgb, var(--color-dark) 74.1176%, transparent) 0%, color-mix(in srgb, var(--color-dark-soft) 72.1569%, transparent) 52%, color-mix(in srgb, var(--color-dark) 78.0392%, transparent) 100%),
    url('../../images/fond.svg');
  background-size: cover;
  background-position: center 46%;
}

.ui-cta-band-row {
  position: relative;
  z-index: 1;
  /* gouttière par défaut (1.5rem) → les colonnes s'alignent sur la grille 12 ;
     la respiration entre texte et carte est portée par la colonne vide
     (offset-lg-1). gutter-y = espace vertical une fois empilé (< lg). */
  --bs-gutter-y: 2.75rem;
}

.ui-cta-band-copy {
  position: relative;
  padding-left: calc(var(--bs-gutter-x, 1.5rem) * 0.5 + var(--space-3));
}

.ui-cta-band-copy .ui-eyebrow { margin-bottom: var(--space-4); color: var(--on-dark-muted); }

.ui-cta-band-copy h2 {
  margin: 0 0 var(--space-4);
  color: var(--surface-douce);
  font-family: var(--font-display);
  font-size: var(--fs-display-3);
  font-weight: var(--fw-medium);
  line-height: var(--lh-heading);
  text-shadow: 0 2px 18px color-mix(in srgb, var(--shadow-color) 40%, transparent);
}

.ui-cta-band-copy h2 span { color: var(--accent-clair); }

.ui-cta-band-copy > p:last-child {
  max-width: 650px;
  color: var(--on-dark-muted);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
}

/* ---- FAQ ---- */
/* Fond posé sur le COMPOSANT et non par la page, contrairement aux autres
   sections. C'est un choix, et il a une raison : une FAQ n'est pas une bande
   parmi d'autres, c'est un registre à part — on y vient chercher une réponse,
   pas lire la page. Le teinter partout de la même façon le signale.

   Conséquence assumée : sur faq.html, trois sections FAQ se suivent et forment
   donc un seul champ teinté continu. C'est cohérent — trois chapitres d'une
   même consultation — mais cela déroge à la règle d'alternance. */
.ui-faq {
  position: relative;
  overflow: hidden;
  background: var(--section-teinte-7);
}

.ui-faq-row {
  --bs-gutter-x: 6.25rem;
  --bs-gutter-y: 6.25rem;
}

@media (width <= 767px) {
  .ui-faq-row > div:first-child > p:not(.ui-eyebrow) { margin-bottom: var(--space-5); }
}

@media (width <= 560px) {
  .ui-faq-row { --bs-gutter-x: 3.125rem; --bs-gutter-y: 3.125rem; }
}

/* ---- Hero de page (bande décorative pleine largeur) ----
   Générique et réutilisable au-delà du /ds. Recette identique à
   .product-hero (organismes.css) : purement décoratif, aucun
   texte dedans — le titre de page vit dans .ds-page-intro (doc),
   juste en dessous du fil d'Ariane. */
.ui-page-hero {
  min-height: 180px;
  color: var(--color-dark);
  /* Le point d'arrivée du dégradé garde la TEINTE du token à opacité nulle.
     Écrire « transparent » y mettrait un noir transparent, et le fondu
     virerait au gris — mesuré à 194 niveaux d'écart par canal. */
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--white) 74.902%, transparent) 0%, color-mix(in srgb, var(--white) 57.6471%, transparent) 36%, color-mix(in srgb, var(--white) 35.2941%, transparent) 61%, rgb(from var(--white) r g b / 0) 82%),
    url('../../images/fond.svg') center 58% / cover;
  display: flex;
  align-items: center;
  position: relative;
  overflow: hidden;
}

@media (width <= 820px) {
  .ui-page-hero { min-height: 166px; }
}

/* ---- Fil d'Ariane (Bootstrap .breadcrumb, adapté au style du site) ---- */
.ui-breadcrumb-bar {
  background: var(--white);
  border-bottom: 1px solid var(--border);
}/* ---- Pied de page ---- */
/* Pied de page SOMBRE. Il était clair — le papier, un demi-ton au-dessus du
   crème de la page. Le passage à l'encre inverse tout ce qu'il contient : les
   titres, les liens, le filet et le crédit reprennent la famille --on-dark,
   et le logo bascule sur sa version blanche (voir organismes/footer.snip).

   Le ROSE change de valeur en même temps, et dans l'autre sens : sur fond
   sombre c'est le rose PLEIN qui se lit (4,08:1) et son encre qui échoue
   (2,84). La règle « encre pour le petit texte » vaut sur fond clair ; elle
   s'inverse ici. */
.ui-footer {
  isolation: isolate;
  background: var(--section-pied);
  color: var(--on-dark);
  /* Pas de filet haut : le pied est une bande d'encre pleine, la rupture de
     valeur avec la section qui le précède suffit à le détacher. Un trait par
     dessus ne ferait que redoubler une limite déjà nette. */
  padding: 0 0 var(--space-4);
  position: relative;
  overflow: hidden;   /* contient les deux halos décoratifs (voir la charte) */
}

.ui-footer > .ui-container { position: relative; z-index: 1; }

.ui-footer-cta {
  text-align: center;
  max-width: 640px;
  margin: 0 auto var(--space-3);
  padding: var(--space-6) 0 var(--space-4);
}

@media (width <= 560px) { .ui-footer-cta { padding: var(--space-5) 0 var(--space-4); } }

/* Blanc et non rose : à 12px sur l'encre du pied, le rose plein donne 4,08
   et son encre 2,84 — aucun des deux n'atteint les 4,5 d'un petit texte. */
.ui-footer-cta .ui-eyebrow { text-align: center; color: var(--on-dark); }

.ui-footer-cta .ui-section-title {
  margin: 0;
  font-size: var(--fs-display-5);
  color: var(--text);
  text-shadow: 0 2px 18px color-mix(in srgb, var(--shadow-color) 40%, transparent);
}

.ui-footer-cta .ui-section-title .ui-accent { color: var(--color-brand-pink); }

.ui-footer-grid {
  /* Sans filet haut. Il en restait un ici après celui du pied lui-même : deux
     traits pour une seule limite, et celui-ci coupait la grille du contenu de
     la page juste au-dessus. Le changement de fond suffit à marquer l'entrée
     dans le pied. Le filet du BAS, lui, reste : il sépare deux blocs de même
     fond, où rien d'autre ne ferait la césure. */
  padding-top: var(--space-5);
  --bs-gutter-x: 2rem;
  --bs-gutter-y: 2rem;
}

@media (width <= 560px) {
  .ui-footer-grid {
    padding-top: var(--space-4);
    --bs-gutter-x: 1.25rem;
    --bs-gutter-y: 2.1875rem;
  }
}

.ui-footer-logo {
  /* En flux, dans la colonne de marque — comme la charte. Le socle le posait
     en absolu, débordant de 66px au-dessus d'un pied de page sombre : un
     parti pris qui n'a plus d'objet maintenant que le pied est clair, et qui
     obligeait le logo à être lisible sur deux fonds à la fois. */
  display: block;
  /* 220px : la valeur d'AVANT l'agrandissement de 20 % que le logo du pied
     avait suivi (264 / 1,2 = 220). Le retour se fait donc par la division et
     non par un -20 %, qui aurait donné 211 — un même pourcentage ne rend
     jamais ce qu'il a pris. */
  width: 270px;                         /* logo Vibe2Meet, −10 % (3 oct. 2026) */
  max-width: 100%;
  height: auto;
  margin-bottom: var(--space-3);
}

@media (width <= 560px) {
  .ui-footer-logo {
    /* Même retour en arrière que le palier large : 192 / 1,2 = 160. Les deux
       valeurs bougent ensemble, sans quoi le logo sauterait d'une largeur
       d'écran à l'autre. */
    width: 198px;
    margin: 0 auto var(--space-3);
  }
}

.ui-footer-brand > p {
  color: var(--on-dark-soft);
  font-size: var(--fs-sm);
  line-height: var(--lh-base);
}

.ui-footer-contact {
  flex-direction: column;
  margin-top: var(--space-4);
  display: flex;
}

.ui-footer-contact span,
.ui-footer-brand .ui-footer-contact a {
  color: var(--on-dark-soft);
  font-size: var(--fs-sm);
  line-height: var(--lh-base);
}

.ui-footer-contact span { margin-bottom: var(--space-2); }
.ui-footer-contact .material-symbols-outlined { font-size: var(--icon-sm); }

.ui-footer-address { align-items: center; gap: var(--space-2); display: inline-flex; }
.ui-footer-address svg { flex-shrink: 0; }

.ui-footer-brand .ui-footer-contact a {
  align-items: center;
  gap: var(--space-2);
  display: inline-flex;
  transition: color 0.2s ease;
}

.ui-footer-contact a svg { flex-shrink: 0; }
.ui-footer-brand .ui-footer-contact a:hover { color: var(--on-dark); }

.ui-footer-grid h3 { color: var(--on-dark); margin-bottom: var(--space-3); }

.ui-footer-grid a {
  color: var(--on-dark-soft);
  margin-bottom: var(--space-3);
  font-size: var(--fs-sm);
  display: block;
  transition: color 0.2s ease, transform 0.2s ease;
}

/* Blanc et non rose au survol : sur l'encre #043c48 le rose plein tombe à
   3,19:1, sous les 4,5 d'un petit texte. Le rose reste présent, mais en
   GRAPHIQUE (le filet des titres, la pastille de survol) — un aplat non
   textuel n'a que 3:1 à tenir, et 3,19 le tient. */
.ui-footer-grid a:hover { color: var(--on-dark); }

/* Mention centrée du bas de page. Le cœur y est un PICTOGRAMME, non un
   caractère : ♥ se rend différemment d'un système à l'autre, et il est ici
   au milieu d'une phrase, là où l'écart se voit. */
.ui-footer-signature {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  justify-self: center;
  /* La mention porte maintenant DEUX énoncés (la signature et le crédit) :
     elle est trop longue pour le « nowrap » d'origine, qui la faisait pousser
     les deux colonnes voisines hors de la rangée sur les écrans étroits. Elle
     se replie, et le point médian reste avec ce qui le précède. */
  flex-wrap: wrap;
}

.ui-footer-signature .material-symbols-outlined {
  color: var(--color-brand-pink);
  font-size: var(--icon-sm);
  font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 20;
}

.ui-footer-bottom {
  color: var(--on-dark-soft);
  border-top: 1px solid var(--border-on-dark);
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  font-size: var(--fs-sm);
  display: grid;
}

.ui-footer-bottom a { color: inherit; }
.ui-footer-legal { text-align: center; }
.ui-footer-credit { color: var(--on-dark-soft); }
.ui-footer-credit:hover { color: var(--on-dark); }

.ui-footer-social {
  justify-self: end;
  align-items: center;
  gap: var(--space-3);
  display: flex;
}

.ui-footer-social a {
  width: 36px;
  height: 36px;
  border: 1px solid var(--border-on-dark-strong);
  border-radius: 50%;
  color: var(--on-dark-soft);
  display: inline-grid;
  place-items: center;
  transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.ui-footer-social a:hover {
  color: var(--color-dark);
  background: var(--accent-clair);
  border-color: var(--accent-clair);
  transform: translateY(-2px);
}

@media (width <= 560px) {
  .ui-footer-bottom {
    grid-template-columns: 1fr;
    justify-items: center;
    gap: var(--space-3);
  }
  .ui-footer-social { justify-self: center; }
}

/* ---- Section témoignages (carrousel) ----
   Le carrousel Bootstrap est repris tel quel ; on n'habille ici que
   ce que Bootstrap laisse en style « admin » : flèches blanches sur
   15% de large et pastilles rectangulaires blanches, illisibles sur
   nos cartes claires. Flèches et pastilles se superposent tous à la
   carte : deux boutons ronds centrés sur sa hauteur contre ses bords,
   les pastilles dans le bas. Les boutons sont pleins en --surface-douce
   (chevron blanc, --surface-appuyee au survol) : le vert puis l'or pleins
   tranchaient trop sur la carte beige. La carte ouvre son padding
   latéral pour
   les flèches et son padding bas pour les pastilles. En dessous de
   560px les flèches ressortent sous la carte : --ui-carousel-foot
   mesure alors cette bande, et sert à recaler les deux. */
.ui-testimonials .ui-section-heading { margin-bottom: var(--space-5); }

/* Le lien du sous-titre est un .ui-text-link (rouge, souligné au
   survol) posé au fil du texte : il reprend la taille du paragraphe
   plutôt que le 14px de l'atome, sinon il décroche de la ligne. */
.ui-testimonials .ui-section-heading .ui-text-link { font-size: inherit; }

.ui-testimonial-carousel {
  --ui-carousel-foot: 0px;
  position: relative;
  padding-bottom: var(--ui-carousel-foot);
}

/* Réserve dans la carte : les flèches se posent dans le padding
   latéral, les pastilles dans le padding bas — jamais sur la
   citation ni sur le nom. */
.ui-testimonial-carousel .ui-testimonial-card {
  padding-inline: var(--space-7);
  padding-bottom: var(--space-6);
}

/* Centrage vertical sur la CARTE, pas sur le carrousel : on retire du
   calcul la bande basse occupée par les pastilles. */
.ui-testimonial-carousel .carousel-control-prev,
.ui-testimonial-carousel .carousel-control-next {
  top: calc((100% - var(--ui-carousel-foot)) / 2);
  bottom: auto;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  border: 0;
  border-radius: 50%;
  background: var(--surface-douce);
  box-shadow: var(--shadow-md);
  opacity: 1;
  transition: background-color 0.2s ease;
}

.ui-testimonial-carousel .carousel-control-prev { left: 1.25rem; }
.ui-testimonial-carousel .carousel-control-next { right: 1.25rem; }

.ui-testimonial-carousel .carousel-control-prev:hover,
.ui-testimonial-carousel .carousel-control-next:hover,
.ui-testimonial-carousel .carousel-control-prev:focus-visible,
.ui-testimonial-carousel .carousel-control-next:focus-visible {
  background: var(--surface-appuyee);
  opacity: 1;
}

/* Chevron blanc. On ne reprend pas le tracé
   de Bootstrap : c'est une forme pleine d'environ 1px d'épais, que
   l'antialiasing délave à cette taille. Même géométrie, mais en
   trait (stroke 2.25) à 20px. */
/* Flèches du carrousel : Material Symbols, comme partout ailleurs dans le
   système. On neutralise l'image de fond de Bootstrap et on rend le glyphe
   dans un ::before — l'icône suit alors la couleur et la taille du texte,
   ce qu'un SVG en background-image ne permet pas. */
.ui-testimonial-carousel .carousel-control-prev-icon,
.ui-testimonial-carousel .carousel-control-next-icon {
  width: auto;
  height: auto;
  background-image: none;
}

.ui-testimonial-carousel .carousel-control-prev-icon::before,
.ui-testimonial-carousel .carousel-control-next-icon::before {
  font-family: 'Material Symbols Outlined';
  font-size: var(--icon-md);
  line-height: 1;
  color: var(--white);
}

.ui-testimonial-carousel .carousel-control-prev-icon::before { content: 'chevron_left'; }
.ui-testimonial-carousel .carousel-control-next-icon::before { content: 'chevron_right'; }

/* Calées sur le bas de la CARTE : au-dessus de la bande basse quand
   elle existe (mobile), à 20px du bord de la carte sinon. */
.ui-testimonial-carousel .carousel-indicators {
  bottom: calc(var(--ui-carousel-foot) + 1.25rem);
  margin: 0;
  gap: var(--space-3);
}

.ui-testimonial-carousel .carousel-indicators [data-bs-target] {
  width: 8px;
  height: 8px;
  margin: 0;
  border: 0;
  border-radius: 50%;
  background-color: var(--color-dark-soft);
  opacity: 0.28;
}

.ui-testimonial-carousel .carousel-indicators .active {
  background-color: var(--color-primary);
  opacity: 1;
}

/* Sous 560px la carte est trop étroite pour loger les flèches sans
   étrangler la citation : elles redescendent aux extrémités de la
   ligne du bas, de part et d'autre des pastilles. */
@media (width <= 560px) {
  .ui-testimonial-carousel { --ui-carousel-foot: 56px; }

  .ui-testimonial-carousel .ui-testimonial-card {
    padding-inline: var(--space-4);
    padding-bottom: var(--space-5);
  }

  .ui-testimonial-carousel .carousel-control-prev,
  .ui-testimonial-carousel .carousel-control-next {
    top: auto;
    bottom: 0;
    transform: none;
    width: 40px;
    height: 40px;
  }

  .ui-testimonial-carousel .carousel-control-prev { left: 0; }
  .ui-testimonial-carousel .carousel-control-next { right: 0; }
}

/* Hero — variante « sélection » ---------------------------------------------
   Le visuel du hero n'est plus une image mais une scène : un jeton nommant la
   personne qui reçoit la sélection, et la pile de vignettes. Les deux se
   superposent sur une forme organique, d'où le contexte
   d'empilement local — « isolation » borne les z-index à cette scène et évite
   qu'ils se mesurent au reste de la page. */

.ui-hero-selection {
  position: relative;
  isolation: isolate;
  display: grid;
  align-content: center;
  min-height: 575px;
  /* Rien en bas : les deux colonnes du hero sont alignées sur leur base, donc
     un rembourrage ici ferait finir l'illustration au-dessus du texte alors
     que leurs BOÎTES, elles, finiraient ensemble. Le haut garde ses 92px :
     c'est là que le jeton déborde. */
  padding: var(--space-7) 0 0;
}

/* C'est au CONTENEUR de borner la pile : la classe .ui-selection-mini ne porte
   plus de largeur, elle sert aussi bien ici que sur une pleine page. */
/* La pile épouse la colonne du hero : c'est ce qui la CALE sur la grille
   Bootstrap — chaque vignette occupe une demi-colonne exacte.
   Surtout PAS de « width: 100% » ici : une rangée Bootstrap porte des marges
   négatives d'une demi-gouttière de chaque côté, et lui fixer la largeur du
   parent n'en annule qu'une — la pile se décalait alors de 12px vers la
   gauche, et le trait du jeton ne tombait plus au milieu des vignettes. */
.ui-hero-selection .ui-selection-mini {
  position: relative;
  z-index: 2;
}

.ui-selection-forme {
  position: absolute;
  z-index: -1;
  width: 510px;
  height: 505px;
  /* Rayons dissymétriques : c'est ce qui fait la forme organique du modèle.
     Aucun token ne peut porter cela — un rayon nommé serait un rayon réutilisé,
     or cette forme n'existe qu'ici. */
  border-radius: 46% 54% 58% 42% / 47% 43% 57% 53%;
  /* 30% : la forme et le disque qui la double sont des masses de fond, elles
     ne doivent jamais concurrencer les vignettes posées dessus. */
  background: color-mix(in srgb, var(--surface-appuyee) 40%, transparent);
  transform: rotate(-5deg);
}

.ui-selection-forme::after {
  content: "";
  position: absolute;
  right: -35px;
  bottom: 10px;
  width: 255px;
  height: 280px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--color-primary) 25%, transparent);
}

/* Le jeton n'a plus de largeur à lui : il occupe une colonne Bootstrap, ce
   qui le CALE sur la grille à toutes les tailles au lieu de flotter sur un
   « min-width » qui ne tombait juste qu'à une largeur d'écran. La rangée qui
   le porte est posée en absolu, la grille fait le reste. */
.ui-selection-token-rangee {
  position: absolute;
  z-index: 4;
  top: 0.0625rem;
  left: 0;
  right: 0;
}

.ui-selection-token {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--white);
  box-shadow: var(--shadow-md);
}

/* Le trait pointillé relie le jeton à la pile : il DIT la relation « voici sa
   sélection ». Purement graphique, donc porté par un pseudo-élément — rien à
   annoncer, rien à lire. */
.ui-selection-token::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  height: 32px;
  border-left: 2px dashed color-mix(in srgb, var(--color-primary) 40%, transparent);
}

/* Le jeton du hero « concept » ne montre pas un portrait mais un carré nommé :
   la personne dont on parle est le LECTEUR, qui n'a pas de photo dans la scène. */
.ui-selection-carre {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--white);
  font-size: var(--fs-sm);
  font-weight: 600;               /* même famille que .ui-carte-icone-num */
}

.ui-selection-token > img {
  flex: 0 0 auto;
  width: 54px;
  height: 54px;
  border-radius: var(--radius-sm);
  object-fit: cover;
}

.ui-selection-token div {
  display: grid;
}

.ui-selection-token strong {
  font-size: var(--fs-base);
}

.ui-selection-token small {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  /* Interligne resserré : la mention tient sur deux lignes, et l'aération du
     corps courant (1,5) y creusait un écart que le jeton n'a pas la hauteur
     d'absorber. */
  line-height: 1.3;
}

/* ---- Hero « sélection », variante concept ----
   Rien d'autre que le jeton déplacé. Sur « Découvrir » il est EN HAUT : une
   personne, puis les profils qu'elle voit. Ici il est EN BAS : des profils
   dont les attentes convergent, puis le lecteur. Tout le reste — la pile, la
   forme, les inclinaisons, les vignettes — est celui de .ui-hero-selection.
   Le rembourrage se retourne avec le jeton : la place réservée à son
   débordement passe du haut au bas. */

.ui-hero-selection--concept {
  padding: 0 0 var(--space-7);
  /* Descendue de 30px : le décalage passe par « top » sur un élément déjà en
     position relative, non par une marge — la scène garde ainsi la hauteur
     qu'elle occupe dans la rangée, et l'alignement des deux colonnes n'est
     pas recalculé autour du déplacement. */
  top: 1.875rem;
}

.ui-hero-selection--concept .ui-selection-token-rangee {
  top: auto;
  bottom: 0.0625rem;
}

/* Le trait pointillé part du HAUT du jeton, puisqu'il monte vers la pile. */
.ui-hero-selection--concept .ui-selection-token::after {
  top: auto;
  bottom: 100%;
}

/* Hero — visuel « la fiche et ses critères » --------------------------------
   Second visuel possible du hero, exclusif de la scène de sélection. Il ne
   montre pas une rencontre, il DÉMONTRE le mécanisme : une personne, la liste
   de ce qu'elle recherche, et la valeur du lecteur en regard de chaque critère
   satisfait. Le lecteur se reconnaît ligne à ligne au lieu qu'on le lui dise. */

.ui-hero-board {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: var(--space-4);
  width: 100%;
  min-height: 620px;
  padding: var(--space-4) 0;
}

.ui-hero-board::before {
  content: "";
  position: absolute;
  z-index: 0;
  width: 620px;
  height: 580px;
  border-radius: 46% 54% 58% 42% / 47% 43% 57% 53%;
  background: color-mix(in srgb, var(--surface-appuyee) 40%, transparent);
  transform: rotate(-6deg);
}

.ui-board-bloc {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 286px minmax(0, 1fr);
  align-items: start;
  gap: 0;
  width: min(640px, 100%);
}

.ui-board-fiche {
  /* Décalée de 10px vers la droite. Par « left » et non par une marge : la
     colonne de grille est de largeur fixe, une marge y comprimerait la fiche
     au lieu de la déplacer. */
  position: relative;
  left: 10px;
  z-index: 1;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
  transform: rotate(-2deg);
}

.ui-board-fiche img {
  display: block;
  width: 100%;
  height: 319px;
  object-fit: cover;
}

.ui-board-identite {
  display: grid;
  gap: 0.125rem;
  padding: var(--space-3) var(--space-3) var(--space-3);
}

.ui-board-identite strong {
  font-size: var(--fs-h5);
  letter-spacing: -0.025em;
}

.ui-board-identite span {
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Le panneau chevauche la fiche et passe DEVANT : rien de son contenu n'est
   donc couvert, et c'est la fiche qui perd une vingtaine de pixels sur son
   bord droit, là où elle n'a que de la photo — son nom est calé à gauche.
   Marge négative, et non un « gap » négatif, qui n'existe pas en grille.
   Les deux cartes étant inclinées, chacune déborde de sa boîte d'environ 5px :
   le recouvrement vu dépasse la marge d'une dizaine de pixels. */
.ui-board-panneau {
  position: relative;
  z-index: 2;
  margin-top: var(--space-5);
  margin-left: calc(var(--space-4) * -1);
  padding: var(--space-4) var(--space-4) var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
  transform: rotate(1.5deg);
}

/* Un vrai tableau, non une grille de div : ce sont deux colonnes de données
   comparées, avec des en-têtes que les technologies d'assistance annoncent.
   Sans ces deux en-têtes, la pastille de droite n'est qu'une valeur flottante
   dont le lecteur doit deviner à qui elle appartient. */
.ui-board-criteres {
  width: 100%;
  border-collapse: collapse;
}

.ui-board-criteres th {
  padding: 0 0 var(--space-3);
  border-bottom: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: 800;
  letter-spacing: 0.13em;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

/* La colonne « Vous » se réduit à son contenu ; son rembourrage est symétrique
   parce qu'elle est centrée — inégal, il décalerait tout vers la droite. */
.ui-board-criteres th:last-child,
.ui-board-criteres td:last-child {
  width: 1%;
  padding-left: var(--space-3);
  padding-right: var(--space-3);
  border-left: 1px solid var(--border);
  text-align: center;
  white-space: nowrap;
}

.ui-board-criteres td {
  padding: var(--space-2) var(--space-3) var(--space-2) 0;
  font-size: var(--fs-sm);
  line-height: 1.3;
  vertical-align: middle;
}

.ui-board-criteres tbody tr + tr td {
  border-top: 1px solid var(--border-soft);
}

.ui-board-criteres td .material-symbols-outlined {
  margin-right: var(--space-2);
  font-size: var(--icon-md);
  color: var(--color-success);
  vertical-align: -4px;
}

/* La ligne sans valeur garde sa place : c'est elle qui explique que le score
   ne soit pas de 100, et donc qu'il veuille dire quelque chose. */
.ui-board-critere--vide td {
  color: var(--text-muted);
}

.ui-board-critere--vide td .material-symbols-outlined {
  color: var(--border);
}

/* Les valeurs cochées et le score portent le ROSE : ce sont les points de
   rencontre entre deux profils, le sujet même de la page. Le pétrole y disait
   « donnée » ; le rose y dit « c'est vous ».
   L'encre et non le rose plein — 4,82:1 sur la surface douce contre 3,25. */
.ui-board-valeur,
.ui-board-score b {
  display: inline-block;
  border-radius: var(--radius-pill);
  background: var(--color-brand-pink-soft);
  color: var(--color-brand-pink-ink);
  /* 650 et non 800 : sur une pastille de 12px, le gras extrême tassait les
     lettres, et le rose y ajoutait déjà de l'insistance. */
  font-weight: 550;
}

.ui-board-valeur {
  padding: 0.0625rem var(--space-2);
  /* Même corps que le critère qu'elle fait face : les deux colonnes se lisent
     en vis-à-vis, une différence de taille y hiérarchiserait ce qui doit être
     lu comme deux moitiés d'une même ligne. */
  font-size: var(--fs-sm);
}

/* Le score porte le même jeton que les valeurs de la colonne, en plus grand :
   il est de la même nature qu'elles — une donnée de correspondance — et le
   lecteur n'a pas deux codes visuels à apprendre pour une seule idée. */
.ui-board-score {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.4;
  text-align: center;
}

.ui-board-score b {
  padding: 0.1875rem var(--space-2);
  font-size: var(--fs-h5);
  font-weight: 550;
  letter-spacing: -0.03em;
  white-space: nowrap;
  vertical-align: -2px;
}

/* Elle répond au « il n'y en aura qu'un ? » sans ajouter de second
   raisonnement dans l'image : des visages et un nombre suffisent. */
.ui-board-suite {
  position: relative;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-sm);
  font-weight: 600;
}

.ui-board-suite .ui-hero-confiance-avatars img {
  width: 34px;
  height: 34px;
}

.ui-board-suite b {
  color: var(--color-brand-pink-ink);
  font-weight: 800;
}

/* Ligne de réassurance. Le modèle l'ancre au bas du hero en absolu ; ici elle
   suit les actions dans le flux — même lecture, et rien à recalculer quand la
   hauteur du hero change. */
.ui-hero-confiance {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.ui-hero-confiance p {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

.ui-hero-confiance strong {
  color: var(--text);
}

.ui-hero-confiance-avatars {
  display: flex;
}

/* Chevauchement : chaque portrait mord sur le précédent, le liseré blanc les
   sépare. Le premier ne mord sur rien. */
.ui-hero-confiance-avatars img {
  width: 35px;
  height: 35px;
  margin-left: calc(var(--space-2) * -1);
  border: 3px solid var(--surface-card);
  border-radius: var(--radius-photo);
  object-fit: cover;
}

.ui-hero-confiance-avatars img:first-child {
  margin-left: 0;
}

/* Section « mur de profil » --------------------------------------------------
   Une fenêtre d'application posée en regard du texte : elle montre ce qu'un
   profil publie, là où une capture d'écran aurait montré une interface. La
   nuance compte — on illustre la matière, pas le produit. */

.ui-mur-demo {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  min-height: 650px;
}

.ui-mur-forme {
  position: absolute;
  z-index: -1;
  width: 485px;
  height: 550px;
  border-radius: 51% 49% 43% 57% / 48% 56% 44% 52%;
  background: color-mix(in srgb, var(--surface-appuyee) 40%, transparent);
  transform: rotate(-6deg);
}

.ui-mur-fenetre {
  width: min(445px, 100%);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
  transform: rotate(1.5deg);
}

.ui-mur-entete {
  display: grid;
  grid-template-columns: 50px 1fr auto;
  align-items: center;
  gap: var(--space-2);
  padding: 0.125rem 0.1875rem var(--space-3);
}

.ui-mur-entete img {
  width: 50px;
  height: 50px;
  border-radius: var(--radius-md);
  object-fit: cover;
}

.ui-mur-entete div {
  display: grid;
  gap: 0.1875rem;
}

.ui-mur-entete strong {
  font-size: var(--fs-base);
}

.ui-mur-entete span:not(.material-symbols-outlined) {
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-mur-entete .material-symbols-outlined {
  color: var(--text-muted);
  font-size: var(--icon-md);
}

.ui-mur-publications {
  display: grid;
  gap: var(--space-2);
}

/* Deux colonnes fixes : la vignette garde sa largeur quelle que soit la
   longueur du texte, sans quoi les publications ne s'aligneraient plus entre
   elles et la fenêtre perdrait son air de fil. */
.ui-mur-publication {
  display: grid;
  grid-template-columns: 92px 1fr;
  min-height: 96px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--white);
}

.ui-mur-visuel {
  width: 92px;
  height: 100%;
  min-height: 96px;
  object-fit: cover;
}

.ui-mur-icone {
  display: grid;
  place-items: center;
  width: 92px;
  min-height: 96px;
  background: var(--surface-douce);
  color: var(--color-primary);
}

.ui-mur-icone .material-symbols-outlined {
  font-size: var(--icon-xl);
}

/* Teintes : la publication sans photo emprunte le procédé de progression des
   cartes — du doux au soutenu — pour que le fil ne soit pas monotone. */
.ui-mur-icone--primary {
  background: var(--color-primary);
  color: var(--white);
}

.ui-mur-icone--peach {
  background: var(--color-secondary);
  color: var(--color-dark);
}

.ui-mur-corps {
  display: grid;
  align-content: center;
  padding: var(--space-2) var(--space-3);
}

.ui-mur-corps > div:first-child {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.ui-mur-corps small {
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-mur-corps > div:first-child .material-symbols-outlined {
  color: var(--text-muted);
  font-size: var(--icon-sm);
}

.ui-mur-corps p {
  margin: var(--space-1) 0 var(--space-2);
  font-size: var(--fs-base);
  font-weight: 650;
  line-height: 1.3;
}

.ui-mur-reactions {
  display: flex;
  gap: var(--space-1);
}

.ui-mur-reactions span {
  display: grid;
  place-items: center;
  width: 25px;
  height: 25px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  color: var(--text-muted);
  font-size: var(--icon-sm);
}

.ui-mur-lead {
  max-width: 610px;
  margin: var(--space-4) 0 var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-lead);
  line-height: 1.7;
}

/* Un SECOND chapô n'en est pas un. Quand deux paragraphes se suivent ici, le
   premier ouvre la section et le second précise : le second revient donc au
   corps de texte (16px au lieu de 18). Deux paragraphes de 18px à la suite
   donneraient deux ouvertures, et la hiérarchie du bloc disparaîtrait.

   L'écart entre eux passe de 31 à 16px : c'est la respiration d'un paragraphe
   à l'intérieur d'un même propos, non celle qui sépare le chapô de la liste
   numérotée qui vient après. Réglé en annulant la marge basse du premier —
   les marges verticales fusionnent, garder les deux n'aurait rien donné. */
.ui-mur-lead:has(+ .ui-mur-lead) { margin-bottom: 0; }

.ui-mur-lead + .ui-mur-lead {
  margin-top: var(--space-3);
  font-size: var(--fs-base);
}

/* Étapes numérotées — PARTAGÉES par plusieurs sections (mur, communautés).
   Le nom ne porte donc pas celui d'un organisme : la même liste sert partout
   où trois gestes s'enchaînent.
   C'est une LISTE ORDONNÉE : leur ordre est le propos. Le numéro visible
   remplace la puce, d'où la puce retirée et le compteur écrit à la main dans
   la donnée — un « ol » sans marqueur reste un ol pour qui l'écoute. */
.ui-etapes {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-etapes li {
  display: grid;
  grid-template-columns: 39px 1fr;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
}

.ui-etapes li > span {
  display: grid;
  place-items: center;
  width: 39px;
  height: 39px;
  border-radius: var(--radius-sm);
  background: var(--surface-douce);
  color: var(--color-primary);
  font-size: var(--fs-sm);
  font-weight: 600;               /* même famille que .ui-carte-icone-num */
}

.ui-etapes p {
  display: grid;
  gap: var(--space-1);
  margin: 0;
}

.ui-etapes strong {
  font-size: var(--fs-base);
}

.ui-etapes small {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

.ui-mur-legende {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: max-content;
  max-width: 100%;
  margin: var(--space-5) auto 0;
  padding: var(--space-3) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-align: center;
}

.ui-mur-legende .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-sm);
}

/* Section « communautés » ----------------------------------------------------
   Elle repose sur une teinte de retrait, ce qui la détache des sections
   blanches qui l'entourent et marque un changement de sujet : on passe de
   l'individu au groupe. */

.ui-communaute-section {
  overflow: hidden;
}

/* Les jetons de numéro passent au blanc : sur un fond déjà pêche, la teinte
   habituelle du jeton ne se détacherait plus de la section. */
.ui-communaute-section .ui-etapes li {
  border-top-color: var(--border);
}

.ui-communaute-section .ui-etapes li > span {
  background: var(--surface-card);
}

.ui-communaute-visuel {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  min-height: 620px;
}

.ui-communaute-forme {
  position: absolute;
  z-index: -1;
  width: 495px;
  height: 535px;
  border-radius: 54% 46% 43% 57% / 49% 56% 44% 51%;
  background: color-mix(in srgb, var(--surface-appuyee) 55%, transparent);
  transform: rotate(7deg);
}

.ui-communaute-carte {
  width: min(405px, 100%);
  overflow: hidden;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
  transform: rotate(-2deg);
}

.ui-communaute-photo {
  position: relative;
  height: 255px;
}

.ui-communaute-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Voile en dégradé sur la moitié basse : il ne décore pas, il garantit que la
   pastille reste lisible quelle que soit la photo placée dessous. */
.ui-communaute-photo::after {
  content: "";
  position: absolute;
  inset: 45% 0 0;
  background: linear-gradient(rgb(from var(--color-dark) r g b / 0), color-mix(in srgb, var(--color-dark) 55%, transparent));
}

.ui-communaute-photo > span {
  position: absolute;
  z-index: 2;
  left: 1.0625rem;
  bottom: 1rem;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--white) 90%, transparent);
  font-size: var(--fs-sm);
  font-weight: 700;
}

.ui-communaute-photo > span .material-symbols-outlined {
  color: var(--color-success);
  font-size: var(--icon-sm);
}

.ui-communaute-corps {
  padding: var(--space-4) var(--space-4) var(--space-4);
}

.ui-communaute-corps small {
  color: var(--color-primary);
  font-size: var(--fs-sm);
  font-weight: 700;
}

.ui-communaute-corps h3 {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-h4);
  letter-spacing: -0.03em;
}

.ui-communaute-corps > p {
  margin: var(--space-3) 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-base);
  line-height: 1.55;
}

.ui-communaute-membres {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

.ui-communaute-membres > div {
  display: flex;
}

.ui-communaute-membres img {
  width: 34px;
  height: 34px;
  margin-left: calc(var(--space-2) * -1);
  border: 3px solid var(--surface-card);
  border-radius: var(--radius-photo);
  object-fit: cover;
}

.ui-communaute-membres img:first-child {
  margin-left: 0;
}

.ui-communaute-membres strong {
  font-size: var(--fs-sm);
}

/* Posée en absolu et penchée en sens inverse de la carte : elle doit avoir
   l'air ajoutée par-dessus, comme une notification, non alignée avec elle. */
.ui-communaute-evenement {
  position: absolute;
  z-index: 3;
  right: -5px;
  bottom: 3.6875rem;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: min(290px, 90%);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  box-shadow: var(--shadow-md);
  transform: rotate(3deg);
}

.ui-communaute-date {
  flex: 0 0 auto;
  display: grid;
  place-content: center;
  width: 53px;
  height: 58px;
  border-radius: var(--radius-sm);
  background: var(--surface-douce);
  color: var(--color-primary);
  text-align: center;
}

.ui-communaute-date b {
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
}

.ui-communaute-date strong {
  font-size: var(--fs-h5);
  line-height: 1;
}

.ui-communaute-evenement > div {
  display: grid;
  gap: 0.1875rem;
}

.ui-communaute-evenement small,
.ui-communaute-evenement > div > span {
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-communaute-evenement > div > strong {
  font-size: var(--fs-base);
}

/* Section d'appel ------------------------------------------------------------
   Bloc centré de clôture. Le sur-titre est en flux inline, il faut donc le
   recentrer explicitement : « text-align » ne déplace pas une boîte
   inline-flex, il n'aligne que le texte qui la contient. */

.ui-appel-copy {
  text-align: center;
}

.ui-appel-lead {
  max-width: 620px;
  margin: var(--space-4) auto var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-lead);
  line-height: 1.65;
}

.ui-appel-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
}

/* Hero de communauté ---------------------------------------------------------
   La photo de la communauté en FOND, pleine largeur, et posée dessus, à
   GAUCHE, une carte blanche qui porte les informations de base. Le fil
   d'Ariane le précède, dans la barre blanche des pages intérieures
   (.ui-breadcrumb-bar, plus haut) : un chemin de navigation se lit d'un coup
   d'œil, et sur une image il se cherche.

   La carte fait ce qu'un voile en dégradé faisait avant elle, et mieux : le
   texte est lisible sans qu'on ait à parier sur ce qu'il y a dessous. Un
   dégradé dépend de l'image — clair sur une photo claire, il ne tient plus ;
   un fond plein, non. Et la photo reste entière à côté, au lieu d'être à
   demi effacée.

   Le hero remplace la bande décorative de .ui-page-hero — une page de
   communauté a mieux à mettre en tête qu'un aplat. Le préfixe lui est
   propre : .ui-hero- appartient au hero de l'accueil, réglé pour lui (680px,
   animations d'entrée) ; on ne partage que .ui-display-title et .ui-eyebrow,
   qui ont le même sens partout.

   Le rôle de ce bloc n'est pas décoratif : la page s'ouvre à un visiteur qui
   n'a pas de compte, et il faut qu'il sache en un écran de quoi parle la
   communauté, où elle se réunit, combien ils sont, et ce qu'il gagnerait à
   s'inscrire. Le mur d'échanges, lui, n'est pas rendu du tout. */

/* 740px, et le rembourrage de section ramené de --space-7 à --space-6 : les
   deux vont ensemble. Le hero est un flex centré, sa carte tombe donc au
   milieu de la BOÎTE DE CONTENU — celle que le rembourrage creuse. Avec les
   120px du rythme de section, la carte (524px de haut) et ses deux marges
   faisaient 764px à elles seules : le contenu menait, le plancher de 740 ne
   servait à rien, et la hauteur suivait la longueur du texte de chaque
   communauté. À 64px, le total tombe à 652 — sous le plancher, qui reprend
   la main et fixe la hauteur pour toutes les fiches.
   Le rembourrage reste ÉGAL en haut et en bas : c'est ce qui, avec
   align-items:center, centre la carte sur la hauteur. */
.ui-hero-communaute {
  position: relative;                /* repère de la photo et de la carte */
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: 740px;
  padding-block: var(--space-6);
  background: var(--white);   /* le temps que la photo charge */
}

/* Le conteneur est l'unique enfant en flux du hero, donc son ITEM FLEX : sans
   largeur pleine il se dimensionnerait sur son contenu, et la grille qu'il
   porte ne mesurerait plus la page. */
.ui-hero-communaute > .ui-container { width: 100%; }

/* Une <img> et non un background-image : le build ne pose ses ?v= que sur
   « src » et « srcset ». Une URL enfermée dans un style en ligne échapperait
   au cache-busting, et un remplacement de photo resterait invisible chez
   ceux qui ont déjà vu la page. */
.ui-hero-communaute-fond {
  position: absolute;
  z-index: -2;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;       /* le groupe, pas la cime des arbres */
}

/* La carte occupe SIX COLONNES de la grille, à gauche : sa largeur est celle
   d'une colonne Bootstrap, pas une valeur à elle. Elle tombe donc sur les
   mêmes lignes que les sections en dessous, ce qu'un max-width en rem ne
   garantissait pas. Le rembourrage est posé sur la carte et non sur la
   colonne, qui garde sa demi-gouttière : c'est ce qui aligne le bord de la
   carte sur la marge du conteneur.
   L'ombre est la plus profonde du dépôt, celle des cartes posées sur une
   photo : c'est l'altitude qui dit qu'elle flotte au-dessus. Elle a son
   token — --shadow-photo — depuis que la carte du hero d'evenements.html la
   porte aussi. */
.ui-hero-communaute-copy {
  position: relative;
  z-index: 1;
  padding: var(--space-5);
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-photo);
}

/* Sur-titre et lieu sur la même ligne : le second dit OÙ, le premier dit
   QUOI. Les séparer aurait fait deux lignes de service au-dessus du nom. */
.ui-hero-communaute-kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.ui-hero-communaute-kicker .ui-eyebrow { margin-bottom: 0; }

/* Fond plein et non voile : sur une photo claire comme sur une photo sombre,
   le libellé doit se lire sans qu'on ait à parier sur ce qu'il y a dessous. */
.ui-hero-communaute-lieu { /* le dessin vient de .ui-badge */ }


/* Un cran sous l'échelle du hero d'accueil. Le titre n'est pas une phrase
   d'accroche que l'on calibre, c'est un NOM que la communauté choisit : à
   72px, « Concerts & découvertes en Brabant wallon » occuperait l'écran
   entier. Les paliers de l'atome reprennent la main sous 820px. */
.ui-hero-communaute .ui-display-title {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-display-4);
  color: var(--color-titre);
}

/* LE BANDEAU RÉDUIT D'UN MEMBRE (3 oct. 2026) : il a déjà vu la
   présentation ; le bandeau ne garde que le nom, le lieu, les membres et le
   geste, et laisse la place à ce qu'il est venu lire, sous les onglets. */
.ui-hero-communaute.ui-hero-communaute--compacte {
  min-height: 0;
  padding-block: var(--space-5);
}

.ui-hero-communaute--compacte .ui-hero-communaute-copy { padding: var(--space-4); }

.ui-hero-communaute--compacte .ui-display-title {
  margin-bottom: var(--space-3);
  font-size: var(--fs-h2);
}

.ui-hero-communaute--compacte :is(.ui-hero-communaute-lead, .ui-hero-communaute-meta) { display: none; }

.ui-hero-communaute-lead {
  margin: 0;
  max-width: 46ch;
  color: var(--text);
  font-size: var(--fs-lead);
  line-height: 1.65;
}

/* Repères en ligne : une liste, parce que c'en est une, mais sans ses puces
   ni son retrait — chaque entrée porte déjà son pictogramme. */
.ui-hero-communaute-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

.ui-hero-communaute-meta li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text);
  font-size: var(--fs-sm);
}

.ui-hero-communaute-meta .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
}

.ui-hero-communaute-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

/* Une action qui change des données (rejoindre, quitter) est un bouton dans
   un formulaire. Le formulaire s'efface de la mise en page : le bouton se
   range dans la rangée d'actions comme s'il était un lien. */
.ui-hero-communaute-form { display: contents; }

/* Ligne des membres : même geste que la ligne de réassurance du hero
   d'accueil — des visages, puis ce qu'ils sont. */
.ui-hero-communaute-membres {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.ui-hero-communaute-avatars { display: inline-flex; }
/* Vignettes RONDES et petites, et c'est tout ce que le visiteur verra des
   membres : sans compte, aucun profil ne s'ouvre. Chevauchement et liseré
   repris de la carte communauté — même geste, même lecture. */
.ui-hero-communaute-avatars img {
  width: 38px;
  height: 38px;
  margin-left: calc(var(--space-2) * -1);
  border: 3px solid var(--surface-card);
  border-radius: var(--radius-photo);
  object-fit: cover;
}

.ui-hero-communaute-avatars img:first-child { margin-left: 0; }

/* Le reste du groupe ferme la pile, au gabarit d'un avatar. */
.ui-hero-communaute-compteur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 38px;
  padding-inline: var(--space-2);
  /* « +1 244 » passait à la ligne dans le jeton dès que la carte se
     resserrait — six colonnes de 1024px suffisaient. Un jeton est une
     pastille, pas un paragraphe. */
  white-space: nowrap;
  margin-left: calc(var(--space-2) * -1);
  border: 3px solid var(--surface-card);
  border-radius: var(--radius-pill);
  background: var(--color-brand-pink-ink);
  color: var(--on-dark);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
}

.ui-hero-communaute-membres-texte {
  display: flex;
  flex-direction: column;
  margin: 0;
  line-height: var(--lh-heading);
}

.ui-hero-communaute-membres-texte strong {
  color: var(--text);
  font-size: var(--fs-base);
}

.ui-hero-communaute-membres-texte span {
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

/* LA BARRE D'ONGLETS D'UNE COMMUNAUTÉ — à propos, discussions, membres,
   évènements. C'est le même objet que .ui-evenement-nav, et il porte donc le
   même châssis : le fond de carte, les deux filets, le rembourrage, et des
   onglets dessinés par « .ui-fil-filtre », les pilules du profil.
   POURQUOI UN AUTRE NOM, alors : .ui-communaute- est déjà pris — c'est la
   famille de la section du concept (.ui-communaute-carte, -photo, -date).
   La barre appartient au hero de communauté, qui la rend ; elle prend donc
   son préfixe, .ui-hero-communaute-. Le voisinage de noms compte plus ici
   que la symétrie avec l'évènement, dont la famille, elle, était libre. */
.ui-hero-communaute-nav {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--surface-card);
}

.ui-hero-communaute-nav > .ui-fil-filtres {
  margin-bottom: 0;
  padding-block: var(--space-3);
}

/* Elle DIT ce qui manque à la page. Un visiteur qui ne verrait que le texte
   conclurait qu'il n'y a rien à échanger ici ; la ligne lui apprend que le
   mur existe, et derrière quoi il se trouve. Teinte de retrait, et non un
   blanc translucide : dans une carte blanche, ce dernier ne se voyait
   plus. */
.ui-hero-communaute-verrou {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--surface-douce);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-hero-communaute-verrou .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
}

/* Sous 992px la carte prend toute la largeur : il n'y a plus de place pour
   la photo À CÔTÉ d'elle. Celle-ci redevient donc une BANDE, en tête du
   hero, et la carte remonte mordre dessus — la composition dit alors la
   même chose qu'en grand écran : la carte est POSÉE sur la photo.

   Borner la bande en hauteur n'est pas cosmétique : « cover » sur un écran
   étroit remplit la hauteur et rogne les côtés, et le sujet — le groupe, au
   milieu du cadre — sortait du champ. */
@media (width <= 991.98px) {

  /* Sélecteur à DEUX classes, comme .ui-hero-scroll-zone--degrade .ui-hero
     plus haut : ui-charte.css est chargée après cette feuille et y pose
     « .ui-section { padding-block: 5.5rem } » sous 1100px. À spécificité
     égale, c'est elle qui l'emporte — le rembourrage haut retombait à 88px
     et la carte remontait sur la photo. */
  .ui-section.ui-hero-communaute {
    min-height: 0;
    padding-top: calc(18rem - var(--space-5));
    background: var(--white);
  }

  .ui-hero-communaute-fond {
    inset: 0 0 auto;
    height: 18rem;
  }

}

@media (width <= 560px) {

  .ui-section.ui-hero-communaute { padding-top: calc(13rem - var(--space-4)); }

  .ui-hero-communaute-fond { height: 13rem; }

  .ui-hero-communaute-copy { padding: var(--space-4); }

  .ui-hero-communaute-actions,
  .ui-hero-communaute-membres {
    align-items: flex-start;
    flex-direction: column;
  }
}

/* Hero d'ÉVÈNEMENT ------------------------------------------------------------
   Le texte à gauche sur le fond de page, la photo à droite dans un cadre.
   C'est la réponse à un problème qui n'a rien de graphique : la photo d'un
   évènement est TÉLÉVERSÉE PAR UN UTILISATEUR. Ratio, définition, cadrage,
   luminosité : rien n'est connu d'elle avant qu'elle s'affiche. Une photo
   posée en fond de section, avec du texte par-dessus, fait dépendre la page
   de ces quatre inconnues ; un cadre les enferme toutes.

   Le hero de communauté, lui, garde son fond photo : ses images sont
   choisies et cadrées pour ce format. Les deux organismes ne se ressemblent
   pas parce qu'ils ne courent pas le même risque. */
.ui-hero-evenement {
  padding-block: var(--space-6);
  background: var(--white);
}

/* Le cadre est la pièce maîtresse, et il tient en trois déclarations.

   « aspect-ratio » d'abord : c'est LUI qui fixe la hauteur, jamais l'image.
   Un portrait, un panorama et un carré donnent le même bloc, et deux fiches
   d'évènement tombent sur la même ligne — ce qui n'arriverait pas si la
   hauteur venait du fichier. Le ratio est une variable pour que le snippet
   puisse le changer par page sans qu'on écrive une classe par valeur.

   « overflow: hidden » ensuite, avec le rayon : c'est le rognage. Sans lui,
   une image plus grande que son cadre déborderait par-dessus le texte.

   Le FOND, enfin, et il n'est pas décoratif : il est ce qu'on voit pendant
   le chargement, derrière une image à transparence, et à la place d'une
   image cassée. Sans lui, l'un des trois cas laisse un trou blanc dans la
   page. */
.ui-hero-evenement-cadre {
  position: relative;
  margin: 0;
  aspect-ratio: var(--hero-evenement-ratio, 4 / 3);
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface-appuyee);
  box-shadow: var(--shadow-photo);
}

/* « object-fit: cover » recadre au centre : le sujet d'une photo y est neuf
   fois sur dix. « height: 100% » est indispensable — sans elle, une image
   plus courte que le cadre laisserait une bande de fond en bas, et le
   « cover » n'aurait rien à recadrer. */
.ui-hero-evenement-cadre > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* La pastille de lieu, le sur-titre, la ligne de repères : le même
   vocabulaire que le hero de communauté, aux mêmes valeurs. Les deux heros
   diffèrent par leur COMPOSITION, pas par leur typographie — un visiteur qui
   passe de l'un à l'autre ne doit pas changer de repères. */
.ui-hero-evenement-kicker {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

/* Elle est posée DANS la photo, en haut à gauche — et c'est la seule chose
   que cet organisme accepte de poser sur une image dont il ne sait rien.
   Elle le peut parce qu'elle n'est pas du texte nu : c'est un aplat opaque
   de la couleur d'accent, avec sa propre ombre. Sa lisibilité ne dépend donc
   pas de ce qui passe dessous, qu'il s'agisse d'un ciel blanc ou d'un
   sous-bois. Un titre au même endroit, lui, aurait dépendu des deux.

   z-index positif : l'image du cadre est un élément en flux, la pastille est
   positionnée — elle passerait déjà devant. On l'écrit parce que c'est une
   intention, pas un effet de bord. */
.ui-hero-evenement-cadre .ui-hero-evenement-lieu {
  position: absolute;
  z-index: 1;
  top: var(--space-3);
  left: var(--space-3);
  box-shadow: var(--shadow-md);
}

/* La date COIFFE la photo avec le lieu, chacun dans son coin haut : le lieu
   à gauche, la date à droite. Les deux repères tiennent ainsi la même ligne
   — c'est OÙ et QUAND, les deux questions qu'on pose devant une sortie, et
   elles se lisent d'un seul regard. Le bas de la photo reste libre, là où
   une image a le plus souvent son sujet.
   Les valeurs, elles, restent celles de .ui-evenement-date, la pastille de
   la carte de la liste. Reprise et non partage de
   classe : le dépôt fait de même entre .ui-communaute-date et
   .ui-evenement-date. Une carte de la liste et la fiche qu'elle ouvre
   montrent ainsi le même objet — c'est ce qui dit au visiteur qu'il a bien
   ouvert celle sur laquelle il a cliqué.

   Fond plein et non voile, comme là-bas : au-dessus d'une photo dont on ne
   sait rien, un voile ne garantit aucun contraste. */
.ui-hero-evenement-date {
  position: absolute;
  z-index: 1;
  inset-block-start: var(--space-4);
  inset-inline-end: var(--space-4);
  display: grid;
  place-content: center;
  min-width: 56px;
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--surface-card);
  /* Le ROUGE de la marque, comme la pastille de la carte (.ui-evenement-date) :
     la fiche était restée au pétrole, et la carte et la fiche qu'elle ouvre ne
     montraient plus le même objet (demande du 27 sept. 2026). */
  color: var(--color-brand-pink-ink);
  text-align: center;
  line-height: 1.1;
  box-shadow: var(--shadow-md);
}

.ui-hero-evenement-date b {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ui-hero-evenement-date strong { font-size: var(--fs-h5); }

.ui-hero-evenement-date i {
  font-size: var(--fs-caption);
  font-style: normal;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.ui-hero-evenement-lieu { /* le dessin vient de .ui-badge */ }


.ui-hero-evenement-lead {
  max-width: 34rem;
  margin-bottom: var(--space-4);
  font-size: var(--fs-lead);
  line-height: var(--lh-base);
}

.ui-hero-evenement-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  margin: 0 0 var(--space-5);
  padding: 0;
  list-style: none;
}

.ui-hero-evenement-meta li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-hero-evenement-meta .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
}

.ui-hero-evenement-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

/* Une action qui change des données (participer, annuler) est un bouton dans
   un formulaire, qui s'efface de la mise en page — voir .ui-hero-communaute-form. */
.ui-hero-evenement-form { display: contents; }

.ui-hero-evenement-participants {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.ui-hero-evenement-avatars { display: inline-flex; }

/* Les vignettes se chevauchent d'un tiers : c'est ce qui les lit comme un
   groupe et non comme une liste. La bordure de la couleur de page les
   sépare — sans elle, deux portraits sombres se confondent. */
.ui-hero-evenement-avatars img,
.ui-hero-evenement-compteur {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-photo);
  border: 2px solid var(--white);
  object-fit: cover;
}

.ui-hero-evenement-avatars img + img,
.ui-hero-evenement-compteur { margin-left: calc(var(--space-3) * -1); }

.ui-hero-evenement-compteur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-brand-pink-ink);
  color: var(--on-dark);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
}

/* Mêmes teintes et mêmes corps que .ui-hero-communaute-membres-texte : c'est
   la même information, elle se lit pareil. */
.ui-hero-evenement-participants-texte {
  display: flex;
  flex-direction: column;
  margin: 0;
  line-height: var(--lh-heading);
}

.ui-hero-evenement-participants-texte strong {
  color: var(--text);
  font-size: var(--fs-base);
}

.ui-hero-evenement-participants-texte span {
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

/* Sous 992px, les deux colonnes s'empilent, et deux choses doivent changer.

   La PHOTO passe en tête : le texte est premier dans le document — c'est ce
   que doit lire un moteur ou un lecteur d'écran — mais empilée telle quelle,
   l'image se retrouvait tout en bas, après le bouton et les participants,
   c'est-à-dire hors du premier écran d'un téléphone. « order » ne déplace que
   le rendu, l'ordre du document ne bouge pas.

   (La mention d'organisateur, elle, reperd sa marge automatique — mais plus
   bas dans cette feuille, juste après la règle qui la lui donne : écrite
   ici, elle serait arrivée AVANT et aurait perdu l'arbitrage.) */
@media (width <= 991.98px) {
  .ui-hero-evenement-visuel {
    order: -1;
    margin-bottom: var(--space-5);
  }
}

/* Pas de masse décorative derrière la photo. Il y en a eu : deux ellipses
   débordant du cadre, l'une beige en bas à gauche, l'autre corail en haut à
   droite, reprises du vocabulaire du hero d'accueil. Elles ont été agrandies
   puis effacées jusqu'à 10 % avant d'être retirées — et c'est ce qui a
   tranché : à cette échelle elles ne se voyaient plus, et plus visibles elles
   devenaient une forme que l'oeil cherche à lire. Le hero d'accueil peut les
   porter, son visuel est un PNG détouré posé sur le papier ; ici la photo est
   un rectangle net dans un cadre à ombre, elle n'a rien à quoi les accrocher.

   Le conteneur garde son positionnement : la pastille de date et celle de
   lieu s'y placent en absolu. */
.ui-hero-evenement-visuel {
  position: relative;
  z-index: 0;
}

/* Recomposition 2026 du hero événement : en-tête d'identification d'une page
   profonde. Ces règles prennent le pas sur la première version ci-dessus ;
   elles gardent ses primitives mais changent la hiérarchie et la composition. */
.ui-breadcrumb-bar--evenement {
  border-bottom: 1px solid var(--border);
  background: var(--surface-card);
}

/* Le dégradé est celui de .ui-hero-scroll-zone--degrade, le hero de
   l'accueil : du papier vers --surface-appuyee, de haut en bas. Repris à
   l'identique, sens compris — une même page ne doit pas avoir deux façons de
   descendre du blanc vers le beige. Il se referme sur la barre blanche du
   fil d'Ariane au-dessus et sur le fond blanc de la section suivante. */
.ui-hero-evenement {
  padding-block: var(--space-6);
  background: linear-gradient(to bottom, var(--white), var(--surface-appuyee));
}

/* La PHOTO à gauche, le TEXTE à droite, à partir de 992px. C'est un
   changement de RENDU seulement : dans le document, le texte reste premier —
   le titre de la page ne doit pas être précédé d'une image, ni pour un
   moteur, ni pour un lecteur d'écran. « order » ne déplace que ce qui est
   peint.

   Le rembourrage suit le déplacement : il sépare le texte de la photo, il
   est donc du côté où se trouve la photo — à gauche maintenant, à droite
   avant. Laissé à droite, il aurait creusé au bord de la page et collé le
   texte à l'image. */
@media (width >= 992px) {
  .ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-visuel {
    order: -1;
  }

  .ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-copy {
    padding-left: var(--space-5);
    padding-right: calc(var(--bs-gutter-x) * 0.5);
  }
}

.ui-hero-evenement-copy { padding-right: var(--space-5); }

.ui-hero-evenement-kicker {
  align-items: center;
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
}

.ui-hero-evenement-kicker .ui-eyebrow { margin: 0; }

/* ---- Variante BANNIÈRE ----
   L'autre modèle de page d'évènement : la photo pleine largeur en tête, le
   titre et les faits dessous. Elle sert quand l'image PORTE l'évènement — un
   concert, une salle, une affiche — là où la disposition en colonnes sert
   quand ce sont les faits qui comptent d'abord.

   Aucune classe de plus dans le balisage, et c'est le point : les mêmes
   colonnes Bootstrap sont remises à plat par « flex-basis », et l'ordre
   d'affichage est inversé par « order ». Le document, lui, garde le texte
   avant l'image — ce que doit lire un moteur ou un lecteur d'écran, quelle
   que soit la mise en page.

   Le cadre garde tout ce qui fait sa sûreté : ratio imposé, rognage, fond de
   secours, pastilles opaques. Une bannière reste une image téléversée ; elle
   n'est pas plus fiable parce qu'elle est plus large. */
.ui-hero-evenement--banniere .ui-hero-evenement-row {
  --bs-gutter-y: 0;
  align-items: flex-start;
}

.ui-section.ui-hero-evenement--banniere {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding: var(--space-5) 0 var(--space-6);
}

/* La bannière et les informations forment UNE SEULE CARTE, comme la carte
   évènement de la liste : la photo en tête, le corps blanc soudé dessous,
   un seul rayon, une seule ombre. C'est la même silhouette que celle qu'on
   vient de cliquer, à ceci près qu'elle occupe ici les douze colonnes.

   La carte est portée par la RANGÉE, et non par un conteneur ajouté au
   balisage : les deux colonnes s'y empilent déjà, il suffit de leur retirer
   leurs gouttières. C'est ce que fait « --bs-gutter-x: 0 » — sans lui, le
   corps aurait été 12px plus large que la photo de chaque côté, la gouttière
   étant peinte avec le fond de la colonne mais pas avec celui de l'image.

   « overflow: hidden » fait suivre le rayon à la photo, exactement comme sur
   .ui-evenement-carte. Les valeurs sont les siennes : --radius, --shadow-md,
   fond --surface-card. Une fiche et sa carte de liste ne doivent pas se
   dessiner différemment. */
.ui-hero-evenement--banniere .ui-hero-evenement-row {
  --bs-gutter-x: 0;
  --bs-gutter-y: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--surface-card);
  box-shadow: var(--shadow-md);
}

.ui-hero-evenement--banniere .ui-hero-evenement-visuel {
  flex: 0 0 100%;
  max-width: 100%;
  order: -1;
}

/* La photo perd son rayon et son ombre : elle n'est plus un objet posé sur la
   page, elle est le HAUT de la carte, et c'est la carte qui porte les deux. */
.ui-hero-evenement--banniere .ui-hero-evenement-cadre {
  border-radius: 0;
  box-shadow: none;
}

/* Le corps de la carte. Son rembourrage est celui d'une carte, pas d'une
   colonne de hero — et il n'y a plus de marge au-dessus : la soudure avec la
   photo est ce qui fait l'objet unique. */
.ui-hero-evenement--banniere .ui-hero-evenement-copy {
  flex: 0 0 100%;
  max-width: 100%;
  margin: 0;
  padding: var(--space-5);
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* Le cadre reste positionné : c'est le repère des deux pastilles. Les masses
   décoratives, elles, n'existent plus nulle part — ni ici, ni dans la
   disposition en colonnes. */
.ui-hero-evenement--banniere .ui-hero-evenement-cadre {
  position: relative;
  z-index: 1;
}

/* ---- Les deux pastilles de la BANNIÈRE ----
   Elles coiffent l'image, chacune dans son coin haut : la DATE à gauche, la
   VILLE à droite. Sur une bande de 1272px, les garder du même bord les
   laissait toutes deux dans le premier dixième de l'image et le reste de la
   largeur sans repère ; aux deux angles, elles la tiennent.

   La date passe à une fois et demie sa taille — 84px de large contre 56, et
   ses trois corps dans le même rapport. Ce n'est pas une décoration : sur une
   bannière, la pastille des cartes de liste devenait un timbre perdu au coin
   d'une grande image. Elle reste la MÊME pastille, au même dessin ; c'est
   l'échelle qui suit la surface.

   Les valeurs sont écrites en toutes lettres plutôt qu'en « scale() » : une
   transformation aurait aussi grossi l'ombre et flouté le texte, et le rayon
   n'aurait plus été celui du dépôt. */
.ui-hero-evenement--banniere .ui-hero-evenement-date {
  inset-block-start: var(--space-3);
  inset-block-end: auto;
  inset-inline-start: var(--space-3);
  inset-inline-end: auto;
}

.ui-hero-evenement--banniere .ui-hero-evenement-lieu {
  left: auto;
  right: var(--space-3);
}

/* Sous la bannière, DEUX COLONNES : à gauche ce qu'est la sortie — la ligne
   de service, le titre, le descriptif ; à droite ce qu'il faut en savoir pour
   venir — les faits, le bouton, les participants. Le partage suit la question
   posée : la gauche raconte, la droite organise.

   Sept colonnes contre cinq : le texte a besoin d'une mesure de lecture, les
   faits n'ont besoin que de leur largeur. Les blocs de droite sont calés en
   HAUT de leur rangée (« align-self: start ») — leurs hauteurs n'ont aucune
   raison d'égaler celles de gauche, et les laisser s'étirer aurait fait
   flotter le bouton au milieu d'un vide.

   C'est le retour d'une composition en deux colonnes essayée ici — titre à
   gauche, faits à droite, puis bouton et participants croisés dessous. Elle
   tenait sur un écran large et se défaisait ailleurs : quatre placements de
   grille à maintenir, deux calages à droite, et une lecture en zigzag pour
   trois lignes d'information. Une bannière donne déjà la largeur ; ce qui
   vient dessous n'a plus qu'à se lire.

   Ce qui ouvre la colonne de gauche est une ligne de SERVICE — la date et qui
   reçoit — et non la ligne de faits entière : elle est rendue là par le
   snippet, pas déplacée par « order ». La distinction compte, parce qu'un
   ordre de rendu inversé aurait menti au document ; ici les deux lignes
   existent séparément et se lisent chacune à son moment.

   Le placement est explicite, colonne ET rangée, pour chaque bloc : sans
   cela, la grille les rangerait dans l'ordre du document, et le bouton
   serait tombé sous le descriptif au lieu de tenir la colonne de droite. */
@media (width >= 992px) {
  .ui-hero-evenement--banniere .ui-hero-evenement-copy {
    display: grid;
    grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
    column-gap: var(--space-6);
    align-content: start;
  }

  /* À GAUCHE le récit : le titre, le descriptif, puis le geste qui les
     conclut. À DROITE les repères, par paires : quand et qui reçoit, puis
     l'heure et le lieu, puis qui vient. */
  .ui-hero-evenement--banniere .ui-hero-evenement-copy > .ui-display-title { grid-area: 1 / 1; }
  .ui-hero-evenement--banniere .ui-hero-evenement-lead { grid-area: 2 / 1; }
  .ui-hero-evenement--banniere .ui-hero-evenement-actions { grid-area: 3 / 1; }

  .ui-hero-evenement--banniere .ui-hero-evenement-service { grid-area: 1 / 2; }
  .ui-hero-evenement--banniere .ui-hero-evenement-meta { grid-area: 2 / 2; }
  .ui-hero-evenement--banniere .ui-hero-evenement-pied { grid-area: 3 / 2; }

  /* Les deux colonnes de repères tombent sur la même verticale d'une rangée
     à l'autre : la ligne de service et la ligne de faits partagent la même
     grille intérieure — deux colonnes égales — de sorte que « Heure » se
     range sous « Date », et « Ville » sous la communauté. */
  .ui-hero-evenement--banniere .ui-hero-evenement-service,
  .ui-hero-evenement--banniere .ui-hero-evenement-meta {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3) var(--space-4);
  }

  .ui-hero-evenement--banniere .ui-hero-evenement-service,
  .ui-hero-evenement--banniere .ui-hero-evenement-meta,
  .ui-hero-evenement--banniere .ui-hero-evenement-pied {
    align-self: start;
  }
}

.ui-hero-evenement--banniere .ui-hero-evenement-meta {
  max-width: none;
  margin: var(--space-3) 0 var(--space-4);
}

.ui-hero-evenement--banniere .ui-hero-evenement-service {
  margin: 0;
}

/* Le descriptif est collé au titre — 16px — et non posé à la distance des
   blocs : c'est sa suite, pas un bloc de plus. */
.ui-hero-evenement--banniere .ui-hero-evenement-copy > .ui-display-title {
  margin-bottom: var(--space-3);
}

.ui-hero-evenement--banniere .ui-hero-evenement-lead {
  max-width: 46rem;
  margin: 0;
}

.ui-hero-evenement--banniere .ui-hero-evenement-actions {
  margin: 0 0 var(--space-5);
}

.ui-hero-evenement--banniere .ui-hero-evenement-pied {
  margin: 0;
}

/* ---- Les deux pastilles de la disposition en COLONNES ----
   La DATE coiffe la photo à gauche, la VILLE lui répond à droite : les deux
   repères tiennent la même ligne haute, et le bas de l'image reste libre —
   là où une photo de groupe a le plus souvent ses sujets.

   La date prend 30 % de plus que la pastille des cartes de liste. Le rapport
   n'est pas gratuit : sur une photo de 499px, celle des cartes — dessinée
   pour une vignette de 300 — se lisait comme un timbre. Toutes ses mesures
   suivent le même facteur, largeur, rembourrage et les trois corps de texte,
   pour que ce soit la MÊME pastille agrandie et non une autre.

   Écrit en valeurs et non en « scale() » : une transformation aurait grossi
   l'ombre avec le reste et flouté le texte, et le rayon n'aurait plus été
   celui du dépôt. */
.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-date {
  inset-block-start: var(--space-3);
  inset-block-end: auto;
  inset-inline-start: var(--space-3);
  inset-inline-end: auto;
  min-width: 73px;                   /* 56 × 1,3 */
  padding: var(--space-2) var(--space-2);        /* 7,8px / 10,4px — 6 et 8 × 1,3 */
}

.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-date b,
.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-date i {
  font-size: var(--fs-caption);                   /* 10 × 1,3 */
}

.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-date strong {
  font-size: var(--fs-h2);                   /* 24 × 1,3 */
}

.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-lieu {
  left: auto;
  right: var(--space-3);
}

/* Le lien de retour ouvre la SECTION, avant la rangée : c'est la sortie de
   la page, et elle se cherche en haut à gauche — la place qu'aurait occupée
   le fil d'Ariane. Dans la colonne de texte, il suivait son centrage vertical
   et se retrouvait à mi-hauteur du hero, en face du milieu de la photo.
   Discret par la teinte, pas par la taille : il doit rester cliquable au
   pouce sur un téléphone. La flèche est DANS le lien et avant le libellé :
   c'est elle qui dit le retour, un « Tous les évènements » seul se lirait
   comme une destination parmi d'autres. */
.ui-hero-evenement-retour {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-sm);
}

.ui-hero-evenement-retour a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) 0;
  color: var(--text-muted);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}

.ui-hero-evenement-retour a:hover,
.ui-hero-evenement-retour a:focus-visible { color: var(--color-primary); }

.ui-hero-evenement-retour .material-symbols-outlined { font-size: var(--icon-sm); }

/* Le pied de colonne : les participants à gauche, l'organisateur poussé au
   BOUT de la ligne. Deux réponses à la même question — qui vient, qui
   reçoit — tenues aux deux extrémités du bloc, ce qui donne au bas de la
   colonne la même largeur d'appui que la ligne de repères au-dessus.
   La poussée se fait par « margin-inline-start: auto » sur la mention, et
   non par « justify-content: space-between » sur le conteneur : quand elle
   passe à la ligne sur une colonne étroite, la marge automatique n'a plus
   d'espace à distribuer et se neutralise d'elle-même, là où le
   « space-between » aurait continué d'écarter ce qui reste. */
.ui-hero-evenement-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  /* L'écart sous le bouton est porté ICI, par la ligne, et non par le bloc
     d'action : les marges verticales de deux frères FUSIONNENT au lieu de
     s'ajouter, et deux valeurs en vis-à-vis rendaient la plus grande des
     deux — impossible à régler sans savoir laquelle gagnait. Un seul levier,
     donc, et le bloc d'action ne laisse plus rien sous lui.
     46px : les 16 du rythme, plus 10, plus 20. */
  margin-top: var(--space-5);
}

.ui-hero-evenement-pied > .ui-hero-evenement-organisateur {
  margin-inline-start: auto;
}

/* Sous 992px, la mention passe SOUS les participants : la marge automatique
   n'a plus de ligne à remplir, elle la collerait au bord droit alors que
   toute la colonne est alignée à gauche. */
@media (width <= 991.98px) {
  .ui-hero-evenement-pied > .ui-hero-evenement-organisateur {
    margin-inline-start: 0;
  }
}

/* ---- La ligne de SERVICE, au-dessus du titre ----
   Ce qui se sait avant de lire le nom de la sortie : la date, et qui reçoit.
   Une ligne, deux repères, et rien d'autre — c'est ce qui la distingue de la
   ligne de faits sous le descriptif, où se rangent l'horaire, le lieu et le
   prix. Les mettre tous en haut aurait fait un bandeau d'étiquettes devant le
   titre, et ce sont deux moments de lecture différents : on situe d'abord,
   on s'organise ensuite.

   Les faits y prennent le même dessin que ceux de « meta » — pictogramme
   corail, valeur en demi-gras — parce que ce sont les mêmes objets ; seul
   leur moment change. */
.ui-hero-evenement-service {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-5);
  margin-bottom: var(--space-3);
}

.ui-hero-evenement-service-fait {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}

.ui-hero-evenement-service-fait .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
}

/* Dans cette ligne, la mention d'organisateur perd la marge basse qu'elle
   porte en pied de colonne : elle n'ouvre plus rien, elle accompagne. */
.ui-hero-evenement-service .ui-hero-evenement-organisateur {
  margin: 0;
}

/* La mention d'organisateur reprend .ui-evenement-communaute, la ligne du
   pied des cartes de la liste : pictogramme corail, « Avec » en gris, le nom
   en gras sombre. Mêmes valeurs — 0.375rem de gouttière, 20px de
   pictogramme, --fs-sm — parce que c'est la MÊME mention : le visiteur l'a
   lue sur la carte qu'il vient de cliquer.
   Ce qui ne suit pas : le filet et le rembourrage haut de la carte, qui
   séparent la mention du corps d'une carte. Ici elle vit dans une colonne de
   hero, où elle n'a rien à refermer. */
.ui-hero-evenement-organisateur {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-hero-evenement-organisateur .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
}

.ui-hero-evenement-organisateur a,
.ui-hero-evenement-organisateur strong {
  color: var(--text);
  font-weight: var(--fw-semibold);
  text-decoration: none;
}

.ui-hero-evenement-organisateur a:hover,
.ui-hero-evenement-organisateur a:focus-visible { color: var(--color-primary); }

.ui-hero-evenement-lead {
  max-width: 42rem;
  margin: var(--space-4) 0 var(--space-5);
  color: var(--text-muted);
  font-size: var(--fs-lead);
  line-height: 1.7;
}

/* La ligne de repères remonte de 20px quand un CHAPÔ la précède : celui-ci
   laissait sous lui l'écart plein du rythme de colonne, et les faits — date,
   horaire, lieu, prix — s'en trouvaient détachés du bloc de tête auquel ils
   appartiennent. Marge négative en haut plutôt qu'une marge basse rabotée
   au-dessus : c'est ce bloc-ci qui vient se rapprocher, les autres gardent
   leur rythme.

   Le sélecteur nomme le chapô, et ce n'est pas une précaution de style :
   posée sans condition, cette remontée s'appliquait aussi quand le hero n'a
   PAS de chapô — les repères suivent alors le titre, qui ne laisse que 8px
   sous lui, et les 20px les faisaient monter DANS la dernière ligne du
   titre. Le décalage n'a de sens que face à la marge qu'il rattrape. */
.ui-hero-evenement-lead + .ui-hero-evenement-meta { margin-top: calc(var(--space-4) * -1); }

.ui-hero-evenement-meta {
  /* Une LIGNE de faits, et non plus une grille de deux colonnes sur deux
     rangées. Les libellés partis, chaque fait tient en une icône et trois
     mots : les mettre en grille réservait à chacun un quart de la largeur et
     laissait des trous. En ligne, ils se suivent et se lisent d'un trait. */
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  max-width: 42rem;
  /* La marge du haut est celle du rythme de colonne — le titre ne laisse que
     8px sous lui, et les repères ont besoin de respirer après lui.
     20px de plus SOUS les repères : ils forment un bloc dense de quatre
     faits, et le bouton s'y collait — le geste et l'information doivent se
     détacher l'un de l'autre. Le bouton porte de son côté une remontée de
     20px, qui reste : les deux se combinent, et l'écart rendu vaut le
     rythme plein de la colonne. */
  margin: var(--space-4) 0 var(--space-6);
}

.ui-hero-evenement-meta > div {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Le « dt » n'est plus qu'un porte-icône : son libellé est caché à l'écran et
   lu par les lecteurs d'écran. Plus de capitales, plus d'interlettrage, plus
   de ligne au-dessus de la valeur — c'était quatre lignes de micro-typo pour
   quatre faits qui tiennent en trois mots chacun. */
.ui-hero-evenement-meta dt {
  display: flex;
  align-items: center;
  margin: 0;
}

.ui-hero-evenement-meta dd {
  margin: 0;
  color: var(--text);
  font-weight: var(--fw-semibold);
}

.ui-hero-evenement-meta .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-sm);
}

/* Le bouton remonte de 20px : la ligne de repères au-dessus lui laissait
   l'écart plein du rythme de colonne, et l'action s'en trouvait détachée des
   faits qu'elle conclut. Marge négative en haut plutôt que --space-4 en
   moins sur les repères : ce sont les repères qui gardent leur rythme, c'est
   l'action qui vient s'y rapprocher.
   Rien en BAS : c'est la ligne des participants qui porte l'écart, d'un seul
   côté — voir sa règle. */
.ui-hero-evenement-actions {
  margin-top: calc(var(--space-4) * -1);
  margin-bottom: 0;
}

.ui-hero-evenement-action-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  max-width: 39rem;
  margin: 0 0 var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-hero-evenement-action-note .material-symbols-outlined {
  flex: 0 0 auto;
  color: var(--color-primary);
  font-size: var(--icon-sm);
}

/* Pas de rembourrage, pas de fond de carte : la photo va d'un bord à l'autre
   du cadre, qui la rogne. Le liseré blanc qu'ils dessinaient ensemble
   ajoutait un cartouche là où il n'y a qu'une image — et il décalait de
   16px tout ce qui est posé dessus, à commencer par la pastille de date, qui
   ne tombait plus sur les retraits des cartes de la liste.
   « overflow » repasse donc à « hidden », hérité de la règle du dessus :
   c'est lui qui applique le rayon à la photo, et plus rien ne déborde. */
.ui-hero-evenement-placeholder {
  width: 100%;
  height: 100%;
}

.ui-hero-evenement-placeholder {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--space-2);
  background: linear-gradient(145deg, var(--surface-douce), var(--surface-appuyee));
  color: var(--text-muted);
  text-align: center;
}

.ui-hero-evenement-placeholder .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-2xl);
}

/* La date ferme la photo par le BAS et la ville la coiffe : les deux
   pastilles occupent le même bord gauche, aux mêmes retraits de --space-3,
   et c'est exactement la disposition d'une carte de la liste — le lieu en
   haut, la date en bas. Une carte cliquée et la fiche qu'elle ouvre montrent
   ainsi le même objet aux mêmes endroits, ce qui confirme au visiteur qu'il
   a ouvert la bonne. */
.ui-hero-evenement-date {
  inset-block-start: auto;
  inset-block-end: var(--space-3);
  inset-inline-start: var(--space-3);
  inset-inline-end: auto;
}

.ui-hero-evenement-date strong { font-size: var(--fs-h4); }

/* 30px d'air au-dessus des onglets, et rien en dessous : le trait actif de
   l'onglet courant doit rester collé au bas de la barre, c'est lui qui
   raccorde la barre à la section qu'elle annonce. Le rembourrage est posé
   sur la BARRE et non sur les onglets — sur eux, il aurait aussi déplacé ce
   trait, qui suit le bas de leur boîte. */
/* LA BARRE D'ONGLETS D'UN ÉVÈNEMENT ne porte plus que son CHÂSSIS : le fond,
   les deux filets et le rembourrage. Le dessin des onglets eux-mêmes vient de
   « .ui-fil-filtre », les pilules du profil — un sujet unique vu sous plusieurs
   angles se présente de la même façon, qu'il s'agisse d'une personne ou d'une
   sortie.
   ÉCARTÉ : le soulignement de 3px sous l'onglet courant, qui était le dessin
   d'ici. Il n'avait rien de faux, mais il faisait deux barres d'onglets
   différentes à deux clics l'une de l'autre. */
.ui-evenement-nav {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--surface-card);
}

/* La rangée de pilules n'a pas de marge basse ici : c'est la barre qui tient
   le rembourrage, et le sien s'ajoutait au nôtre. */
.ui-evenement-nav > .ui-fil-filtres {
  margin-bottom: 0;
  padding-block: var(--space-3);
}

.ui-evenement-detail-layout { --bs-gutter-y: var(--space-5); }
.ui-evenement-detail-main > p { color: var(--text-muted); }

.ui-evenement-programme { margin-top: var(--space-5); }
.ui-evenement-programme ol { margin: var(--space-3) 0 0; padding: 0; list-style: none; }

.ui-evenement-programme li {
  display: grid;
  grid-template-columns: 5rem minmax(0, 1fr);
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
}

.ui-evenement-programme time {
  color: var(--color-primary);
  font-weight: var(--fw-bold);
}

.ui-evenement-pratique { align-self: flex-start; }

/* Rien sous le sur-titre : ni sa marge de 22px, ni celle de 24 que la liste
   posait au-dessus d'elle. La liste ouvre sur un filet, et c'est lui qui
   sépare — un filet précédé de 46px de vide se lit comme un bloc détaché,
   alors qu'il appartient au libellé qui le coiffe. Le rembourrage des
   entrées, lui, reste : c'est l'air DANS la liste, pas devant elle. */
.ui-evenement-pratique > .ui-eyebrow { margin-bottom: 0; }

.ui-evenement-pratique > ul { margin: 0 0 var(--space-4); padding: 0; list-style: none; }

.ui-evenement-pratique > ul > li {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: var(--space-2);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border);
}

.ui-evenement-pratique > ul .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-md);
}

.ui-evenement-pratique > ul li > span:last-child {
  display: flex;
  flex-direction: column;
  color: var(--text-muted);
}

.ui-evenement-pratique > ul strong { color: var(--text); }



.ui-evenement-prive {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-douce);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-evenement-prive .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-sm);
}

.ui-evenement-organisateur {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

@media (width <= 991.98px) {
  .ui-hero-evenement-copy { padding-right: calc(var(--bs-gutter-x) * 0.5); }
  /* La marge est EN BAS et non en haut : la photo est remontée en tête de
     l'empilement par « order », l'air doit donc la séparer du texte qui la
     suit. Écrite en haut, elle creusait entre le lien de retour et elle, et
     le titre venait se coller au bas de l'image. */
  .ui-hero-evenement-visuel { margin: 0 0 var(--space-5); }
  .ui-hero-evenement-cadre { max-width: 38rem; }
}

@media (width <= 560px) {
  .ui-hero-evenement { padding-block: var(--space-5); }
  .ui-hero-evenement-meta { grid-template-columns: 1fr; gap: var(--space-3); }
  .ui-hero-evenement-actions .ui-btn { width: 100%; }
  .ui-hero-evenement-participants { align-items: flex-start; flex-direction: column; }
  .ui-evenement-nav > .ui-container { gap: var(--space-4); }
}

@media (width <= 560px) {
  /* Le même resserrement que la barre d'onglets de l'évènement, au même
     palier : les deux barres sont le même objet. */
  .ui-hero-communaute-nav > .ui-container { gap: var(--space-4); }
}

/* Section « évènements » — la page de liste ----------------------------------
   Filtres en haut, grille au milieu, pagination en bas : la sélection, le
   résultat, le déplacement. Préfixe au pluriel — .ui-evenement- au singulier
   est la carte (ui-molecules.css), comme .ui-groupe- / .ui-groupes-. */

.ui-evenements-filtres {
  margin-bottom: var(--space-5);
}

/* Hauteur égale posée ICI et non par la donnée de chaque carte : c'est une
   contrainte de RANGÉE — une carte ne sait rien de ses voisines. Même geste
   que .ui-groupes-grille. */
.ui-evenements-grille {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 1.5rem;
}

.ui-evenements-grille > .ui-grid-item > .ui-evenement-carte {
  height: 100%;
}

/* Les deux paginations de liste — évènements et communautés — partagent la
   règle. Elles se ressemblaient déjà à la valeur près ; les écrire ensemble
   garantit qu'elles ne divergeront pas, et dit qu'il s'agit du même objet
   posé sous deux grilles. */
.ui-evenements-pagination,
.ui-groupes-pagination {
  display: flex;
  justify-content: center;
  margin-top: var(--space-6);
}

/* Le bouton de l'APERÇU — la variante sans filtres ni pagination, posée sur
   une page d'accueil. Mêmes valeurs que .ui-groupes-action, et c'est délibéré :
   les deux aperçus se suivent sur l'accueil, leurs boutons doivent tomber au
   même endroit sous leur grille. */
.ui-evenements-action {
  margin-top: var(--space-6);
  text-align: center;
}

/* ══════════════════════════════════════════════════════════════════════════
   ESPACE MEMBRE — en-tête et fil d'actualité
   Le châssis d'une vue d'APPLICATION, distinct de celui du site : pas de
   barre utilitaire, pas de hero, pas de pied marchand. Mêmes couleurs,
   même typographie, même logo — c'est le même produit vu de l'intérieur.
   ══════════════════════════════════════════════════════════════════════════ */

/* Fond légèrement teinté sous le fil, et non blanc : les publications sont
   des cartes blanches, elles ont besoin d'un fond dont elles se détachent.
   Sur du blanc, seul leur filet les aurait séparées les unes des autres. */
/* LE MÊME DÉGRADÉ QUE LE HERO DE L'ACCUEIL — blanc en haut, teinte 7 en bas,
   exactement « .ui-hero-scroll-zone--degrade ». C'était un aplat de
   --surface-douce ; passé la connexion, on quittait la respiration du premier
   écran pour une surface plate, et c'est une des choses qui faisaient lire
   l'espace membre comme une autre application.
   Le fond du BODY se propage au canevas : le dégradé couvre donc toute la
   hauteur du document, quelle qu'elle soit, sans qu'on ait à la connaître. */
/* DES DISQUES TOUT AU LONG DE LA PAGE, en COUCHES DE FOND et non en éléments.
   Un pseudo-élément ne se compte qu'à deux par balise, et le fil descend sur
   dix écrans : il aurait fallu accrocher des ronds à des publications, donc
   coupler le décor au contenu. Une couche de fond, elle, se répète aussi loin
   que la page descend, quelle que soit sa longueur, et sans une ligne de HTML.
   Elle est aussi hors de toute question d'empilement : le fond du body se
   propage au canevas, il est donc derrière TOUT par construction, et n'a ni
   z-index ni « overflow » à négocier avec les rails collants.

   L'ALÉATOIRE EST OBTENU PAR DES PÉRIODES PREMIÈRES ENTRE ELLES. Chaque couche
   se répète à sa propre hauteur — 1900, 2600, 3300px — et ces trois-là ne
   retombent ensemble qu'au bout de leur plus petit commun multiple, soit très
   au-delà de la plus longue page du site (le fil en fait 4507). Sur toute
   hauteur réelle, les formes ne se réalignent donc jamais : l'œil ne trouve
   pas de trame.

   ET ELLES NE SE TOUCHENT PAS. Les périodes ont été allongées — c'était 1450,
   1930, 2350, et deux formes finissaient par se croiser en milieu de fil —,
   mais l'espacement vertical ne suffisait pas : ce qui les sépare vraiment est
   l'ABSCISSE. 90 %, 4 % et 46 % : quand deux couches tombent à la même hauteur,
   elles sont aux deux bords opposés ou au centre, jamais côte à côte.

   DES FORMES PLEINES, À BORD FRANC — « var(--decor-disque) 0 99 %,
   transparent 100 % ». Elles ont été DIFFUSES le temps de quelques essais, avec
   un fondu du centre au bord ; elles ont disparu. Un fondu n'atteint son pic
   qu'au point central exact, et à mi-rayon l'écart au fond tombait à 3 ou 4 sur
   255 — sous le seuil où l'œil sépare deux gris. Monter la densité n'y a rien
   fait : à 4,4 % l'arrondi à l'octet mangeait la différence, à 20 % le décor
   devenait une forme posée sur la page et le chapô des listes passait sous le
   seuil AA. C'est le PROFIL qui gouvernait la visibilité, pas la densité : une
   forme pleine à 4 % se voit, parce que toute sa surface est à 4 % et non son
   seul centre.

   PLUS DE FORME EN HAUT À GAUCHE. La couche de gauche est calée sur 190px et
   non 1500 : « repeat-y » répète dans les DEUX sens, si bien qu'un décalage de
   1500 posait aussi une instance à 1500 − 2600 = −1100, dont le centre tombait
   à 408px — juste sous l'en-tête, à gauche. On ne peut pas retirer une seule
   instance d'une couche qui se répète ; on décale la couche pour que la
   première visible arrive à 1700 et que la précédente sorte par le haut.
   La couche du CENTRE a suivi, de 2100 à 2600 : son instance tombait à 1704,
   soit six pixels de la précédente. Les deux étaient à 4 % et 46 % d'abscisse,
   donc elles ne se recouvraient pas — mais leurs bords se touchaient, et deux
   formes qui s'effleurent se lisent comme une seule mal découpée. Elle est
   maintenant à 2204.

   ELLIPSES ET NON CERCLES, aux proportions toutes différentes — 322×266,
   231×301, 364×280, soit 30 % de moins que les 460×380, 330×430 et 520×400
   d'origine. Un dégradé de fond ne sait pas faire mieux qu'une ellipse ;
   la déformation organique des galets vient d'un « border-radius » à huit
   valeurs, qui demande un ÉLÉMENT. La forme d'ancrage en haut de « main » en est
   une, elle, et porte donc le vrai galbe. À 4 % d'encre, la différence entre une
   ellipse et un galet ne se voit pas ; ce qui se voit, c'est qu'aucune n'est un
   cercle parfait.
   Un vrai tirage au sort aurait demandé du JavaScript, donc un décor qui
   n'existe pas tant que le script n'a pas tourné, et qui change à chaque
   rechargement — un fond de page n'a pas à bouger d'une visite à l'autre.

   4 % du pétrole, comme la forme d'ancrage posée en haut de « main » : c'est le
   même objet, il n'a pas deux densités.
   Le décalage vertical de chaque couche — 780, 1500, 2100px — l'évite, elle qui
   occupe déjà le coin haut droit du premier écran.

   Le DÉGRADÉ vient en DERNIER de la liste : les couches de fond se peignent
   dans l'ordre inverse de leur déclaration, la dernière au fond. Les disques
   se posent donc dessus.

   SES DEUX BORNES SONT LES DEUX SURFACES DU SITE, et pas des valeurs neuves :
   --surface-douce en haut, --surface-appuyee en bas, soit le pétrole #015f6d à
   4 % puis à 15 % sur blanc.
   LE HAUT EST ÉCLAIRCI DE MOITIÉ — « color-mix » de --surface-douce à 50 % avec
   le blanc, ce qui donne #fafcfc, soit le même pétrole à 2 %. Écrit comme un
   mélange et non comme une valeur neuve : le jour où --surface-douce bouge, le
   haut du dégradé suit, et l'intention « la moitié de la surface douce » reste
   lisible là où « #fafcfc » n'aurait rien dit.
   Deux effets, tous deux favorables : l'amplitude du dégradé passe de 28·18·16
   à 33·21·19 en RVB — il se voit un peu mieux sur les pages longues, où il
   s'étire —, et le chapô des listes gagne du contraste en haut de page, de
   5,52 à 5,68:1 sur fond nu. Le point tendu reste le BAS, inchangé.
   Le dégradé descend jusqu'à la teinte des états et des ornements — une seule
   couleur, trois dilutions : 2 % en haut, 15 % en bas, 4 % pour les formes.
   Il partait du BLANC PUR et finissait sur --section-teinte-7 (#e1eced, dérivé
   d'un autre pétrole, #045e6d) : plus froid en haut, plus clair en bas. Le
   passage aux surfaces le rend plus vert des deux côtés et plus soutenu au
   pied.

   15 % EST LE PLAFOND, et ce n'est pas un arrondi. Sur --surface-appuyee,
   --text-muted donne 4,61:1 — le seuil AA est à 4,5. Le cran d'après, 18 %,
   passe sous la barre. Le bas du dégradé ne peut donc pas être assombri
   davantage sans repeindre le gris sauge, qui sert partout ailleurs.
   Les trois encres mesurées au point le plus sombre : --text 12,03,
   --text-muted 4,61, --color-titre 6,18. */
.ui-page-membre {
  background-image:
    radial-gradient(ellipse 322px 266px at 90% 26%, var(--decor-disque) 0 99%, transparent 100%),
    radial-gradient(ellipse 231px 301px at 4% 58%, var(--decor-disque) 0 99%, transparent 100%),
    radial-gradient(ellipse 364px 280px at 46% 88%, var(--decor-disque) 0 99%, transparent 100%),
    linear-gradient(to bottom, color-mix(in srgb, var(--surface-douce) 50%, var(--white)), var(--surface-appuyee));
  background-repeat: repeat-y, repeat-y, repeat-y, no-repeat;
  background-size: 100% 1900px, 100% 2600px, 100% 3300px, 100% 100%;
  background-position: 0 780px, 0 190px, 0 2600px, 0 0;
  background-attachment: scroll;
}

/* ---- En-tête membre ---- */
.ui-header-membre {
  position: sticky;
  top: 0;
  z-index: 1030;
  background: var(--white);
  border-bottom: 1px solid var(--border);
}

.ui-header-membre-inner {
  display: flex;
  align-items: center;
  /* Pas de gap ici : les écarts sont portés par les blocs eux-mêmes, parce
     que la position de la navigation doit être CALCULÉE et non subie (voir
     .ui-header-membre-marque juste dessous). Un gap s'ajouterait au calcul et
     décalerait l'alignement de sa propre valeur. */
  gap: 0;
  min-height: 68px;
}

/* La marque n'occupe pas la largeur de son logo mais celle de la COLONNE DE
   GAUCHE DU FIL, écart compris : 240px de rail plus les 48 de gouttière. La
   première entrée de navigation tombe donc exactement sur le bord gauche des
   cartes du mur, deux rangées plus bas. L'en-tête et le fil partagent le même
   .ui-container, donc la même marge de départ : l'alignement tient sans qu'on
   ait à mesurer quoi que ce soit.

   Ce n'est pas un centrage : centrer la navigation l'aurait fait glisser au
   gré de la longueur du prénom affiché à droite, et elle n'aurait été alignée
   sur rien. Ici elle est accrochée à une colonne, qui est elle-même la
   structure de la page. */
.ui-header-membre-marque {
  flex: 0 0 calc(240px + var(--space-5));
  display: block;
}

/* Le logo est décalé de 10px vers la DROITE à l'intérieur de sa colonne — 30
   d'abord, puis 20 repris. Écrit comme un seul décalage plutôt qu'en pile de
   corrections : la règle dit où le logo doit être, pas par quels essais on y
   est arrivé. La
   marge est posée sur l'image et non sur le bloc : celui-ci a une largeur
   calculée — celle du rail plus sa gouttière — dont dépend l'alignement de la
   navigation sur le contenu. Le décaler l'aurait entraînée avec lui ; décaler
   l'image ne bouge qu'elle, et il reste 126px de jeu avant qu'elle ne touche
   le bord de sa colonne. */
.ui-header-membre-marque img {
  width: 171px;                         /* logo Vibe2Meet ×1,45 puis −10 % (3 oct. 2026) */
  position: relative;
  top: var(--space-1);                  /* 4px plus bas, à l'œil (3 oct. 2026) */
  height: auto;
  margin-inline-start: var(--space-2);
  display: block;
}

/* La recherche est unique et globale : profils, communautés et évènements
   passent par le même champ. Trois recherches séparées auraient demandé au
   membre de savoir à l'avance dans quelle catégorie ranger ce qu'il cherche. */
/* Les commandes rondes de droite — chercher, être notifié. Une seule règle
   pour les deux : ce sont deux boutons du même ordre, ils doivent avoir la
   même présence. La recherche était un champ large au milieu de la rangée ;
   réduite à sa loupe elle rejoint la famille des outils, et rend sa place à
   la navigation. */
.ui-membre-rond {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: var(--control-h-sm);
  height: var(--control-h-sm);
  border-radius: 50%;
  background: var(--surface-douce);
  color: var(--color-titre);
  flex-shrink: 0;
  transition: background 0.2s ease;
}

.ui-membre-rond:hover,
.ui-membre-rond:focus-visible { background: var(--surface-appuyee); }

/* DANS L'EN-TÊTE, CARRÉS À ANGLES ARRONDIS (29 sept. 2026) : ils rejoignent
   la vignette du compte, carrée elle aussi, à droite de la rangée. Ailleurs
   (les outils de la messagerie), la classe garde son rond. La règle s'était
   perdue : les boutons de l'en-tête étaient redevenus ronds (rétablie le
   30 sept. 2026). */
.ui-header-membre .ui-membre-rond,
.ui-header-membre .ui-membre-burger { border-radius: var(--radius-photo); }

/* La navigation démarre où la marque s’arrête : rien à pousser. */
.ui-membre-nav { flex: 0 0 auto; }

.ui-membre-nav-liste {
  display: flex;
  align-items: center;
  /* 10px entre les entrées. Elles étaient à 4 : les pastilles de survol se
     touchaient presque, et l'onglet actif — désormais un aplat plein —
     paraissait collé à son voisin. */
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-membre-nav-item { position: relative; }

.ui-membre-nav-lien {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border-radius: var(--radius-sm);
  /* 13px de part et d'autre : la capsule de l'onglet actif serrait son
     libellé de trop près depuis qu'elle est un aplat plein. Le rembourrage
     vertical ne bouge pas — c'est la largeur de la pastille qui manquait, pas
     sa hauteur. Sous 1200px il retombe à 8, où il n'y a plus qu'un
     pictogramme à entourer. */
  padding: var(--space-2) var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.2s ease, color 0.2s ease;
}

.ui-membre-nav-lien .material-symbols-outlined { font-size: var(--icon-md); }
.ui-membre-nav-chevron { font-size: var(--icon-md) !important; }

.ui-membre-nav-lien:hover,
.ui-membre-nav-lien:focus-visible {
  background: var(--surface-douce);
  color: var(--color-titre);
}

/* L'onglet courant est une PASTILLE PLEINE en pétrole, exactement comme le
   filtre actif du fil juste en dessous : deux sélections dans la même vue
   doivent se marquer de la même façon, sinon le lecteur cherche ce qui les
   distingue alors que rien ne les distingue.

   Il portait d'abord un trait rose sous le libellé, pour ne pas se confondre
   avec l'aplat du survol. Le conflit se règle mieux à l'endroit où il naît :
   l'état courant est écrit APRÈS le survol et le reprend en plus sombre, si
   bien que passer la souris sur l'onglet actif l'appuie au lieu de l'effacer.

   Blanc sur pétrole : 7,10:1. */
.ui-membre-nav-lien.is-current,
.ui-membre-nav-lien.is-current:hover,
.ui-membre-nav-lien.is-current:focus-visible {
  background: var(--color-titre);
  color: var(--white);
}

.ui-membre-nav-lien.is-current:hover {
  background: var(--color-primary-hover);
}

/* ---- Panneau de sous-navigation ----
   Ouvert au survol ET au focus-within : un menu qui ne s'ouvre qu'à la souris
   est inatteignable au clavier. Pas de délai de fermeture ni de script — le
   panneau est collé au lien (pas d'espace mort entre les deux), la souris ne
   peut pas le perdre en descendant. */
.ui-membre-sous-nav {
  position: absolute;
  z-index: 10;
  inset-block-start: 100%;
  inset-inline-start: 0;
  min-width: 232px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-2);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
}

.ui-membre-nav-item--groupe:hover .ui-membre-sous-nav,
.ui-membre-nav-item--groupe:focus-within .ui-membre-sous-nav {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.ui-membre-sous-nav-titre {
  margin: var(--space-1) var(--space-3) var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Le panneau thématique ne garde que la mise en rangée et le renvoi du
   compteur au bord droit ; tout le reste vient de la règle partagée des menus
   verticaux, dans ui-molecules.css. */
.ui-membre-sous-nav-lien {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Le compteur est rose : c'est le seul endroit du panneau où un chiffre
   demande une action. Aplat très dilué et encre rose — le rose plein ne
   passe pas à cette taille. */
.ui-membre-sous-nav-compteur { /* le dessin vient de .ui-count-badge */ }

.ui-header-membre-compte {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  /* Collé au bord droit : c'est la marge automatique qui absorbe tout ce qui
     reste entre la navigation et lui. */
  margin-inline-start: auto;
}

.ui-membre-cloche-pastille {
  position: absolute;
  inset-block-start: -2px;
  inset-inline-end: -2px;
  /* 5px de part et d'autre : à 4, un nombre à deux chiffres touchait le bord
     arrondi de la pastille. La largeur minimale de 18px la garde ronde tant
     qu'il n'y a qu'un chiffre. */
  /* 600 et non 700 : sur un aplat rose plein, un chiffre blanc de 11px n'a pas
     besoin de graisse pour se voir — c'est la pastille qui le signale, pas
     l'épaisseur du glyphe. Même graisse que les libellés de bouton, pour que
     les deux marques d'interface parlent d'une seule voix. */
}

/* Le compte a son propre panneau, ancré à DROITE (inset-inline: auto 0) :
   c'est le dernier élément de la rangée, un panneau aligné à gauche sortirait
   de l'écran. Il s'ouvre comme ceux de la navigation — survol et focus-within,
   sans script. */
.ui-membre-compte-menu { position: relative; }

.ui-membre-sous-nav--compte {
  inset-inline: auto 0;
  /* 240 et non 208 : la fiche du haut porte un portrait de 40px, un nom et une
     mention ; à 208 la mention se coupait au milieu d'un mot. */
  min-width: 240px;
}

/* ── LA FICHE EN TÊTE DU PANNEAU DE COMPTE ────────────────────────────────
   C'est un LIEN vers le profil, et non un simple titre : c'est la destination
   qu'on vient chercher le plus souvent ici, autant qu'elle soit la première
   chose qu'on touche. Elle remplace le prénom en petites capitales grises, qui
   rangeait la personne au même rang qu'une rubrique thématique.
   Le survol emprunte le voile des menus verticaux — même valeur, même
   translucidité : tout ce qui se survole dans ce panneau se marque pareil. */
.ui-membre-compte-tete {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  text-decoration: none;
  transition: background 0.15s ease;
}

.ui-membre-compte-tete:hover,
.ui-membre-compte-tete:focus-visible { background: var(--teinte-neutre); }

.ui-membre-compte-tete > img {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-photo);
  object-fit: cover;
  flex-shrink: 0;
}

.ui-membre-compte-tete-corps {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.ui-membre-compte-tete-nom {
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: 1.35;
}

.ui-membre-compte-tete-mention {
  color: var(--text-muted);
  font-size: var(--fs-caption);
  line-height: 1.35;
}

/* LE FILET, entre la fiche et le menu. Sans lui, les deux se touchaient et se
   lisaient comme deux lignes d'une même liste. Marges négatives pour qu'il
   traverse le panneau de bord à bord : un trait qui s'arrête au rembourrage
   sépare moins qu'il ne décore. */
/* LES ENTRÉES DU PANNEAU DE COMPTE portent un pictogramme, et se rangent donc
   à GAUCHE. Le « space-between » du socle sert aux panneaux thématiques, où il
   renvoie le compteur au bord droit ; ici il aurait écarté le pictogramme de
   son libellé de toute la largeur du panneau. */
.ui-membre-sous-nav--compte .ui-membre-sous-nav-lien { justify-content: flex-start; }

/* 18px et pétrole, comme les pictogrammes du menu vertical du socle : c'est le
   même objet, une liste d'actions à pictogramme. */
.ui-membre-sous-nav--compte .ui-membre-sous-nav-lien > .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-primary);
  flex-shrink: 0;
}

.ui-membre-sous-nav-filet {
  /* Marges latérales NÉGATIVES de la valeur du rembourrage : le trait traverse
     le panneau d'un bord à l'autre. Un trait qui s'arrête au rembourrage sépare
     moins qu'il ne décore.
     Mesuré : panneau 240, filet 238, un pixel de chaque côté. Ce pixel n'est pas
     un défaut de réglage — c'est le FILET DU PANNEAU lui-même, et le séparateur
     s'arrête à son bord intérieur, ce qui est exactement ce qu'il doit faire.
     Ne pas chercher à gagner ces deux pixels : on ne ferait que peindre par
     dessus la bordure. */
  margin: var(--space-2) calc(var(--space-2) * -1);
  border: 0;
  border-top: 1px solid var(--border);
  opacity: 1;
}

/* AU CLIC SEULEMENT (29 sept. 2026) : ni au survol, ni au focus — un panneau
   qui s'ouvre quand la souris passe vers la photo ou la cloche gênait. Le
   script (setupMenusCompte) pose « is-open » au clic, au toucher, à Entrée. */
.ui-membre-compte-menu.is-open .ui-membre-sous-nav {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* ── LA CLOCHE ET SON PANNEAU ─────────────────────────────────────────────
   Le même panneau que celui du compte, ancré à droite, plus large : une
   notification est une phrase, pas un libellé. Il défile au-delà de la
   hauteur utile, pour ne jamais passer sous le bas de l'écran. */
/* Un rond en BOUTON (la cloche, l'outil de la page) : sans l'habit du
   navigateur — bordure, rembourrage —, il a le dessin des liens voisins. */
button.ui-membre-rond { border: 0; padding: 0; font: inherit; cursor: pointer; }

.ui-membre-sous-nav--notifs {
  inset-inline: auto 0;
  width: min(360px, calc(100vw - 2 * var(--space-3)));
  max-height: min(480px, 70vh);
  overflow-y: auto;
}

/* Au téléphone, la cloche n'est pas au bord droit (l'avatar la suit) : un
   panneau ancré sur elle sortait de l'écran par la gauche. Il s'ancre alors
   à l'en-tête entier — collant, donc positionné —, en pleine largeur moins
   les marges, sous la navigation. */
@media (width <= 640px) {
  .ui-membre-notifs { position: static; }
  .ui-membre-notifs .ui-membre-sous-nav--notifs {
    inset-inline: var(--space-3);
    width: auto;
  }
}

.ui-membre-notifs-tete {
  display: flex;
  align-items: center;
  /* Plus de titre à sa gauche : « Tout marquer comme lu » se range à droite. */
  justify-content: flex-end;
  gap: var(--space-2);
}

.ui-membre-notifs-toutlu {
  border: 0;
  background: none;
  padding: var(--space-1) var(--space-2);
  color: var(--color-primary);
  font-size: var(--fs-caption);
  font-weight: 600;
  cursor: pointer;
}

.ui-membre-notifs-toutlu:hover { text-decoration: underline; }

.ui-membre-notifs-liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-membre-notif {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.4;
  text-decoration: none;
  transition: background 0.15s ease;
}

.ui-membre-notif:hover,
.ui-membre-notif:focus-visible { background: var(--teinte-neutre); }

/* Non lue : un aplat rose très dilué et un point, comme la pastille. */
.ui-membre-notif.is-non-lu { background: var(--color-brand-pink-soft); }
.ui-membre-notif.is-non-lu .ui-membre-notif-quand::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-inline-end: var(--space-2);
  border-radius: 50%;
  background: var(--color-brand-pink-ink);
  vertical-align: 1px;
}

.ui-membre-notif-avatar,
.ui-membre-notif-icone {
  width: 36px;
  height: 36px;
  flex-shrink: 0;
  border-radius: var(--radius-photo);
}

.ui-membre-notif-avatar { object-fit: cover; }

.ui-membre-notif-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-douce);
  color: var(--color-primary);
}

.ui-membre-notif-icone .material-symbols-outlined { font-size: var(--icon-md); }

.ui-membre-notif-corps {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.ui-membre-notif-texte strong { color: var(--color-titre); font-weight: var(--fw-semibold); }

.ui-membre-notif-quand {
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

.ui-membre-notifs-vide {
  margin: var(--space-2) var(--space-3) var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-membre-avatar {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-3) var(--space-1) var(--space-1);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: background 0.2s ease;
}

.ui-membre-avatar:hover { background: var(--surface-douce); }

/* Un BOUTON quand il ouvre le menu du compte : on lui retire l'habit du
   navigateur, il garde celui du lien qu'il était. */
button.ui-membre-avatar {
  border: 0;
  background: none;
  font-family: inherit;
  cursor: pointer;
}

/* UNE ENTRÉE À SOUS-MENU du compte (« Paramètres ») : un lien sur écran
   large, un groupe dépliable sur téléphone. */
.ui-membre-compte-groupe { display: none; }
.ui-membre-compte-groupe > summary { list-style: none; cursor: pointer; }
.ui-membre-compte-groupe > summary::-webkit-details-marker { display: none; }
.ui-membre-compte-groupe-chevron { margin-inline-start: auto; transition: transform 0.2s ease; }
.ui-membre-compte-groupe[open] .ui-membre-compte-groupe-chevron { transform: rotate(180deg); }
.ui-membre-compte-groupe-lien { padding-inline-start: var(--space-4); }

@media (width <= 900px) {
  .ui-membre-sous-nav-lien--large { display: none; }
  .ui-membre-compte-groupe { display: block; }
}

/* LE BLOC DU MENU EN FLEX : en bloc, la ligne de texte qu'il ouvrait autour
   du bouton ajoutait 7px sous la vignette (47px pour 40), qui se centrait
   alors 4px plus haut que les boutons voisins. */
.ui-header-membre-compte > .ui-membre-compte-menu { display: flex; }

/* 40px, LA TAILLE DES BOUTONS À SA GAUCHE (29 sept. 2026) : à 34, la
   vignette paraissait d'un autre rang que ses voisins. */
.ui-membre-avatar img {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-photo);
  object-fit: cover;
}

/* Sous 1200px la navigation perd ses libellés et ne garde que ses
   pictogrammes : quatre entrées écrites ne tiennent plus à côté des trois
   commandes de droite. Les panneaux, eux, restent — c'est là que les libellés
   reviennent. Le rail du fil rétrécit au même seuil, de 240 à 220 : la marque
   suit, sans quoi l'alignement de la navigation sur les cartes se perdrait
   exactement là où la page se resserre. */
@media (width <= 1200px) {
  .ui-header-membre-marque { flex-basis: calc(220px + var(--space-5)); }

  .ui-membre-nav-libelle,
  .ui-membre-nav-chevron,
  .ui-membre-avatar-nom { display: none; }
  .ui-membre-nav-lien { padding: var(--space-2); }
  /* Sans le nom, la vignette seule : aucune marge autour, elle tombe au
     carré exact des boutons voisins. */
  .ui-membre-avatar { padding: 0; border-radius: var(--radius-photo); }

  /* Les panneaux s'ancrent à DROITE dès que la navigation se resserre contre
     le bord. Ils sont « visibility: hidden » au repos, pas « display: none » —
     et un élément invisible occupe quand même sa place dans la zone de
     défilement : ancrés à gauche, ils sortaient de l'écran et faisaient
     défiler la page entière de côté sans que rien ne se voie. Mesuré à
     900px : scrollWidth 1021 pour un écran de 900, le dernier panneau
     s'arrêtant à 939. */
  .ui-membre-sous-nav { inset-inline: auto 0; }
}

/* À 900px le fil n'a plus qu'une colonne : il n'y a plus rien sur quoi
   aligner la navigation, la marque reprend donc la largeur de son logo et la
   navigation la suit immédiatement. */
@media (width <= 900px) {
  .ui-header-membre-marque { flex: 0 0 auto; margin-inline-end: var(--space-3); }
}

/* Sous 640px la navigation passe SOUS la rangée, en pleine largeur et
   centrée : le logo et les trois commandes rondes remplissent déjà la
   première ligne, et quatre pictogrammes de plus la faisaient déborder. */
@media (width <= 640px) {
  .ui-header-membre-inner {
    flex-wrap: wrap;
    padding-block: var(--space-2);
    min-height: 0;
  }

  .ui-header-membre-marque { margin-inline-end: 0; }

  .ui-membre-nav {
    order: 3;
    flex-basis: 100%;
    margin-block-start: var(--space-2);
  }

  .ui-membre-nav-liste { justify-content: center; }
}

/* ---- Fil d'actualité ----
   48px sous l'en-tête, comme la recherche de profils : le fil en avait 24, et
   passer d'une page à l'autre faisait sauter le contenu d'un demi-pouce vers
   le haut alors que le châssis, lui, ne bougeait pas. Deux pages qui
   partagent leur en-tête et leur rail doivent aussi partager leur entrée. */
.ui-fil { padding-block: var(--space-5) var(--space-6); }

/* TROIS colonnes : le rail « Mon activité », le fil, la colonne d'appoint. Ce
   qui se centre est l'ENSEMBLE, pas le fil tout seul — d'où
   « justify-content: center » sur une grille dont les trois pistes sont fixes.
   636px, ET PAS « 1fr ». C'est au pixel près la largeur qu'avait le fil quand
   il était pris entre 240 et 300 : en perdant son voisin de gauche il ne
   s'élargit donc pas, il se déplace. Une publication se lit comme un
   paragraphe, et rien de ce qu'elle contient n'a bougé d'un pixel le jour du
   retrait — c'était la condition pour ne rien avoir à revérifier à
   l'intérieur.
   Le rail est parti puis revenu ; le fil n'a pas bougé entre-temps, ce qui
   était l'intérêt d'une piste fixe plutôt que d'un « 1fr » qui aurait ravalé
   la place libérée.
   La page « mon profil » avait la même règle du temps des trois colonnes ;
   elle a sa propre grille de douze depuis, et les deux n'ont plus rien à
   partager. */
.ui-fil-inner {
  display: grid;
  grid-template-columns: 240px 636px 300px;
  justify-content: center;
  align-items: start;
  gap: var(--space-5);
}

/* CHAQUE COLONNE EST NOMMÉE, et ce n'est pas de la précision pour rien : le
   rail de GAUCHE est facultatif — le mur d'un évènement s'en passe, ses
   onglets étant sa navigation. Laissée au placement automatique, la colonne
   centrale glissait alors dans la première piste et se retrouvait large de
   240px, le rail de droite prenant les 636 du centre. Mesuré tel quel avant
   correction.
   La piste vide de 240px RESTE, et c'est voulu : c'est elle qui aligne le
   contenu sur la marque de l'en-tête, page à rail ou non. */
.ui-fil-inner > .ui-fil-rail--gauche { grid-column: 1; }
.ui-fil-inner > .ui-fil-centre { grid-column: 2; }

/* La colonne du fil peut rétrécir sous la largeur de ses filtres : sans ce
   « min-width: 0 », la rangée de filtres (cinq pastilles) l'élargissait
   au-delà de l'écran du téléphone, et la dernière était coupée sans qu'on
   puisse la faire défiler. */
.ui-fil-centre { min-width: 0; }
.ui-fil-inner > .ui-fil-rail--droit { grid-column: 3; }


/* Le profil n'a pas de rail. Une grille de DOUZE colonnes, comme celle de
   Bootstrap qui régit le reste du site : l'identité les occupe toutes, le
   contenu n'en prend que huit, centrées.
   Pourquoi huit et non toute la largeur : sous les onglets, on lit — une bio,
   une fiche, des centres d'intérêt. À 1250px de large, une ligne de texte
   dépasse les 120 caractères et l'œil perd le début de la ligne suivante.
   Huit colonnes ramènent la mesure à quelque chose qui se lit, et le
   centrage met le sujet au milieu de la page plutôt que collé à gauche. */
.ui-monprofil-inner {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  align-items: start;
  gap: var(--space-3) var(--space-4);
  /* LES TROIS MESURES DU PORTRAIT sont publiées ICI, sur la grille de page, et
     non sur l'en-tête : la barre d'onglets a quitté l'en-tête et doit les lire
     elle aussi pour démarrer sous le nom. Déclarées plus bas, « var(--avatar) »
     y était vide, le calcul invalide, et la déclaration entière abandonnée
     sans un mot — la barre restait collée au bord gauche. */
  /* PLUS DE TAILLE DE PORTRAIT EN VARIABLE. Elle a valu 140, puis 168, puis
     176px de boîte ; c'est désormais la grille qui la donne — deux colonnes,
     gouttière comprise — et il n'y a plus de nombre à tenir accordé avec elle.
     Le retrait des onglets, qui la lisait, est passé lui aussi à la grille. */
  /* L'ÉPAISSEUR DU CADRE BLANC, partagée par la bannière et le portrait : ce
     sont deux tirages posés sur la même page, un cadre de 8 et un de 4 les
     auraient donnés pour deux objets différents. */
  --cadre-photo: 6px;   /* 6px sur ordinateur, 3px en mobile (29 sept. 2026) */
  /* DE COMBIEN LE PORTRAIT MONTE dans la bannière, au-delà de la moitié de sa
     hauteur. Les deux pastilles de taux, posées à sa droite sur l'image,
     montent d'exactement autant : deux valeurs recopiées à la main auraient
     fini par se désaccorder d'un pixel, et cela se voit tout de suite sur deux
     objets côte à côte.
     Historique : 10 à l'origine, +30 demandés, -20, puis -10. */
  --saillie-photo: 10px;
}

/* Les rails COLLENT sous l'en-tête : ce sont des repères, pas du contenu à
   parcourir. 68px, la hauteur de l'en-tête, plus la respiration du fil. */
.ui-fil-rail {
  position: sticky;
  top: calc(68px + var(--space-5));
}

/* ---- Ligne de rail ----
   Il n'y a plus qu'UN rail de gauche dans l'espace membre : celui des listes
   — profils, communautés, évènements —, où les entrées sont des VUES d'un même
   contenu. Le fil en avait un second, fait de raccourcis vers cinq pages sans
   rapport entre elles ; il a été retiré, parce que deux colonnes de gauche
   identiques d'aspect et opposées de fonction apprennent à se méfier de la
   colonne de gauche. Cette règle servait les deux ; elle ne sert plus que
   l'onglet, et « .ui-fil-raccourci » est parti avec le rail. */
/* Le rail ne garde que sa MISE EN RANGÉE et son écart de pictogramme : sa
   typographie, son rayon, son rembourrage et ses états viennent de la règle
   partagée des menus verticaux, dans ui-molecules.css. 8px d'écart comme le
   menu du socle, contre 12 auparavant. */
.ui-membre-onglet {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* DANS LE RAIL, la pastille est poussée au BORD DROIT de la ligne — une marge
   automatique à sa gauche, sur un onglet qui est déjà une rangée flexible.
   Collée au libellé, elle se posait à une distance différente sur chaque
   entrée, puisque les libellés n'ont pas la même longueur : quatre pastilles
   en escalier le long d'une colonne se lisent comme quatre objets sans
   rapport. Alignées, elles forment une colonne de chiffres qu'on parcourt d'un
   coup d'œil.
   La règle est portée par l'ONGLET et non par la pastille : la même pastille
   sert la barre d'onglets du profil, où elle suit son libellé de près et n'a
   pas de bord droit vers lequel fuir. */
.ui-membre-onglet .ui-pastille-nouveaux { margin-inline-start: auto; }


/* 18px comme les pictogrammes du menu du socle, contre 22 auparavant : à
   graisse normale et 14px de texte, un glyphe de 22 pesait plus que son
   libellé. */
.ui-membre-onglet .material-symbols-outlined {
  color: var(--color-primary);
  font-size: var(--icon-md);
  flex-shrink: 0;
}



/* ---- Zone de saisie ---- */
.ui-fil-composer {
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-4);
  /* 8px en haut et en bas (2 oct. 2026 : 24, puis 16, puis la moitié) ;
     24 sur les côtés, l'avatar à l'aplomb des billets. */
  padding: var(--space-2) var(--space-4);
}

/* Sur petit écran, 16px sur les côtés, comme les billets (≤ 560px) : les
   avatars restent alignés. */
@media (width <= 560px) {
  .ui-fil-composer { padding-inline: var(--space-3); }
}

.ui-fil-composer-champ {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.ui-fil-composer-champ img {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-photo);
  object-fit: cover;
  flex-shrink: 0;
}

/* Un BOUTON et non un champ de saisie : rien n'est encore branché, et un
   champ qui accepte du texte sans rien en faire ment sur ce que la page sait
   faire. Le bouton, lui, promet une suite. */
.ui-fil-composer-champ button {
  flex: 1;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--surface-douce);
  padding: var(--space-3) var(--space-4);
  color: var(--text-muted);
  font-family: inherit;
  font-size: var(--fs-base);
  text-align: start;
  cursor: pointer;
  transition: background 0.2s ease;
}

.ui-fil-composer-champ button:hover { background: var(--surface-appuyee); }

/* LE COMPOSEUR SIMPLIFIÉ (1er oct. 2026) : le champ, et à sa droite un
   ROND appareil photo qui ouvre la même fenêtre de publication. La rangée
   Photo / Évènement / Publier a été retirée (on crée un évènement depuis
   les pages Évènements). */
.ui-fil-composer-champ .ui-fil-composer-photo {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--control-h-tactile);
  height: var(--control-h-tactile);
  border-radius: 50%;
  background: var(--surface-douce);
  padding: 0;
  color: var(--color-titre);
}
.ui-fil-composer-photo .material-symbols-outlined { font-size: var(--icon-lg); }
/* Le champ tient sur une ligne : au téléphone, « Quoi de neuf, … ? »
   passait sur deux. */
@media (width <= 576px) {
  .ui-fil-composer-champ { gap: var(--space-2); }
  .ui-fil-composer-champ button:not(.ui-fil-composer-photo) {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}

/* ---- Filtres du fil ---- */
.ui-fil-filtres {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  overflow-x: auto;
  scrollbar-width: none;
}

.ui-fil-filtres::-webkit-scrollbar { display: none; }

.ui-fil-filtre {
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h-sm);
  padding: 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

/* Sur l'onglet courant, dont le fond est déjà sombre, la pastille garde son
   rose : c'est le seul endroit de la barre où deux aplats se superposent, et
   le rose sur pétrole reste franc. Rien à redéclarer, donc — la note est là
   pour dire que le cas a été regardé. */

.ui-fil-filtre:hover { color: var(--color-titre); border-color: var(--color-titre); }

.ui-fil-filtre.is-current {
  background: var(--color-titre);
  border-color: var(--color-titre);
  color: var(--white);
}

/* Le compte posé dans un onglet-pilule (« Participants 1 », « Évènements 4 »,
   « Likes 3 ») : 10px d'air entre le libellé et la pastille, sur TOUS les
   onglets de ce type — ceux d'une sortie, d'une communauté, d'un profil
   (demande du 27 sept. 2026). Collée au mot, la pastille se lisait comme
   une faute de frappe. */
.ui-fil-filtre > .ui-count-badge,
.ui-fil-filtre > .ui-pastille-nouveaux { margin-inline-start: var(--space-2); }

/* ---- Filtre du fil (29 sept. 2026) ----
   Un lien discret, calé à gauche au-dessus des billets, qui ouvre un petit
   menu de cases à cocher : les pilules disaient cinq choix exclusifs, alors
   qu'on veut souvent en lire deux ou trois. */
.ui-fil-filtrer {
  display: flex;
  justify-content: flex-start;
  margin-bottom: var(--space-3);
}

.ui-fil-filtrer-bouton {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  margin-inline-start: calc(var(--space-2) * -1);
  min-height: var(--control-h-sm);
  padding: 0 var(--space-2);
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
}

.ui-fil-filtrer-bouton:hover,
.ui-fil-filtrer-bouton.show { background: var(--surface-douce); }
.ui-fil-filtrer-bouton > .material-symbols-outlined { font-size: var(--icon-md); }

.ui-fil-filtrer-compte { /* le dessin vient de .ui-count-badge */ }

/* Le panneau est celui des autres menus (ui-molecules : cadre, rayon, ombre) ;
   seules la largeur et l'air intérieur changent, pour les cases. */
.ui-fil-filtrer-menu {
  width: 240px;
  padding: var(--space-3);
}

.ui-fil-filtrer-titre {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

.ui-fil-filtrer-choix {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-2) 0;
  font-size: var(--fs-sm);
  cursor: pointer;
}

.ui-fil-filtrer-choix .form-check-input { margin: 0; float: none; cursor: pointer; }
.ui-fil-filtrer-menu > .ui-btn { width: 100%; margin-top: var(--space-2); }

/* Le même menu sur les listes de profils (29 sept. 2026) : des listes
   déroulantes sous les cases, deux par rangée pour « Âge de … à … ». Plus
   haut que l'écran, il défile. */
.ui-liste-filtrer-menu {
  width: 300px;
  max-height: min(70vh, 560px);
  overflow-y: auto;
}
.ui-liste-filtrer-champs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.ui-liste-filtrer-champ { grid-column: 1 / -1; }
.ui-liste-filtrer-champ--court { grid-column: auto; }
.ui-liste-filtrer-champ > .ui-fil-filtrer-titre { display: block; margin-bottom: var(--space-1); }
.ui-liste-filtrer-menu > .ui-liste-avances-bouton { margin-top: var(--space-3); }
.ui-fil-filtrer-choix + .ui-fil-filtrer-titre { margin-top: var(--space-3); }
/* L'alerte de la liste, AU-DESSUS du filtre : le « i » suit la première
   ligne du texte, et non le milieu du bloc. */
.ui-liste-alerte { align-items: flex-start; margin-bottom: var(--space-3); }
.ui-liste-alerte > .material-symbols-outlined { line-height: 1.5; }
.ui-liste-alerte a { color: inherit; font-weight: 600; }

/* ---- Encarts du rail droit ---- */
/* L'encart (carte de colonne) est une molécule depuis le 3 oct. 2026 :
   molecules/encart.snip, règles dans ui-molecules.css. */

/* ---- Repli du rail ----
   1200px : les suggestions s'en vont. 636 + 300 + la gouttière ne tiennent
   plus, et les suggestions sont la seule des deux colonnes dont on puisse se
   passer sans rien perdre. Le fil prend alors toute la largeur.
   LE RAIL NE DISPARAÎT PAS TOUT À FAIT : il porte les mentions légales, et une
   obligation ne se replie pas. Ce sont ses ENCARTS qui s'effacent ; lui
   redevient un bloc ordinaire et descend sous le fil, ce qui est de toute
   façon là qu'on va chercher des mentions.
   LE FIL RESTE À 636, il ne récupère pas la place libérée. Écrit « 1fr » le
   temps d'une version, il s'étalait sur 1068px à 1100 de large — au-delà de
   120 caractères par ligne, très exactement la mesure que le reste du produit
   s'interdit. Une seule largeur de fil, à toutes les tailles d'écran, et c'est
   la marge autour de lui qui absorbe la différence. */
@media (width <= 1200px) {
  .ui-fil-inner { grid-template-columns: 240px minmax(0, 636px); }
  .ui-fil-rail--droit { display: none; }

  /* Le rail qui ne garde que ses mentions prend la LARGEUR ENTIÈRE et se pose
     en dessous : laissé dans sa colonne, il serait retombé sous le rail de
     gauche, à 240px du bord, ce qui n'a plus rien d'un pied de page. */
  .ui-fil-inner > .ui-fil-rail--droit {
    display: block;
    position: static;
    grid-column: 1 / -1;
  }
  .ui-fil-inner > .ui-fil-rail--droit > .ui-fil-encart:not(.ui-fil-encart--essentiel) { display: none; }

  /* Le profil, lui, GARDE ses encarts. En dessous de 1200px, sa colonne
     d'appoint passe SOUS le contenu et s'y range en deux encarts côte à côte :
     trois colonnes sur une page étroite ne feraient plus qu'une bande de
     200px, où la date d'un évènement ne tient plus sur sa ligne. */
  .ui-monprofil-inner > .ui-fil-rail--droit { display: grid; }
  .ui-monprofil-inner > .ui-monprofil-onglets,
  .ui-monprofil-inner:has(> .ui-fil-rail--droit) > .ui-liste-contenu,
  .ui-monprofil-inner > .ui-liste-contenu { grid-column: 2 / span 10; }

  .ui-monprofil-inner > .ui-fil-rail--droit {
    grid-column: 2 / span 10;
    grid-row: 4;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* 900px : le rail de gauche passe AU-DESSUS du fil, en bande horizontale qui
   défile, et cesse de coller — une bande collante mangerait un quart de la
   hauteur d'un téléphone. Le repli des onglets eux-mêmes est écrit une seule
   fois, avec celui des listes, puisque c'est la même classe. */
@media (width <= 900px) {
  .ui-fil-inner { grid-template-columns: minmax(0, 636px); }
  .ui-fil-rail--gauche { position: static; min-width: 0; }
  /* Une seule colonne : le fil l'occupe. Placé en « colonne 2 », il créait
     une seconde colonne implicite, que seule sa largeur de contenu tenait. */
  .ui-fil-inner > .ui-fil-centre { grid-column: 1 / -1; }
}

/* ---- LISTE DE L'ESPACE MEMBRE ----
   Un seul gabarit pour les trois pages de l'espace connecté : profils,
   communautés, évènements. Elles ont exactement la même armature — un rail à
   gauche, un titre, une barre de filtres, une grille de cartes, une pagination
   — et ne diffèrent que par la CARTE qu'elles répètent.
   D'où le préfixe neutre .ui-liste-* : il ne désigne pas les profils mais la
   PAGE DE LISTE. Le singulier .ui-profil-* reste la carte, et n'a rien à voir.
   Les faire diverger est désormais impossible : elles partagent le snippet
   organismes/section-membre-liste.snip, que le contrôle de châssis compare
   d'une page à l'autre. ---- */
.ui-liste-section { padding-block: var(--space-5) var(--space-6); }

/* Deux colonnes, exactement celles du fil : 240px de rail et la même
   gouttière de 48. Les deux pages de l'espace membre partagent ainsi leur
   armature — le contenu commence à la même abscisse, et la première entrée de
   la navigation, calée dessus par l'en-tête, tombe juste sur les deux. */
.ui-liste-inner {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-5);
}


/* ══════════ LA VUE CENTRÉE — armature des onglets d'un évènement ══════════
   DOUZE COLONNES, LE CONTENU SUR LES DIX DU MILIEU, PAS DE RAIL. C'est
   exactement l'armature des vues d'un profil — « .ui-monprofil-inner » —, aux
   mêmes jetons de gouttière, et c'est voulu : les onglets d'une sortie et ceux
   d'une personne sont le même objet, un sujet unique vu sous plusieurs angles.
   Passer de « Followers » à « Participants » ne doit rien déplacer à l'écran.
   Elle vaut pour les DEUX organismes qui servent ces onglets : la liste de
   profils et le fil. L'un et l'autre ont leur grille à eux — un rail de 240 et
   le reste, ou 240/636/300 —, faites pour des pages où l'on navigue dans une
   colonne latérale. Une fiche d'évènement navigue par ses onglets ; le rail y
   serait une seconde barre, et la page tomberait de travers. */
.ui-liste-section--centree > .ui-liste-inner,
.ui-fil--centre > .ui-fil-inner {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-3) var(--space-4);
}

.ui-liste-section--centree .ui-liste-rail,
.ui-fil--centre .ui-fil-rail--droit { display: none; }

.ui-liste-section--centree > .ui-liste-inner > .ui-liste-contenu,
.ui-fil--centre > .ui-fil-inner > .ui-fil-centre { grid-column: 2 / span 10; }

/* Sous 1200px, les dix colonnes deviennent les douze : à cette largeur, la
   colonne vide de chaque côté ne centre plus rien, elle rétrécit. C'est la
   même bascule que sur les vues d'un profil. */
@media (width <= 1200px) {
  .ui-liste-section--centree > .ui-liste-inner > .ui-liste-contenu,
  .ui-fil--centre > .ui-fil-inner > .ui-fil-centre { grid-column: 1 / -1; }
}

/* Le rail colle sous l'en-tête, comme celui du fil et pour la même raison :
   c'est un repère, pas du contenu à parcourir. */
.ui-liste-rail {
  position: sticky;
  top: calc(68px + var(--space-5));
}

/* Le titre et son action de création sur une même ligne, l'action poussée à
   droite. « wrap » et non « nowrap » : sous 520px, « Créer un évènement »
   et un titre en 2,25rem ne tiennent pas côte à côte, et le bouton passe
   dessous plutôt que de comprimer le titre. */
.ui-liste-tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* NOWRAP, ET C'EST LE POINT DÉLICAT. La tête était en « wrap » : quand le
     titre s'est allongé, le bouton de création est passé à la ligne et s'est
     glissé entre le titre et le chapô, où il coupait la lecture.
     Donner « flex: 1 1 auto » au bloc des titres n'y a rien changé, et la
     raison mérite d'être écrite : l'algorithme flex REPLIE AVANT DE RÉTRÉCIR.
     Tant que « wrap » est là, un élément qui ne tient pas passe à la ligne
     suivante au lieu de céder de la largeur — le « flex-shrink » ne joue
     qu'entre éléments d'une MÊME ligne. Mesuré : titre 818px, bouton 254, tête
     984 ; il manquait 88px et le titre restait sur une seule ligne.
     En « nowrap », les deux restent sur la rangée et c'est le titre qui passe
     sur deux lignes — ce qu'on veut, puisqu'il est le seul des deux à pouvoir
     se replier sans rien perdre. */
  flex-wrap: nowrap;
  gap: var(--space-3);
  /* 20px SOUS LE TITRE. Il n'y en avait aucun : le chapô se posait au contact
     du titre — écart mesuré à 0px — parce que la tête n'a pas de marge et que
     le chapô ne porte la sienne qu'en bas. Le titre et son chapô se lisaient
     comme un seul bloc de texte, alors que l'un annonce et l'autre explique.
     Sur la TÊTE et non sur le titre : le bouton de création, quand il y en a
     un, partage cette rangée et doit descendre avec elle. */
  margin-bottom: var(--space-4);
}

/* LE SUR-TITRE ET LE TITRE FORMENT UN BLOC, l'action reste à leur droite.
   La tête est une rangée flexible : titre à gauche, bouton de création à
   droite. Le sur-titre y a d'abord été posé en « flex: 0 0 100% », ce qui le
   mettait bien sur sa propre ligne — mais l'ensemble restait trois éléments
   dans une seule rangée, et le jour où le titre s'est allongé (« Vos passions
   sont déjà un premier point commun »), le bouton n'a plus tenu à côté : il est
   passé à la ligne, entre le titre et le chapô, où il coupait la lecture.
   Deux blocs plutôt que trois éléments : les titres d'un côté, l'action de
   l'autre. Le bouton reste à droite quelle que soit la longueur du titre, et le
   sur-titre est au-dessus du sien par simple flux. */
.ui-liste-titres {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  /* IL DOIT POUVOIR RÉTRÉCIR. Sans « flex: 1 1 auto », le bloc prend la largeur
     naturelle de son titre — 818px pour « Vos passions sont déjà un premier
     point commun » — et 818 + 254 pour le bouton ne tiennent pas dans les 984
     de la tête : le bouton passait à la ligne et s'intercalait entre le titre
     et le chapô. En rétrécissant, le titre se met sur deux lignes et le bouton
     reste sur la rangée, centré verticalement contre lui.
     « min-width: 0 » parce qu'un élément flexible refuse par défaut de passer
     sous la largeur de son contenu — sans lui, « flex: 1 » ne rétrécit rien. */
  flex: 1 1 auto;
  min-width: 0;
}

.ui-liste-titres > .ui-eyebrow { margin: 0; }

.ui-liste-titre {
  margin: 0;
  color: var(--color-titre);
  font-size: var(--fs-h2);
}

/* La hauteur de commande pleine — 56px — écraserait la rangée du titre : ce
   bouton accompagne un titre, il ne commande pas un formulaire. */
/* Le bouton ne cède JAMAIS de largeur : son libellé ne se replie pas — « Créer
   une communauté » sur deux lignes dans une capsule est illisible — et c'est le
   titre, à sa gauche, qui absorbe la contrainte. */
.ui-liste-action { flex: 0 0 auto; }   /* la taille vient de .ui-btn-slim */

/* ---- Onglets de l'espace membre ----
   Empilés dans le rail, ils reprennent trait pour trait les raccourcis du
   fil : même rayon, même rembourrage, même comportement au survol. Les deux
   rails doivent se ressembler assez pour qu'on ne se demande pas, en changeant
   de page, si l'on a changé d'application.

   Ni bordure ni fond au repos, contrairement aux filtres du fil qui sont des
   pastilles posées sur le contenu : ici c'est une LISTE, et une liste dont
   chaque ligne est encadrée se lit comme un formulaire. ---- */
.ui-membre-onglets {
  display: flex;
  flex-direction: column;
  /* 1px comme le menu du socle : les entrées se touchent presque, ce qui les
     donne pour une liste et non pour une série de boutons. */
  gap: 1px;
  min-width: 0;
}

/* L'ÉTAT COURANT, commun aux deux rails : le MÊME habillage que le survol —
   sauge appuyée et texte pétrole. Il portait l'aplat pétrole plein des
   filtres et des onglets ; à côté d'une colonne de sept lignes claires, ce
   bloc sombre pesait plus que le fil qu'il borde, et l'œil y revenait sans
   cesse. Un menu de gauche dit où l'on est, il ne le crie pas.

   Survol et état courant se confondent donc, à dessein : le survol est
   passager, et il ne se produit jamais sur la ligne où l'on est déjà.

   Pétrole sur sauge appuyée : 5,61:1. */


/* Le compte se lit sur le fond de l'onglet, pas sur une pastille : ici il
   renseigne, il ne presse pas. Une pastille rose en aurait fait une alerte. */
.ui-liste-lead {
  max-width: 62ch;
  margin: 0 0 var(--space-4);
  color: var(--text-muted);
  line-height: var(--lh-base);
}

/* Un picto dans la phrase (la demande de contact, sur « Mes demandes ») :
   à la taille du texte, posé sur sa ligne. */
.ui-liste-lead .material-symbols-outlined {
  font-size: 1.1em;
  vertical-align: -0.15em;
}

/* SUR TOUTE LA LARGEUR DES CARTES. Le chapô des listes est plafonné à 62ch —
   la mesure au-delà de laquelle l'œil perd le début de la ligne suivante. Sur
   les likes et les matchs, il ne présente pas la page : il explique les
   boutons des cartes posées juste en dessous, et un mode d'emploi qui s'arrête
   à mi-chemin de ce qu'il explique se lit comme un paragraphe d'introduction
   quelconque. Aligné au bord droit de la grille, il en devient la légende.
   LE PRIX EST MESURÉ : 1056px à 1400 de large, soit environ 130 caractères
   par ligne, plus du double de la mesure confortable. C'est un arbitrage
   assumé — trois lignes larges qu'on lit une fois contre un pavé étroit qu'on
   saute — et non un oubli. */
.ui-liste-lead--large { max-width: none; }

/* ---- Barre de filtres ----
   Une rangée qui se replie : chaque champ prend la largeur qu'il lui faut, et
   le bouton reste collé au dernier. Sous 720px, un champ par ligne — trois
   champs de 90px ne se remplissent pas. ---- */
.ui-liste-filtres {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-3);
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
}

.ui-liste-champ {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1 1 180px;
  min-width: 0;
}

/* Les bornes d'un intervalle : assez larges pour deux chiffres et la flèche
   du select, pas davantage. Elles ne grandissent pas quand la rangée s'étire
   (flex-grow 0) — c'est aux listes de choix, qui portent des mots, de prendre
   la place que ces deux-là n'utiliseraient pas. */
.ui-liste-champ--court { flex: 0 1 132px; }

.ui-liste-champ label {
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: 700;
}

/* Le bouton se pousse au bout de sa ligne. À quatre champs, il passe seul à
   la ligne suivante : collé à gauche il paraissait orphelin, poussé à droite
   il retombe sous le dernier champ, là où le regard finit sa course. */
.ui-liste-filtres .ui-btn { flex: 0 0 auto; margin-inline-start: auto; }

.ui-liste-grille {
  --bs-gutter-x: 1.5rem;
  --bs-gutter-y: 1.5rem;
}

.ui-liste-grille > .ui-grid-item { display: flex; }
.ui-liste-grille .ui-profil { width: 100%; }

.ui-liste-pagination {
  display: flex;
  justify-content: center;
  margin-top: var(--space-6);
}

/* ---- MON PROFIL (espace membre) ----
   L'identité en tête du contenu, au-dessus de la vue ouverte. Elle occupe la
   largeur utile et n'est pas dans le rail : c'est le sujet de la page, pas un
   élément de navigation.
   Une CARTE, comme l'agenda : le contenu qui se détache de la surface douce.
   Elle emploie l'armature des pages de liste — même rail, même gouttière,
   même abscisse — pour que passer de « Profils » à « Mon profil » ne déplace
   rien à l'écran. ---- */
/* DEUX COLONNES, sans rail de gauche : le contenu et sa colonne d'appoint.
   L'identité et les onglets s'étendent sur les deux — l'une est le sujet de la
   page, l'autre le choix de vue sur ce sujet ; ni l'une ni l'autre n'appartient
   à une colonne. */
.ui-monprofil-inner > .ui-monprofil-identite { grid-column: 1 / -1; grid-row: 1; }
/* DIX COLONNES CENTRÉES : une colonne vide à gauche, une à droite. Le bloc
   n'est plus calé sur une verticale de la rangée du haut — il est centré dans
   la page, et c'est la symétrie qui le tient. Le nom démarre donc une colonne
   à gauche des onglets : c'est le prix du centrage, et il se paie une fois,
   dans la rangée d'en-tête, plutôt qu'à chaque bloc de la colonne de lecture.
   Seule la couverture et sa rangée de portrait gardent les douze — une
   couverture n'est pas du texte. */
.ui-monprofil-inner > .ui-monprofil-onglets { grid-column: 2 / span 10; grid-row: 2; }
.ui-monprofil-inner > .ui-liste-contenu { grid-column: 2 / span 10; grid-row: 3; }

/* DEUX COLONNES QUAND LA PAGE PORTE UNE COLONNE D'APPOINT — c'est-à-dire sur
   son propre profil, et seulement là. Le test se fait sur la présence du rail
   plutôt que sur une classe de page : une classe posée par les données aurait
   dit « cette page est différente », quand ce qui est vrai est « cette page a
   quelque chose à mettre à droite ». Le jour où le profil d'autrui en aura un,
   il en héritera sans qu'on y touche.
   LE COMPTE EST SUR DOUZE : une colonne vide, sept pour le contenu, trois
   pour l'appoint, une colonne vide. Les deux zones se suivent, séparées d'une
   seule gouttière, et la bande n'a plus de trou au milieu — c'était le défaut
   d'un 5 + 3, qui ne fait que huit et laissait deux colonnes flotter entre
   les deux. Le contenu démarre en colonne 2, sous le portrait ; l'appoint
   s'arrête en colonne 11, sur le bord droit de la bannière et des onglets. */
.ui-monprofil-inner:has(> .ui-fil-rail--droit) > .ui-liste-contenu {
  grid-column: 2 / span 7;
}

/* LES DEUX COLONNES DÉMARRENT SUR LA MÊME LIGNE. Le premier bloc du contenu
   porte une marge haute qui lui vient de la page à une seule colonne, où elle
   règle son écart aux onglets ; en vis-à-vis d'une colonne d'appoint, elle le
   fait descendre de 9px sous « Mes photos » — et deux cartes voisines mal
   calées se voient tout de suite. La marge est donc annulée sur le PREMIER
   bloc seulement, et seulement quand il y a un vis-à-vis : les suivants
   gardent leur rythme, et la page à une colonne n'est pas touchée. */
.ui-monprofil-inner:has(> .ui-fil-rail--droit) > .ui-liste-contenu > :first-child {
  margin-top: 0;
}

/* L'encart porte une marge basse — elle sert dans le fil, où le rail est une
   simple pile. Ici le rail est une GRILLE, qui a déjà sa gouttière : les deux
   s'additionnaient et faisaient 48px entre les encarts, contre 24 partout
   ailleurs. On rend l'écart à la grille, qui est seule à le décider. */
.ui-monprofil-inner .ui-fil-encart { margin-bottom: 0; }
/* LES DEUX ENCARTS À DROITE, sur trois colonnes, en vis-à-vis du contenu.
   Ils étaient passés sous le contenu quand celui-ci occupait toute la bande ;
   il en cède maintenant trois, et ils reprennent leur place. L'album et les
   sorties sont ce qu'on regarde après avoir lu la fiche — à côté, pas après.
   À noter tout de même : ils font double emploi avec deux des cinq onglets,
   et le jour où ceux-ci fonctionneront, ils pourront disparaître. */
.ui-monprofil-inner > .ui-fil-rail--droit {
  grid-column: 9 / span 3;
  grid-row: 3;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: var(--space-4);
  position: static;
}

/* L'annulation, sur une seule colonne. Elle est ICI et non dans le repli des
   rails plus haut : à spécificité égale, c'est la dernière règle écrite qui
   l'emporte, et une media query n'y change rien. Placée avant, elle perdait
   contre les quatre lignes ci-dessus — la grille gardait deux colonnes dont
   une de 0px, le rail était écrasé et les onglets disparaissaient. Mesuré à
   420px avant correction : grid-template-columns « 0px 347.98px ». */
@media (width <= 900px) {
  .ui-monprofil-inner { grid-template-columns: minmax(0, 1fr); }

  .ui-monprofil-inner > .ui-monprofil-identite,
  .ui-monprofil-inner > .ui-monprofil-onglets,
  .ui-monprofil-inner > .ui-liste-contenu,
  /* Plus spécifique que les trois autres, la règle « avec colonne
     d'appoint » les battait : le contenu restait calé en « 2 / span 10 », ce
     qui recréait dix colonnes implicites et écrasait la bannière à 16px
     (profil d'un autre membre, 375px, 28 sept. 2026). */
  .ui-monprofil-inner:has(> .ui-fil-rail--droit) > .ui-liste-contenu,
  .ui-monprofil-inner > .ui-fil-rail--droit {
    grid-column: auto;
    grid-row: auto;
  }

  .ui-monprofil-inner > .ui-fil-rail--droit { grid-template-columns: minmax(0, 1fr); }

}

/* La barre d'onglets, posée SOUS la rangée du portrait et au-dessus du
   contenu qu'elle commande, sur la même largeur que lui. « min-width: 0 »
   reste indispensable : sans lui, les cinq intitulés imposent leur largeur à
   la piste de grille au lieu de défiler, et c'est la page entière qui se met
   à glisser de côté. Déjà rencontré sur la bande de raccourcis du fil. */
.ui-monprofil-onglets {
  min-width: 0;
  /* LA BARRE DÉMARRE SOUS LE NOM, pas sous le portrait. Le retrait vaut la
     largeur du portrait plus la gouttière — il est écrit en fonction de
     « --avatar » et non en pixels, pour qu'il suive le jour où le portrait
     change de taille. Le contenu, lui, reste calé sur le bord de la bande :
     les onglets accompagnent le nom, ce qu'ils commandent occupe la page. */
  /* Le retrait vaut DEUX COLONNES de la rangée, gouttières comprises : la
     piste 3, celle où démarre le nom et où finit le portrait. Il était écrit
     en fonction de « --avatar » ; la grille est le bon repère, pas la
     photographie — le jour où celle-ci change de taille, les onglets ne
     doivent pas glisser à sa suite. */
  padding-inline-start: calc(2 * (100% - 9 * var(--space-4)) / 10 + 2 * var(--space-4));
  /* LA BARRE REMONTE de 34px sur sa position naturelle. L'écart naturel
     valait 54 — la rangée réserve la place du
     portrait entier, quand le nom, lui, est remonté par une retouche visuelle
     qui ne rend pas cette place. On reprend la différence en marge NÉGATIVE et
     non par « top » : la marge remonte aussi ce qui suit, là où « top » aurait
     laissé le trou plus bas, entre les onglets et le contenu. */
  margin-top: calc(var(--space-5) * -1);
  /* 20px DE PLUS que l'écart de base, soit 36 : les onglets commandent ce qui
     vient dessous, et collés à lui ils se lisaient comme le titre du premier
     bloc plutôt que comme la barre de la page. Un premier essai à 30 de
     supplément — 46 en tout — creusait trop. L'addition est écrite telle
     quelle plutôt que résolue en un nombre rond : le jour où l'échelle change,
     c'est le supplément qu'on voudra relire, pas le total. */
  margin-bottom: var(--space-5);
  overflow-x: auto;
  scrollbar-width: none;
}

.ui-monprofil-onglets::-webkit-scrollbar { display: none; }

/* La taille de l'avatar est publiée en variable : la couverture, la carte et
   la marge négative en dépendent toutes trois, et trois clamp identiques
   recopiés à la main auraient fini par diverger d'un pixel. */
.ui-monprofil-identite {
  /* LA BANNIÈRE SEULE garde les douze colonnes. Tout le reste de la page —
     portrait, nom, actions, onglets, contenu — tient dans la bande de dix,
     centrée. Une seule largeur pour tout ce qui se lit, une exception pour
     l'image : c'est la règle la plus simple à tenir, et celle qu'on ne se
     retrouve pas à rattraper bloc par bloc. */
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--space-4);
  /* L'écart entre le portrait et le nom. Publié en variable parce que TROIS
     règles en dépendent : la gouttière de la rangée, le retrait de la bio qui
     s'aligne sur le nom, et — indirectement — l'écart visible, que le décalage
     de 20px de l'avatar vers la droite ampute d'autant. À 48px de gouttière,
     il en reste 28 à l'œil. */
  --ecart-portrait: var(--space-5);
}

/* LA COUVERTURE, détachée. Un cadre blanc de 8px et une ombre : c'est un
   tirage posé sur la page, pas le chapeau d'une carte. Enfermée dans la carte
   avec « overflow: hidden », elle en devenait un bandeau décoratif — et
   l'ombre, comme l'avatar qui déborde, y aurait été rognée. */
.ui-monprofil-identite > .ui-monprofil-couverture { grid-column: 1 / -1; grid-row: 1; }
.ui-monprofil-identite > .ui-monprofil-tete { grid-column: 2 / span 10; grid-row: 2; }

/* LES ACTIONS SONT DANS LA BANNIÈRE, angle bas droit : même cellule que
   l'image, calées en fin de ligne et en fin de colonne. Les rangées sont
   déclarées explicitement — sans « grid-row », le placement automatique leur
   aurait ouvert une rangée à elles, exactement comme il l'avait fait pour les
   pastilles de taux, et la page entière serait redescendue.
   Le retrait vaut le cadre blanc plus une gouttière : mesuré depuis le bord de
   l'image, pas depuis celui du cadre, sans quoi les boutons paraissaient collés
   d'un côté et flottants de l'autre. */
.ui-monprofil-identite > .ui-monprofil-actions {
  grid-column: 1 / -1;
  grid-row: 1;
  align-self: end;
  justify-self: end;
  /* Le retrait bas porte un terme de plus : la cellule de grille descend
     jusqu'au bord de MARGE de la bannière, marge basse comprise, alors que le
     retrait doit se mesurer depuis l'image. Sans ce rattrapage, 32px à droite
     et 16 en bas — mesuré. */
  margin: 0 calc(var(--cadre-photo) + var(--space-4))
             calc(var(--cadre-photo) + var(--space-4) + var(--space-3)) 0;
  /* POSITIONNÉ, et au-dessus. La bannière est devenue « position: relative »
     pour accueillir le badge de retour ; un élément positionné se peint
     par-dessus ses voisins de grille qui ne le sont pas, et les deux boutons
     ont disparu SOUS la photographie sans que rien ne le signale — ils
     restaient dans le flux, à la bonne place, simplement recouverts.
     Mesuré au moment de la panne : rangée à 1029,295, 215x40, « display:
     flex », opacité 1, et invisible à l'écran. */
  position: relative;
  z-index: 2;
}

.ui-monprofil-couverture {
  position: relative;
  border: var(--cadre-photo) solid var(--white);
  border-radius: var(--radius-md);
  background: var(--white);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  margin-bottom: var(--space-3);
}

/* LE BADGE DE RETOUR, angle haut gauche de la bannière — en vis-à-vis exact
   des deux actions qui en occupent l'angle bas droit, et au même retrait. Les
   quatre coins de l'image se répondent ainsi : d'où l'on vient en haut à
   gauche, ce qu'on peut y faire en bas à droite.
   Fond blanc plein et non translucide : il est posé sur une photographie
   inconnue, et un voile ne garantit aucun contraste. Encre pétrole, 7,84:1.
   La flèche AVANT le mot, contrairement aux CTA où le pictogramme suit : on
   revient vers la gauche, le sens de lecture doit aller dans le même sens. */
/* LA FLÈCHE SEULE, DANS UN CARRÉ AUX COINS ARRONDIS (1er oct. 2026), comme
   les ronds de l'en-tête : le mot « Retour » n'est plus lu que par les
   lecteurs d'écran (et l'infobulle). */
.ui-monprofil-retour {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--control-h-tactile);
  height: var(--control-h-tactile);
  border-radius: var(--radius-photo);
  background: var(--white);
  padding: 0;
  color: var(--color-titre);
  text-decoration: none;
  box-shadow: var(--shadow-md);
  transition: background 0.2s ease, color 0.2s ease;
}

.ui-monprofil-retour > .material-symbols-outlined { font-size: var(--icon-lg); }

.ui-monprofil-retour:hover,
.ui-monprofil-retour:focus-visible {
  background: var(--color-bouton);
  color: var(--white);
}

/* Hauteur fluide plutôt que fixe : à 220px sur 1200 de large, la bande
   devenait un bandeau ; 280 lui rendent une proportion tenable. Sur un
   téléphone elle retombe à 150 — au-delà, elle mangerait l'écran avant le
   premier mot.
   Le rayon intérieur est plus petit que celui du cadre, de l'épaisseur du
   cadre : deux arrondis concentriques ne le sont que si le second est réduit
   d'autant, sinon le blanc s'épaissit dans les angles. */
.ui-monprofil-couverture img {
  display: block;
  width: 100%;
  height: clamp(150px, 18vw, 280px);
  object-fit: cover;
  border-radius: calc(var(--radius-md) - var(--cadre-photo));
}

/* L'identité tient en DEUX ÉTAGES, et c'est ce qui l'équilibre.
   En un seul, le nom, la bio et les chiffres formaient une colonne haute à
   droite de l'avatar : il restait un grand vide au-dessus du nom — la part de
   couverture que l'avatar chevauche — et le bouton, poussé tout en bas, se
   retrouvait seul dans un angle.
   Étage 1 : l'avatar, le nom et l'action, alignés sur le BAS de l'avatar.
   Étage 2 : la bio et les chiffres, sur toute la largeur.
   Le nom se pose ainsi au pied du portrait, là où l'œil l'attend, et la mesure
   de la bio n'est plus commandée par la largeur de l'avatar.
   Le rembourrage horizontal SURVIT à la disparition de la carte, et ce n'est
   pas un oubli : il tient l'avatar en retrait du coin de la photographie, où
   il mordrait sinon l'angle du cadre. Le bloc est donc décalé de 24px par
   rapport au composeur et aux publications en dessous — décalage assumé, le
   portrait prime sur l'alignement de colonne. */
/* LA RANGÉE DU PORTRAIT reprend les DOUZE COLONNES de la page, avec la même
   gouttière : c'est la seule façon de faire tomber les onglets exactement sur
   la piste où commencent le nom, la bio et les photographies. Un écart en
   flex ne pouvait pas y arriver — il dépend de la largeur de l'avatar, pas de
   celle des colonnes.
   L'avatar occupe les deux premières, les onglets partent de la troisième, les
   actions se rangent à droite. */
.ui-monprofil-tete {
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
  column-gap: var(--space-4);
  align-items: end;
}

/* Le portrait démarre en COLONNE 2, sur la même verticale que les onglets et
   que tout le contenu en dessous. Il ne pend plus dans la marge : la page n'a
   plus qu'une seule abscisse, et c'est elle qui la tient.
   Le nom occupe la place que les onglets viennent de quitter — on lit un
   visage, puis un nom, et l'on choisit une vue seulement après. */
/* TOUS SUR LA RANGÉE 1, la déclaration est explicite. Les pastilles de taux
   occupent la même piste que le nom : sans « grid-row », le placement
   automatique leur ouvrait une SECONDE rangée sous la première, qui poussait
   le nom de 70px vers le bas et cassait l'alignement réglé juste avant. Elles ne se chevauchent pas à l'œil — les pastilles sont
   remontées sur la bannière — et la hauteur de rangée reste celle du nom. */
.ui-monprofil-tete > .ui-monprofil-avatar { grid-column: 1 / span 2; grid-row: 1; }
.ui-monprofil-tete > .ui-monprofil-taux { grid-row: 1; }
.ui-monprofil-tete > .ui-monprofil-detail { grid-column: 3 / span 8; grid-row: 1; }

/* LE REPLI DE LA RANGÉE, écrit ICI et non dans le bloc 900px plus haut : à
   spécificité égale c'est la dernière règle du fichier qui l'emporte, et une
   media query n'y change rien. Placé avant, il perdait contre les trois lignes
   ci-dessus — la rangée gardait ses douze colonnes, l'avatar débordait de sa
   piste de 55px et venait chevaucher le nom. Mesuré à 420px avant correction :
   portrait 50→165, nom 119→301, et 431px de page pour 420 de fenêtre.
   Même piège que l'annulation de la grille de page, déjà commenté plus haut. */
@media (width <= 900px) {
  .ui-monprofil-identite { grid-template-columns: minmax(0, 1fr); }

  .ui-monprofil-identite > .ui-monprofil-couverture,
  .ui-monprofil-identite > .ui-monprofil-tete,
  .ui-monprofil-identite > .ui-monprofil-actions {
    grid-column: auto;
    grid-row: auto;
    /* Empilées, les actions ne peuvent plus se poser sur l'image : une
       bannière haute de 150px n'a pas de place pour deux boutons en plus du
       portrait. Elles reprennent leur rang dans le flux. */
    justify-self: start;
    margin: 0;
  }

  .ui-monprofil-tete {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
  }

  /* Le nom prend sa propre ligne : à cette largeur, portrait et nom côte à
     côte ne laissent au second que la largeur d'un mot. */
  .ui-monprofil-tete > .ui-monprofil-detail {
    flex: 1 1 100%;
  }

  .ui-monprofil-avatar { left: 0; }

  /* Les deux retouches du bloc « nom » ne valent que pour la rangée à trois
     éléments : elles l'amenaient sur l'axe des boutons placés à sa droite.
     Empilé, il n'a plus rien à rattraper — et les 30px vers la gauche le
     sortaient de la page. Mesuré à 420px avant correction : bord gauche à
     -14px pour une marge de 16. */
  .ui-monprofil-tete > .ui-monprofil-detail { top: 0; left: 0; }
  /* Empilé, le nom démarre au bord de la bande : les onglets aussi. */
  .ui-monprofil-onglets { padding-inline-start: 0; margin-top: 0; }
}

/* L'avatar CHEVAUCHE la couverture — d'où la marge négative, calculée sur sa
   propre taille pour qu'il en dépasse toujours d'un peu moins de la moitié
   quelle que soit la largeur.
   Le cerne blanc n'est pas un ornement : c'est ce qui détache un portrait
   d'une photographie qu'on ne choisit pas. Sans lui, un avatar sombre sur une
   couverture sombre disparaît. Il a la MÊME épaisseur que le cadre de la
   bannière, par « --cadre-photo » : deux tirages sur la même page se
   présentent de la même façon. */
/* L'avatar enjambe l'intervalle entre le cadre et la carte : on le remonte de
   la moitié de sa hauteur, PLUS cet intervalle, PLUS « --saillie-photo ».
   Les deux premiers termes plaçaient son centre exactement sur le bord bas du
   cadre.
   La remontée passe par « top » et NON par la marge haute. La
   rangée est alignée sur le bas (align-items: flex-end) : la position de
   l'avatar y est calculée depuis le bas de la ligne, si bien qu'une marge
   haute plus négative ne le déplace pas d'un pixel — elle ne fait que réduire
   sa contribution à la hauteur de ligne. Essayé, mesuré, aucun effet.
   « top » le décale visuellement sans toucher à la mise en page, ce qui est
   exactement ce qu'on attend d'un ornement posé sur une photographie.
   « left » procède de même pour les 20px vers la droite. Conséquence à
   connaître : le nom, lui, ne bouge pas — l'écart entre le portrait et lui
   passe donc de 24 à 4px. Le déplacer aussi aurait décalé le nom de la bio,
   qui commence, elle, au retrait de la colonne. */
.ui-monprofil-avatar {
  position: relative;
  flex: 0 0 auto;
  /* DEUX COLONNES, gouttière comprise — c'est la grille qui donne la mesure,
     plus une variable en pixels. Le portrait est placé sur les pistes 1 et 2 de
     la rangée ; « width: 100% » lui fait prendre sa cellule, ni plus ni moins,
     et il suivra la grille à toutes les largeurs sans qu'on ait à recopier un
     clamp.
     La remontée passe de « --avatar / -2 » à « -50% » : un pourcentage de
     marge se calcule sur la LARGEUR du bloc conteneur, ici la cellule de deux
     colonnes — c'est-à-dire la largeur du portrait, donc sa hauteur, puisqu'il
     est carré. Même résultat, sans avoir à connaître le nombre. */
  width: 100%;
  aspect-ratio: 1;
  height: auto;
  margin-top: calc(-50% - var(--space-3));
  top: calc(var(--saillie-photo) * -1);
  /* PLUS DE DÉCALAGE HORIZONTAL. Les 44px qui tenaient ici (24 de l'ancien
     rembourrage, 20 demandés) servaient à rattraper à la main une abscisse que
     la grille ne donnait pas : le portrait est maintenant posé en colonne 2,
     il tombe donc juste, et un « left » de plus l'en sortirait. */
  margin-bottom: var(--space-3);
  border: var(--cadre-photo) solid var(--white);
  /* Le portrait suit les vignettes : carré à coins arrondis. Le cadre blanc
     de 8px l'entoure donc en suivant le même galbe — un cadre rond autour
     d'une photo carrée aurait laissé quatre croissants de blanc aux angles. */
  border-radius: var(--radius-photo);
  object-fit: cover;
}

/* SUR UN TÉLÉPHONE (28 sept. 2026) : empilée, la rangée n'a plus de grille,
   et « width: 100% » faisait du portrait un carré de la largeur de l'écran —
   le nom tombait sous la ligne de flottaison, la remontée de « -50% » (de la
   LARGEUR) creusait un vide au-dessus. Un portrait de taille fixe, qui mord
   la bannière de la moitié de sa hauteur, comme sur un écran large.
   Écrit APRÈS la règle du portrait : à spécificité égale, la dernière
   l'emporte, media query ou non (voir le repli de la rangée, plus haut). */
@media (width <= 900px) {
  .ui-monprofil-avatar {
    --portrait-mobile: 112px;
    width: var(--portrait-mobile);
    margin-top: calc(var(--portrait-mobile) / -2);
    top: 0;
    margin-bottom: 0;
  }
  /* La bannière garde toute la largeur (le repli plus haut cale les trois
     blocs au début de ligne, ce qui la réduisait à son contenu). */
  .ui-monprofil-identite > .ui-monprofil-couverture { justify-self: stretch; }
  /* Sans bannière, rien à mordre : le portrait reste dans la page. */
  .ui-monprofil-identite:not(:has(.ui-monprofil-couverture)) .ui-monprofil-avatar { margin-top: 0; }
}

/* Le nom et le lieu seuls : ils tiennent en deux lignes courtes, la base
   peut donc être petite sans rien comprimer — et le bouton reste sur la même
   ligne jusqu'à des largeurs bien plus faibles qu'avant.
   Aucun rembourrage bas : il creusait un vide entre le nom et la bio que rien
   ne justifiait — l'écart entre les deux étages est déjà porté par la rangée. */
/* Le nom et le lieu sur UNE SEULE LIGNE, alignés sur leur ligne de base et non
   sur leur boîte : « Sacha, 42 » est en 30px et « Bruxelles » en 14, les caler
   par le haut ou par le centre aurait fait flotter le second.
   L'écart de 20px est porté par « gap » plutôt que par une marge gauche : le
   rendu est le même tant que les deux tiennent sur la ligne, mais quand la
   place manque le lieu passe dessous SANS retrait — une marge l'aurait
   indenté de 20px sous le nom, ce qui ne veut rien dire.
   Plus de décalage de 6px : il rattrapait la position du nom quand celui-ci
   était encore à côté du portrait. */
.ui-monprofil-infos {
  min-width: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-4);
}

/* Second étage : la bio et les chiffres, alignés sur le bord gauche de
   l'avatar plutôt que sur son bord droit. La ligne de départ du texte est
   ainsi la même que celle du composeur et des publications en dessous. */
/* La bio et les chiffres s'alignent sur LE NOM, pas sur le bord du bloc.
   Le retrait vaut donc exactement ce qui précède le nom dans la rangée du
   dessus : le rembourrage, la largeur de l'avatar, puis l'écart de la rangée.
   Il est calculé et non écrit en dur — l'avatar est un clamp, un 188px figé
   se serait décalé au premier changement de largeur.
   Sous 640px, retour au retrait simple : à cette taille l'avatar vaut 96px et
   l'alignement laisserait à la bio moins de 200px de mesure, c'est-à-dire
   quatre mots par ligne.
   Les 6px de remontée passent par « top », comme ceux de l'avatar : un
   décalage d'ajustement optique ne doit rien déplacer de ce qui l'entoure. Le
   nom en reçoit autant, par la même voie — les deux blocs remontent ensemble
   ou l'alignement qu'on vient d'établir se défait. */
/* Le bloc « nom » vit maintenant DANS la rangée du portrait, à sa droite. Les
   6px de remontée restent : la rangée est alignée sur le bas, et le nom tombe
   sinon d'aplomb sur la ligne du portrait, ce qui le colle au bord de la
   carte. */
.ui-monprofil-detail {
  position: relative;
  /* 6px d'origine + 22px pour amener le nom sur l'axe des boutons de droite.
     La rangée est alignée sur le BAS : le nom, haut de 31px, s'y trouvait donc
     posé sur la même ligne d'assise que des boutons hauts de 44, et son centre
     tombait 22px plus bas que le leur. Mesuré : nom 430→461, boutons 401→445.
     La somme est écrite, pas le total : le jour où l'un des deux bouge, on
     sait lequel. */
  /* 28px + 20px remontés - 10px - 10px redescendus, puis 7px repris : la
     rangée avait grandi de 3px quand les pastilles de taux sont passées à la
     hauteur du badge de disponibilité, et de 4px de plus quand la
     photographie est passée à 176 de boîte. Le nom, aligné sur le bas, avait
     suivi sans qu'on le lui demande. Puis 10px de plus, demandés — les
     onglets les prennent au même moment, l'écart de 16px entre les deux est
     donc conservé. La somme est gardée en clair. */
  top: -45px;
  /* 30px vers la gauche, pour resserrer l'écart avec le portrait sans toucher
     à la piste de grille — la déplacer aurait décalé du même coup la colonne
     des onglets, qui démarre au même endroit. « left » déplace à l'œil et
     laisse la mise en page où elle est, ce qu'on attend d'un ajustement. */
  /* AUCUN DÉCALAGE : le nom démarre sur la piste 3, là où finit le portrait
     et où démarrent les onglets. Il a porté jusqu'ici -30 puis -20px, des
     rattrapages d'une époque où le portrait ne tombait pas juste. */
  min-width: 0;
}

.ui-monprofil-nom {
  margin: 0;
  color: var(--color-titre);
  font-size: var(--fs-h3);
}

.ui-monprofil-meta {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-monprofil-meta .material-symbols-outlined { font-size: var(--icon-sm); }
.ui-monprofil-meta.ui-monprofil-mention { color: var(--color-brand-pink); font-weight: 600; }
/* Une demande de contact en attente se dit en pétrole, comme sur les cartes. */
.ui-monprofil-meta.ui-monprofil-mention--contact { color: var(--color-titre); }

/* LE BADGE DE DISPONIBILITÉ, repris tel quel de la carte profil — même fond,
   même pastille, mêmes couleurs. Une seule chose change : sur la vignette il
   est posé dans l'angle de la photo, ici il prend son rang sur la ligne du
   nom. D'où l'annulation du positionnement absolu, et rien d'autre. */
.ui-monprofil-statut,
.ui-monprofil-taux {
  position: static;
  flex: 0 0 auto;
  /* CENTRÉES SUR LA RANGÉE et non posées sur la ligne de base. Les trois
     pastilles ont la même hauteur, mais celles des taux portent un
     pictogramme qui déplace leur ligne de base : alignées dessus, elles se
     décalaient de 4px de leur voisine — deux objets identiques mal calés se
     lisent comme un défaut, pas comme une intention. Mesuré : badge à 405,
     pastilles à 401. */
  align-self: center;
}

/* 68ch : la mesure de confort de lecture. Sans cette borne, la bio
   s'étalerait maintenant sur toute la largeur de la page — et une ligne de
   140 caractères ne se lit pas, elle se saute. */
.ui-monprofil-bio {
  max-width: 68ch;
  /* 10px au-dessus, et rien de plus : la bio suit le lieu de très près — les
     deux appartiennent au même bloc d'identité — mais sans ces 10px la ligne
     « Bruxelles » et la première ligne de texte se touchaient presque. */
  margin: var(--space-2) 0 0;
  color: var(--text);
  line-height: var(--lh-base);
}

/* Des CHIFFRES et non des scores : sur son propre profil, il n'y a pas de taux
   de compatibilité — on ne se compare pas à soi-même. Le nombre porte, le mot
   accompagne : c'est le nombre qu'on cherche du regard. */
.ui-monprofil-chiffres {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.ui-monprofil-chiffre,
.ui-monprofil-chiffre a {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-decoration: none;
}

/* Les nombres au ROSE DE MARQUE. Ce sont les seuls chiffres de la page qui
   parlent de la personne : ils méritent la couleur qui signe, là où le pétrole
   des titres les rangeait avec le reste.
   4,89:1 sur le blanc de la carte — au-dessus du seuil du petit texte, ce
   qu'ils sont à 18px. Le libellé qui suit reste en gris : les mettre au rose
   tous les deux aurait fait trois taches de couleur au lieu de trois nombres. */
.ui-monprofil-chiffre strong {
  color: var(--color-brand-pink);
  font-size: var(--fs-base);
  font-weight: 600;
}

.ui-monprofil-chiffre a:hover strong,
.ui-monprofil-chiffre a:focus-visible strong { color: var(--color-brand-pink-ink); }

/* Le bouton part à droite tant qu'il y a la place, et retombe sous l'identité
   quand la carte se resserre — « margin-inline-start: auto » ne tient que sur
   une ligne, et c'est exactement ce qu'on veut.
   « align-self: flex-start » le cale en HAUT de la rangée : sans lui, il se
   centrait sur une colonne d'infos haute de 130px et flottait au milieu de
   nulle part. Sa place est à hauteur du nom, qu'il désigne. */
/* Le bouton s'aligne sur le BAS de la rangée, donc sur le pied de l'avatar et
   sur la ligne du nom. C'est le seul calage qui tienne : « flex-start »
   l'envoyait au niveau de la couverture, « center » le faisait flotter.
   Puis les mêmes 6px de remontée que le nom, par la même voie : ils sont sur
   la même ligne, ils doivent bouger ensemble. Un « top » plutôt qu'une marge,
   pour ne rien déplacer autour. */
.ui-monprofil-actions {
  position: relative;
  /* Plus de remontée : les boutons ont quitté la rangée du nom pour la
     bannière, où c'est le retrait qui les place. */
  top: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-inline-start: auto;
  align-self: flex-end;
  margin-bottom: var(--space-3);
}

/* LES DEUX TAUX, sur la ligne du nom, après le badge de disponibilité. Ils
   réemploient les pastilles de la carte profil — mêmes classes, même CSS,
   mêmes couleurs de genre : c'est le même objet, et le lecteur qui vient de la
   grille doit le reconnaître.
   La molécule d'origine est posée dans l'angle HAUT DROIT de la vignette, d'où
   son « position: absolute » et son « justify-content: flex-end ». Ici elle
   n'est plus dans un angle mais dans une ligne : les deux déclarations sont
   reprises, sans quoi les pastilles se décrochaient du flux et partaient se
   coller au bout de leur piste.
   Ils n'existent QUE sur le profil d'autrui : sur le sien, il n'y a rien à
   comparer. */
.ui-monprofil-taux {
  justify-content: flex-start;
}

/* MÊME HAUTEUR QUE LE BADGE DE DISPONIBILITÉ qui les précède sur la ligne.
   Les deux objets portent déjà le même corps et la même graisse ; seul le
   rembourrage vertical les séparait — 5px contre 8, soit 28px de haut contre
   34. Trois pastilles à la file dont une plus courte se lisent comme une
   erreur, pas comme une hiérarchie. C'est le rembourrage qu'on aligne, et non
   une hauteur fixe : le jour où le corps de texte change, les trois suivent
   ensemble. */

/* ---- L'OMBRE LÉGÈRE, posée d'un seul endroit ----
   Toutes les cartes et toutes les photographies de la page en prennent une :
   sur la surface douce du fond, un cadre d'un pixel ne suffisait plus à les
   détacher depuis que la bannière et le portrait, eux, portent la leur — trois
   objets ombrés et douze objets plats se lisaient comme deux calques.
   C'est « --shadow-sm » et non « --shadow-md » : la bannière et le portrait
   sont les deux tirages de la page, ils gardent l'ombre franche ; le reste ne
   fait que se décoller du fond.
   Une seule règle et une liste de sélecteurs, plutôt qu'une déclaration
   recopiée dans chacun des blocs — l'ombre est une décision de page, pas une
   propriété de chaque carte.
   Portée volontairement limitée à « .ui-monprofil-inner » : les mêmes classes
   de publication, d'encart et de carte profil servent le fil d'actualité et la
   page « Profils », qui n'ont pas demandé à changer. */
.ui-monprofil-inner .ui-monprofil-presentation,
.ui-monprofil-inner .ui-monprofil-interets,
.ui-monprofil-inner .ui-monprofil-fiche-ligne,
.ui-monprofil-inner .ui-monprofil-citation,
.ui-monprofil-inner .ui-profil,
.ui-monprofil-inner .ui-monprofil-photo,
.ui-monprofil-inner .ui-monprofil-vignette,
.ui-monprofil-inner .ui-publication,
.ui-monprofil-inner .ui-publication-visuel,
.ui-monprofil-inner .ui-fil-composer,
.ui-monprofil-inner .ui-fil-encart {
  box-shadow: var(--shadow-md);
}

/* Le billet à vitrine n'a pas de carte autour de lui (en-tête et vitrine
   sur le fond) : pas d'ombre sur lui, qui dessinait un cadre fantôme. */
.ui-monprofil-inner .ui-publication--vitrine { box-shadow: none; }

/* ---- LE MODE ÉDITION ----
   Un seul objet, décliné : un bouton rond, blanc, posé dans l'angle du bloc
   qu'il modifie. Il n'y a pas de « page de réglages » — la page publiée EST le
   formulaire, et c'est ce qui évite à l'utilisateur d'imaginer le résultat de
   ce qu'il tape.
   Discret au repos, franc au survol : il ne doit pas concurrencer le contenu,
   mais il doit se trouver du premier coup d'œil quand on le cherche. */
/* LA TAILLE ET LA FORME VIENNENT DU SOCLE : le gabarit pose
   « .ui-icon-button.ui-icon-button-slim », et le bouton d'édition prend donc
   les 44px du rang « small », comme tout autre petit bouton du système. Rien
   n'est recopié ici — le jour où l'échelle bouge, ces boutons suivent, ce qui
   n'était pas le cas quand l'organisme écrivait ses propres 30px.
   L'organisme ne garde que ce qui lui appartient VRAIMENT : la place dans
   l'angle du bloc, et un fond blanc plein — l'atome est transparent, ce qui
   convient sur une page mais pas posé sur une photographie. */
.ui-monprofil-editer {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: 2;
  border-color: var(--border);
  background: var(--white);
  color: var(--text-muted);
  box-shadow: var(--shadow-md);
}

.ui-monprofil-editer > .material-symbols-outlined { font-size: var(--icon-md); }

/* LES BOUTONS D'ÉDITION NE PARAISSENT QU'AU SURVOL DE LEUR BLOC. Ils sont une
   dizaine sur la page — un par ligne de fiche, un par photographie, un par
   citation —, et tous visibles en permanence ils faisaient de la page un
   FORMULAIRE : on lisait les commandes avant de lire ce qu'elles modifient,
   alors que cette page sert d'abord à se relire. Au survol, chacun se montre
   là où l'on regarde déjà, et la page redevient ce qu'elle publie.

   « opacity » ET NON « display: none » : un bouton retiré du rendu sort aussi
   du parcours au clavier, et l'édition serait devenue impossible sans souris.
   Invisible mais présent, il reste atteignable par tabulation — d'où le
   « :focus-visible », qui le rend visible dès qu'il a le focus.

   LE PORTRAIT EST À PART : son bouton vit dans « .ui-monprofil-tete », qui
   couvre toute la bannière, nom et actions compris. Survoler le titre de la
   page aurait allumé le bouton de la photographie, trente centimètres plus
   loin. « :has » resserre la condition sur le portrait lui-même.

   Rien ici n'est propre à « Mon profil » : la règle vaut partout où ces boutons
   paraissent — la page Photos les emploie aussi. */
.ui-monprofil-editer {
  opacity: 0;
  transition: opacity 0.15s ease, background 0.2s ease, color 0.2s ease;
}

.ui-monprofil-presentation:hover > .ui-monprofil-editer,
.ui-monprofil-photo-case:hover > .ui-monprofil-editer,
.ui-monprofil-fiche-ligne:hover > .ui-monprofil-editer,
.ui-monprofil-citation:hover > .ui-monprofil-editer,
.ui-monprofil-interets:hover > .ui-monprofil-editer,
.ui-monprofil-tete:has(> .ui-monprofil-avatar:hover) > .ui-monprofil-editer--portrait,
.ui-monprofil-editer:hover,
.ui-monprofil-editer:focus-visible {
  opacity: 1;
}

.ui-monprofil-editer:hover,
.ui-monprofil-editer:focus-visible {
  border-color: var(--color-bouton);
  background: var(--color-bouton);
  color: var(--white);
}

/* Les blocs qui l'accueillent doivent lui servir de repère. Déclaré ici, à
   côté du bouton, et non dans chacune des règles concernées : c'est une
   conséquence du mode édition, pas une propriété de la carte. */
.ui-monprofil-presentation,
.ui-monprofil-interets,
.ui-monprofil-fiche-ligne,
.ui-monprofil-photo-case { position: relative; }

/* SUR LE PORTRAIT, le bouton n'a pas de bloc à qui s'accrocher : le portrait
   est une image, pas un conteneur. Il est donc posé sur LA MÊME CELLULE de
   grille, calé dans son angle bas droit — ce qui le suit automatiquement quand
   la photographie change de taille avec la grille. */
.ui-monprofil-tete > .ui-monprofil-editer--portrait {
  grid-column: 1 / span 2;
  grid-row: 1;
  position: relative;
  align-self: end;
  justify-self: end;
  /* Il accompagne le portrait dans sa remontée sur la bannière, et se pose dans
     son ANGLE BAS DROIT avec 10px de dégagement sur l'image.
     LE COMPTE : 8px de cadre blanc + 10px de marge = 18px depuis le bord
     extérieur du portrait, en bas comme à droite. Le décalage vertical suit —
     40px le collaient à l'intérieur du cadre, 30 lui laissent les 10.
     Ces 40 avaient d'abord valu 11, du temps où le portrait était un DISQUE :
     le bouton rentrait alors de quelques pixels pour se poser sur sa courbe,
     un coin n'existant pas. Depuis le passage au carré à coins arrondis ce coin
     existe, et le bouton flottait 37px au-dessus — mesurés. */
  top: calc(-50% - var(--space-3) - var(--saillie-photo) + 30px);
  right: 18px;
  border-width: 2px;
  border-color: var(--white);
}

/* SUR UNE PHOTOGRAPHIE, en haut à droite comme partout ailleurs. Il avait
   été mis en bas au motif que le haut d'une image est ce qu'on regarde ; mais
   une page d'édition se parcourt d'abord par ses commandes, et une commande
   qui change de coin selon le bloc oblige à la chercher. La cohérence de la
   colonne l'emporte sur le cadrage d'une photographie.
   Le modificateur reste, et il porte ce qui lui appartient vraiment : ce
   bouton-là est posé SUR une image, pas sur un fond blanc. Il lui faut un
   cerne et une ombre plus francs, sans quoi il disparaît sur un ciel clair. */
.ui-monprofil-editer--photo {
  border-color: var(--white);
  box-shadow: var(--shadow-md);
}

/* Sur une carte de citation, le guillemet occupait déjà l'angle haut droit et
   le bouton s'était rangé à sa gauche — il n'était donc plus aligné sur les
   boutons de toutes les autres cartes, ce qui se voit d'autant mieux qu'ils
   sont en colonne. C'est l'inverse qu'il faut : le bouton garde l'angle, qui
   est sa place partout ailleurs, et le guillemet — qui n'est qu'un ornement,
   « aria-hidden » dans le gabarit — se décale pour lui laisser le passage. */
.ui-monprofil-citation:has(.ui-monprofil-editer) .ui-monprofil-citation-marque {
  right: calc(var(--space-4) + 44px);
}

.ui-monprofil-photo-case { display: block; }

/* LE BOUTON MESSAGE de la ligne du nom. Même dessin et même bleu que celui des
   cartes de match — c'est le même geste —, mais au rang « small » du socle,
   comme le menu à trois points qu'il précède : deux ronds voisins de tailles
   différentes se liraient comme deux objets sans rapport.
   « auto » sur la marge de début : c'est LUI qui décroche du nom, et le menu
   le suit à distance fixe. Sans cela, le menu seul partait à droite et le
   message restait collé aux taux. */
.ui-monprofil-message {
  margin-inline-start: auto;
  /* CENTRÉ SUR LA RANGÉE, comme le badge et les deux taux : la ligne du nom
     est alignée sur la LIGNE DE BASE, où un rond se calerait sur le bas de la
     lettre. Mesuré avant correction : message à 392, menu à 385. */
  align-self: center;
  /* CONSTRUIT COMME SON VOISIN, le menu à trois points : disque blanc, filet
     d'un pixel, glyphe coloré, aucune ombre. Seule la couleur les distingue —
     bleu pour écrire, gris pour les actions secondaires.
     Deux essais écartés, notés pour ne pas y revenir : sans filet et avec une
     ombre portée, il ne ressemblait à rien d'autre sur la page ; et le dessin
     de la carte profil (47px, sans filet) n'est pas transposable ici, où le
     bouton voisine un rond de 40 qui, lui, est cerclé. */
  background: var(--white);
  border-color: var(--genre-lui);
  color: var(--genre-lui);
}

/* Le pictogramme au même corps que celui du menu voisin : 18 contre 20 se
   voyait, deux ronds côte à côte ne pardonnent pas l'écart. */
.ui-monprofil-message > .material-symbols-outlined { font-size: var(--icon-md); }

/* 8px entre les deux ronds, LA MÊME VALEUR QUE SUR UNE CARTE PROFIL — sa
   rangée d'actions emploie « gap: 0.5rem ». La ligne du nom, elle, écarte ses
   éléments de 20px : on retire donc ces 20px pour retomber sur 8. La soustraction
   est écrite plutôt que le résultat, sinon le jour où la rangée change de
   gouttière, l'écart entre les deux boutons partirait sans qu'on sache
   pourquoi. Mesuré avant correction : 28px ici contre 8 sur une carte. */
.ui-monprofil-message + .ui-menu-actions {
  margin-inline-start: calc(0.5rem - 20px);
}

.ui-monprofil-message:hover,
.ui-monprofil-message:focus-visible {
  background: var(--genre-lui);
  border-color: var(--genre-lui);
  color: var(--white);
}

/* ---- LA GRILLE DE CARTES PROFIL ----
   TROIS PAR RANGÉE, et c'est une grille IMBRIQUÉE : les cartes divisent la
   colonne de contenu, elles ne se posent pas sur les pistes de la page. La
   bande en compte dix, qui ne se divisent ni par trois ni par quatre — les
   seuls découpages qui tomberaient juste sont deux cartes de cinq pistes
   (508px, trop large pour un portrait) ou cinq cartes de deux (188px, trop
   étroit pour un nom, un lieu et deux taux).
   Ce n'est pas une entorse : les six cartes d'informations et les
   photographies procèdent déjà ainsi. On n'attend pas qu'une carte s'aligne
   sur une piste de page — on attend que la COLONNE s'y aligne, et elle le
   fait. À trois, chaque carte fait 330px, la mesure d'un portrait qu'on
   regarde sans le scruter.
   Quatre resteraient possibles à 242px ; c'est la limite basse, en dessous de
   laquelle les deux pastilles de taux se marchent dessus. */
/* Un groupe par sujet, empilés. L'écart entre deux groupes vaut celui de tous
   les blocs de la page — 24px —, et le titre s'en détache par sa graisse, pas
   par un vide plus grand : c'est une liste qui se poursuit, pas deux pages. */
.ui-monprofil-groupe + .ui-monprofil-groupe { margin-top: var(--space-4); }

/* 60px AU-DESSUS DE LA PLANCHE DES PUBLICATIONS, contre 24 entre deux groupes
   ordinaires. Les deux grilles de la page Photos ne disent pas la même chose —
   ce que je choisis de montrer, puis ce que j'ai posté — et 24px les faisaient
   lire comme deux rangées d'une même série. L'écart nomme la coupure que le
   titre annonce.
   Sélecteur d'ADJACENCE et non « :has » seul : la marge ne vaut que si la
   planche suit un autre groupe. Première de sa page, elle n'aurait rien à
   pousser. */
.ui-monprofil-groupe + .ui-monprofil-groupe:has(.ui-monprofil-galerie) {
  margin-top: var(--space-6);
}

/* LE MOT D'ABSENCE, sous une grille vide. Une page d'agenda qui ne dit rien
   laisse croire à une panne ; il faut qu'elle dise qu'il n'y a rien, ce qui
   n'est pas la même chose. */
.ui-monprofil-vide {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Le compte, à côté du titre et non dedans : il chiffre le groupe, il ne le
   nomme pas. Même pastille que celle de l'onglet, dont il reprend le nombre —
   les deux disent la même chose au même moment. */
.ui-monprofil-groupe-compte {
  margin-inline-start: var(--space-2);
  vertical-align: 1px;
}

.ui-monprofil-profils {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  align-items: start;
}

@media (width <= 1100px) {
  .ui-monprofil-profils { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (width <= 640px) {
  .ui-monprofil-profils { grid-template-columns: minmax(0, 1fr); }
}

/* QUATRE PAR RANGÉE, pour les grilles en cartes réduites. Le modificateur ne
   touche qu'au PALIER LARGE : en dessous de 1100px la grille retombe à deux
   colonnes puis à une, et ces deux paliers-là valent pour les deux largeurs —
   une carte de 245px n'a pas de raison d'en accepter quatre sur un écran qui
   n'en tient plus que deux à 330.
   D'où un sélecteur qui vient APRÈS les deux media queries ci-dessus mais ne
   les contredit pas : elles réécrivent « grid-template-columns » à leur
   palier, celui-ci au palier par défaut, et le dernier déclaré l'emporte à
   spécificité égale — ce qui est vrai ici, deux classes contre une, et dans
   le bon sens.
   Non : une media query qui réécrirait 4 colonnes au-dessus de 1100px ferait
   la même chose en trois lignes de plus. Le palier par défaut EST le palier
   large ; les deux autres sont les exceptions, et elles suivent. */
@media (width >= 1100.02px) {
  .ui-monprofil-profils--quatre { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* CINQ, à l'essai sur Mon réseau (30 sept. 2026) : même palier, même repli. */
  .ui-monprofil-profils--cinq { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* L'ALBUM de la colonne droite : des vignettes carrées, trois par rangée.
   Carrées et non au format des photos : ce qu'on regarde là est une trace de
   ce qu'on fait, pas un contenu à lire — une grille régulière se parcourt
   d'un coup d'œil, une mosaïque de formats se déchiffre. */
.ui-monprofil-album {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

.ui-monprofil-vignette {
  display: block;
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.ui-monprofil-vignette img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.2s ease;
}

.ui-monprofil-vignette:hover img,
.ui-monprofil-vignette:focus-visible img { transform: scale(1.06); }

/* ---- LA FICHE, LES INTÉRÊTS ET LES PHOTOGRAPHIES ----
   Ce que la bio ne dit pas et qu'on vient chercher : ce qu'elle cherche, où
   elle vit, ce qu'elle fait. Une liste de définition et non un tableau — ce
   sont des paires terme/valeur, et le balisage doit le dire.
   Deux colonnes tant qu'il y a la place : cinq lignes empilées sur toute la
   largeur donneraient une facture. ---- */
/* UNE CARTE PAR INFORMATION, deux par rangée. La liste n'est plus qu'une
   grille : ce sont les lignes qui portent le cadre.
   Cinq informations dans une seule carte formaient un tableau, et un tableau
   se consulte ; cinq cartes se regardent. Chacune tient une question et sa
   réponse, ce qui est l'unité de lecture réelle — on cherche « a-t-elle des
   enfants », pas « la fiche ». */
/* UN SEUL ÉCART ENTRE CARTES, dans les deux sens : --space-4. Les cartes
   d'informations et les photographies étaient à --space-3 entre elles et à
   --space-4 avec le bloc suivant ; deux valeurs voisines dans une même page
   ne se lisent pas comme une hiérarchie, elles se lisent comme un défaut. */
/* L'ÉCART ENTRE LES BLOCS DE LA FICHE : 24 + 20 = 44px, et il est posé EN
   HAUT de chaque bloc, jamais en bas. La colonne est une SUITE DE SUJETS — qui
   je suis, deux photographies, mes informations, une citation, mes centres
   d'intérêt — et non un texte continu : à 24px ils se touchaient et la page se
   lisait comme un seul pavé.
   ÉCARTÉ, ET C'EST LE PIÈGE À CONNAÎTRE : les mêmes 20px posés en
   « margin-bottom ». Deux marges verticales de frères voisins FUSIONNENT, elles
   ne s'ajoutent pas — 20 en bas contre 24 en haut donnaient 24, c'est-à-dire
   exactement ce qu'on avait avant. Mesuré à l'écran : sept écarts à 24px alors
   que les cinq blocs annonçaient bien leurs 20px de marge basse.
   Tout l'écart tient donc d'un seul côté. */
.ui-monprofil-fiche {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin: var(--space-5) 0 0;
}

/* L'intitulé AU-DESSUS de la valeur, et non à côté. Dans une carte étroite,
   deux colonnes auraient rendu la valeur à une largeur où « Deux, en garde
   alternée » se coupe en trois lignes. Empilés, l'intitulé annonce et la
   valeur répond — et toutes les valeurs commencent alors sur la même
   verticale de leur colonne, ce qui était l'objet du réglage précédent. */
.ui-monprofil-fiche-ligne {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-3) var(--space-4);
}

.ui-monprofil-fiche dt {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: 400;
}

.ui-monprofil-fiche dt .material-symbols-outlined {
  font-size: var(--icon-md);
  color: var(--color-titre);
}

.ui-monprofil-fiche dd {
  margin: var(--space-1) 0 0;
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: 600;
}

/* Sélecteur d'ENFANT DIRECT pour la pastille : « .ui-monprofil-tags span »
   attraperait aussi le pictogramme, qui deviendrait à son tour un conteneur
   flex — et le nom de glyphe cesserait de se replier en ligature. On lirait
   « sports_tennis » en toutes lettres. Déjà rencontré deux fois. */
.ui-monprofil-tags > span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.ui-monprofil-tags > span > .material-symbols-outlined {
  font-size: var(--icon-sm);
  color: var(--color-titre);
}

/* LA CARTE DE PRÉSENTATION et celle des centres d'intérêt sont le MÊME objet :
   un bloc de fond blanc, bordé, coiffé d'un intitulé. Une seule règle les tient
   — deux règles jumelles se seraient désaccordées à la première retouche. */
.ui-monprofil-presentation,
.ui-monprofil-interets {
  margin-top: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-4);
}

/* La présentation seule remonte de 15px : elle suit la bio, qui dit déjà la
   même voix — l'écart d'une carte à l'autre y serait une rupture, alors qu'il
   sépare légitimement les blocs suivants, qui changent de sujet. */
.ui-monprofil-presentation {
  /* 24px de gouttière - 15px - 20px remontés + 10px + 10px redescendus. La
     somme est écrite, pas le total : le jour où l'un des termes bouge, on sait
     lequel. */
  margin-top: var(--space-2);
  /* PAS DE MARGE BASSE ICI : voir l'écart des blocs, porté par « margin-top »
     un peu plus bas. */
}

/* TOUTE LA LARGEUR DE LA CARTE. La mesure de lecture de 68 signes qui tenait
   ici laissait, sur dix colonnes, un blanc à droite que rien ne venait
   occuper — la carte paraissait à moitié remplie. Entre une ligne un peu
   longue et une carte trouée, c'est la carte pleine qui gagne : le cadre fait
   déjà le travail que la mesure faisait, il ramène l'œil à la ligne. */
.ui-monprofil-presentation-texte {
  margin: 0 0 var(--space-3);
  color: var(--text);
  font-size: var(--fs-base);
  line-height: 1.6;
}

.ui-monprofil-presentation-texte:last-child {
  margin-bottom: 0;
}

.ui-monprofil-sous-titre {
  margin: 0 0 var(--space-3);
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: 600;
}

/* ---- LES CARTES DE CITATION ----
   Une question, une réponse. C'est ce qui fait parler quelqu'un mieux qu'une
   bio : on répond à une question, on se décrit rarement bien.
   Elles sont RÉPARTIES entre les photographies et les blocs de texte, pour la
   même raison qu'elles — le profil se parcourt comme une page, pas comme un
   formulaire, et l'alternance est ce qui donne envie de continuer.
   La réponse est en grand et sans graisse : c'est une parole, elle n'a pas à
   crier. Le guillemet est décoratif, d'où « aria-hidden » dans le gabarit. */
.ui-monprofil-citation {
  position: relative;
  margin: var(--space-5) 0 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-4);
}

.ui-monprofil-citation-marque {
  position: absolute;
  top: var(--space-3);
  right: var(--space-4);
  font-size: var(--icon-2xl);
  color: var(--color-brand-pink-soft);
  line-height: 1;
}

.ui-monprofil-citation-question {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: 600;
}

/* LA RÉPONSE SEULE est centrée et en italique — pas la question qui la coiffe.
   La question est une étiquette : elle appartient à la mécanique de la page et
   se range sur la même verticale que tous les autres intitulés de carte. La
   réponse, elle, est une parole ; le centrage et l'italique la sortent de la
   colonne de lecture et disent qu'on change de registre. Centrer les deux
   aurait remis l'étiquette au même rang que la parole. */
.ui-monprofil-citation-reponse {
  position: relative;
  max-width: 46ch;
  margin: 0 auto;
  font-style: italic;
  text-align: center;
  color: var(--color-titre);
  font-size: var(--fs-h5);
  line-height: 1.45;
}

/* LES PHOTOGRAPHIES SONT RÉPARTIES, pas rassemblées en planche. Une planche se
   parcourt d'un coup d'œil et se compare ; réparties entre la bio, la fiche et
   les centres d'intérêt, elles se rencontrent au fil de la lecture — ce qui
   est exactement ce qu'on demande à un profil.
   Deux par rangée, en portrait : c'est le format des photographies de profil,
   et deux côte à côte tiennent dans la colonne sans devenir des vignettes. */
/* ══════════ LA PAGE PHOTOS ══════════
   DEUX GRILLES DE DENSITÉS DIFFÉRENTES, et c'est tout le sujet de la page.
   Les six emplacements de la FICHE vont par trois : ce sont les photos qu'on
   choisit pour se présenter, elles se regardent une par une. Les photos
   PUBLIÉES vont par six : on ne les choisit pas, on les parcourt, et une
   planche contact se lit d'un balayage.
   Leur donner la même densité aurait effacé la différence entre « ce que je
   montre de moi » et « ce que j'ai posté ». */
.ui-monprofil-zones {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

/* LE PAVÉ D'APPEL d'une zone vide. Filet TIRETÉ : le pointillé est la
   convention de ce qui n'est pas encore là, et il évite d'avoir à écrire
   « emplacement libre » sous chaque case.
   Il porte la même proportion et le même rayon qu'une photo, sans quoi la
   grille se déformerait à mesure qu'on la remplit. */
/* ── LES CARTES VIDES D'UNE LISTE ────────────────────────────────────────
   Le même pointillé pétrole et le même voile que les emplacements photo
   libres (.ui-monprofil-zone-vide, juste en dessous) : « pas encore rempli »
   se dit d'une seule façon sur tout le site. La hauteur est celle d'une
   carte profil — un carré de photo et son pied — pour que la grille garde
   sa forme, et le pied de page sa place. */
.ui-liste-vide {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  height: 100%;
  min-height: 380px;
  border-radius: var(--radius);
  outline: 1px dashed var(--color-primary);
  outline-offset: -1px;
  background: var(--teinte-neutre);
  padding: var(--space-4);
  color: var(--color-titre);
  text-align: center;
}

.ui-liste-vide > .material-symbols-outlined { font-size: var(--icon-xl); color: var(--color-primary); }

.ui-liste-vide-titre {
  margin: 0;
  font-size: var(--fs-h6);
  font-weight: var(--fw-semibold);
}

.ui-liste-vide-texte {
  margin: 0 0 var(--space-2);
  max-width: 30ch;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

/* Les pavés suivants se retirent d'un cran : ils tiennent la place, le
   premier parle. */
.ui-liste-vide--muet { opacity: 0.55; }
.ui-liste-vide--muet > .material-symbols-outlined { font-size: var(--icon-xl); }

/* Au téléphone, une seule colonne : un pavé suffit à dire la chose. */
@media (width < 576px) {
  .ui-liste-grille .ui-grid-item:has(.ui-liste-vide--muet) { display: none; }
  .ui-liste-vide { min-height: 260px; }
}

/* LE PAVÉ VIDE PORTE LE MÊME CADRE, en transparent : sans lui sa boîte
   intérieure serait plus grande que celle des photos voisines de 16px, et le
   pointillé ne tomberait pas au même endroit que le bord d'un tirage. */
.ui-monprofil-zone-vide {
  border: var(--cadre-photo) solid transparent;
  background-clip: padding-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  aspect-ratio: 1;
  /* L'ÉTAT DE REPOS EST CELUI QUI ÉTAIT AU SURVOL — filet pétrole, fond voilé,
     encre de titre. L'inverse était l'usage : un pavé presque effacé, qui ne se
     révélait qu'une fois la souris dessus. Mais un emplacement libre n'est pas
     une commande secondaire qu'on découvre en passant : c'est ce que la page
     vient dire — il reste de la place, et voilà où. Il doit se voir AVANT
     qu'on le cherche.
     Le pointillé reste, lui : c'est ce qui dit « pas encore rempli », et le
     contraste ne change rien à cette information-là. */
  outline: 1px dashed var(--color-primary);
  outline-offset: calc(var(--cadre-photo) * -1);
  border-radius: var(--radius-md);
  background: var(--teinte-neutre);
  color: var(--color-titre);
  font-size: var(--fs-sm);
  transition: background 0.15s ease, outline-color 0.15s ease, color 0.15s ease;
}

/* Le survol appuie d'un cran — il fallait bien qu'il reste quelque chose à
   répondre au pointeur une fois le repos monté à son niveau. */
.ui-monprofil-zone-vide:hover,
.ui-monprofil-zone-vide:focus-visible {
  background: var(--teinte-neutre-forte);
}

.ui-monprofil-zone-vide > .material-symbols-outlined { font-size: var(--icon-lg); }

/* MÊME GOUTTIÈRE QUE PARTOUT : 24px, la valeur de --bs-gutter-x que Bootstrap
   pose entre deux cartes, et celle qu'emploient les six emplacements juste
   au-dessus. Elle était à 16 ici : les deux grilles de la même page ne
   respiraient pas pareil, et l'écart visible entre deux images — gouttière plus
   les deux cadres blancs de 8 — passait de 40 en haut à 32 en bas.
   Une page dont les grilles ne partagent pas leur pas se lit comme deux pages
   collées l'une sous l'autre. */
/* ── L'INCLINAISON D'ALBUM ────────────────────────────────────────────────
   Chaque tirage est posé de travers de quelques dixièmes de degré, comme une
   photographie glissée dans un album sans être recollée. Avec le cadre blanc,
   c'est ce qui la fait lire comme un OBJET posé sur la page plutôt que comme
   une image détourée dans une case.

   L'ALÉATOIRE EST FEINT, et c'est voulu. Un tirage au sort demanderait du
   JavaScript, donc des photos droites tant que le script n'a pas tourné, et un
   album qui se redresse autrement à chaque rechargement. Cinq angles en cycle
   « nth-child » donnent le même désordre, une fois pour toutes.

   CINQ, ET PAS SIX NI TROIS. Le cycle doit être PREMIER avec le nombre de
   colonnes — trois en haut, six en bas — sans quoi la même inclinaison
   retomberait dans la même colonne à chaque rangée, et l'œil y verrait aussitôt
   la règle. Cinq n'a de diviseur commun ni avec trois ni avec six : sur les
   deux grilles, aucune colonne ne répète son angle.

   Les angles restent sous 2° : au-delà, le coin d'un tirage entre dans la
   gouttière de 24px et deux voisins se chevauchent. Mesuré sur la plus grande
   case, 330px : 1,8° déplace son coin de 5px.

   LES TROIS PLANCHES PENCHENT — la bande de deux de la fiche, les six
   emplacements et la planche contact. C'est le même geste partout : des
   tirages posés, pas des vignettes alignées. Dans la bande de la fiche, une
   seule des deux penche ; voir plus bas, à côté des tailles.

   LES CASES VIDES NE S'INCLINENT PAS. Un emplacement libre n'est pas un tirage
   posé de travers, c'est une place qui attend — et son pointillé bien d'aplomb
   le dit mieux que n'importe quel libellé. */
.ui-monprofil-zones > .ui-monprofil-photo-case,
.ui-monprofil-photos > .ui-monprofil-photo-case,
.ui-monprofil-galerie-case {
  box-shadow: var(--shadow-md);
  /* L'ANGLE PASSE PAR UNE VARIABLE, et le « transform » est écrit une seule
     fois. Les cinq règles qui suivent ne posent plus qu'une valeur : remettre
     un tirage d'aplomb devient « --inclinaison: 0deg » au lieu d'un
     « transform » qui écrase le précédent — et qui aurait obligé à recopier
     l'angle des voisins pour le leur rendre. */
  transform: rotate(var(--inclinaison, 0deg));
  /* LE RAYON SUR LA CASE, et pas seulement sur l'image qu'elle porte : une
     ombre suit le bord de SON élément. La case étant carrée, l'ombre dessinait
     quatre angles droits autour d'une photographie arrondie — invisible tant
     qu'un cadre blanc de 8px s'interposait, net dès qu'on l'a retiré. */
  border-radius: var(--radius-md);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.ui-monprofil-zones > .ui-monprofil-photo-case:nth-child(5n+1),
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(5n+1),
.ui-monprofil-galerie-case:nth-child(5n+1) { --inclinaison: -1.6deg; }

.ui-monprofil-zones > .ui-monprofil-photo-case:nth-child(5n+2),
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(5n+2),
.ui-monprofil-galerie-case:nth-child(5n+2) { --inclinaison: 1.1deg; }

.ui-monprofil-zones > .ui-monprofil-photo-case:nth-child(5n+3),
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(5n+3),
.ui-monprofil-galerie-case:nth-child(5n+3) { --inclinaison: -0.7deg; }

.ui-monprofil-zones > .ui-monprofil-photo-case:nth-child(5n+4),
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(5n+4),
.ui-monprofil-galerie-case:nth-child(5n+4) { --inclinaison: 1.8deg; }

.ui-monprofil-zones > .ui-monprofil-photo-case:nth-child(5n+5),
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(5n+5),
.ui-monprofil-galerie-case:nth-child(5n+5) { --inclinaison: -1.2deg; }

/* DANS LA PLANCHE CONTACT, UNE PETITE SUR DEUX EST D'APLOMB. Quarante-deux
   tirages penchés à la suite ne font plus un album : la page entière semble de
   travers, et l'œil corrige au lieu de regarder. En alternant, chaque
   photographie inclinée a une voisine droite qui lui sert de repère — c'est
   l'écart qui se voit, pas la pente.
   LES DEUX GRANDES DE LA MOSAÏQUE GARDENT LA LEUR, d'où le « :not » : elles
   tombent en 1 et en 22, et la seconde, de rang pair, se serait redressée avec
   les petites. Ce sont les deux images sur lesquelles on s'arrête ; ce sont
   celles qui doivent le plus ressembler à un tirage posé.
   Écrit APRÈS les cinq angles et portant une pseudo-classe de plus, il
   l'emporte des deux façons. */
.ui-monprofil-galerie-case:nth-child(2n):not(:nth-child(42n+22)) {
  --inclinaison: 0deg;
}

/* UNE SEULE PHOTOGRAPHIE SORT DU RANG PAR BANDE, et elle CHANGE DE CÔTÉ d'une
   bande à l'autre : à gauche, à droite, à gauche. Elle est un peu plus grande
   ET penchée ; l'autre reste à sa taille exacte et d'aplomb.
   POURQUOI UNE SEULE : deux tirages qui sortent du rang chacun de son côté
   font une composition — l'œil lit la paire, pas les images. Un seul qui
   dépasse, à côté d'une photographie posée droit, fait un tirage qu'on vient
   de déplacer. Et le même déséquilibre répété trois fois de suite
   redeviendrait une règle, donc une grille : l'alternance garde l'air du
   hasard.
   L'écart reste faible, 3 % : au-delà, la lecture s'arrête sur la différence
   de taille au lieu de sur les photographies. ÉCARTÉ : la seconde réduite à
   95 %, qui donnait deux formats et rendait la bande bancale.
   LA PROPRIÉTÉ « scale » ET NON UN « transform: scale() » : l'inclinaison est
   déjà dans « transform », et un second transform sur le même élément aurait
   effacé le premier. Les propriétés séparées se composent — et le
   redressement au survol ne touche que la rotation, la taille ne bouge pas.
   COMMENT ON COMPTE LES BANDES : par CHAÎNES DE FRÈRES. Chaque bande est un
   conteneur à part, si bien que « nth-child » y repart à un et ne peut pas
   distinguer la première de la troisième ; et « nth-of-type » compterait les
   div voisins, qui ne sont pas tous des bandes. Une chaîne de deux ne
   s'accroche qu'à partir de la deuxième bande, une chaîne de trois à partir de
   la troisième — et la plus longue l'emporte, portant une classe de plus.
   Une QUATRIÈME bande garderait la disposition de la troisième ; il n'y en a
   que trois, et le jour où il y en aura une de plus, c'est ici qu'on ajoutera
   un maillon.
   Cela ne vaut QUE POUR LA FICHE : les six emplacements et la planche contact
   sont des grilles qu'on parcourt, où des tailles inégales feraient croire à
   un défaut de chargement. */
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(1) { scale: 1.03; }
.ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(2) { scale: 1; --inclinaison: 0deg; }

.ui-monprofil-photos ~ .ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(1) { scale: 1; --inclinaison: 0deg; }
.ui-monprofil-photos ~ .ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(2) { scale: 1.03; --inclinaison: 1.1deg; }

.ui-monprofil-photos ~ .ui-monprofil-photos ~ .ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(1) { scale: 1.03; --inclinaison: -1.6deg; }
.ui-monprofil-photos ~ .ui-monprofil-photos ~ .ui-monprofil-photos > .ui-monprofil-photo-case:nth-child(2) { scale: 1; --inclinaison: 0deg; }

/* LES CARTES D'ÉVÈNEMENT ET DE COMMUNAUTÉ NE PENCHENT PAS. Essayé sur les
   onglets « Mes évènements » et « Mes communautés », écarté à l'œil : une
   photographie penchée est un tirage posé sur une table, une CARTE penchée est
   une carte mal imprimée. Ce qui tient debout dans un album — le désordre —
   se lit comme un défaut dès qu'il y a du texte à lire dedans.
   L'inclinaison reste donc l'affaire des photographies seules. */

/* AU SURVOL, LE TIRAGE SE REDRESSE et se soulève. C'est le geste qu'on a
   devant un album : on remet la photo d'aplomb pour la regarder. Il remplace
   l'agrandissement de l'image, qui tirait dans l'autre sens — on ne zoome pas
   une photographie qu'on vient de redresser.
   « !important » nulle part : la règle est écrite APRÈS les cinq angles et
   pèse une classe de plus, elle l'emporte des deux façons. */
.ui-monprofil-zones > .ui-monprofil-photo-case:hover,
.ui-monprofil-photos > .ui-monprofil-photo-case:hover,
.ui-monprofil-galerie-case:hover,
.ui-monprofil-galerie-case:focus-visible {
  transform: rotate(0) translateY(-3px);
  box-shadow: var(--shadow-md);
}

.ui-monprofil-galerie {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--space-4);
  /* « dense » : les vignettes REMPLISSENT les trous laissés par les grandes.
     Sans lui, la grande photo posée à droite laisserait quatre cases vides à
     sa gauche — le flux normal ne revient jamais en arrière. */
  grid-auto-flow: dense;
}

/* ── LA MOSAÏQUE, PAR COUCHES DE DEUX RANGÉES ─────────────────────────────
   Quatre couches se succèdent, puis le motif recommence :
     A. une grande photo à GAUCHE sur 2 colonnes et 2 rangées, huit vignettes
        autour                                            → 9 photos, 2 rangées
     B. une double rangée de vignettes                    → 12 photos, 2 rangées
     C. une grande photo à DROITE, huit vignettes autour  → 9 photos, 2 rangées
     D. une double rangée de vignettes                    → 12 photos, 2 rangées
   Soit un cycle de 42 photos sur huit rangées — d'où les « 42n ».

   POURQUOI SI LONG, ET C'EST LE POINT. Un motif court se voit : deux grandes
   photos qui reviennent toutes les quatre rangées, toujours du même côté, et
   l'œil a compris la règle. Quarante-deux photos, avec les deux grandes
   décalées de vingt et une l'une de l'autre et jamais du même côté, donnent une
   page qui paraît composée à la main. C'est très exactement ce qu'on demande à
   une mise en page d'album : qu'on y sente une intention sans pouvoir la
   formuler.

   CE QUE ÇA COÛTE, et il faut le savoir : la chronologie stricte cède sur les
   couches à grande photo. Les vignettes qui comblent la gauche d'une grande
   photo posée à DROITE sont plus récentes qu'elle et se lisent pourtant avant.
   L'ordre du document reste juste — c'est celui que suit un lecteur d'écran —,
   seul le parcours de l'œil s'écarte, et de huit vignettes au plus. */
.ui-monprofil-galerie-case:nth-child(42n+1) {
  grid-column: 1 / span 2;
  grid-row: span 2;
}

.ui-monprofil-galerie-case:nth-child(42n+22) {
  grid-column: 5 / span 2;
  grid-row: span 2;
}

/* Le bouton d'appoint, CENTRÉ sous la planche. Une planche contact se lit du
   centre vers les bords, et le geste qui la prolonge se pose là où le regard
   revient. Aligné à gauche, il se serait rangé avec les titres, c'est-à-dire
   avec ce qui ANNONCE une section plutôt qu'avec ce qui la continue. */
.ui-monprofil-plus {
  display: flex;
  justify-content: center;
  margin-top: var(--space-5);
}

/* Sous 1200px la grille tombe à quatre colonnes, sous 720 à trois : une grande
   photo y prendrait la moitié ou les deux tiers de la largeur, ce qui n'est
   plus une mosaïque mais une bannière. Le motif ne vaut qu'à six colonnes. */
@media (width <= 1200px) {
  .ui-monprofil-galerie-case:nth-child(42n+1),
  .ui-monprofil-galerie-case:nth-child(42n+22) {
    grid-column: auto;
    grid-row: auto;
  }
}

.ui-monprofil-galerie-case {
  display: block;
  aspect-ratio: 1;
  border-radius: var(--radius-md);
  overflow: hidden;
}

/* PAS DE CADRE BLANC SUR LES DEUX PLANCHES DE « MES PHOTOS ». Le cadre de 8px
   reste ce qui distingue un TIRAGE UNIQUE — le portrait, la bannière, les deux
   photographies de la fiche : une image qu'on a choisie et posée là. Répété
   quarante-deux fois dans une planche contact, il ne distingue plus rien ; il
   ajoute seulement une trame blanche entre des images déjà séparées par 24px
   de gouttière, et mange 16px de chaque case.
   L'effet d'album ne tient pas au cadre mais à l'INCLINAISON et à l'ombre
   portée, qui restent.
   Le pavé vide perd le sien en même temps, et pour la même raison qu'il
   l'avait : sa boîte doit faire exactement celle d'une photographie voisine,
   cadre ou pas. Son pointillé se recale d'autant — à -8px il flottait
   maintenant au milieu du vide. */
.ui-monprofil-zone-vide { border: 0; }

.ui-monprofil-zone-vide { outline-offset: -1px; }

.ui-monprofil-galerie-case > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.25s ease;
}



/* LE REPLI : quatre puis deux colonnes pour la planche, deux puis une pour la
   fiche. Six vignettes sur un téléphone donneraient des timbres-poste. */
@media (width <= 1200px) {
  .ui-monprofil-galerie { grid-template-columns: repeat(4, 1fr); }
}

@media (width <= 720px) {
  .ui-monprofil-zones { grid-template-columns: repeat(2, 1fr); }
  .ui-monprofil-galerie { grid-template-columns: repeat(3, 1fr); }
}

.ui-monprofil-photos {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  margin: var(--space-5) 0 0;
}

/* CARRÉES. On ne sait pas ce que l'utilisateur téléversera — un portrait pris
   à la verticale, une scène à l'horizontale — et une grille qui mélange les
   deux se lit comme une page mal montée. Le carré est le seul format qui
   accueille les deux sans privilégier l'un : il se recadre à l'envoi, et la
   page n'a plus à s'adapter à ce qu'elle reçoit.
   « object-fit: cover » double la sécurité : si une image non carrée arrive
   malgré tout, elle est recadrée à l'affichage plutôt que déformée.
   Le 4/5 qui tenait ici supposait des photographies verticales, ce que rien ne
   garantissait ; le 16/10 de la variante pleine largeur supposait l'inverse. */
/* LE CADRE BLANC DU PORTRAIT, sur toutes les photographies de l'espace : même
   jeton « --cadre-photo », donc même épaisseur, et le jour où le portrait
   change la page suit. C'est ce qui fait lire une photographie comme un tirage
   posé sur la page plutôt que comme une image détourée dans une grille.
   « box-sizing: border-box » est global : le cadre rentre DANS la case, la
   grille garde donc exactement les mêmes mesures qu'avant. */
/* PAS DE CADRE BLANC : ni sur la bande de deux de la fiche, ni sur les six
   emplacements de « Mes photos ». Le cadre de 8px reste ce qui distingue un
   TIRAGE UNIQUE — le portrait et la bannière —, et cesse de distinguer quoi
   que ce soit dès qu'on le pose sur toutes les images d'une page. */
.ui-monprofil-photo {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius-md);
  display: block;
}

@media (width <= 640px) {
  .ui-monprofil-photos,
  .ui-monprofil-fiche { grid-template-columns: 1fr; }
}

.ui-monprofil-titre {
  margin: 0 0 var(--space-4);
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: 600;
}

/* L'agenda du rail : molecules/agenda.snip, son dessin est dans ui-molecules.css. */

/* Sous 900px le rail passe AU-DESSUS du contenu, en bande horizontale qui
   défile — même repli que celui du fil, et pour la même raison : à cette
   largeur, deux colonnes laisseraient au contenu moins qu'une carte. Les
   onglets y reprennent leur forme de pastille, la seule qui tienne sur une
   ligne. */
@media (width <= 900px) {
  .ui-liste-inner { grid-template-columns: minmax(0, 1fr); }
  .ui-liste-rail { position: static; min-width: 0; }

  .ui-membre-onglets {
    flex-direction: row;
    gap: var(--space-2);
    overflow-x: auto;
    scrollbar-width: none;
  }

  .ui-membre-onglets::-webkit-scrollbar { display: none; }


  .ui-membre-onglet {
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface-card);
    padding: var(--space-2) var(--space-3);
    gap: var(--space-2);
    white-space: nowrap;
  }



  /* En pilules, l'entrée active garde un BORD, qui est ce qui distingue une
     pilule d'une autre à cette taille ; le fond suit la règle partagée et
     n'est donc pas redéclaré ici. */
  .ui-membre-onglet.is-current,
  .ui-membre-onglet.is-current:hover {
    border-color: color-mix(in srgb, var(--color-dark-soft) 12%, transparent);
  }

}

@media (width <= 720px) {
  .ui-liste-filtres { padding: var(--space-3); }
  .ui-liste-champ { flex-basis: 100%; }
  /* Les deux bornes restent CÔTE À CÔTE même à l'étroit : les séparer sur
     deux lignes casserait la paire, et « à » seul sur sa ligne ne veut rien
     dire. */
  .ui-liste-champ--court { flex: 1 1 0; }
  .ui-liste-filtres .ui-btn { width: 100%; }
}

/* ---- Filtres avancés ----
   Un second rang de critères, replié. La rangée du haut porte ce qu'on règle
   à chaque recherche ; ce panneau porte ce qu'on règle une fois. ---- */
.ui-liste-avances-bouton {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  padding: var(--space-2) 0;
  color: var(--color-titre);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 700;
  cursor: pointer;
  transition: color 0.2s ease;
}

/* « Filtrer » d'abord, « Filtres avancés » ensuite. Le premier est ce qu'on
   vient faire ; le second est une porte de côté, qu'on ne prend qu'après
   avoir vu que les quatre champs du haut ne suffisaient pas. Mettre le
   déclencheur avant le bouton d'action revenait à proposer le détour avant
   le chemin.

   La marge automatique reste donc sur « Filtrer » : elle pousse la PAIRE au
   bout de la rangée, et le déclencheur la suit. */

.ui-liste-avances-bouton:hover,
.ui-liste-avances-bouton:focus-visible { color: var(--color-primary-hover); }

.ui-liste-avances-bouton .material-symbols-outlined { font-size: var(--icon-md); }

/* Le chevron dit l'état du panneau. Il tourne sur aria-expanded, que Bootstrap
   tient à jour tout seul : aucune classe à poser, aucun script à écrire. */
.ui-liste-avances-chevron { transition: transform 0.2s ease; }
.ui-liste-avances-bouton[aria-expanded="true"] .ui-liste-avances-chevron { transform: rotate(180deg); }

/* Le panneau prend toute la largeur de la rangée : c'est un élément de flex,
   sans quoi il se glisserait à côté du dernier champ. */
.ui-liste-avances { flex: 0 0 100%; }

.ui-liste-avances-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-3);
  border-top: 1px solid var(--border);
  margin-top: var(--space-3);
  padding-top: var(--space-4);
}

@media (width <= 720px) {
  .ui-liste-avances-grille { grid-template-columns: minmax(0, 1fr); }
}

/* Le déclencheur de « mes critères », à côté de celui des filtres avancés.
   Même forme que lui — un lien pétrole avec pictogramme — parce que ce sont
   deux portes de côté de la même rangée. Il ne prend pas l'apparence d'un
   bouton : ce serait le mettre au niveau de « Filtrer », alors qu'il ne
   filtre rien. */
.ui-liste-criteres-lien {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
  border: 0;
  background: transparent;
  padding: var(--space-2) 0;
  color: var(--color-titre);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 700;
  cursor: pointer;
  transition: color 0.2s ease;
}

.ui-liste-criteres-lien:hover,
.ui-liste-criteres-lien:focus-visible { color: var(--color-primary-hover); }

.ui-liste-criteres-lien .material-symbols-outlined { font-size: var(--icon-md); }

/* Le lien quitte la barre de filtres, sur tous les écrans : l'outil « Mes
   critères » de l'en-tête, à gauche de la cloche, le remplace
   (29 sept. 2026). */
.ui-liste-criteres-lien { display: none; }

/* ══════════════ MESSAGERIE — la colonne centrale de « Mes discussions » ══════
   Elle occupe la place du fil dans la même grille « ui-fil-inner », et pour la
   même raison : c'est ce qu'on est venu faire.
   PAS DE DÉFILEMENT PROPRE. Un panneau à défilement interne aurait donné deux
   barres imbriquées sur une page qui a déjà des rails collants. Le fil de
   messages s'allonge, la PAGE défile, et c'est le champ de saisie qui reste
   sous la main. */
/* UN SEUL GRAND CONTENEUR BLANC — l'en-tête, le fil et le champ de saisie sont
   les trois étages d'une même carte, et non trois cartes empilées. Ils l'ont
   été : chacun avait son fond, son filet et son rayon, ce qui donnait trois
   objets séparés par deux gouttières là où il n'y a qu'une conversation.
   « overflow: hidden » pour que le fil, qui défile, soit recoupé par le rayon
   de la carte au lieu de passer par-dessus ses coins. */
.ui-messagerie {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-card);
  overflow: hidden;
}

/* L'EN-TÊTE BORNE LA COLONNE par le haut. Il a été collant un moment ; il ne
   l'est plus, parce que la page tient dans l'écran et que c'est le fil de
   messages qui défile sous lui. Un élément collant dans un conteneur qui ne
   défile pas ne colle à rien. */
.ui-messagerie-entete {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* Un simple FILET pour borner l'étage, plus de carte à lui : il n'est plus un
     objet posé au-dessus de la conversation, il en est la tête. */
  border-bottom: 1px solid var(--border);
  padding: var(--space-3) var(--space-4);
}

.ui-messagerie-entete > img {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-photo);
  object-fit: cover;
  flex-shrink: 0;
}

.ui-messagerie-identite { display: flex; flex-direction: column; min-width: 0; }

/* Le menu au BOUT de la ligne. Marge automatique plutôt qu'un
   « justify-content: space-between » sur l'en-tête : l'identité et le portrait
   doivent rester collés l'un à l'autre, et c'est le seul élément qui ait à
   fuir vers la droite. */
.ui-messagerie-entete > .ui-menu-actions { margin-inline-start: auto; }

.ui-messagerie-nom {
  margin: 0;
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
}

.ui-messagerie-nom a { color: inherit; }
.ui-messagerie-nom a:hover { text-decoration: underline; }

.ui-messagerie-etat {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

/* LE SEUL ÉLÉMENT QUI DÉFILE de la page. « min-height: 0 » est indispensable :
   un enfant de conteneur flexible refuse par défaut de descendre sous la
   hauteur de son contenu, et sans lui la colonne s'allongeait au lieu de faire
   défiler son fil — c'est toute la page qui reprenait l'ascenseur. */
.ui-messagerie-fil {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-4);
}

/* LA DATE, centrée sur un filet. Le trait passe derrière le libellé et la
   couleur de page le masque de part et d'autre — un pseudo-élément de chaque
   côté aurait demandé de connaître la largeur du texte. Le fond du libellé
   reprend celui de la page, qui est un DÉGRADÉ : d'où « transparent » et un
   simple écart, plutôt qu'un aplat qui trancherait à mi-hauteur. */
.ui-messagerie-jour {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
}

.ui-messagerie-jour::before,
.ui-messagerie-jour::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* Un fil sans message, une liste sans conversation : une phrase, en gris,
   qui dit quoi faire — pas un vide qui ferait croire à une panne. */
.ui-messagerie-vide {
  margin: var(--space-4) auto;
  max-width: 36ch;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-align: center;
}

.ui-fil-encart .ui-messagerie-vide { margin: var(--space-2) 0; text-align: start; }

/* La conversation FERMÉE (match retiré) : la phrase prend la place du champ,
   au même endroit, avec le même filet — on voit où l'on aurait écrit. */
.ui-messagerie-ferme {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  border-top: 1px solid var(--border);
  padding: var(--space-3) var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

.ui-messagerie-ferme .material-symbols-outlined { font-size: var(--icon-md); }

/* LE CHAMP BORNE LA COLONNE par le bas, et il y est toujours sous la main
   puisque la colonne fait la hauteur de l'écran. Il a été « sticky » le temps
   d'une version, quand la page défilait ; ce n'est plus nécessaire, et un
   collage sans conteneur défilant n'aurait servi qu'à porter une ombre. */
.ui-messagerie-composer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  border-top: 1px solid var(--border);
  padding: var(--space-3) var(--space-4);
}

/* « RÉPONSE À … » : le bandeau passe sur sa propre ligne, au-dessus du champ.
   Un filet pétrole à gauche, comme la citation qu'il deviendra dans la
   bulle — ce qu'on voit en écrivant est ce qu'on verra envoyé. */
.ui-messagerie-composer:has(.ui-messagerie-reponse) { flex-wrap: wrap; }

.ui-messagerie-reponse {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  border-inline-start: 3px solid var(--color-primary);
  border-radius: var(--radius-sm);
  background: var(--surface-douce);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

.ui-messagerie-reponse[hidden] { display: none; }
.ui-messagerie-reponse > .material-symbols-outlined { font-size: var(--icon-md); color: var(--color-primary); }

.ui-messagerie-reponse-texte {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ui-messagerie-reponse-nom { margin-inline-end: var(--space-2); color: var(--color-titre); font-weight: var(--fw-semibold); }

.ui-messagerie-reponse-annuler {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
}

.ui-messagerie-reponse-annuler:hover { background: var(--surface-appuyee); }
.ui-messagerie-reponse-annuler .material-symbols-outlined { font-size: var(--icon-md); }

.ui-messagerie-champ {
  flex: 1;
  min-width: 0;
  border: 0;
  /* 22px et non une pilule : sur plusieurs lignes, un arrondi complet
     rognait le texte des coins. Sur une ligne, l'effet est le même. */
  border-radius: var(--radius-md);
  background: var(--surface-douce);
  padding: var(--space-2) var(--space-4);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
  line-height: 1.45;
  resize: none;
  max-height: calc(6 * 1.45em + 1.375rem);
  overflow-y: auto;
}

/* Les boutons restent en bas quand le champ grandit. */
.ui-messagerie-composer:has(textarea) { align-items: flex-end; }

.ui-messagerie-champ::placeholder { color: var(--text-muted); }
.ui-messagerie-champ:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* Les deux outils entre le champ et l'envoi : ils enrichissent ce qu'on écrit,
   ils ne le concluent pas. */
.ui-messagerie-outils {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-shrink: 0;
}

/* « ui-membre-rond » porte tout le dessin ; il ne reste ici que ce qu'un
   BOUTON doit défaire — le socle en donne un filet et un rembourrage que les
   ronds de l'en-tête, qui sont des liens, n'ont jamais eus. */
.ui-messagerie-outil {
  border: 0;
  padding: 0;
}

.ui-messagerie-outil > .material-symbols-outlined { font-size: var(--icon-md); }

.ui-messagerie-envoi { flex-shrink: 0; }

/* Sous 1200px la colonne de droite s'en va, comme sur le fil : la liste des
   conversations est utile, elle n'est pas nécessaire tant qu'on en lit une. Le
   retour à la liste passe alors par le rond de l'en-tête. */
@media (width <= 1200px) {
  .ui-fil-inner > .ui-messagerie { grid-column: 2; }
}

/* ══════════ PAGE CALÉE SUR L'ÉCRAN ══════════
   « Mes discussions » n'a pas de pied de page et ne défile pas : la fenêtre est
   découpée une fois pour toutes en filet + en-tête + trois colonnes, et seul le
   fil de messages défile, à l'intérieur de sa colonne.

   AUCUN NOMBRE MAGIQUE. La tentation est d'écrire « height: calc(100dvh - 68px) »
   sur le contenu ; ce 68 est la hauteur de l'en-tête, il se périme au premier
   réglage, et il faudrait y ajouter les 6px du filet. Le corps de page devient
   donc une COLONNE FLEXIBLE : le filet et l'en-tête prennent ce qu'il leur
   faut, « main » prend le reste. Rien à recalculer si l'en-tête change.

   « 100dvh » et non « 100vh » : sur mobile, « vh » compte la barre d'adresse
   même quand elle est repliée, et le champ de saisie se serait retrouvé sous le
   bord de l'écran. « dvh » suit la hauteur réellement visible.

   Chaque « min-height: 0 » de cette cascade est nécessaire : un enfant de
   conteneur flexible refuse par défaut de descendre sous la hauteur de son
   contenu. Il en manque un et c'est la page entière qui reprend l'ascenseur. */
.ui-page-membre--ecran {
  display: flex;
  flex-direction: column;
  /* « height » ET NON « min-height », et c'est tout le sujet. Avec un minimum,
     le corps est autorisé à GRANDIR : la chaîne de « flex: 1 » n'a alors aucune
     hauteur définie à répartir, chaque enfant reprend la taille de son contenu
     et le fil de messages cesse de défiler — mesuré 1197px de page pour une
     fenêtre de 950. Une hauteur définie donne à la cascade quelque chose à
     partager.
     « overflow: hidden » sur le corps se propage à la fenêtre, ce qui est
     précisément ce qu'on veut ici : la page ne défile pas, seul le fil le
     fait. */
  height: 100dvh;
  overflow: hidden;
}

.ui-page-membre--ecran > main {
  flex: 1;
  min-height: 0;
  display: flex;
}

.ui-fil--ecran {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* PLUS DE REMBOURRAGE EN BAS. « ui-fil » en pose 64px, qui sont justes sur
     une page qui défile — c'est ce qui sépare le dernier billet du pied de
     page. Ici il n'y a pas de pied, et ces 64px laissaient une bande de fond
     vide sous la carte : le champ de saisie s'arrêtait à 886 dans une fenêtre
     de 950, alors qu'on veut précisément qu'il ferme l'écran.
     Le haut garde les siens : c'est ce qui détache la carte de l'en-tête. */
  padding-block: var(--space-5) var(--space-4);
}

/* Les coins du bas ont été droits, la carte touchant le bord de la fenêtre.
   Depuis le fond texturé (1er oct. 2026), la conversation et la liste sont
   des cartes posées sur le fond, à l'arrondi standard sur leurs quatre coins
   — le rembourrage du bas de .ui-fil--ecran les détache de la fenêtre. */
.ui-fil--ecran .ui-messagerie { border-radius: var(--radius-md); }

.ui-fil--ecran > .ui-fil-inner {
  flex: 1;
  min-height: 0;
  /* Les colonnes prennent toute la hauteur au lieu de se caler en haut : la
     conversation doit remplir la sienne pour que son champ tombe en bas. */
  align-items: stretch;
}

/* Les rails ne collent plus : rien ne défile derrière eux. Ils défilent
   EUX-MÊMES si leur contenu déborde — une liste de conversations peut être
   longue, le rail de gauche ne le sera jamais. */
.ui-fil--ecran .ui-fil-rail {
  position: static;
  min-height: 0;
  overflow-y: auto;
}

/* =========================================================================
   TUNNEL D'INSCRIPTION — en-tête épuré et étape
   Un châssis à part, et c'est le sujet : la seule chose qu'on puisse faire sur
   ces cinq pages est d'avancer ou de reculer d'une étape. Pas de navigation,
   pas de pied de page, pas de mention cliquable. Tout ce qui est écrit ici
   existe pour retirer des portes, non pour en poser.
   ========================================================================= */

/* ---- En-tête ----
   Une bande blanche, la marque centrée, un filet, puis la bande des étapes sur
   fond doux. Pas de --header-h : cet en-tête ne réserve la place de rien —
   ni barre utilitaire, ni actions, ni bouton de menu — et la hauteur de la
   barre du site l'aurait fait paraître vide plutôt que sobre. */
.ui-header-inscription {
  position: relative;
  z-index: 30;
  background: var(--white);
  border-bottom: 1px solid var(--border);
}

.ui-header-inscription-inner {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 84px;
  padding-block: var(--space-3);
}

/* Un <span> et non un <a> : voir organismes/header-inscription.snip. Le
   curseur reste celui du texte — rien ici ne doit avoir l'air cliquable. */
.ui-header-inscription-marque {
  display: block;
  width: 216px;                         /* logo Vibe2Meet ×1,45 puis −10 % (3 oct. 2026) */
}

.ui-header-inscription-marque img {
  width: 100%;
  height: auto;
  display: block;
}

/* La bande des étapes : fond doux et filet haut, pour qu'elle se lise comme un
   bandeau d'information et non comme une seconde barre de navigation. */
.ui-header-inscription-etapes {
  border-top: 1px solid var(--border-soft);
  background: var(--surface-douce);
}

/* Le fil est centré dans la mesure du formulaire, pas étalé sur toute la
   largeur : à 1280px, cinq étapes réparties d'un bord à l'autre s'éloignaient
   tellement les unes des autres qu'on n'y lisait plus une suite.
   La mesure est posée sur un CONTENEUR propre et non sur .ui-stepper lui-même :
   une classe se définit au niveau où elle est employée, et le stepper est une
   molécule — le contrôle « couches CSS » l'a signalé, à juste titre. */
.ui-header-inscription-fil {
  /* 56rem = 896px : la mesure des cinq étapes, libellés compris (869px) plus
     de quoi respirer. Étalé sur toute la largeur d'un écran de 1280, le fil
     éloignait tellement les étapes les unes des autres qu'on n'y lisait plus
     une suite ; plus étroit, les libellés se chevauchaient. */
  max-width: 56rem;
  margin-inline: auto;
}

@media (width <= 640px) {
  .ui-header-inscription-inner { min-height: 68px; }
  .ui-header-inscription-marque { width: 132px; }
}

/* ---- L'étape ----
   La section porte le fond de page ; la carte porte le formulaire.

   LE DÉGRADÉ, et non l'aplat ivoire du départ. C'est celui du site — le hero
   de l'accueil et les sections en « ui-fond-degrade » l'emploient déjà — mais
   il PART d'ici de --surface-douce et non du blanc, parce qu'il ne commence
   pas la page : la bande des étapes est juste au-dessus, et elle porte
   exactement cette valeur. Les deux se rejoignent donc sans couture, et tout
   ce qui est sous la marque se fonce d'un seul mouvement. Le faire partir du
   blanc aurait posé une marche à l'endroit précis où l'œil passe du fil des
   étapes au formulaire.

   Il doit ARRIVER en bas de l'écran même quand l'étape est courte : la
   dernière tient dans moins d'une hauteur de fenêtre, et le dégradé s'y
   terminait à mi-page en laissant le fond nu en dessous. D'où la hauteur
   minimale — la fenêtre moins l'en-tête, dont la mesure est écrite ici en
   toutes lettres parce qu'elle est celle de deux bandes fixes. */
.ui-inscription-section {
  /* 84 (marque) + 1 (filet) + 68 (étapes) + 1 (filet) — les quatre mesures
     sont posées trente lignes plus haut, dans ce même bloc. Le couplage est
     donc local et visible ; changer la hauteur d'une bande demande de changer
     ce nombre, et c'est écrit là où on la change. */
  --entete-h: 154px;
  padding-block: var(--space-6) var(--space-7);
  min-height: calc(100vh - var(--entete-h));
  /* dvh sur les mobiles, dont la barre d'outils rentre et sort : 100vh y vaut
     la hauteur BARRE SORTIE, et le dégradé dépassait donc toujours d'un cran
     sous le pli. La ligne vh reste, pour les agents qui ignorent dvh. */
  min-height: calc(100dvh - var(--entete-h));
  background: linear-gradient(to bottom, var(--surface-douce), var(--section-teinte-7));
}

/* 40rem, soit 640px : la mesure de deux champs côte à côte dans une grille de
   champs, gouttière comprise. Plus large, la ligne « Prénom / Nom » s'étirait
   et l'œil perdait le lien entre l'intitulé et sa saisie. */
.ui-inscription-carte {
  max-width: 40rem;
  margin-inline: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  box-shadow: var(--shadow-md);
  padding: var(--space-5);
}

.ui-inscription-entete {
  text-align: center;
  margin-bottom: var(--space-4);
}

/* Le médaillon de réussite, sur la dernière étape seule. Le vert de succès à
   faible saturation : il annonce sans crier, et le titre juste en dessous dit
   déjà la nouvelle. */
.ui-inscription-sceau {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  margin: 0 auto var(--space-3);
  border-radius: 50%;
  background: color-mix(in srgb, var(--color-success) 12%, var(--white));
  color: var(--color-success);
}

.ui-inscription-sceau .material-symbols-outlined { font-size: var(--icon-xl); }

.ui-inscription-titre {
  margin: 0;
  font-size: var(--fs-h4);
  line-height: var(--lh-heading);
  color: var(--color-titre);
}

.ui-inscription-intro {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
}

/* Les blocs se suivent, séparés par un filet et non par du seul blanc : une
   étape qui demande trois choses différentes — une photo, une description, une
   apparence — se lit comme trois questions, pas comme un formulaire long. */
.ui-inscription-bloc + .ui-inscription-bloc {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-soft);
}

.ui-inscription-bloc-titre {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-h6);
  font-weight: var(--fw-bold);
  color: var(--color-titre);
}

.ui-inscription-bloc-intro {
  margin: 0 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* Deux groupes de pastilles dans un même bloc : l'écart entre eux est celui de
   deux champs d'une grille, pour que la page garde un seul rythme vertical. */
.ui-inscription-bloc .ui-pastilles + .ui-pastilles { margin-top: var(--space-3); }

.ui-inscription-lieu {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* ---- Connexion déléguée ----
   Le séparateur porte son libellé au milieu du filet : deux filets et un mot
   entre eux, obtenus sans balise supplémentaire par deux pseudo-éléments. */
.ui-inscription-social {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-soft);
  text-align: center;
}

.ui-inscription-social-mention {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.ui-inscription-social-mention::before,
.ui-inscription-social-mention::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: var(--border);
}

.ui-inscription-social-boutons {
  display: flex;
  gap: var(--space-2);
}

/* Les deux boutons prennent la moitié chacun : ils désignent deux voies
   équivalentes, et une largeur au contenu aurait fait paraître « Facebook »
   plus important que « Google ». */
.ui-inscription-social-bouton {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-width: 0;
}

/* Désactivés, et le dire : opacité réduite, curseur d'interdiction. L'aide
   juste en dessous explique pourquoi, ce qu'un grisé seul ne fait pas. */
.ui-inscription-social-bouton:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.ui-inscription-social-aide {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-caption);
}

/* ---- Cases d'accord ---- */
.ui-inscription-conditions {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-soft);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.ui-inscription-conditions .form-check-label {
  font-size: var(--fs-sm);
  color: var(--text);
}

/* ---- Repères de la dernière étape ----
   Trois lignes, pictogramme à gauche : ce qui attend de l'autre côté. Une
   liste et non une grille de cartes — c'est une énumération, pas un choix. */
.ui-inscription-reperes {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.ui-inscription-repere {
  display: grid;
  grid-template-columns: 28px 1fr;
  grid-template-areas: 'icone titre' 'icone texte';
  column-gap: var(--space-3);
  row-gap: 0.125rem;
  text-align: start;
}

.ui-inscription-repere .material-symbols-outlined {
  grid-area: icone;
  align-self: start;
  font-size: var(--icon-lg);
  color: var(--color-primary);
}

.ui-inscription-repere strong {
  grid-area: titre;
  color: var(--color-titre);
  font-size: var(--fs-sm);
}

.ui-inscription-repere > span:last-child {
  grid-area: texte;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}

/* ---- Actions ----
   « Retour » à gauche, ce qui engage à droite : on lit de gauche à droite, et
   la porte de sortie doit se rencontrer avant l'action. Le « Passer »
   facultatif se glisse entre les deux, en lien de texte — un troisième bouton
   aurait mis trois choix de même poids là où il n'y en a qu'un. */
.ui-inscription-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.ui-inscription-actions .ui-btn-primary { margin-inline-start: auto; }

.ui-inscription-actions .ui-btn-outline .material-symbols-outlined {
  font-size: var(--icon-md);
  margin-inline-end: var(--space-1);
}

.ui-inscription-passer {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.ui-inscription-passer:hover { color: var(--color-primary); }

/* Sans lien, donc sans couleur de lien : c'est une mention, pas une note de
   bas de page qu'on suit. Voir organismes/section-inscription.snip. */
.ui-inscription-mention {
  margin: var(--space-4) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-caption);
  text-align: center;
  line-height: var(--lh-base);
}

/* La bande de marque descend de 84 à 68 sous 640px : l'en-tête perd 16px, et
   la hauteur que la section doit reprendre les récupère.
   Écrit ICI, après la règle de base, et non dans le bloc de l'en-tête plus
   haut : à spécificité égale c'est l'ordre du fichier qui tranche, et la
   déclaration posée avant se faisait écraser par le 154px de la base — le
   dégradé s'arrêtait alors 16px trop haut sur téléphone. */
@media (width <= 640px) {
  .ui-inscription-section { --entete-h: 139px; }
}

@media (width <= 560px) {
  .ui-inscription-section { padding-block: var(--space-4) var(--space-5); }
  .ui-inscription-carte {
    padding: var(--space-4) var(--space-3);
    border-radius: var(--radius-sm);
  }
  .ui-inscription-social-boutons { flex-direction: column; }
  /* Les actions s'empilent, et l'ordre s'inverse : ce qui engage passe en
     haut, sous le pouce, et « Retour » en bas. Sur un écran étroit, deux
     boutons pleine largeur côte à côte tombaient à 140px chacun. */
  .ui-inscription-actions {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: var(--space-2);
  }
  .ui-inscription-actions .ui-btn { width: 100%; justify-content: center; }
  .ui-inscription-actions .ui-btn-primary { margin-inline-start: 0; }
  .ui-inscription-passer { text-align: center; }
}


/* Le fil sans rien à lire — ou rien à lire pour vous : une phrase, pas une
   carte vide. */
.ui-fil-vide {
  margin: 0 0 var(--space-4);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-4);
  color: var(--text-muted);
  text-align: center;
}


/* La suite du fil, sous le dernier billet. Un bouton en filet, centré : il
   ne fait que dire qu'il y a encore à lire. Avec le script, la suite se
   charge seule avant qu'on l'atteigne ; il reste visible le temps du
   chargement, et comme porte de secours sans script. */
.ui-fil-plus {
  display: flex;
  justify-content: center;
  margin: var(--space-3) 0 var(--space-5);
}

.ui-fil-plus[aria-busy="true"] .ui-btn { opacity: 0.6; pointer-events: none; }


/* LES TROIS PAGES D'UNE SORTIE — À propos, Discussions, Participants — gardent
   « En pratique » À DROITE, à la même place (demande du 27 sept. 2026). La
   page À propos le pose dans une rangée Bootstrap : sept colonnes, une
   d'écart, quatre. Le fil et la liste de participants reprennent exactement
   cette armature sur leur grille de douze. */
.ui-fil--sortie > .ui-fil-inner,
.ui-liste-section--sortie > .ui-liste-inner {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-3) var(--space-4);
}

.ui-fil--sortie > .ui-fil-inner > .ui-fil-centre,
.ui-liste-section--sortie > .ui-liste-inner > .ui-liste-contenu { grid-column: 1 / span 7; }

.ui-fil--sortie > .ui-fil-inner > .ui-fil-rail--droit,
.ui-liste-section--sortie > .ui-liste-inner > .ui-liste-apart { grid-column: 9 / span 4; }

.ui-liste-section--sortie .ui-liste-rail { display: none; }

/* Sous 992px, l'encart passe sous le contenu, comme sur la page À propos. */
@media (width < 992px) {
  .ui-fil--sortie > .ui-fil-inner > .ui-fil-centre,
  .ui-liste-section--sortie > .ui-liste-inner > .ui-liste-contenu,
  .ui-fil--sortie > .ui-fil-inner > .ui-fil-rail--droit,
  .ui-liste-section--sortie > .ui-liste-inner > .ui-liste-apart { grid-column: 1 / -1; }
}


/* Une action d'en-tête de profil qui change des données (liker, accepter,
   masquer) : un bouton dans un formulaire, qui s'efface de la rangée. */
.ui-monprofil-action-form { display: contents; }

/* ── L'ESPACE PARAMÈTRES ─────────────────────────────────────────────────
   L'armature de la liste des profils (rail + contenu) ; le contenu d'une vue
   tient dans 760px — des formulaires, pas une grille — et ses encarts
   s'espacent du pas des cartes. */
.ui-espace-contenu {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

/* ── LA LISTE DE « MES CRITÈRES » ────────────────────────────────────────
   Une seule carte, une ligne par groupe — comme les réglages d'un
   téléphone : l'icône, le nom, le choix en une ligne (coupé par « … »),
   « 1 important » en rose, et « Modifier ». Toutes les lignes ont la même
   hauteur : la page se lit d'un coup d'œil, le détail est dans la fenêtre.
   (Deux versions en cartes l'ont précédée, trop chargées.) */
/* Deux classes : le rembourrage de .ui-carte arrêtait les filets entre
   les lignes avant les bords. */
.ui-carte.ui-espace-liste {
  margin: 0;
  padding: 0;
  list-style: none;
}

.ui-espace-ligne {
  display: grid;
  /* La colonne du badge existe sur chaque ligne, vide ou non : « Modifier »
     tombe ainsi au même endroit partout. */
  grid-template-columns: 24px minmax(160px, 220px) minmax(0, 1fr) 7.5rem auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
}

.ui-espace-ligne + .ui-espace-ligne { border-top: 1px solid var(--border); }

.ui-espace-ligne-icone { color: var(--color-primary); font-size: var(--icon-lg); }

.ui-espace-ligne-titre {
  margin: 0;
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
}

.ui-espace-ligne .ui-espace-ligne-resume {
  margin: 0;
  overflow: hidden;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ui-espace-ligne-resume.is-vide { font-style: italic; }

.ui-espace-ligne-badge { justify-self: end; }

.ui-espace-important {
  border-radius: var(--radius-pill);
  background: var(--color-brand-pink-soft);
  padding: 0.125rem var(--space-2);
  color: var(--color-brand-pink-ink);
  font-size: var(--fs-caption);
  font-weight: 700;
  white-space: nowrap;
}

/* Au téléphone : le nom et « Modifier » sur la première ligne, le résumé
   dessous, sur toute la largeur. */
@media (width <= 640px) {
  .ui-espace-ligne { grid-template-columns: 24px minmax(0, 1fr) auto; row-gap: var(--space-1); }
  .ui-espace-ligne-modifier { grid-column: 3; grid-row: 1; }
  .ui-espace-ligne .ui-espace-ligne-resume {
    display: -webkit-box;
    grid-column: 2 / -1;
    grid-row: 2;
    white-space: normal;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }
  .ui-espace-ligne-badge { grid-column: 2 / -1; grid-row: 3; justify-self: start; }
  .ui-espace-ligne-badge:empty { display: none; }
}

.ui-parametres-points {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-sm);
}

.ui-parametres-points li { display: flex; align-items: flex-start; gap: var(--space-2); }
.ui-parametres-points .material-symbols-outlined { color: var(--color-primary); font-size: var(--icon-md); }

.ui-parametres-encart { gap: var(--space-2); padding: var(--space-4); }
/* L'arrondi STANDARD des cartes (18px, 1er oct. 2026) : ui-charte.css pose 28
   sur .ui-carte, d'où la précision du sélecteur. */
.ui-espace-contenu > .ui-parametres-encart { border-radius: var(--radius-md); }

.ui-parametres-encart-tete {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.ui-parametres-encart-tete > .material-symbols-outlined { color: var(--color-primary); font-size: var(--icon-lg); }

.ui-parametres-encart-titre {
  margin: 0;
  color: var(--color-titre);
  font-size: var(--fs-h5);
  font-weight: var(--fw-semibold);
}

/* Deux classes : une règle générale des paragraphes de carte lui donnait
   28px de marge haute, et un blanc s'ouvrait sous la valeur. */
.ui-parametres-encart .ui-parametres-encart-intro {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.55;
}

/* L'état actuel du réglage, en encre de titre : c'est ce qu'on vient
   vérifier avant de changer quoi que ce soit. */
.ui-parametres-valeur {
  margin: 0;
  color: var(--color-titre);
  font-weight: var(--fw-semibold);
}

.ui-parametres-valeur small { color: var(--text-muted); font-weight: 400; }

.ui-parametres-pied {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-2);
}

/* Le geste qui détruit ne se présente pas comme les autres : un filet rose. */
.ui-parametres-encart--danger { border-color: color-mix(in srgb, var(--color-brand-pink-ink) 40%, var(--border)); }
.ui-parametres-encart--danger .ui-parametres-encart-tete > .material-symbols-outlined { color: var(--color-brand-pink-ink); }

/* ══ LA BARRE DU BAS — sur téléphone (29 sept. 2026) ═══════════════════════
   Accueil · Profils · Sorties · Communautés · Activité, sous le pouce, fixe.
   La barre du haut perd alors sa rangée de rubriques et tient sur une ligne :
   le logo, la loupe, les messages, la cloche, le compte. « Activité » ouvre
   un panneau par le bas — demandes, contacts, matchs, discussions — qui
   remplace le rail « Mon activité ». Écrit en fin de fichier : à spécificité
   égale, il l'emporte sur les replis plus anciens de l'en-tête. */
.ui-membre-barre-bas { display: none; }

@media (width <= 900px) {
  body:has(.ui-membre-barre-bas) { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }

  .ui-header-membre .ui-header-membre-inner > .ui-membre-nav { display: none; }

  .ui-membre-barre-bas {
    position: fixed;
    inset: auto 0 0;
    z-index: 1030;
    display: grid;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    border-top: 1px solid var(--border);
    background: var(--surface-card);
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: var(--shadow-haut);
  }

  .ui-membre-barre-bas-lien {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    width: 100%;
    height: 64px;
    border: 0;
    background: none;
    padding: 0;
    color: var(--text-muted);
    font: inherit;
    font-size: var(--fs-caption);
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
  }

  .ui-membre-barre-bas-lien .material-symbols-outlined { font-size: var(--icon-lg); }
  .ui-membre-barre-bas-lien.is-current,
  .ui-membre-barre-bas-groupe.is-open > .ui-membre-barre-bas-lien { color: var(--color-titre); }
  .ui-membre-barre-bas-lien.is-current .material-symbols-outlined { font-variation-settings: 'FILL' 1; }

  .ui-membre-barre-bas-lien .ui-membre-cloche-pastille {
    position: absolute;
    top: 6px;
    left: calc(50% + 4px);
    /* La pastille de la cloche est ancrée à DROITE : ancrée ici à gauche
       aussi, elle s'étirait d'un bord à l'autre au lieu de rester ronde. */
    inset-inline-end: auto;
  }

  .ui-membre-barre-bas-groupe { position: relative; }

  /* Le panneau d'« Activité » : une carte posée au-dessus de la barre. */
  .ui-membre-barre-bas-panneau {
    position: fixed;
    inset: auto var(--space-3) calc(72px + env(safe-area-inset-bottom));
    display: none;
    flex-direction: column;
    border-radius: var(--radius-md);
    background: var(--surface-card);
    padding: var(--space-3);
    box-shadow: var(--shadow-md);
  }

  /* Ouvert par le script (assets/js, setupBarreBas) : au toucher, au clic,
     à Entrée. Pas de « focus-within » : il le gardait ouvert au second
     toucher, le bouton restant focalisé. */
  .ui-membre-barre-bas-groupe.is-open > .ui-membre-barre-bas-panneau { display: flex; }

  .ui-membre-barre-bas-entree {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 48px;
    border-radius: var(--radius-sm);
    padding: 0 var(--space-2);
    color: var(--color-titre);
    font-weight: 600;
    text-decoration: none;
  }

  .ui-membre-barre-bas-entree:hover,
  .ui-membre-barre-bas-entree:focus-visible { background: var(--teinte-neutre); }
  .ui-membre-barre-bas-entree .material-symbols-outlined { color: var(--color-primary); }
  .ui-membre-barre-bas-entree .ui-pastille-nouveaux { margin-inline-start: auto; }
}

/* LE MENU ☰ DU TÉLÉPHONE (29 sept. 2026) : un bouton devant le logo, qui
   ouvre un panneau latéral — Activité, puis les vues et thèmes de Profils,
   Sorties, Communautés, en groupes dépliables. Le logo perd 15 % pour lui
   faire place. */
.ui-membre-burger { display: none; }

@media (width <= 900px) {
  .ui-membre-burger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    margin-inline-start: calc(var(--space-2) * -1);
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--color-titre);
    cursor: pointer;
  }

  .ui-membre-burger .material-symbols-outlined { font-size: var(--icon-lg); }
  /* La loupe quitte l'en-tête : la recherche est en tête du menu ☰. Les
     messages aussi : « Discussions » est dans la barre du bas. */
  .ui-header-membre-compte > .ui-membre-rond--recherche,
  .ui-header-membre-compte > .ui-membre-rond--messages { display: none; }
  /* Sans marge à gauche : le ☰ la précède déjà (29 sept. 2026). */
  .ui-header-membre-marque img { width: 144px; margin-inline-start: 0; }
}

.ui-menu-mobile { --bs-offcanvas-width: min(340px, 88vw); }
.ui-menu-mobile-tete { border-bottom: 1px solid var(--border); }
.ui-menu-mobile-tete img { width: 144px; height: auto; }
.ui-menu-mobile-corps { padding: var(--space-3) var(--space-3) var(--space-5); }
.ui-menu-mobile-nav { display: flex; flex-direction: column; gap: var(--space-1); }

.ui-menu-mobile-recherche {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 48px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--teinte-neutre);
  padding: 0 var(--space-3);
}

.ui-menu-mobile-recherche .material-symbols-outlined { color: var(--text-muted); }
.ui-menu-mobile-recherche input { flex: 1; min-width: 0; border: 0; background: none; color: var(--color-titre); font: inherit; outline: none; }
.ui-menu-mobile-recherche:focus-within { border-color: var(--color-primary); }
.ui-menu-mobile-filet { margin: var(--space-3) 0 var(--space-2); border-color: var(--border); opacity: 1; }
/* LE PANNEAU EST DROIT (demande du 29 sept. 2026) : collé au bord de
   l'écran, un angle arrondi n'y a pas de sens. Ses composants — champ de
   recherche, liens, pastilles — gardent les leurs. */
.ui-menu-mobile { border-radius: 0; }

.ui-menu-mobile-groupe { border-bottom: 1px solid var(--border); padding-bottom: var(--space-2); }
.ui-menu-mobile-groupe:last-child { border-bottom: 0; }

.ui-menu-mobile-titre {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 52px;
  padding: 0 var(--space-2);
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: 800;
  list-style: none;
  cursor: pointer;
}

.ui-menu-mobile-titre::-webkit-details-marker { display: none; }
.ui-menu-mobile-titre > .material-symbols-outlined:first-child { color: var(--color-primary); }
.ui-menu-mobile-chevron { margin-inline-start: auto; transition: transform 0.2s ease; }
.ui-menu-mobile-titre .ui-pastille-nouveaux { margin-inline-start: auto; }
.ui-menu-mobile-titre .ui-pastille-nouveaux + .ui-menu-mobile-chevron { margin-inline-start: 0; }
.ui-menu-mobile-groupe[open] > .ui-menu-mobile-titre .ui-menu-mobile-chevron { transform: rotate(180deg); }

.ui-menu-mobile-lien {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2) 0 var(--space-3);
  color: var(--color-titre);
  font-weight: 600;
  text-decoration: none;
}

.ui-menu-mobile-lien:hover,
.ui-menu-mobile-lien:focus-visible { background: var(--teinte-neutre); }
.ui-menu-mobile-lien .material-symbols-outlined { color: var(--color-primary); font-size: var(--icon-md); }
.ui-menu-mobile-lien .ui-pastille-nouveaux { margin-inline-start: auto; }
.ui-menu-mobile-lien--theme { min-height: 40px; padding-inline-start: calc(var(--space-2) + 44px); font-weight: 500; }
/* « Mes … », sous les thèmes : détaché d'eux par un peu d'air. */
.ui-menu-mobile-lien--theme + .ui-menu-mobile-lien--apres { margin-top: var(--space-2); }

.ui-menu-mobile-sous-titre {
  margin: var(--space-2) 0 0;
  padding-inline-start: calc(var(--space-2) + 44px);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* LES RAILS DE GAUCHE, sur téléphone (29 sept. 2026). « Mon activité »
   disparaît — fil, discussions, demandes, contacts, matchs : la barre du bas
   l'offre sous « Activité ». Les VUES d'une liste (Tous · Selon mon profil…,
   Toutes · Mes communautés, l'espace Paramètres) restent, en rangée d'onglets
   compacte au-dessus du contenu. De l'agenda des sorties ne restent que ses
   deux liens, en onglets eux aussi. */
@media (width <= 900px) {
  .ui-fil-rail--gauche,
  .ui-liste-rail--activite { display: none; }

  /* Les vues des listes (Tous les profils · Selon mon profil, Tous les
     évènements · Mes évènements…) quittent aussi la page : le menu ☰ les
     porte toutes (29 sept. 2026). Restent les onglets de l'espace
     Paramètres, que le menu n'a pas. */
  .ui-liste-section:not(.ui-espace) .ui-liste-rail { display: none; }
  /* L'espace Paramètres aussi : ses trois pages se déplient sous
     « Paramètres », dans le menu du compte. */
  .ui-espace .ui-liste-rail { display: none; }

  .ui-liste-rail .ui-membre-onglets {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
    margin: 0 0 var(--space-3);
    padding-bottom: 2px;
    scrollbar-width: none;
  }

  .ui-liste-rail .ui-membre-onglet {
    flex: 0 0 auto;
    min-height: 40px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    padding: 0 var(--space-3);
    white-space: nowrap;
  }

  .ui-liste-rail .ui-membre-onglet.is-current {
    border-color: var(--color-titre);
    background: var(--color-titre);
    color: var(--white);
  }

  .ui-liste-rail .ui-membre-onglet.is-current .material-symbols-outlined { color: inherit; }

  /* L'agenda (molécule encart --repli) se réduit à ses liens en pilules :
     règle de la molécule. */
}

/* L'AUDIT TÉLÉPHONE (29 sept. 2026) — les premiers correctifs.
   · la marge haute des pages de liste : 24px au lieu d'une bande vide de
     près de 90px sous l'en-tête ;
   · le titre et son bouton de création s'empilent : côte à côte, le bouton
     écrasait le titre sur une colonne de trois mots ;
   · les fenêtres d'édition et de critères prennent l'écran, boutons en bas —
     centrées, elles laissaient 16px de marge et coupaient le pied. La
     fenêtre d'une publication aussi : son champ de commentaire tombe alors
     au bas de l'écran, sous le pouce. */
@media (width <= 900px) {
  .ui-section.ui-liste-section { padding-top: var(--space-3); }
  /* Le fil : 28px sous l'en-tête au lieu de 48 (29 sept. 2026). */
  .ui-fil:not(.ui-fil--ecran) { padding-top: var(--space-4); }
}

@media (width <= 640px) {
  .ui-liste-tete {
    flex-direction: column;
    align-items: flex-start;
  }

  .ui-liste-tete > .ui-liste-action { align-self: stretch; justify-content: center; }

  /* Les filtres, repliés derrière leur bouton. */
  .ui-liste-filtres-bouton {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    margin-bottom: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface-card);
    padding: 0 var(--space-3);
    color: var(--color-titre);
    font: inherit;
    font-weight: 700;
    cursor: pointer;
  }

  .ui-liste-filtres-bouton[aria-expanded="true"] .ui-liste-filtres-chevron { transform: rotate(180deg); }
  .ui-liste-filtres:not(.is-open) { display: none; }
}

.ui-liste-filtres-bouton { display: none; }
@media (width <= 640px) {
  .ui-liste-filtres-bouton { display: inline-flex; }
}

/* Plein écran jusqu'à 767px (1er oct. 2026), comme le reste du dessin du
   téléphone : entre 577 et 767, les fenêtres restaient des cartes arrondies. */
@media (width <= 767px) {
  .ui-edition-dialog,
  .ui-criteres-dialog,
  .ui-discussion-dialog {
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    min-height: 100%;
    margin: 0;
  }

  .ui-edition-dialog > .modal-content,
  .ui-criteres-dialog > .modal-content,
  .ui-discussion-dialog > .modal-content {
    height: 100%;
    max-height: 100%;
    border: 0;
    border-radius: 0;
  }
}

/* LES DISCUSSIONS, sur téléphone : une chose à la fois, sur toute la
   largeur. « Mes discussions » montre la liste ; une conversation ouverte
   prend l'écran, flèche de retour en tête, sans la barre du bas — le
   clavier a besoin de la place. */
.ui-messagerie-retour { display: none; }

@media (width <= 900px) {
  .ui-fil--ecran > .ui-fil-inner {
    display: flex;
    flex-direction: column;
    padding-inline: 0;
  }

  .ui-fil--ecran { padding-top: 0; padding-bottom: 0; }

  .ui-fil--ecran .ui-messagerie,
  .ui-fil--ecran .ui-fil-rail--droit {
    flex: 1;
    width: 100%;
    min-height: 0;
    border-inline: 0;
    border-radius: 0;
  }

  .ui-fil--ecran .ui-fil-rail--droit { position: static; }
  .ui-fil--ecran .ui-fil-rail--droit .ui-fil-encart { border-radius: 0; border-inline: 0; }

  .ui-fil--ecran.ui-fil--liste .ui-messagerie,
  .ui-fil--ecran:not(.ui-fil--liste) .ui-fil-rail--droit { display: none; }
  /* Le repli des rails du fil masque leurs encarts sous 1200px ; ici,
     l'encart EST la liste des conversations. */
  .ui-fil--ecran.ui-fil--liste .ui-fil-rail--droit > .ui-fil-encart { display: block; }

  body:has(.ui-fil--ecran:not(.ui-fil--liste)) .ui-membre-barre-bas { display: none; }
  /* Ni l'en-tête du site : la conversation ouverte prend tout l'écran, sa
     flèche de retour en tête (1er oct. 2026). */
  body:has(.ui-fil--ecran:not(.ui-fil--liste)) .ui-header-membre { display: none; }
  body:has(.ui-fil--ecran:not(.ui-fil--liste)) { padding-bottom: 0; }

  .ui-messagerie-retour {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-inline-start: calc(var(--space-2) * -1);
    border-radius: 50%;
    color: var(--color-titre);
    text-decoration: none;
  }
}

/* ---- Le selfie pris à la caméra (29 sept. 2026) ----
   Un cadre au format portrait : l'icône tant que la caméra est fermée, la
   vidéo en miroir pendant la prise, puis l'image prise. */
.ui-selfie { display: grid; gap: var(--space-3); justify-items: center; }
.ui-selfie-cadre {
  position: relative;
  display: grid;
  place-items: center;
  width: min(100%, 320px);
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--surface-douce);
}
.ui-selfie-video, .ui-selfie-apercu { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ui-selfie-video { transform: scaleX(-1); }
.ui-selfie-icone { font-size: var(--icon-2xl); color: var(--text-muted); }
.ui-selfie-aide { margin: 0; color: var(--text-muted); font-size: var(--fs-sm); text-align: center; }
.ui-selfie-boutons { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center; }
.ui-selfie-boutons [hidden], .ui-selfie-cadre [hidden] { display: none; }

/* LA BANNIÈRE DU PROFIL, BORD À BORD JUSQU'À 1440PX (29 sept. 2026) :
   collée sous l'en-tête, sur toute la largeur de la fenêtre — ni cadre, ni
   ombre, ni arrondi. « 50% - 50vw » la sort du conteneur quelle que soit sa
   gouttière ; la section perd son rembourrage du haut. Au-delà de 1440px, la
   bannière redevient un tirage encadré dans la page. */
@media (width <= 1440px) {
  .ui-section:has(.ui-monprofil-identite) { padding-top: 0; }
  .ui-monprofil-identite > .ui-monprofil-couverture {
    margin-inline: calc(50% - 50vw);
    margin-top: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .ui-monprofil-couverture img { border-radius: 0; height: var(--haut-banniere); }
  /* Plus basse que la bannière encadrée : sur toute la largeur, une bande
     plus mince suffit (29 sept. 2026). */
  /* Hauteur : voir « des tailles par palier », en fin de fichier. */
}

/* LE NOM À DROITE DU PORTRAIT EN MOBILE (29 sept. 2026), comme sur
   ordinateur : le nom, puis la ville dessous. Le bloc « détail » s'efface
   (display: contents) pour que ses enfants se rangent dans la rangée du
   portrait — le nom à côté, la bio et les chiffres en dessous, pleine
   largeur. Le cadre blanc du portrait est deux fois plus fin. */
@media (width <= 767px) {
  .ui-monprofil-tete { flex-wrap: wrap; align-items: flex-end; }
  /* La rangée du nom prend toute la largeur du contenu (le repli à 900px la
     calait au début et la réduisait à son contenu) : taux et « … » tombent
     ainsi sur le bord droit de la grille. */
  .ui-monprofil-identite > .ui-monprofil-tete { justify-self: stretch; }
  .ui-monprofil-tete > .ui-monprofil-detail { display: contents; }
  .ui-monprofil-tete .ui-monprofil-infos { flex: 1 1 0; min-width: 0; gap: var(--space-1) var(--space-3); padding-bottom: var(--space-1); }
  .ui-monprofil-tete .ui-monprofil-bio,
  .ui-monprofil-tete .ui-monprofil-chiffres { flex-basis: 100%; }
  .ui-monprofil-avatar { border-width: var(--cadre-photo); }
  /* Le crayon du portrait ne prend pas de place dans la rangée : il se pose
     sur l'angle bas droit du portrait, et le nom vient contre celui-ci. */
  .ui-monprofil-tete { position: relative; }
  /* Plus discret (1er oct. 2026) : une pastille de 28px à cheval sur l'angle
     bas droit du portrait, 6px en dehors, au lieu d'un rond de 40 posé
     dessus. La zone de toucher garde ses 48px, par un débord invisible
     (::after) ; la règle des cibles tactiles, qui la grossissait, est
     dépassée en précision (.ui-monprofil-inner). */
  .ui-monprofil-tete > .ui-monprofil-editer--portrait {
    position: absolute;
    inset: auto auto 8px calc(112px - 40px - 8px);
    margin: 0;
    opacity: 1;                /* pas de survol sur un téléphone */
  }
  .ui-monprofil-inner .ui-monprofil-tete > .ui-monprofil-editer--portrait {
    inset: auto auto -6px calc(112px - 28px + 6px);
    width: 1.75rem;
    min-width: 0;
    height: 1.75rem;
    min-height: 0;
  }
  .ui-monprofil-inner .ui-monprofil-tete > .ui-monprofil-editer--portrait::after {
    content: "";
    position: absolute;
    inset: -10px;
  }
  .ui-monprofil-tete > .ui-monprofil-editer--portrait > .material-symbols-outlined { font-size: var(--icon-sm); }
  /* Les taux, en bas à droite de la couverture : la rangée du nom commence
     au bord bas de l'image, on remonte donc de leur propre hauteur, à 12px
     du bas de l'image et du bord de l'écran. */
  .ui-monprofil-tete .ui-monprofil-taux {
    position: absolute;
    top: -12px;
    right: 0;                  /* sur le bord droit du contenu, comme « Liker » */
    transform: translateY(-100%);
    z-index: 2;
  }
  /* Le menu « … » va au bout de la rangée du nom, au même bord droit. */
  .ui-monprofil-tete .ui-monprofil-infos > .ui-menu-actions { margin-inline-start: auto; }
  /* LE NOM À GAUCHE, LE « … » À SA DROITE (1er oct. 2026), en grille : le
     menu ne passe plus sous le nom (la rangée grandissait, et le portrait,
     calé sur son bas, ne mordait plus la bannière). Il garde son dessin. */
  .ui-monprofil-tete .ui-monprofil-infos {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: var(--space-2);
    row-gap: var(--space-1);
  }
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-nom { grid-column: 1; grid-row: 1; }
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-meta { grid-column: 1; grid-row: 2; }
  .ui-monprofil-tete .ui-monprofil-infos > .ui-menu-actions { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
  /* « Écrire » (sur un match) revient À GAUCHE DU « … » (1er oct. 2026),
     dans son dessin : rond blanc, filet, glyphe gris. Il avait été posé en
     haut à droite de la bannière. */
  .ui-monprofil-tete .ui-monprofil-infos:has(> .ui-monprofil-message) { grid-template-columns: minmax(0, 1fr) auto auto; }
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-message { grid-column: 2; grid-row: 1 / span 2; margin: 0; }
  .ui-monprofil-tete .ui-monprofil-infos:has(> .ui-monprofil-message) > .ui-menu-actions { grid-column: 3; }
}

/* LES TIRAGES PENCHÉS EN MOBILE (29 sept. 2026) restent dans les marges de
   la page : penchés de 1,8° et grossis de 3 %, ils débordaient de la
   gouttière de quelques pixels. Un retrait de 12px de chaque côté les
   ramène dedans, angle compris. */
@media (width <= 767px) {
  .ui-monprofil-photos { padding-inline: var(--space-3); }
}

/* LES BOUTONS DU BANDEAU D'UN PROFIL, REPLIÉS DANS « … » EN MOBILE
   (29 sept. 2026) : contact, like et masquer quittent le bandeau ; le badge
   « demande envoyée », lui, y reste. Sur ordinateur, les entrées de menu
   marquées « mobile » ne paraissent pas. */
@media (width <= 767px) {
  /* Replié : le bandeau d'actions disparaît ; le badge « demande envoyée »
     passe en haut à droite de la bannière. */
  .ui-monprofil-identite--repli .ui-monprofil-actions:not(:has(> a)) { display: none; }
  /* 20px au-dessus du nom ; le « … » sur la ligne du nom, au bord droit, et
     la ville (et ce qui suit) sur la ligne d'après. */
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-nom { flex: 1 1 calc(100% - 52px); margin-top: var(--space-4); }
  /* Le « … » (40px) ne grandit pas la ligne du nom (29px) : il déborde vers
     le bas au lieu de repousser la ville. */
  .ui-monprofil-tete .ui-monprofil-infos > .ui-menu-actions,
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-message { order: 1; align-self: flex-start; margin: var(--space-3) 0 calc(var(--space-2) * -1); }
  /* Un match ajoute « Écrire » : deux boutons au bout de la ligne du nom. */
  .ui-monprofil-tete .ui-monprofil-infos:has(> .ui-monprofil-message) > .ui-monprofil-nom { flex-basis: calc(100% - 104px); }
  .ui-monprofil-tete .ui-monprofil-infos > p { order: 2; flex-basis: 100%; }
  /* Le badge « Like envoyé » / « Demande de contact envoyée » : en haut à
     droite de la bannière (la rangée du nom commence à son bord bas, haute
     de --haut-banniere), en face du « Retour ». */
  .ui-monprofil-tete .ui-monprofil-infos > .ui-monprofil-envoye-mobile {
    position: absolute;
    /* Le reflet du « Retour » : même hauteur, même retrait du haut et du
       bord (la rangée est dans la gouttière, la bannière non). */
    top: calc(var(--haut-banniere, 150px) * -1 + var(--space-4));
    right: calc(var(--space-4) - var(--space-3));
    min-height: var(--control-h-sm);
    z-index: 2;
  }
}

/* Le badge « demande envoyée » recopié sur la bannière : le mobile
   seulement (29 sept. 2026). */
@media (width >= 768px) {
  .ui-monprofil-envoye-mobile { display: none; }
}

/* LA BARRE D'ONGLETS VIDE (profil hors de mes contacts : la fiche seule)
   ne tient plus de place : du bandeau à la description, le même écart que
   entre les blocs (29 sept. 2026) — l'écart de grille, plus ce qu'il faut
   pour faire 24px. */
.ui-monprofil-onglets:not(:has(*)) { display: none; }
/* Un « :has » ne peut pas en contenir un autre : on passe par le frère. */
.ui-monprofil-onglets:not(:has(*)) ~ .ui-liste-contenu {
  margin-top: calc(var(--space-4) - var(--ecart-grille-profil, 16px));
}
/* Au-delà de 900px, le portrait garde sa marge basse de 16px dans le
   bandeau : on la reprend — et la description remonte encore de 20px
   (demande du 29 sept. 2026). */
@media (width > 900px) {
  /* La colonne de droite (« Ses photos ») suit : alignée sur la description. */
  /* + 24px : le portrait est descendu de 31 ; 32px sous lui, comme avec onglets. */
  .ui-monprofil-onglets:not(:has(*)) ~ .ui-liste-contenu,
  .ui-monprofil-onglets:not(:has(*)) ~ .ui-fil-rail { margin-top: calc(var(--space-4) - 52px + 24px); }
}

/* UN SEUL ÉCART ENTRE LES BLOCS DU PROFIL, SUR TOUS LES ÉCRANS (29 sept.
   2026) : les blocs se suivaient à 44px, les cartes d'un même bloc (lignes
   de fiche, photos) à 24px — l'œil voyait deux rythmes. Tout passe à 24px. */
.ui-monprofil-inner .ui-liste-contenu > * + * { margin-top: var(--space-4); }
/* Entre deux rangées de photos penchées et grossies, 14px de plus : l'écart
   VU redevient 24px. De même au-dessus d'une planche dont la première photo
   penche (la 1re et la 3e ; la 2e commence d'aplomb). Une planche vide ne
   compte pas. */
.ui-monprofil-inner { --debord-photo: 10px; }   /* mesuré sur ordinateur */
@media (width <= 767px) { .ui-monprofil-inner { --debord-photo: 14px; } }
.ui-monprofil-inner .ui-monprofil-photos { row-gap: calc(var(--space-4) + var(--debord-photo)); }
.ui-monprofil-inner .ui-liste-contenu > * + .ui-monprofil-photos:not(.ui-monprofil-photos ~ .ui-monprofil-photos),
.ui-monprofil-inner .ui-liste-contenu > * + .ui-monprofil-photos ~ .ui-monprofil-photos ~ .ui-monprofil-photos { margin-top: calc(var(--space-4) + var(--debord-photo)); }
.ui-monprofil-inner .ui-liste-contenu > .ui-monprofil-photos:not(:has(*)) { display: none; }
/* Sur ordinateur, les photos d'une planche sont côte à côte : la penchée
   déborde aussi en bas, sur le bloc suivant. */
@media (width >= 768px) {
  .ui-monprofil-inner .ui-liste-contenu > .ui-monprofil-photos:not(.ui-monprofil-photos ~ .ui-monprofil-photos) + *,
  .ui-monprofil-inner .ui-liste-contenu > .ui-monprofil-photos ~ .ui-monprofil-photos ~ .ui-monprofil-photos + * { margin-top: calc(var(--space-4) + var(--debord-photo)); }
}

/* LE PORTRAIT DESCEND DE 31PX sur ordinateur (29 sept. 2026) — onglets ou
   pas, il garde la même place partout : avec onglets, son bas s'aligne sur
   le leur. Un décalage (« translate ») et non une marge : rien d'autre ne
   bouge. Son crayon le suit. */
@media (width > 900px) {
  .ui-monprofil-tete > .ui-monprofil-avatar,
  .ui-monprofil-tete > .ui-monprofil-editer--portrait { translate: 0 31px; }
}

/* Sous les onglets, 20px de moins avant le contenu, sur ordinateur (29 sept.
   2026) : du bas du portrait — aligné sur les onglets — à la description. */
@media (width > 900px) {
  .ui-monprofil-inner > .ui-monprofil-onglets:has(*) { margin-bottom: var(--space-3); }
}

/* LE CADRE BLANC SUR TOUTES LES PHOTOS DU PROFIL (29 sept. 2026) : la
   fiche, « Mes photos » et l'onglet Photos — le même tirage que le portrait.
   L'image garde un arrondi réduit de l'épaisseur du cadre, pour que les deux
   restent concentriques. Les petites photos des publications ont un cadre
   deux fois plus fin. */
.ui-monprofil-photos > .ui-monprofil-photo-case,
.ui-monprofil-zones > .ui-monprofil-photo-case {
  border: var(--cadre-photo) solid var(--white);
  background: var(--white);
}
.ui-monprofil-photos > .ui-monprofil-photo-case > .ui-monprofil-photo,
.ui-monprofil-zones > .ui-monprofil-photo-case > .ui-monprofil-photo {
  border-radius: calc(var(--radius-md) - var(--cadre-photo));
}
.ui-monprofil-galerie-case {
  border: var(--cadre-photo) solid var(--white);
  background: var(--white);
}

/* Les cadres blancs des photos : 3px en mobile. */
@media (width <= 767px) {
  .ui-monprofil-inner { --cadre-photo: 3px; }
}

/* RÉORDONNER LES PHOTOS (29 sept. 2026) : la photo attrapée s'efface à sa
   place, sa copie suit la main, la case visée s'entoure d'un pointillé. */
.ui-monprofil-zones[data-ordre] > .ui-monprofil-photo-case { cursor: grab; -webkit-touch-callout: none; user-select: none; }
.ui-monprofil-zones[data-ordre] .ui-monprofil-photo { -webkit-user-drag: none; }
.ui-monprofil-photo-case.is-deplacee { opacity: 0.35; }
.ui-monprofil-zones > .is-cible { outline: 3px dashed var(--color-primary); outline-offset: 4px; }
.ui-monprofil-fantome {
  position: fixed;
  inset: 0 auto auto 0;
  z-index: 2000;
  margin: 0;
  pointer-events: none;
  cursor: grabbing;
  opacity: 0.92;
  rotate: 2deg;
  box-shadow: var(--shadow-md);
  border: var(--cadre-photo, 6px) solid var(--white);
  border-radius: var(--radius-md);
  background: var(--white);
}

/* LE ROND QUI CHANGE LA COUVERTURE (29 sept. 2026), en haut à droite de la
   bannière — le pendant du crayon du portrait : il paraît au survol de
   l'image, et toujours sur un écran tactile. */
.ui-monprofil-couverture > .ui-monprofil-editer--couverture {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 2;
}
.ui-monprofil-couverture:hover > .ui-monprofil-editer--couverture { opacity: 1; }
@media (hover: none), (width <= 767px) {
  /* Au téléphone, toujours affichés (1er oct. 2026) — quelle que soit la
     largeur où l'écran est tactile : les boutons des images, couverture,
     portrait et photos. */
  .ui-monprofil-couverture > .ui-monprofil-editer--couverture,
  .ui-monprofil-editer--portrait,
  .ui-monprofil-photo-case > .ui-monprofil-editer { opacity: 1; }
}

/* LE VOILE DE LA BANNIÈRE (29 sept. 2026) : un dégradé pétrole transparent
   sur la photo — à peine en haut, plus dense en bas, là où se posent les
   taux et les boutons. Pas sur la couverture par défaut, déjà douce. Sous
   les boutons (z-index 1), et sans capter les clics. « isolation » garde ce
   z-index DANS la bannière : sans elle, le voile passait sur le portrait
   qui la chevauche. */
.ui-monprofil-couverture { isolation: isolate; }
.ui-monprofil-couverture:not(:has(> img[src*="couverture.svg"]))::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--color-titre) 18%, transparent) 0%,
    transparent 35%,
    color-mix(in srgb, var(--color-titre) 45%, transparent) 100%);
}

/* LE ROND AU BOUT DES ONGLETS (aperçu public, 29 sept. 2026) : calé tout à
   droite de la rangée ; quand les onglets défilent (mobile), il reste
   accroché au bord. */
/* 40px, comme tout bouton rond (30 sept. 2026). */
.ui-monprofil-onglets > .ui-monprofil-onglets-outil {
  flex-shrink: 0;
  margin-inline-start: auto;
  position: sticky;
  right: 0;
  /* La pastille d'un onglet, réduite à son icône : un rond de même hauteur. */
  display: inline-grid;
  place-items: center;
  width: var(--control-h-sm);               /* la hauteur d'une pastille d'onglet */
  height: var(--control-h-sm);
  padding: 0;
}
/* Le rond reste dans les dix colonnes du contenu (la rangée des onglets ne
   déborde pas sur la colonne de droite). */
.ui-monprofil-onglets-outil > .material-symbols-outlined { font-size: var(--icon-md); }

/* LES TAUX DANS LA BANNIÈRE, SUR ORDINATEUR (29 sept. 2026) : à droite du
   portrait, calés sur le début du nom, 12px au-dessus du bas de l'image —
   le nom commence 24px sous la bannière, les pastilles font 30px ; remontées
   de 10px (22px du bas) à la demande. */
@media (width > 900px) {
  .ui-monprofil-detail > .ui-monprofil-infos > .ui-monprofil-taux {
    position: absolute;
    left: 0;
    top: calc(-24px - 22px - 30px);   /* 22px au-dessus du bas de l’image */
    z-index: 2;
  }
}

/* DES TAILLES PAR PALIER, PLUS RIEN DE FLUIDE (29 sept. 2026). La hauteur de
   la bannière suivait la largeur (clamp en vw) et le portrait la grille : le
   nom, la ville et les pastilles glissaient à chaque pixel de fenêtre. Chaque
   palier a maintenant ses deux mesures fixes, et le nom se pose à 24px du
   portrait (une piste à la taille du portrait, puis le reste) au lieu de la
   colonne 3, qui bougeait avec la page.
     > 1440     bannière encadrée 280, portrait 192
     1101–1440  bannière 200, portrait 192
     901–1100   bannière 170, portrait 150
     768–900    bannière 150, portrait 112 (règle du téléphone, plus haut)
     ≤ 767      bannière 130, portrait 112 */
.ui-monprofil-inner { --haut-banniere: 280px; --portrait: 192px; }
.ui-monprofil-couverture img { height: var(--haut-banniere); }
@media (width <= 1440px) { .ui-monprofil-inner { --haut-banniere: 200px; } }
@media (width <= 1100px) { .ui-monprofil-inner { --haut-banniere: 170px; --portrait: 150px; } }
@media (width <= 900px)  { .ui-monprofil-inner { --haut-banniere: 150px; } }
@media (width <= 767px)  { .ui-monprofil-inner { --haut-banniere: 130px; } }
@media (width > 900px) {
  .ui-monprofil-tete { grid-template-columns: var(--portrait) minmax(0, 1fr); }
  .ui-monprofil-tete > .ui-monprofil-avatar,
  .ui-monprofil-tete > .ui-monprofil-editer--portrait { grid-column: 1; }
  .ui-monprofil-tete > .ui-monprofil-detail { grid-column: 2; }
}
/* Portrait de 150 : le nom arrive 21px plus haut, les pastilles le suivent
   d'autant de trop — on les redescend pour rester à 19px du bas de l'image. */
@media (900px < width <= 1100px) {
  .ui-monprofil-detail > .ui-monprofil-infos > .ui-monprofil-taux { top: calc(-24px - 22px - 30px + 21px); }
}

/* LES GESTES D'UN PROFIL SUR LA LIGNE DU NOM (30 sept. 2026) : plus de CTA sur
   la bannière. Ce sont les ronds de la carte profil — croix grise pour
   masquer, pétrole pour le contact, rose pour le like, aplat quand c'est
   fait —, posés à gauche de « Écrire » et du « … », à la taille de ces deux
   voisins (40px, sans ombre) et à 8px les uns des autres, comme eux. */
.ui-monprofil-identite--repli > .ui-monprofil-actions { display: none; }
.ui-monprofil-ronds {
  display: flex;
  gap: var(--space-2);
  align-self: center;
  margin-inline-start: auto;
}
.ui-monprofil-ronds > form { margin: 0; }
.ui-monprofil-ronds .ui-monprofil-rond {
  width: var(--control-h-sm);
  height: var(--control-h-sm);
  margin: 0;
  box-shadow: none;
}
.ui-monprofil-ronds .ui-monprofil-rond[aria-disabled="true"] { cursor: default; }
.ui-monprofil-ronds + .ui-monprofil-message,
.ui-monprofil-ronds + .ui-menu-actions { margin-inline-start: calc(0.5rem - 20px); }
/* En mobile, ces gestes vivent dans le « … ». */
@media (width <= 767px) {
  .ui-monprofil-ronds { display: none; }
}
/* SUR LA LIGNE DU NOM, « Écrire » prend le dessin du « … » — fond blanc,
   filet gris clair, glyphe gris, aplat sombre au survol. Masquer, contact et
   like, eux, sont en COULEURS VIVES comme les ronds de la carte profil
   (1er oct. 2026 ; ils prenaient tous le dessin blanc auparavant). */
.ui-monprofil-infos > .ui-monprofil-message {
  border: 1px solid var(--border);
  background: var(--white);
  color: var(--text-muted);
}
.ui-monprofil-infos > .ui-monprofil-message:hover,
.ui-monprofil-infos > .ui-monprofil-message:focus-visible {
  border-color: var(--border);
  background: var(--color-dark);
  color: var(--white);
}

/* AVEC DES ONGLETS, LES RONDS DESCENDENT SUR LEUR RANGÉE (30 sept. 2026) :
   même axe que les pastilles, même bord droit (déjà commun). 52px = l'écart
   mesuré entre l'axe de la ligne du nom et celui des onglets, constant sur
   ordinateur. Pas sous 1101px : cinq onglets vont jusqu'à 768px à 1000px de
   fenêtre, et quatre ronds commenceraient à 717 — ils se chevaucheraient. */
@media (width > 1100px) {
  .ui-monprofil-inner:has(> .ui-monprofil-onglets > *) .ui-monprofil-infos > :is(.ui-monprofil-ronds, .ui-monprofil-message, .ui-menu-actions) {
    translate: 0 52px;
  }
}

/* LE BADGE « C'EST UN MATCH ! » (30 sept. 2026), à droite des taux : une
   pastille de leur famille, à l'aplat rose du cœur allumé de la carte profil
   — blanc sur #e20138, 4,57:1. */
.ui-profil-score.ui-monprofil-match {
  border-
}
.ui-monprofil-match > .material-symbols-outlined { font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 20; }


/* LES PARTICIPANTS SUR TOUTE LA LARGEUR, SOUS « EN PRATIQUE » (30 sept. 2026).
   Le contenu s'efface (display: contents) : son titre et « L'organisation »
   se rangent à côté de l'encart, sur sept colonnes, et le groupe suivant — les
   participants — passe dessous, sur les douze. Sur un téléphone, l'ordre
   devient titre, organisation, encart, participants : l'encart ne finit pas
   sous soixante cartes. Les membres d'une communauté suivent la même règle,
   avec « La communauté » pour encart : le DERNIER groupe passe dessous. */
.ui-liste-section--sortie > .ui-liste-inner:has(> .ui-liste-apart) > .ui-liste-contenu { display: contents; }
.ui-liste-section--sortie > .ui-liste-inner > .ui-liste-contenu > .ui-monprofil-groupe:last-of-type { order: 2; grid-column: 1 / -1; }
.ui-liste-section--sortie > .ui-liste-inner > .ui-liste-apart { order: 1; }
/* Les filtres des participants (30 sept. 2026) : juste au-dessus d'eux,
   sur toute la largeur. */
.ui-liste-section--sortie > .ui-liste-inner > .ui-liste-contenu:has(> .ui-monprofil-groupe:last-of-type) > .ui-fil-filtrer { order: 2; grid-column: 1 / -1; }
@media (width >= 992px) {
  .ui-liste-section--sortie > .ui-liste-inner:has(> .ui-liste-apart) > .ui-liste-contenu > :not(.ui-monprofil-groupe:last-of-type, .ui-fil-filtrer) { grid-column: 1 / span 7; }
  .ui-liste-section--sortie > .ui-liste-inner > .ui-liste-apart { grid-row: 1 / span 2; align-self: start; }
}
@media (width >= 768px) {
  .ui-liste-section--sortie .ui-monprofil-groupe:last-of-type .ui-liste-grille > .ui-grid-item { flex: 0 0 auto; width: 25%; }
}
@media (width >= 1200px) {
  .ui-liste-section--sortie .ui-monprofil-groupe:last-of-type .ui-liste-grille > .ui-grid-item { width: calc(100% / 6); }
}

/* LES FILTRES ACTIFS EN TAGS, à droite de « Filtrer » (30 sept. 2026) : une
   pastille par critère, sa croix le retire. Sur une ligne avec le bouton,
   et à la ligne quand ils sont nombreux. */
.ui-fil-filtrer:has(> .ui-fil-filtrer-tags),
.ui-fil-filtrer:has(> .ui-fil-trier) { flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.ui-fil-filtrer-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ui-fil-filtrer-tags-el { display: flex; }
.ui-fil-filtrer-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 30px;
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--white);
  color: var(--color-titre);
  font-size: var(--fs-caption);
  font-weight: 600;
  text-decoration: none;
}
.ui-fil-filtrer-tag > .material-symbols-outlined { font-size: var(--icon-sm); color: var(--text-muted); }
.ui-fil-filtrer-tag:hover,
.ui-fil-filtrer-tag:focus-visible { border-color: var(--color-titre); color: var(--color-titre); text-decoration: none; }
.ui-fil-filtrer-tag:hover > .material-symbols-outlined { color: var(--color-titre); }

/* TRIER PAR, au bout de la ligne des filtres (30 sept. 2026) : même bouton
   que « Filtrer », un menu de liens. */
.ui-fil-filtrer > .ui-fil-trier { margin-inline-start: auto; }
/* LES ÉTIQUETTES SOUS LE LIEN « FILTRER » (30 sept. 2026), sur une ligne à
   elles, sur toutes les pages qui filtrent : « Trier par » garde la droite de
   la première ligne. */
.ui-fil-filtrer > .ui-fil-filtrer-tags { order: 3; flex-basis: 100%; }
/* Au téléphone, pas de « Trier par » (30 sept. 2026) : l'ordre par défaut
   suffit, et la ligne reste à « Filtrer » et ses étiquettes. */
@media (width <= 576px) {
  .ui-fil-filtrer > .ui-fil-trier { display: none; }
}
.ui-fil-trier-bouton { margin-inline-start: 0; margin-inline-end: calc(var(--space-2) * -1); }
.ui-fil-trier-menu {
  min-width: 14rem;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.ui-fil-trier-choix {
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.ui-fil-trier-choix:hover,
.ui-fil-trier-choix:focus-visible { background: var(--surface-douce); color: var(--color-titre); }
.ui-fil-trier-choix.is-current { background: var(--surface-douce); }

/* LE BADGE « C'EST UN MATCH ! » DANS LE PORTRAIT (30 sept. 2026), centré en
   bas, à 10px du bord de la photo (cadre blanc compris). Le portrait est
   une image : le badge se pose PAR-DESSUS, dans la même case de la grille,
   et suit ses décalages (remontée, « translate » de 31px). */
@media (width > 900px) {
  .ui-monprofil-tete > .ui-monprofil-match {
    grid-column: 1;
    grid-row: 1;
    align-self: end;
    justify-self: center;              /* centré en bas de la photo (30 sept. 2026) */
    position: relative;
    z-index: 2;
    top: calc(var(--saillie-photo, 0px) * -1);
    translate: 0 31px;
    margin: 0 0 calc(var(--space-3) + var(--cadre-photo) + 10px);
  }
}
/* En colonne (≤ 900px), le portrait de 112px remonte de sa moitié dans la
   bannière : le badge se pose au même endroit, en absolu. */
@media (width <= 900px) {
  .ui-monprofil-tete { position: relative; }
  .ui-monprofil-tete > .ui-monprofil-match {
    position: absolute;
    z-index: 2;
    left: calc(112px / 2);             /* le milieu du portrait de 112px */
    top: calc(112px / 2 + 22px - var(--cadre-photo) - 6px);   /* mesuré : 6px du bas de la photo */
    translate: -50% -100%;
  }
}

/* « VOIR PLUS DE MEMBRES », sous la grille (30 sept. 2026). */
.ui-liste-plus {
  display: flex;
  justify-content: center;
  margin: var(--space-4) 0 0;
}
.ui-liste-plus[aria-busy="true"] .ui-btn { opacity: 0.6; pointer-events: none; }

/* LA VARIANTE PLEINE (30 sept. 2026) — les membres d'une communauté : douze
   colonnes, ni rail ni encart, six cartes par rangée (quatre sous 1200px),
   comme les participants d'une sortie. */
.ui-liste-section--pleine > .ui-liste-inner {
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--space-3) var(--space-4);
}
.ui-liste-section--pleine .ui-liste-rail { display: none; }
.ui-liste-section--pleine > .ui-liste-inner > .ui-liste-contenu { grid-column: 1 / -1; }
/* Six par rangée pour les cartes PROFIL seulement : les cartes d'évènement
   (onglet Évènements) gardent la grille de leur page, quatre par rangée. */
@media (width >= 768px) {
  .ui-liste-section--pleine .ui-liste-grille > .ui-grid-item:has(> .ui-profil) { flex: 0 0 auto; width: 25%; }
}
@media (width >= 1200px) {
  .ui-liste-section--pleine .ui-liste-grille > .ui-grid-item:has(> .ui-profil) { width: calc(100% / 6); }
}

/* LE BANDEAU D'UNE SORTIE, MOINS HAUT (30 sept. 2026) — 620px à 1440 de
   large : 48px de marge au lieu de 64, le titre en display-4 (56px au lieu
   de 72), 24px avant le pied au lieu de 46, et la photo en 16:10 au lieu
   de 4:3. Des tailles fixes, pas de clamp. */
.ui-hero-evenement:not(.ui-hero-evenement--banniere) { padding-block: var(--space-5); }
/* Le format vient d'ordinaire de l'image (« --hero-evenement-ratio », posé en
   ligne) : il est fixé ici, l'image recadrée au centre. */
.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-cadre { aspect-ratio: 16 / 10; }
@media (width >= 992px) {
  .ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-display-title { font-size: var(--fs-display-4); }
}
.ui-hero-evenement:not(.ui-hero-evenement--banniere) .ui-hero-evenement-pied { margin-top: var(--space-4); }

/* LA PAGE D'UNE SORTIE (30 sept. 2026), refaite sur le modèle de la page des
   évènements, et sur SES pistes : le rail de 240px à gauche (l'agenda seul,
   comme là-bas), puis le contenu. « En pratique » prend à droite la piste de
   300px du fil — la colonne d'appoint du produit, pas une colonne de plus.
   Les onglets de la sortie sont dans son en-tête, pas dans le rail.
     À propos, Discussions : 240 · contenu · 300 ;
     Participants          : 240 · contenu · 300 aussi, trois cartes par
                             rangée.
   Sous 1200px, « En pratique » descend sous le contenu ; sous 900px, le rail
   se retire comme sur la page des évènements. */
@media (width > 900px) {
  .ui-liste-section--detail > .ui-liste-inner:has(> .ui-liste-apart),
  .ui-fil--detail > .ui-fil-inner {
    grid-template-columns: 240px minmax(0, 1fr) 300px;
    justify-content: stretch;
  }
  .ui-liste-section--detail > .ui-liste-inner > .ui-liste-rail,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-rail--gauche { grid-column: 1; grid-row: 1 / span 2; }
  .ui-liste-section--detail > .ui-liste-inner > .ui-liste-contenu,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-centre { grid-column: 2 / -1; }
  .ui-liste-section--detail > .ui-liste-inner:has(> .ui-liste-apart) > .ui-liste-contenu,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-centre { grid-column: 2; }
  .ui-liste-section--detail > .ui-liste-inner > .ui-liste-apart,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-rail--droit {
    display: block;
    grid-column: 3;
    grid-row: 1;
    position: sticky;
    top: calc(68px + var(--space-5));
  }
  .ui-liste-section--detail .ui-liste-grille > .ui-grid-item { flex: 0 0 auto; width: 25%; }
  /* À côté d'« En pratique », trois cartes par rangée. */
  .ui-liste-section--detail > .ui-liste-inner:has(> .ui-liste-apart) .ui-liste-grille > .ui-grid-item { width: calc(100% / 3); }
}
@media (900px < width <= 1200px) {
  .ui-liste-section--detail > .ui-liste-inner:has(> .ui-liste-apart),
  .ui-fil--detail > .ui-fil-inner { grid-template-columns: 240px minmax(0, 1fr); }
  .ui-liste-section--detail > .ui-liste-inner:has(> .ui-liste-apart) > .ui-liste-contenu,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-centre { grid-column: 2; }
  .ui-liste-section--detail > .ui-liste-inner > .ui-liste-apart,
  .ui-fil--detail > .ui-fil-inner > .ui-fil-rail--droit { grid-column: 2; grid-row: auto; position: static; }
  .ui-liste-section--detail .ui-liste-grille > .ui-grid-item { width: calc(100% / 3); }
}

/* Les onglets de la sortie : la rangée des vues d'un profil (ui-fil-filtres),
   sous l'en-tête et au-dessus de ce qu'ils commandent. */
.ui-sortie-onglets { min-width: 0; margin: var(--space-4) 0 0; }

/* L'EN-TÊTE D'UNE SORTIE, écrit par le serveur (teteHtml) : la photo, d'où
   elle vient, son titre, puis l'action et les premiers visages. Quand et où
   sont dans « En pratique » (30 sept. 2026). */
.ui-sortie-entete { margin-bottom: var(--space-4); }
/* Le sur-titre est celui de la page des évènements (.ui-eyebrow), à
   l'identique : taille, graisse, teinte. */
.ui-sortie-origine { display: flex; align-items: center; gap: var(--space-2); }
/* 15px de moins que les 22 du sur-titre de la charte (demande du 30 sept.
   2026) : le sur-titre colle au titre qu'il annonce. */
.ui-sortie-entete > .ui-sortie-origine { margin-bottom: var(--space-2); }
.ui-sortie-origine a { color: inherit; }
.ui-sortie-entete .ui-liste-titre { margin-bottom: var(--space-3); }
.ui-sortie-pied { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); }
/* Les gestes (Je participe, Modifier…) en bas à droite de la photo, en
   vis-à-vis de la date. Fond plein : sur une photo, un bouton au contour seul
   ne se lit pas. */
.ui-sortie-actions {
  position: absolute;
  inset-block-end: var(--space-3);
  inset-inline-end: var(--space-3);
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
}
.ui-sortie-actions .ui-btn-outline { background: var(--surface-card); }
.ui-sortie-actions form { display: contents; }
.ui-sortie-presents { display: flex; align-items: center; gap: var(--space-2); margin: 0; font-size: var(--fs-sm); }
.ui-sortie-presents strong { display: block; }
.ui-sortie-visages { display: flex; }
.ui-sortie-visages img {
  width: 36px;
  height: 36px;
  border: 2px solid var(--white);
  border-radius: var(--radius-photo);
  object-fit: cover;
}
.ui-sortie-visages img + img { margin-left: calc(var(--space-2) * -1); }

/* La photo, tout en haut (ordre demandé le 30 sept. 2026). */
.ui-sortie-photo { position: relative; margin: 0 0 var(--space-4); }  /* repères des pastilles */
.ui-sortie-photo img { display: block; width: 100%; aspect-ratio: 20 / 9; object-fit: cover; border-radius: var(--radius-photo); }
/* La même HAUTEUR sur les trois onglets : Participants est plus large (pas
   d'encart à droite), et un format fixe y grandissait la photo. 286px, c'est
   le 16:9 de la colonne de 636px moins 20 %. */
@media (width > 1200px) {
  .ui-sortie-photo img { height: 286px; aspect-ratio: auto; }
}
/* Au téléphone, un format plus haut : lieu, date et bouton y tiennent. */
@media (width <= 576px) {
  .ui-sortie-photo img { aspect-ratio: 4 / 3; }
}
.ui-sortie-apropos { margin-top: var(--space-2); }
.ui-sortie-description p:last-child { margin-bottom: 0; }

/* « EN PRATIQUE » EST LA JUMELLE DE L'AGENDA (30 sept. 2026) : même carte —
   filet gris, arrondi, fond, marge intérieure, sans ombre — et même titre,
   de part et d'autre du contenu. Elle reste fixe au défilement, comme lui
   (position sticky de sa colonne, plus haut). Le reste de la page s'en tient
   au sur-titre rose, au titre de la sortie et aux sous-titres de groupe.
   La page publique d'un évènement garde sa carte et ses capitales. */
:is(.ui-liste-section--detail, .ui-fil--detail) .ui-evenement-pratique {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-card);
  box-shadow: none;
  padding: var(--space-3);
}
:is(.ui-liste-section--detail, .ui-fil--detail) .ui-evenement-pratique > .ui-eyebrow {
  margin: 0 0 var(--space-3);
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: normal;
  text-transform: none;
}
:is(.ui-liste-section--detail, .ui-fil--detail) .ui-evenement-pratique > ul { margin-bottom: 0; }
:is(.ui-liste-section--detail, .ui-fil--detail) .ui-evenement-pratique > ul > li:last-child { padding-bottom: 0; }
/* Les intitulés (Date et heure, Lieu…) : 14px, gras, noirs. */
:is(.ui-liste-section--detail, .ui-fil--detail) .ui-evenement-pratique strong {
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: 700;
}

/* L'ÉCHELLE DU DS, PAS DE TAILLE FLUIDE (audit typographique du 30 sept.
   2026). Les grands titres qui glissaient en clamp() tombent désormais sur
   les jetons, par paliers : display → h1 → h2 à mesure que l'écran se
   resserre. */
@media (width <= 820px) {
  .ui-footer-cta .ui-section-title { font-size: var(--fs-h1); }
}
@media (width <= 560px) {
  .ui-footer-cta .ui-section-title { font-size: var(--fs-h2); }
  .ui-liste-titre { font-size: var(--fs-h3); }
  .ui-monprofil-nom { font-size: var(--fs-h4); }
}

/* LE MODE SWIPE (30 sept. 2026) — réglé dans les paramètres, smartphone
   seulement pour l'instant. La même grille de cartes profil, rangée en une
   seule ligne qu'on fait passer d'un geste : une carte par écran, une seule
   par geste (setupSwipe). Le portrait
   s'allonge, comme dans une app de rencontre. « Voir plus » disparaît : la
   suite se charge seule, trois cartes avant la fin (setupSwipe). */
@media (width <= 576px) {
  /* PAS DE DÉFILEMENT NATIF (30 sept. 2026) : un élan trop fort y faisait
     passer plusieurs profils. La rangée est déplacée par setupSwipe, qui suit
     le doigt avec de la résistance et n'avance que d'UNE carte par geste ; le
     défilement vertical de la carte, lui, reste au navigateur (pan-y). */
  .ui-liste-section--swipe .ui-liste-contenu { overflow: hidden; }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille {
    flex-wrap: nowrap;
    touch-action: pan-y;
    will-change: transform;
  }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille.is-anime { transition: transform 0.35s cubic-bezier(0.25, 0.8, 0.25, 1); }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille > .ui-grid-item { flex: 0 0 100%; }
  /* LA PHOTO EN CARTE (30 sept. 2026) : carrée, arrondie, avec l'ombre des
     cartes, posée sur le fond de page ; les renseignements viennent dessous
     en mini-cartes. (Une photo plein écran sur fond foncé, essayée avant,
     cassait avec le reste du site.) */
  .ui-liste-section--swipe .ui-profil-photo {
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--radius) var(--radius) 0 0;
    box-shadow: var(--shadow-md);
  }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-plus { display: none; }
  /* L'écran est à la carte : ni titre, ni chapô, ni filtre (30 sept. 2026). */
  .ui-liste-section--swipe .ui-liste-tete,
  .ui-liste-section--swipe .ui-liste-lead,
  .ui-liste-section--swipe .ui-fil-filtrer { display: none; }
  /* LA CARTE REMPLIT L'ÉCRAN entre l'en-tête (74px : ligne 3 + 71) et la barre du bas
     (65px, qui reste fixe) ; son contenu défile en hauteur : la photo, puis
     les renseignements. */
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille > .ui-grid-item {
    height: calc(100dvh - 74px - 65px - env(safe-area-inset-bottom));
    overflow-y: auto;
    /* « none » et non « contain » : ni rebond ni report sur la page quand la
       carte est en haut ou en bas de ses renseignements (30 sept. 2026). */
    overscroll-behavior-y: none;
    scrollbar-width: none;
  }
  /* La carte suit son contenu (la colonne, en flex, l'étirait à sa hauteur
     et rognait les renseignements) : c'est la colonne qui défile. */
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille > .ui-grid-item > .ui-profil { align-self: flex-start; height: auto; min-height: 100%; }
  .ui-section.ui-liste-section.ui-liste-section--swipe { padding-block: 0; }
  /* BORD À BORD (30 sept. 2026) : la carte prend toute la largeur de l'écran,
     sans marge, sans gouttière, sans arrondi ni ombre — l'écran EST la carte. */
  .ui-liste-section--swipe > .ui-liste-inner { padding-inline: 0; }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille { --bs-gutter-x: 0; --bs-gutter-y: 0; margin: 0; }
  .ui-liste-section--swipe .ui-liste-contenu > .ui-liste-grille > .ui-grid-item > .ui-profil { border: 0; border-radius: 0; box-shadow: none; }
}
/* Son taux (selon SES critères) n'est donné qu'au smartphone, dans la carte
   swipe ; la liste de bureau garde son seul taux (30 sept. 2026). */
@media (width > 576px) {
  .ui-liste-section--swipe:not(.ui-liste-section--deux-taux) .ui-profil-score--ses { display: none; }
}
/* LA PAGE FIGÉE (30 sept. 2026) : en swipe, elle tient exactement dans
   l'écran ; on lui retire le défilement et le rebond du navigateur (iOS
   surtout), qui faisaient « flotter » la carte quand on tirait vers le haut
   ou le bas. Seul l'intérieur de la carte défile encore. */
@media (width <= 576px) {
  html:has(.ui-liste-section--swipe),
  body:has(.ui-liste-section--swipe) {
    overflow: hidden;
    overscroll-behavior: none;
  }
}
/* Pas de pied de page sous les cartes en swipe : la page s'arrête à elles. */
@media (width <= 576px) {
  body:has(.ui-liste-section--swipe) .ui-footer { display: none; }
}

/* ══════════ LE SMARTPHONE (30 sept. 2026) ══════════
   Retouches demandées pour le mobile seulement : un en-tête plus haut, des
   boutons ronds de 48px (une vraie cible tactile) et un burger plus grand. */
@media (width <= 576px) {
  .ui-header-membre .ui-header-membre-inner { min-height: 70px; }
  .ui-membre-rond,
  .ui-membre-burger,
  .ui-membre-avatar,
  .ui-profil-passe,
  .ui-profil-favori,
  .ui-profil-like,
  .ui-profil-message,
  .ui-monprofil-ronds .ui-monprofil-rond,
  .ui-icon-button.ui-icon-button-slim {
    width: var(--control-h-tactile);
    height: var(--control-h-tactile);
  }
  .ui-membre-rond .material-symbols-outlined,
  .ui-profil-passe .material-symbols-outlined,
  .ui-profil-favori .material-symbols-outlined,
  .ui-profil-like .material-symbols-outlined,
  .ui-profil-message .material-symbols-outlined { font-size: var(--icon-lg); }
  .ui-membre-burger .material-symbols-outlined { font-size: var(--icon-xl); }

  /* 16px ENTRE LES BOUTONS DE L'EN-TÊTE (30 sept. 2026) : à 8, trois
     cibles de 48 se touchaient presque ; les normes tactiles demandent un
     vrai écart entre deux cibles voisines. */
  .ui-header-membre-compte { gap: var(--space-3); }

  /* LES LIENS DES MENUS, EN CIBLES TACTILES : 48px de haut au moins
     (panneaux de l'en-tête, menu du burger, menus « Trier par »), au lieu de
     37 à 44. */
  .ui-membre-sous-nav-lien,
  .ui-membre-notif,
  .ui-menu-mobile-lien,
  .dropdown-menu .dropdown-item {
    min-height: var(--control-h-tactile);
    align-items: center;
  }
  .dropdown-menu .dropdown-item { display: flex; }

  /* LA FENÊTRE « MES CRITÈRES », AU DOIGT : les pastilles à 44px au lieu
     de 39, l'interrupteur « Important » sur une zone de 44px au lieu de 24,
     les boutons du pied à 48. */
  .ui-criteres-case > span { min-height: 2.75rem; }
  .ui-criteres-important { min-height: 2.75rem; display: inline-flex; align-items: center; }
  .ui-criteres-pied .ui-btn { min-height: var(--control-h-tactile); }
}

/* LA VIGNETTE DU COMPTE QUITTE L'EN-TÊTE sur téléphone et tablette
   (1er oct. 2026) : « Mon profil », tout à droite de la barre du bas, ouvre
   le même menu. */
@media (width <= 900px) {
  .ui-header-membre-compte > .ui-membre-compte-menu:not(.ui-membre-notifs) { display: none; }
}

/* LA BARRE DU BAS, DISCRÈTE (30 sept. 2026) : un fond clair et translucide
   — le pétrole plein ressortait trop, et Safari le reprenait sous la barre
   d'adresse, en un gros pavé vert. C'est l'entrée courante qui se voit : son
   picto dans une pastille teintée, son libellé en pétrole. Elle n'existe
   qu'au mobile et à la tablette. */
@media (width <= 900px) {
  .ui-membre-barre-bas {
    border-top: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface-card) 90%, transparent);
    backdrop-filter: blur(16px) saturate(1.4);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    box-shadow: none;
  }
  .ui-membre-barre-bas-lien .material-symbols-outlined {
    border-radius: var(--radius-pill);
    padding: 2px var(--space-3);
    transition: background-color 0.2s ease;
  }
  .ui-membre-barre-bas-lien.is-current,
  .ui-membre-barre-bas-groupe.is-open > .ui-membre-barre-bas-lien { color: var(--color-titre); font-weight: 600; }
  .ui-membre-barre-bas-lien.is-current .material-symbols-outlined,
  .ui-membre-barre-bas-groupe.is-open > .ui-membre-barre-bas-lien .material-symbols-outlined {
    background: color-mix(in srgb, var(--color-titre) 12%, transparent);
  }
}

/* LES ACTIONS DU SWIPE DANS UNE PASTILLE (30 sept. 2026) : au bas de la
   photo, centrées, dans un conteneur arrondi au fond blanc translucide. Des
   ronds blancs s'y noyaient, et le verre sombre essayé ensuite n'allait pas
   mieux : ce sont les BOUTONS qui prennent la couleur, pleins — encre pour
   « Masquer », pétrole pour « Contact », rose du logo pour le like. Au
   smartphone et sur la page swipe seulement. */
@media (width <= 576px) {
  .ui-liste-section--swipe .ui-profil-actions {
    right: auto;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    max-width: calc(100% - var(--space-5));
    gap: var(--space-3);
    border-radius: var(--radius-pill);
    /* BLANC TRANSLUCIDE (25 %, flouté), sans filet. */
    background: color-mix(in srgb, var(--white) 25%, transparent);
    padding: var(--space-2);
    box-shadow: var(--shadow-md);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
  }
  /* 48px entre « Masquer » et « Contact » : l'écart de 16 plus 32 de marge —
     écarter n'est pas de la même famille que les deux autres. (80 d'abord,
     puis 30 de moins demandés : 50, ramenés au 48 de l'échelle.) */
  .ui-liste-section--swipe .ui-profil-passe { margin-right: calc(var(--space-3) * 2); }

  /* Les couleurs pleines des boutons : la règle commune (ui-charte). */
}

/* LA CARTE SWIPE (30 sept. 2026) — sur le fond classique de la page :
   1. la CARTE PROFIL STANDARD : la photo, puis le nom, la région et la
      dernière connexion dans le bloc blanc dessous ;
   2. « Glisse pour changer de profil » et ses deux flèches sous la photo ;
   3. les renseignements en MINI-CARTES blanches (ui-molecules) : le haut de
      la fiche en pleine largeur, le reste en deux colonnes ;
   4. la pastille des actions, au bas de la photo.
   L'en-tête et la barre du bas restent ceux du site. (Des versions sombres
   — fond encre, puis noir, en-tête pétrole — ont été essayées et écartées.) */
@media (width <= 576px) {
  .ui-liste-section--swipe .ui-profil {
    background: transparent;
    /* 20px au-dessus de la photo, 16 sur les côtés. */
    padding: calc(var(--space-3) + var(--space-1)) var(--space-3) var(--space-3);
  }

  /* LA CARTE PROFIL STANDARD (30 sept. 2026) : sous la photo, le bloc
     blanc du nom, de la région et de la dernière connexion, arrondi en bas —
     le nom n'est plus posé sur la photo. */
  .ui-liste-section--swipe .ui-profil-entete {
    position: relative;
    z-index: 2;
    border-radius: 0 0 var(--radius) var(--radius);
    background: var(--surface-card);
    padding: var(--space-4);
    box-shadow: var(--shadow-md);
    color: inherit;
    /* La dernière connexion à droite de la région, sur la même ligne. */
    display: flex;
    flex-wrap: wrap;
    column-gap: var(--space-3);
  }
  .ui-liste-section--swipe .ui-profil-entete > .ui-profil-titre { flex: 0 0 100%; }
  .ui-liste-section--swipe .ui-profil-entete > p { margin: var(--space-2) 0 0; }

  .ui-liste-section--swipe .ui-profil-corps {
    position: relative;
    z-index: 2;
    padding: 0 0 var(--space-5);
  }
  /* LA PASTILLE DES ACTIONS AU BAS DE LA PHOTO (1er oct. 2026), centrée,
     sans rester fixe à l'écran : elle défile avec la carte. */
  .ui-liste-section--swipe .ui-profil-actions { z-index: 3; bottom: var(--space-3); }
  /* Au toucher, le survol de la carte la soulevait. */
  .ui-liste-section--swipe .ui-profil,
  .ui-liste-section--swipe .ui-profil:hover { transform: none; }
}

/* LA BASCULE LISTE / SWIPE DE L'EN-TÊTE (1er oct. 2026) : au smartphone
   seulement — la présentation swipe n'existe pas au-delà. */
.ui-membre-vue { display: none; margin: 0; }
@media (width <= 576px) {
  .ui-membre-vue { display: contents; }
}

/* LES FLÈCHES DU SWIPE (30 sept. 2026) : au toucher, une pastille
   translucide dit dans quel sens on glisse ; elle s'efface au lâcher
   (setupSwipe). */
@media (width <= 576px) {
  .ui-liste-section--swipe .ui-liste-contenu { position: relative; }
  /* AU CENTRE DE LA PHOTO (4 oct. 2026) : le rond paraît au milieu de la
     photo carrée (16px de marge autour), quelle que soit la direction — les
     deux s'y superposent, seul celui du geste en cours s'allume — et il
     grandit de 10 %. Une rangée de hauteur nulle posée à mi-photo : le rond
     s'y centre, autant au-dessus qu'au-dessous. */
  .ui-swipe-indice {
    position: absolute;
    inset-inline: 0;
    top: calc(var(--space-3) + var(--space-1) + (100vw - var(--space-3) * 2) / 2);
    z-index: 3;
    display: grid;
    grid-template-rows: 0;
    place-items: center;
    pointer-events: none;
  }
  .ui-swipe-indice > span {
    grid-area: 1 / 1;
    display: grid;
    place-items: center;
    width: calc(var(--control-h-tactile) * 2.2);
    height: calc(var(--control-h-tactile) * 2.2);
    border-radius: 50%;
    background: color-mix(in srgb, var(--white) 55%, transparent);
    color: var(--color-titre);
    box-shadow: var(--shadow-md);
    backdrop-filter: blur(4px);
    opacity: 0;
    transition: opacity 0.2s ease;
  }
  .ui-swipe-indice > span .material-symbols-outlined { font-size: var(--icon-2xl); }
  .ui-swipe-indice.vers-avant > .ui-swipe-indice-avant,
  .ui-swipe-indice.vers-apres > .ui-swipe-indice-apres { opacity: 1; }
}

/* ══════════ MON COMPTE (1er oct. 2026) ══════════
   UN SEUL ARRONDI POUR LES CARTES : --radius-md (18px), celui du composant
   carte standard (.ui-carte) et des publications — la complétion comme les
   cartes des pages ; les boutons gardent le leur. LES ÉCARTS SUIVENT LA
   GRILLE : 24px, la gouttière du site.
   organismes/section-compte : la page de « Mon profil » de la barre du bas.
   Une carte d'identité (photo, nom, lien vers mon profil), la complétion de
   ma fiche en barre, puis les pages du compte en cartes, deux par rangée. */
/* En plein écran : ni l'en-tête du site ni le pied de page ; la barre du
   bas, elle, reste (1er oct. 2026). Le haut : l'en-tête d'app (plus bas). */
.ui-section.ui-compte-section {
  position: relative;
  min-height: 100dvh;
  overflow: hidden;
  padding-block: 0 var(--space-5);
}
/* Le fond : .ui-fond-app (ui-charte), posé par le snippet. */
.ui-compte-section > .ui-container { position: relative; z-index: 1; }

/* LA PHOTO AU CENTRE, À LA MANIÈRE DES APPS (1er oct. 2026) : carrée à
   coins arrondis, cerclée de pétrole, sur un halo ; le nom dessous, le badge
   « vérifié » à sa suite ; un crayon pour la changer sur place. */
.ui-compte-tete {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: var(--space-4);
  padding: var(--space-4) 0 var(--space-3);
  background: radial-gradient(closest-side, color-mix(in srgb, var(--color-titre) 12%, transparent), transparent);
  text-align: center;
}
.ui-compte-photo-cadre { position: relative; }
.ui-compte-photo {
  display: block;
  width: 9rem;
  height: 9rem;
  border: 3px solid var(--white);
  border-radius: var(--radius);
  outline: 3px solid var(--color-titre);
  object-fit: cover;
  box-shadow: var(--shadow-md);
}
.ui-compte-photo-form { margin: 0; }
/* La photo et le nom mènent à mon profil (1er oct. 2026) — sans effet au
   survol. */
.ui-compte-vers-profil,
.ui-compte-vers-profil:hover { display: block; color: inherit; text-decoration: none; }
.ui-compte-nom .ui-compte-vers-profil { display: inline; }
.ui-compte-photo-modifier {
  position: absolute;
  top: calc(var(--space-2) * -1);
  right: calc(var(--space-2) * -1);
  display: grid;
  place-items: center;
  width: var(--control-h-sm);
  height: var(--control-h-sm);
  border-radius: 50%;
  background: var(--white);
  color: var(--color-titre);
  box-shadow: var(--shadow-md);
  cursor: pointer;
}
.ui-compte-photo-modifier:focus-within { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.ui-compte-photo-modifier .material-symbols-outlined { font-size: var(--icon-md); }
.ui-compte-nom {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  color: var(--color-dark);
  font-size: var(--fs-h3);
}
.ui-compte-verifie {
  color: var(--color-titre);
  font-size: var(--icon-lg);
  font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.ui-compte-meta {
  margin: var(--space-1) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.ui-compte-lien {
  margin-top: var(--space-2);
  color: var(--color-primary);
  font-size: var(--fs-sm);
  font-weight: 700;
}
.ui-compte-alerte {
  margin: var(--space-3) 0 0;
  color: var(--color-danger);
  font-size: var(--fs-sm);
}

.ui-compte-completion {
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-4);
  box-shadow: var(--shadow-md);
}
.ui-compte-completion { margin-top: var(--space-3); }
.ui-compte-completion-tete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
  color: var(--text);
  font-size: var(--fs-sm);
}
.ui-compte-completion-tete strong { color: var(--color-titre); font-size: var(--fs-base); }
/* La barre : un <progress>, redessiné — rail neutre, remplissage vert. */
.ui-compte-barre {
  display: block;
  width: 100%;
  height: 4px;
  border: 0;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--teinte-neutre);
  appearance: none;
}
.ui-compte-barre::-webkit-progress-bar { background: var(--teinte-neutre); }
.ui-compte-barre::-webkit-progress-value { border-radius: var(--radius-pill); background: var(--color-success); }
.ui-compte-barre::-moz-progress-bar { border-radius: var(--radius-pill); background: var(--color-success); }
.ui-compte-completion-texte {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.ui-compte-completion-texte a { color: var(--color-primary); font-weight: 700; }

.ui-compte-cartes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}
.ui-compte-carte {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  height: 100%;
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-3);
  color: var(--text);
  text-decoration: none;
  box-shadow: var(--shadow-md);
  transition: box-shadow 0.2s ease;
}
.ui-compte-carte:hover,
.ui-compte-carte:focus-visible { box-shadow: var(--shadow-lg); }
/* Ni soulignement ni changement de couleur des textes, au survol comme au
   clic : seule l'ombre de la carte réagit (1er oct. 2026). */
/* « .ui-compte-cartes » devant : la règle des liens de texte courant
   (li:not([class]) a:hover, ui-fondations) l'emportait, et soulignait. */
.ui-compte-cartes .ui-compte-carte:is(:hover, :focus, :focus-visible, :active),
.ui-compte-cartes .ui-compte-carte:is(:hover, :focus, :focus-visible, :active) * {
  color: inherit;
  text-decoration: none;
}
.ui-compte-carte:is(:hover, :focus, :focus-visible, :active) .ui-compte-carte-titre { color: var(--color-titre); }
.ui-compte-carte:is(:hover, :focus, :focus-visible, :active) .ui-compte-carte-texte { color: var(--text-muted); }
.ui-compte-carte:is(:hover, :focus, :focus-visible, :active) > .material-symbols-outlined { color: var(--color-primary); }
.ui-compte-carte > .material-symbols-outlined {
  margin-bottom: var(--space-1);
  color: var(--color-primary);
  font-size: var(--icon-lg);
}
.ui-compte-carte-titre {
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: 700;
}
.ui-compte-carte-texte {
  color: var(--text-muted);
  font-size: var(--fs-caption);
}
.ui-compte-sortie {
  width: 100%;
  margin-top: var(--space-4);
}

/* LES RÉGLAGES MOBILES DE L'EN-TÊTE (1er oct. 2026) : des ronds-liens au
   téléphone seulement, et la cloche retirée au téléphone sur demande. */
.ui-membre-rond--mobile { display: none; }
@media (width <= 576px) {
  .ui-page-membre .ui-header-membre .ui-membre-rond--mobile { display: grid; }
  .ui-header-membre--sans-cloche .ui-membre-notifs { display: none; }
  .ui-header-membre--sans-mobile { display: none; }
}

/* LA PAGE SWIPE COMME MON COMPTE (1er oct. 2026), au smartphone : le même
   fond vivant, fixé. */
@media (width <= 576px) {
  body:has(.ui-liste-section--swipe) {
    background: var(--fond-app);
    background-attachment: fixed;
  }
  body:has(.ui-liste-section--swipe) .ui-section.ui-liste-section--swipe { background: transparent; }
}

/* ══════════ TOUTES LES PAGES MEMBRE AU TÉLÉPHONE (1er oct. 2026) ══════════
   L'en-tête du site prend EXACTEMENT les mesures de l'en-tête d'app
   (organismes/header-app) : 71px, 16px de bord, burger de 48px tiré de 8px
   vers le bord, logo de 112px collé au burger et remonté de 3px, ronds de
   48px espacés de 8px.
   Transparent et non fixé. Et partout, le fond texturé des écrans d'app
   (--fond-app). C'est LE seul en-tête du téléphone : l'en-tête d'app
   séparé (header-app) y a été fusionné. */
@media (width <= 576px) {
  body.ui-page-membre {
    background: var(--fond-app);
    background-attachment: fixed;
  }
  /* Un seul fond : celui de la page. Mon compte portait le sien en plus, qui
     ne s'alignait pas et dessinait une bande sous l'en-tête. */
  .ui-page-membre .ui-fond-app { background: transparent; }
  /* Comme l'en-tête d'app : transparent, et il défile avec la page (le
     voile fixé, essayé, faisait une bande différente). */
  .ui-page-membre .ui-header-membre {
    position: relative;
    border-bottom: 0;
    background: transparent;
    box-shadow: none;
  }
  .ui-page-membre .ui-header-membre .ui-header-membre-inner {
    flex-wrap: nowrap;
    gap: 0;
    height: 71px;
    min-height: 0;
    padding: 0 var(--space-3);
  }
  .ui-page-membre .ui-header-membre .ui-membre-burger {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: var(--control-h-tactile);
    height: var(--control-h-tactile);
    margin: 0 0 0 calc(var(--space-2) * -1);
    border: 0;
    background: none;
    padding: 0;
    color: var(--color-titre);
  }
  .ui-page-membre .ui-header-membre .ui-membre-burger .material-symbols-outlined { font-size: var(--icon-xl); }
  .ui-page-membre .ui-header-membre .ui-header-membre-marque { flex: 0 0 auto; margin: 0; translate: 0 -3px; }
  .ui-page-membre .ui-header-membre .ui-header-membre-marque img { width: 144px; margin: 0; }
  .ui-page-membre .ui-header-membre .ui-header-membre-compte { gap: var(--space-2); }
  .ui-page-membre .ui-header-membre .ui-membre-rond {
    width: var(--control-h-tactile);
    height: var(--control-h-tactile);
    border-radius: var(--radius-photo);
    background: var(--surface-douce);
    color: var(--color-titre);
  }
  .ui-page-membre .ui-header-membre .ui-membre-rond .material-symbols-outlined { font-size: var(--icon-lg); }
}

/* LE TITRE DE LA LISTE DES DISCUSSIONS, au dessin des sur-titres de page
   (« Profils » sur la recherche) — 1er oct. 2026. */
.ui-discussions-titre { margin-bottom: var(--space-3); }

/* LES DISCUSSIONS EN CARTES, AU TÉLÉPHONE (1er oct. 2026) : plus de panneau
   blanc autour de la liste — le fond texturé passe — et chaque conversation
   devient une petite carte (le rayon des cartes, leur ombre), toutes
   pareilles — pas de liseré sur la conversation ouverte. */
@media (width <= 576px) {
  .ui-fil-encart:has(> .ui-conv) {
    border: 0;
    background: transparent;
    padding-inline: var(--space-3);
  }
  .ui-fil-encart:has(> .ui-conv) > .ui-conv {
    margin-bottom: var(--space-2);
    border-radius: var(--radius-md);
    background: var(--surface-card);
    padding: var(--space-3);
    box-shadow: var(--shadow-md);
  }
  .ui-fil-encart:has(> .ui-conv) > .ui-conv:hover,
  .ui-fil-encart:has(> .ui-conv) > .ui-conv:focus-visible { background: var(--surface-card); box-shadow: var(--shadow-lg); }
  .ui-fil-encart:has(> .ui-conv) > .ui-conv.is-current { background: var(--surface-card); }
  /* Plus lisible dans la carte (1er oct. 2026) : le nom en 18, l'extrait en
     16, l'heure en 14, la photo en 50. */
  .ui-fil-encart:has(> .ui-conv) > .ui-conv { gap: var(--space-3); }
  .ui-fil-encart:has(> .ui-conv) .ui-conv-avatar { width: 3.125rem; height: 3.125rem; }
  .ui-fil-encart:has(> .ui-conv) .ui-conv-nom { font-size: var(--fs-lead); }
  .ui-fil-encart:has(> .ui-conv) .ui-conv-extrait { font-size: var(--fs-base); }
  .ui-fil-encart:has(> .ui-conv) .ui-conv-heure { font-size: var(--fs-sm); }
  /* L'extrait ne se tronque que si la chaîne de flex autour accepte de
     rétrécir : sans ce min-width, la liste déborde de l'écran. */
  .ui-fil--liste,
  .ui-fil--liste .ui-fil-inner,
  .ui-fil--liste .ui-fil-rail { min-width: 0; }
}

/* PAS DE TEXTE D'INTRODUCTION SOUS LE TITRE, AU TÉLÉPHONE (1er oct. 2026) :
   sur les pages membre, le chapô des listes et des espaces s'efface — le
   titre suffit, et l'écran va droit au contenu. */
@media (width <= 576px) {
  .ui-page-membre :is(.ui-liste-lead, .ui-mur-lead) { display: none; }
}

/* PAS DE PIED DE PAGE AU TÉLÉPHONE, UNE FOIS CONNECTÉ (1er oct. 2026) : la
   barre du bas porte la navigation ; le pied n'y ajoutait qu'une longue
   descente. */
@media (width <= 576px) {
  .ui-page-membre .ui-footer { display: none; }
}

/* SOUS LES ONGLETS D'UN PROFIL, AU TÉLÉPHONE (1er oct. 2026) : 30px de moins
   au-dessus de la première carte — 18 de marge plus les 16 de la grille, au
   lieu de 48 plus 16. */
@media (width <= 767px) {
  .ui-monprofil-inner > .ui-monprofil-onglets { margin-bottom: calc(var(--space-3) + 2px); }
}

/* VALIDÉ (1er oct. 2026) : le fond texturé du téléphone aussi sur ordinateur,
   pour toutes les pages membre — les sections et l'en-tête laissent passer
   le fond. */
@media (width > 576px) {
  body.ui-page-membre {
    background: var(--fond-app);
    background-attachment: fixed;
  }
  .ui-page-membre main .ui-section,
  .ui-page-membre .ui-fond-app { background: transparent; }
  /* L'en-tête aussi : transparent et sans filet en haut de page ; il reste
     fixé, et reprend son fond blanc dès qu'on défile (.is-defile, posée par
     ui-main.js). */
  .ui-page-membre .ui-header-membre {
    border-bottom-color: transparent;
    background: transparent;
    transition: background-color 0.2s ease, border-color 0.2s ease;
  }
  .ui-page-membre .ui-header-membre.is-defile {
    border-bottom-color: var(--border);
    background: var(--white);
  }
  /* Plus de contraste sur le fond texturé (1er oct. 2026) : les menus de
     l'en-tête et du rail de gauche passent en pétrole. */
  .ui-page-membre .ui-membre-nav-lien:not(.is-current),
  .ui-page-membre .ui-membre-onglet,
  .ui-page-membre .ui-membre-onglet:hover,
  .ui-page-membre .ui-membre-onglet:focus-visible { color: var(--color-titre); }
}

/* SOUS L'EN-TÊTE, SUR ORDINATEUR (1er oct. 2026) : 10px de moins entre
   l'en-tête et le contenu, sur toutes les pages membre. La bannière d'un
   profil, collée à l'en-tête jusqu'à 1440px, n'est pas concernée. */
@media (width > 900px) {
  .ui-page-membre main > .ui-liste-section:first-child:not(:has(.ui-monprofil-identite)),
  .ui-page-membre main > .ui-fil:first-child { padding-top: calc(var(--space-5) - 10px); }
  .ui-page-membre .ui-compte-tete { margin-top: calc(var(--space-4) - 10px); }
}
@media (width > 1440px) {
  .ui-page-membre main > .ui-liste-section:first-child:has(.ui-monprofil-identite) { padding-top: calc(var(--space-5) - 10px); }
}

/* LES BOUTONS D'ÉDITION DES CARTES DU PROFIL, AU TÉLÉPHONE (1er oct. 2026) :
   toujours affichés, mais discrets — une pastille de 28px dans l'angle haut
   droit, teinte douce sans ombre ni filet. Les photos gardent leur grand
   bouton, toujours affiché. La zone de
   toucher garde ses 48px par un débord invisible (::after). */
@media (width <= 767px) {
  .ui-monprofil-inner :is(.ui-monprofil-presentation, .ui-monprofil-fiche-ligne, .ui-monprofil-citation, .ui-monprofil-interets) > .ui-monprofil-editer {
    top: var(--space-2);
    right: var(--space-2);
    width: 1.75rem;
    min-width: 0;
    height: 1.75rem;
    min-height: 0;
    border: 0;
    background: var(--surface-douce);
    color: var(--color-titre);
    box-shadow: none;
    opacity: 1;
  }
  .ui-monprofil-inner :is(.ui-monprofil-presentation, .ui-monprofil-fiche-ligne, .ui-monprofil-citation, .ui-monprofil-interets) > .ui-monprofil-editer::after {
    content: "";
    position: absolute;
    inset: -10px;
  }
  .ui-monprofil-inner :is(.ui-monprofil-presentation, .ui-monprofil-fiche-ligne, .ui-monprofil-citation, .ui-monprofil-interets) > .ui-monprofil-editer > .material-symbols-outlined { font-size: var(--icon-sm); }
}

/* Relation et personnalité en badges (1er oct. 2026) : 14px sous
   l'intitulé. */
.ui-monprofil-fiche-ligne > dd.ui-monprofil-tags { margin-top: calc(var(--space-3) - 2px); }

/* LES NOTIFICATIONS AU TÉLÉPHONE, EN PLEINE PAGE (4 oct. 2026), sur le
   modèle de la fenêtre « Mes critères » : un écran blanc entier, l'en-tête
   « Notifications » et sa croix ronde, « Tout marquer comme lu », puis les
   notifications séparées par un filet — comme les groupes des critères. Les
   cartes du 1er oct. sur fond texturé sont remplacées. Non lue : l'aplat rose
   très dilué et son point restent. La page dessous ne défile plus
   (html.is-notifs-ouvertes, ui-main.js). */
.ui-membre-notifs-entete { display: none; }
@media (width <= 640px) {
  .ui-membre-notifs .ui-membre-sous-nav--notifs {
    position: fixed;
    inset: 0;
    z-index: 1060;
    width: auto;
    max-height: none;
    height: 100dvh;
    overflow-y: auto;
    border: 0;
    border-radius: 0;
    background: var(--white);
    padding: 0 0 var(--space-5);
    box-shadow: none;
  }
  .ui-membre-notifs-entete {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3);
    border-bottom: 1px solid var(--border);
    background: var(--white);
  }
  .ui-membre-notifs-titre {
    margin: 0;
    color: var(--text);
    font-size: var(--fs-h5);
    font-weight: var(--fw-bold);
  }
  .ui-membre-notifs-tete { padding: var(--space-2) var(--space-3) 0; }
  .ui-membre-notifs-liste > li + li { border-top: 1px solid var(--border); }
  .ui-membre-notif {
    gap: var(--space-3);
    border-radius: 0;
    padding: var(--space-3);
    font-size: var(--fs-base);
  }
  .ui-membre-notif-avatar,
  .ui-membre-notif-icone { width: 3.125rem; height: 3.125rem; }
  .ui-membre-notif-quand { font-size: var(--fs-sm); }
  html.is-notifs-ouvertes,
  html.is-notifs-ouvertes body { overflow: hidden; }
  /* Le panneau vit dans l'en-tête, dont le z-index (1030) est celui de la
     barre du bas : l'en-tête passe devant elle le temps de la page. */
  html.is-notifs-ouvertes .ui-header-membre { z-index: 1060; }
}

/* « REVENIR EN HAUT », AU TÉLÉPHONE (1er oct. 2026) : le rond sombre du
   design system, posé au-dessus de la barre du bas, à droite. Il paraît dès
   qu'on a défilé (.is-visible, ui-main.js) et ramène en haut en douceur. */
.ui-haut-page {
  display: none;
  place-items: center;
  width: var(--control-h-tactile);
  height: var(--control-h-tactile);
  border-radius: 50%;
  background: var(--color-dark);
  color: var(--white);
  box-shadow: var(--shadow-md);
  text-decoration: none;
}
.ui-haut-page:hover,
.ui-haut-page:focus-visible { color: var(--white); }
.ui-haut-page .material-symbols-outlined { font-size: var(--icon-md); }
@media (width <= 900px) {
  .ui-haut-page {
    position: fixed;
    inset: auto var(--space-3) calc(64px + var(--space-3) + env(safe-area-inset-bottom)) auto;
    z-index: 1029;
    display: grid;
    opacity: 0;
    visibility: hidden;
    transform: translateY(var(--space-2));
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
  }
  .ui-haut-page.is-visible { opacity: 1; visibility: visible; transform: none; }
}

/* ══════════ LE TABLEAU DE BORD DE L'ADMINISTRATION (1er oct. 2026) ══════════
   organismes/section-espace, « stats » : des groupes de tuiles — l'icône, le
   chiffre en grand, ce qu'il compte, un détail. Une tuile qui mène quelque
   part est un lien ; ce qui attend une action porte l'aplat rose. */
.ui-espace-stats-groupe + .ui-espace-stats-groupe { margin-top: var(--space-4); }
.ui-espace-stats-titre { margin-bottom: var(--space-3); }
/* L'arrondi standard des cartes (ui-charte.css pose 28 sur .ui-carte). */
.ui-espace-stats .ui-espace-stat { border-radius: var(--radius-md); }
.ui-espace-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ui-espace-stat {
  height: 100%;
  gap: var(--space-1);
  padding: var(--space-4);
  color: var(--text);
  text-decoration: none;
  box-shadow: var(--shadow-md);
  transition: box-shadow 0.2s ease;
}
a.ui-espace-stat:hover,
a.ui-espace-stat:focus-visible { color: var(--text); box-shadow: var(--shadow-lg); }
.ui-espace-stat > .material-symbols-outlined { color: var(--color-titre); font-size: var(--icon-lg); }
.ui-espace-stat-valeur { color: var(--color-titre); font-size: var(--fs-h2); font-weight: var(--fw-bold); line-height: var(--lh-heading); }
.ui-espace-stat-label { color: var(--text); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.ui-espace-stat-detail { color: var(--text-muted); font-size: var(--fs-caption); }
.ui-espace-stat--alerte { border-color: transparent; background: var(--color-brand-pink-soft); }
.ui-espace-stat--alerte > .material-symbols-outlined,
.ui-espace-stat--alerte .ui-espace-stat-valeur { color: var(--color-brand-pink-ink); }
@media (width <= 576px) {
  .ui-espace-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ui-espace-stats .ui-espace-stat { padding: var(--space-3); }
}

/* LES SOUS-MENUS DU RAIL (1er oct. 2026) : en retrait sous leur entrée, au
   corps de 14 comme les autres, le picto au même alignement que le libellé
   du parent. */
.ui-membre-onglets > .ui-membre-onglet--sous { padding-left: calc(var(--space-3) + var(--space-4)); }

/* LE DÉTAIL D'UN SIGNALEMENT (administration, 1er oct. 2026) : la fenêtre
   ouverte depuis la ligne — le contenu entier, le profil signalé, où, et qui
   l'a signalé, quand. La ligne s'ouvre d'un clic sur son contenu. */
.ui-signalement-ouvrir {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  border: 0;
  background: none;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.ui-signalement-ouvrir:hover { color: var(--color-titre); text-decoration: underline; }
.ui-signalement-detail { display: grid; gap: var(--space-1); margin: 0; }
.ui-signalement-detail > dt {
  margin-top: var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ui-signalement-detail > dt:first-child { margin-top: 0; }
.ui-signalement-detail > dd { margin: 0; color: var(--text); font-size: var(--fs-base); }
.ui-signalement-contenu {
  margin-top: var(--space-2);
  border-radius: var(--radius-md);
  background: var(--surface-douce);
  padding: var(--space-3);
}
.ui-signalement-image { display: block; max-width: 100%; max-height: 320px; margin-top: var(--space-2); border-radius: var(--radius-md); object-fit: contain; }
.ui-signalement-contenu > .ui-signalement-image:first-child { margin-top: 0; }
.ui-signalement-par { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.ui-signalement-par > li { display: flex; flex-direction: column; }
.ui-signalement-detail a { color: var(--color-titre); font-weight: var(--fw-semibold); text-decoration: underline; }

/* LA MOSAÏQUE DES PHOTOS SIGNALÉES (administration, 2 oct. 2026) : des
   tuiles compactes à l'arrondi standard — l'image carrée, ses badges posés
   dessus en bas à gauche, le menu « … » en haut à droite, le nom sur une
   ligne. Un clic
   sur l'image ouvre son détail. */
.ui-mosaique {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ui-mosaique > .ui-mosaique-tuile {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  padding: 0;
  box-shadow: var(--shadow-md);
}
/* L'image remplit son carré, quoi que pose le cadre autour. */
.ui-mosaique-tuile > .ui-mosaique-image {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  border: 0;
  background: var(--surface-douce);
  padding: 0;
  cursor: zoom-in;
}
.ui-mosaique-image > img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
.ui-mosaique-tuile > .ui-mosaique-badges {
  position: absolute;
  left: var(--space-2);
  right: var(--space-2);
  /* En bas à gauche de l'image (le nom est dessous, 33px). */
  bottom: calc(var(--space-5) - var(--space-1) + var(--space-1));
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: 0;
  pointer-events: none;
}
.ui-mosaique-badges > .ui-tableau-etat { margin: 0; box-shadow: var(--shadow-md); }
.ui-mosaique-badges > .ui-tableau-etat:not([class*="--"]) { background: var(--white); }
/* « Floutée » : un aplat sombre, lisible sur n'importe quelle image (le gris
   translucide des tableaux s'y perdait). */
.ui-mosaique-badges > .ui-tableau-etat--attente { background: var(--color-dark); color: var(--white); }
.ui-mosaique-tuile > .ui-mosaique-menu { position: absolute; top: var(--space-2); right: var(--space-2); z-index: 3; }
.ui-mosaique-menu .ui-menu-actions-bouton { background: var(--white); box-shadow: var(--shadow-md); }
.ui-mosaique-tuile > .ui-mosaique-titre {
  overflow: hidden;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  color: var(--color-titre);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ui-mosaique-titre a { color: inherit; }
/* Le badge « En cours de vérification » n'a rien à dire à l'administration
   qui en décide : pas dans la mosaïque. */
.ui-mosaique-image .ui-verification { display: none; }
@media (width <= 576px) {
  .ui-mosaique { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Les gestes, au pied du détail d'une image signalée (2 oct. 2026). */
.ui-signalement-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
.ui-signalement-actions > form { margin: 0; }

/* ══════════ L'ÉCRAN DE BIENVENUE (organismes/ecran-bienvenue, 3 oct. 2026) ══════════
   Quatre secondes après la connexion, en plein écran. Fond pétrole dense et
   trois formes floues qui dérivent ; au centre, le logo, ma photo dans son
   anneau qui respire, mon prénom, ce qui m'attend, et la barre qui se
   remplit en même temps que le délai (--bienvenue-duree, posé par le script
   depuis data-bienvenue-duree). Chaque élément entre à son tour. */
.ui-page-bienvenue { background: var(--color-dark); }

.ui-bienvenue {
  --bienvenue-duree: 4000ms;
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: var(--space-4);
  background: linear-gradient(160deg, var(--color-titre-hero), var(--color-dark));
  color: var(--white);
  text-align: center;
}

.ui-bienvenue-fond { position: absolute; inset: 0; }
/* --apercu : la fiche du DS — dans sa boîte, et non sur tout l'écran. */
.ui-bienvenue.ui-bienvenue--apercu { position: relative; min-height: 34rem; border-radius: var(--radius); }
.ui-bienvenue-forme {
  position: absolute;
  width: 60vmax;
  height: 60vmax;
  border-radius: 50%;
  opacity: 0.55;
  /* PAS DE filter: blur (3 oct. 2026) : trois flous de cette taille qui
     bougent coûtaient des images, et la barre avançait par à-coups. Le
     dégradé radial, qui s'éteint vers le bord, est déjà flou. Seul le
     transform bouge, sur sa propre couche. */
  will-change: transform;
  animation: ui-bienvenue-derive 9s ease-in-out infinite alternate;
}
.ui-bienvenue-forme--a {
  top: -25vmax;
  left: -15vmax;
  background: radial-gradient(closest-side, var(--color-primary), transparent);
}
.ui-bienvenue-forme--b {
  right: -20vmax;
  bottom: -30vmax;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--surface-appuyee) 70%, transparent), transparent);
  opacity: 0.35;
  animation-duration: 11s;
  animation-direction: alternate-reverse;
}
.ui-bienvenue-forme--c {
  top: 30%;
  left: 45%;
  width: 35vmax;
  height: 35vmax;
  background: radial-gradient(closest-side, var(--color-titre), transparent);
  animation-duration: 7s;
}
@keyframes ui-bienvenue-derive {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to { transform: translate3d(6vmax, 4vmax, 0) scale(1.15); }
}

.ui-bienvenue-corps {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: min(100%, 28rem);
}
/* L'entrée en cascade : chaque enfant monte et apparaît, l'un après l'autre. */
.ui-bienvenue-corps > * { animation: ui-bienvenue-entree 0.6s cubic-bezier(0.2, 0.8, 0.2, 1) both; }
.ui-bienvenue-corps > :nth-child(2) { animation-delay: 0.1s; }
.ui-bienvenue-corps > :nth-child(3) { animation-delay: 0.2s; }
.ui-bienvenue-corps > :nth-child(4) { animation-delay: 0.3s; }
.ui-bienvenue-corps > :nth-child(5) { animation-delay: 0.4s; }
.ui-bienvenue-corps > :nth-child(6) { animation-delay: 0.5s; }
.ui-bienvenue-corps > :nth-child(7) { animation-delay: 0.6s; }
@keyframes ui-bienvenue-entree {
  from { opacity: 0; transform: translateY(var(--space-3)); }
  to { opacity: 1; transform: none; }
}

.ui-bienvenue-logo { width: 11.7rem; height: auto; margin-bottom: var(--space-5); }
/* Au bureau (3 oct. 2026) : le logo plus grand, et 10px plus haut que sa
   place (40, puis redescendu de 30) — décalé seul, la photo et le reste ne
   bougent pas. */
@media (width >= 992px) {
  .ui-bienvenue-logo {
    position: relative;
    top: calc(var(--space-1) * -2.5);
    width: 17.1rem;
  }
}

/* La photo : carrée à coins arrondis, cerclée de blanc ; un anneau en sort
   et s'efface, deux fois pendant l'attente. */
.ui-bienvenue-photo-cadre { position: relative; margin-bottom: var(--space-4); }
.ui-bienvenue-photo-cadre::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--white);
  border-radius: var(--radius);
  animation: ui-bienvenue-onde 1.6s ease-out 0.6s infinite;
  opacity: 0;
}
@keyframes ui-bienvenue-onde {
  from { opacity: 0.7; transform: scale(1); }
  to { opacity: 0; transform: scale(1.35); }
}
.ui-bienvenue-photo {
  display: block;
  width: 7rem;
  height: 7rem;
  border: 3px solid var(--white);
  border-radius: var(--radius);
  object-fit: cover;
  box-shadow: var(--shadow-md);
}

.ui-bienvenue-titre {
  margin: 0 0 var(--space-2);
  color: var(--white);
  font-family: var(--font-titre);
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
}
.ui-bienvenue-texte {
  margin: 0 0 var(--space-4);
  color: color-mix(in srgb, var(--white) 70%, transparent);
  font-size: var(--fs-lead);
}

.ui-bienvenue-reperes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}
.ui-bienvenue-repere {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 1px solid color-mix(in srgb, var(--white) 25%, transparent);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--white) 12%, transparent);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
}
.ui-bienvenue-repere .material-symbols-outlined { font-size: var(--icon-sm); }

/* LA BARRE : elle se remplit en exactement --bienvenue-duree. */
.ui-bienvenue-barre {
  position: relative;
  width: min(100%, 18rem);
  height: var(--space-1);
  margin-top: calc(var(--space-1) * 5);   /* 20px plus bas (3 oct. 2026) */
  margin-bottom: var(--space-4);
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--white) 12%, transparent);
}
.ui-bienvenue-barre-remplie {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--surface-appuyee), var(--white));
  transform: scaleX(0);
  transform-origin: left;
  will-change: transform;
  /* Linéaire : une avance régulière, sans accélération ni freinage. */
  animation: ui-bienvenue-remplit var(--bienvenue-duree) linear forwards;
}
@keyframes ui-bienvenue-remplit { to { transform: scaleX(1); } }
/* UNE SEULE ANIMATION SUR LA BARRE (3 oct. 2026) : le reflet qui la
   parcourait, étiré avec elle, se superposait au remplissage ; et la barre
   n'entre pas en cascade comme le reste — elle est là d'emblée et se
   remplit, sans monter en même temps. */
.ui-bienvenue-corps > .ui-bienvenue-barre { animation: none; }

.ui-bienvenue-passer {
  color: color-mix(in srgb, var(--white) 70%, transparent);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
}
.ui-bienvenue-passer:hover,
.ui-bienvenue-passer:focus-visible { color: var(--white); }

/* Le mouvement s'éteint pour qui le refuse ; la barre se remplit quand même. */
@media (prefers-reduced-motion: reduce) {
  .ui-bienvenue-forme,
  .ui-bienvenue-corps > *,
  .ui-bienvenue-photo-cadre::after { animation: none; }
}

/* LES ENCARTS EN PETITES CARTES (section-espace, encartsEnTuiles — 4 oct.
   2026, « Mes paramètres ») : deux par rangée, du dessin des cartes de
   « Mon compte » — icône, titre, l'état en une ligne ; un clic ouvre la
   fenêtre de modification. Seule l'ombre réagit au survol. */
.ui-espace-tuiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ui-espace-tuile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: var(--radius-md);
  background: var(--surface-card);
  padding: var(--space-3);
  color: var(--text);
  text-align: start;
  box-shadow: var(--shadow-md);
  cursor: pointer;
  transition: box-shadow 0.2s ease;
}
.ui-espace-tuile:hover,
.ui-espace-tuile:focus-visible { box-shadow: var(--shadow-lg); }
.ui-espace-tuile > .material-symbols-outlined {
  margin-bottom: var(--space-1);
  color: var(--color-primary);
  font-size: var(--icon-lg);
}
.ui-espace-tuile-titre {
  color: var(--color-titre);
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
}
.ui-espace-tuile-texte {
  max-width: 100%;
  overflow: hidden;
  color: var(--text-muted);
  font-size: var(--fs-caption);
  text-overflow: ellipsis;
}
/* « Supprimer mon compte » : le rouge sur l'icône seulement. */
.ui-espace-tuile--danger > .material-symbols-outlined { color: var(--color-danger); }

