/* ---- Polices ---- */
@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/inter/Inter-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Big Shoulders";
  src: url("../assets/fonts/big-shoulders/BigShoulders-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Nunito Sans";
  src: url("../assets/fonts/nunito-sans/NunitoSans-Variable.woff2") format("woff2");
  font-weight: 200 1000;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("../assets/fonts/ibm-plex-mono/IBMPlexMono-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "TINY 5x3";
  src: url("../assets/fonts/tiny/TINY5x3-80.woff2") format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* Tiny5, seconde police pixel — à ne pas confondre avec TINY 5x3, qui vient
   d'une autre fonderie et a des métriques bien plus capricieuses. */
@font-face {
  font-family: "Tiny5";
  src: url("../assets/fonts/tiny5/Tiny5-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---- Jetons de design ---- */
.om-racine {
  --om-grey-40: #5c5c70;
  --om-grey-96: #f4f4f6;
  --om-fond: #000000;

  --om-grey-30: #545467;
  --om-grey-60: #8f8fa3;
  --om-grey-88: #dfdde4;

  --om-z-decor: 50;
  --om-z-voile: 100;
  --om-z-interface: 200;
  --om-z-detail: 300;
  /* Au-dessus de la carte ouverte : elle est centrée sur des marges qui ne
     tiennent pas compte de la barre du bas, et pourrait la mordre sur un écran
     court. Les commandes doivent rester atteignables. */
  --om-z-nav: 320;
  --om-z-revelation: 350;
  --om-z-curseur: 400;
  --om-jaune-zinc: #ffe600;
  --om-jaune-zinc-20: rgba(255, 230, 0, 0.2);
  --om-blanc: #ffffff;
  --om-blanc-20: rgba(255, 255, 255, 0.2);
  --om-fond-bouton-rond: rgba(87, 87, 87, 0.6);
  --om-fond-verre: #000000b2;

  --om-marge-haut: 30px;
  --om-marge-cote: 40px;

  --om-mention-padding: 16px;
  /* La mention se retire devant les contrôles du coin opposé, qui occupent 172px
     à gauche : sur un écran étroit elle leur passait dessus. Le retranchement
     couvre leur emprise, son propre retrait de 20px et un écart de 16px. Le
     pourcentage se résout sur le conteneur, pas sur la fenêtre — la div peut être
     posée dans une colonne. */
  --om-mention-largeur: clamp(140px, calc(100% - 208px), 216px);
  /* Le corps suit la largeur, sinon le bloc se rattrape en hauteur : à 13px dans
     167px de large, la mention passe de quatre à sept lignes et grimpe à mi-écran.
     En `vw` faute de mieux — une taille de police ne se calcule pas en pourcentage
     du conteneur, et une requête de conteneur imposerait un `contain` sur la
     racine, qui en ferait le bloc de référence de tout ce qu'elle porte. */
  --om-mention-taille: clamp(10px, 3vw, 13px);

  --om-cartes-echelle: 1;
  /* Course rendue au-delà du débordement strict, pour que les cartes des
     extrémités ne viennent pas se coller au bord de l'écran. */
  --om-cartes-marge-glissement: 100px;
  --om-carte-largeur: 200px;
  --om-carte-hauteur: 280px;
  --om-carte-fond: #000000;
  --om-carte-zone-largeur: 189px;
  --om-carte-zone-hauteur: 268px;
  --om-carte-modele-cote: 17px;
  --om-carte-modele-haut: 32px;
  --om-carte-nom-ecart: 14px;
  --om-carte-compteur-ecart: 10px;
  --om-devoilement: 1s cubic-bezier(0.4, 0, 0.2, 1);
  /* Arrivée d'une matière 3D dans sa carte. Longue et sortante : les matières se
     chargent l'une après l'autre, et cinq apparitions sèches à la suite se
     remarquent bien plus qu'une seule. */
  --om-matiere-fondu: 620ms cubic-bezier(0.22, 1, 0.36, 1);

  --om-detail-largeur: 552px;
  --om-detail-hauteur: 806px;
  /* Espace réservé autour de la carte ouverte, sous lequel elle se réduit. Les
     deux axes sont contraints et c'est le plus petit des deux rapports qui
     l'emporte : sur un écran étroit et haut, ne surveiller que la hauteur
     laissait la carte sortir par les côtés. Lues en JS (`ajusterEchelle`), d'où
     leur présence ici plutôt qu'en constantes de module — le gabarit mobile les
     change en même temps que les dimensions. */
  --om-detail-marge-y: 152px;
  --om-detail-marge-x: 100px;
  /* Retrait sous la carte, distinct de la marge haute : le gabarit tactile la
     pose au-dessus de la barre de commandes. Symétrique par défaut. */
  --om-detail-bas: var(--om-detail-marge-y);
  --om-detail-echelle: 1;
  /* Quel jeu de visuels intérieurs porte la carte ouverte. Lu en JS pour choisir
     le tracé et son fond : le dessin mobile n'est pas une réduction du dessin
     desktop, il faut deux fichiers. Passer par un jeton plutôt que par la classe
     de bascule garde la décision du seuil dans cette feuille de style. */
  --om-detail-variante: "desktop";
  /* Rayon des faces et du halo, qui doivent garder le même contour. */
  --om-detail-rayon: 22.05px;
  --om-detail-titre-taille: 64px;
  --om-detail-interieur-haut: 19px;
  --om-detail-interieur-cote: 18px;
  --om-detail-entete-ecart: 13px;
  --om-detail-entete-gauche: 42px;
  --om-detail-entete-droite: 18px;
  --om-detail-modele-cote: 390px;
  --om-detail-modele-ecart: 8px;
  /* Desktop : le modèle se pose sous le titre, dont il suit la hauteur de ligne.
     Mobile : la DA le cale à une distance fixe du haut de la carte, le jeton est
     alors écrasé par une valeur simple. */
  --om-detail-modele-haut: calc(
    var(--om-detail-interieur-haut) + var(--om-detail-entete-ecart) +
      var(--om-detail-titre-taille) * 1.02 + var(--om-detail-modele-ecart)
  );
  --om-detail-bas-hauteur: 293px;
  --om-detail-encart-haut: 22px;
  --om-detail-encart-hauteur: 173px;
  --om-detail-shapes-cote: 36px;
  --om-detail-shapes-bas: 12px;
  --om-detail-textes-gap: 12px;
  --om-detail-textes-retrait: 60px;
  /* Décalage du groupe badge + talent + accroche par rapport au haut de l'encart.
     Négatif, il l'en fait sortir par le haut sans déplacer le pied ni les shapes,
     qui restent accrochés à l'encart. */
  --om-detail-textes-haut: 0px;
  --om-detail-badge-hauteur: 24px;
  --om-detail-secrets-marge: 18px;
  --om-detail-secrets-gap: 56px;
  --om-detail-picto-bas: 32px;
  --om-fondu: 260ms cubic-bezier(0.4, 0, 0.2, 1);
  --om-fondu-duree: 260ms;

  /* Entrée en scène : le décor s'allume, puis les cartes de la gauche vers la
     droite, puis l'interface. Les valeurs sont des raccourcis d'animation
     complets — durée, courbe, retard — pour que la partition se lise d'un bloc.
     Fin du dernier élément à 1,5s. */
  --om-entree-courbe: cubic-bezier(0.22, 1, 0.36, 1);
  --om-entree-fond: 900ms var(--om-entree-courbe) 0ms;
  --om-entree-lumieres: 1000ms var(--om-entree-courbe) 150ms;
  --om-entree-carte: 600ms var(--om-entree-courbe);
  --om-entree-interface: 600ms var(--om-entree-courbe) 900ms;
  --om-entree-cartes-depart: 260ms;
  --om-entree-cartes-pas: 62ms;
  /* Part de la hauteur d'une carte qui dépasse du bas de l'écran, paquet posé. */
  --om-entree-paquet-visible: 0.66;
  /* Décalage d'une carte à l'autre dans la pile : sans lui, les onze se
     superposent au pixel près et le paquet n'en paraît qu'une. */
  --om-entree-paquet-decalage: 0.8;
  /* Montée de l'entrée en fondu, l'autre variante. */
  --om-entree-cartes-montee: 24px;
  /* Allumage du paquet, avant qu'il ne soit distribué : il se pose en même temps
     que les lumières du décor, sinon il est là dès la première image alors que
     tout le reste s'installe encore. Séparé du reste de la partition parce qu'il
     se joue pendant que la distribution, elle, est retenue. */
  --om-entree-paquet-duree: 700ms;
  --om-entree-paquet: var(--om-entree-paquet-duree) var(--om-entree-courbe);
  --om-entree-paquet-retard: 120ms;
  /* Le point d'interrogation des cartes sans modèle n'entre qu'une fois le paquet
     à pleine opacité : plus tôt, il se voit à travers une carte encore
     transparente, et c'est lui qu'on remarque au lieu du paquet. */
  --om-entree-interro: 320ms var(--om-entree-courbe);
  /* Roulement des libellés du bouton de retournement. Plus long que le fondu et
     nettement sortant : le mouvement doit se lire, pas seulement se deviner. */
  --om-nav-roulement: 360ms cubic-bezier(0.16, 1, 0.3, 1);
  /* Doivent suivre DUREE_OUVERTURE et DUREE_FERMETURE de card-detail.js : le
     décor s'efface et revient au rythme du voile. */
  --om-detail-entree: 900ms;
  --om-detail-sortie: 620ms;
  --om-detail-lueur-opacite: 0.12;
}

/* ---- Racine ---- */

:where(.om-racine) {
  height: 100%;
}

.om-racine {
  position: relative;
  width: 100%;
  overflow: hidden;
  /* Enferme les empilements de l'expérience. Sans cela, ses z-index — qui vont
     jusqu'à 300 — entreraient en concurrence avec ceux de la page hôte. */
  isolation: isolate;

  /* Seuls les deux calques noirs de la maquette restent ici : ils couvrent
     exactement le conteneur, ne bougent pas avec la map — les décaler
     découvrirait un bord — et n'ont rien à allumer, c'est le noir de départ.
     La volute et les lumières sont passées en pseudo-éléments, voir plus bas. */
  background-color: var(--om-fond);
  background-image:
    linear-gradient(0deg, #000000, #000000),
    linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4));
  background-repeat: no-repeat;
  background-position: center, center;
  background-size: 100% 100%, 100% 100%;

  font-family: "Inter", system-ui, sans-serif;
  font-size: 16px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.4;
  letter-spacing: 0em;
  text-align: left;
  text-transform: none;
  color: var(--om-grey-96);
  -webkit-font-smoothing: antialiased;
  /* Chrome Android grossit de lui-même certains textes selon la largeur du bloc
     qui les porte. Garde-fou, pas correctif : les tailles sont réglées à l'œil
     sur la maquette, aucune ne doit être réinterprétée. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Volute et lumières, hors du fond de la racine : les couches d'un
   `background-image` ne se fondent pas séparément, et l'entrée les allume l'une
   après l'autre. Sous les cartes, dont les rangs commencent à 1.

   Le déplacement de la map les emmène de loin. Une transformation ne pose ici
   aucun problème d'empilement — un pseudo-élément sans enfant — et se compose sur
   le GPU, là où décaler un `background-position` repeignait tout l'écran à chaque
   image du glissement. */
.om-racine::before,
.om-racine::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  transform: translateX(var(--om-fond-decalage, 0px));
}

/* Les lumières d'abord, la volute par-dessus — l'ordre de la maquette. Il fallait
   les intervertir en passant du fond aux pseudo-éléments : les couches d'un
   `background-image` se peignent la première au-dessus, alors que deux
   pseudo-éléments de même rang s'empilent dans l'ordre du DOM. */
.om-racine::before {
  background-image:
    url("../assets/img/light-white.svg"),
    url("../assets/img/light-jaune.svg");
  background-position: center, center;
  background-size: cover, cover;
  animation: om-allumer var(--om-entree-lumieres) backwards;
}

/* En `auto 100%` : elle déborde à droite sur un conteneur étroit, `overflow:
   hidden` s'en occupe. */
.om-racine::after {
  background-image: url("../assets/img/volute.svg");
  background-position: left top;
  background-size: auto 100%;
  animation: om-allumer var(--om-entree-fond) backwards;
}

/* Seule la volute change d'ancrage : calée en bas à droite, elle déborde par la
   gauche au lieu de la droite. */
.om-racine.om-racine--compact::after {
  background-position: right bottom;
}

/* ---- Reset défensif ---- */
.om-racine *,
.om-racine *::before,
.om-racine *::after {
  box-sizing: border-box;
}

.om-racine div,
.om-racine span,
.om-racine p,
.om-racine h1,
.om-racine h2,
.om-racine h3,
.om-racine h4,
.om-racine ul,
.om-racine ol,
.om-racine li,
.om-racine figure,
.om-racine img,
.om-racine svg,
.om-racine canvas,
.om-racine button,
.om-racine a {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  color: inherit;
  max-width: none;
  max-height: none;
  min-width: 0;
  min-height: 0;
  float: none;
  box-shadow: none;
  text-shadow: none;
  text-decoration: none;
  list-style: none;
  vertical-align: baseline;
}

.om-racine img,
.om-racine svg,
.om-racine canvas {
  display: block;
  width: auto;
  height: auto;
}

/* Aucune sélection nulle part, et aucun menu d'appui long. L'expérience se
   manipule entièrement au maintien — pencher une carte, déplacer la map — et le
   moindre geste tenu déclenchait sinon la sélection du texte, la poignée de
   copie ou le menu « Enregistrer l'image », qui volent le pointeur en cours de
   route. Rien n'y est un contenu à recopier : ce sont des libellés courts, et la
   mention légale est reprise dans la page hôte. */
.om-racine,
.om-racine img,
.om-racine svg,
.om-racine canvas,
.om-racine button {
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* Aucune image n'est cliquable pour elle-même : celles des articles sont dans un
   lien, qui reçoit le geste à leur place. Les rendre transparentes au pointeur
   coupe à la racine les interactions natives d'Android, qui visent l'image et non
   le bouton qui la porte. */
.om-racine img {
  pointer-events: none;
}

.om-racine button {
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  text-align: center;
}

/* ---- Cartes ---- */
.om-racine .om-cartes {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Ni `z-index`, ni `transform` : l'un comme l'autre enfermeraient les cartes
     dans un contexte d'empilement, et la carte ouverte ne pourrait plus venir
     se glisser à leur rang en fin de fermeture. Le centrage passe donc par des
     marges négatives, posées en JS d'après les dimensions de la disposition. */

  /* La réduction mobile passe par `zoom` pour la même raison : contrairement à
     `transform`, il ne crée pas de contexte d'empilement. Il agit sur la mise en
     page elle-même, donc un seul facteur suffit — carte, textes, décorations et
     écarts de la disposition suivent, il n'y a rien à reprendre élément par
     élément. Il laisse les pourcentages intacts et ne réduit que les longueurs :
     `left: 50%` reste le centre et la marge négative est réduite dans le même
     rapport que la largeur, le centrage tient donc tel quel. */
  zoom: var(--om-cartes-echelle);
  /* Le geste nous revient en entier, y compris quand la map tient à l'écran : le
     pincement y zoome encore, alors qu'il n'y a plus rien à déplacer. En `pan-y`,
     Chrome Android attend d'ailleurs de savoir si le doigt part à l'horizontale
     ou à la verticale avant de livrer les déplacements, et le glissement démarrait
     en retard et par à-coups. La contrepartie est que la page hôte ne défile plus
     depuis la map. */
  touch-action: none;
  /* Pas de transition CSS sur `zoom` : le changement de cran recale aussi le
     décalage du fond et les bornes du déplacement, calculés en JS. Confié au
     navigateur, le zoom s'animait pendant que le fond, lui, sautait à sa
     nouvelle position. Le cran est donc interpolé dans la boucle de `map-pan`,
     avec tout le reste. */
}

/* 180 × 252 au lieu de 200 × 280. La composition de la map est conservée telle
   quelle, seule son échelle change : elle déborde toujours, la croix de
   déplacement prend le relais. */
.om-racine.om-racine--compact {
  --om-cartes-echelle: 0.9;
  --om-cartes-marge-glissement: 50px;
}

.om-racine .om-carte {
  position: absolute;
  width: var(--om-carte-largeur);
  height: var(--om-carte-hauteur);
  background: var(--om-carte-fond);
  border: 1px solid var(--om-grey-30);
  border-radius: 12px;

  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  isolation: isolate;
  cursor: pointer;
}

.om-racine .om-carte__lueur {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: calc(var(--om-detail-lueur-opacite) * var(--om-lueur-survol, 0));
  mix-blend-mode: screen;
  pointer-events: none;
}

.om-racine .om-carte__zone {
  position: relative;
  flex: none;
  width: var(--om-carte-zone-largeur);
  height: var(--om-carte-zone-hauteur);
}

.om-racine .om-carte__contour {
  display: block;
  width: var(--om-carte-zone-largeur);
  height: var(--om-carte-zone-hauteur);
}

.om-racine .om-carte__indice {
  font-family: "TINY 5x3", monospace;
  font-size: 12px;
  font-weight: 200;
  line-height: 115%;
  letter-spacing: 0em;
  text-transform: uppercase;
  color: var(--om-grey-96);
}

.om-racine .om-carte__isotope {
  position: absolute;
  top: 0;
  right: 0;
  text-align: right;
}

/* Alignement sur la baseline et non sur la boîte de ligne : TINY 5x3 déclare un
   interligne de 700/1000 et un ascender de 600 pour des chiffres qui culminent à
   490, donc `align-items: center` centrerait le cercle sur du vide. */
.om-racine .om-numero {
  --om-numero-taille: 12px;
  --om-numero-cercle: 5.5px;

  /* Marges négatives ramenant la boîte sur les glyphes : TINY 5x3 déclare 1em
     (0,591 / 0,409) pour des chiffres qui n'occupent que 0,48em et s'arrêtent
     au-dessus de la baseline, d'où 0,42em de vide qui décalait tout.
     Relevé au canvas (fontBoundingBox contre actualBoundingBox), en em donc
     valable à toute taille. */
  --om-numero-rogne-haut: 0.101em;
  --om-numero-rogne-bas: 0.399em;

  display: inline-block;
  white-space: nowrap;
  font-family: "TINY 5x3", monospace;
  font-size: var(--om-numero-taille);
  font-weight: 200;
  line-height: 1;
  letter-spacing: 0em;
  text-transform: uppercase;
  color: var(--om-grey-96);
  margin-top: calc(-1 * var(--om-numero-rogne-haut));
  margin-bottom: calc(-1 * var(--om-numero-rogne-bas));
}

.om-racine .om-carte__numero {
  position: absolute;
  left: 17.5px;
  top: 6px;
}

.om-racine .om-detail__shapes .om-numero {
  --om-numero-taille: 22px;
  --om-numero-cercle: 11px;
}

.om-racine .om-numero__pastille {
  display: inline-block;
  width: var(--om-numero-cercle);
  height: var(--om-numero-cercle);
  margin-right: 0.167em;
  border-radius: 50%;
  background: var(--om-jaune-zinc);
  border: 0.17px solid var(--om-grey-96);
  vertical-align: calc(0.25em - var(--om-numero-cercle) / 2);
}

.om-racine .om-carte__modele {
  position: absolute;
  left: var(--om-carte-modele-cote);
  right: var(--om-carte-modele-cote);
  top: var(--om-carte-modele-haut);
  aspect-ratio: 1 / 1;
}

/* Le canvas est là avant sa matière : le rendu y dessine dès qu'il existe, mais
   le modèle, lui, met le temps de son `.glb` et de ses trois textures à arriver.
   Il reste donc transparent jusque-là, puis se fond — sans quoi le minéral
   apparaissait d'un coup dans une carte noire, cinq fois de suite à mesure que
   les matières se chargent. */
.om-racine .om-carte__modele canvas {
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity var(--om-matiere-fondu);
}

.om-racine .om-carte--matiere .om-carte__modele canvas {
  opacity: 1;
}

/* Repli quand la matière 3D n'est pas affichable : modèle non livré, `.glb`
   introuvable, mauvais type MIME, ou WebGL absent — ce dernier cas portant la
   classe sur la racine, donc valable pour toutes les cartes. Les posters prévus
   par le contenu n'existent pas encore, d'où le point d'interrogation. */
.om-racine--sans-modele .om-carte__modele::after,
.om-racine .om-carte--sans-modele .om-carte__modele::after,
.om-racine--sans-modele .om-detail__modele::after,
.om-racine .om-detail__modele--vide::after {
  content: "?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Big Shoulders Display", Impact, sans-serif;
  font-size: 64px;
  font-weight: 700;
  line-height: 1;
  color: var(--om-jaune-zinc);
}

.om-racine--sans-modele .om-detail__modele::after,
.om-racine .om-detail__modele--vide::after {
  font-size: 160px;
}

/* Retenu le temps que le paquet s'allume. La règle vise le pseudo-élément sans
   le conditionner à l'absence de modèle : là où il n'a pas de `content`, il
   n'existe pas, et l'animation ne s'applique à rien. */
.om-racine .om-cartes--distribution .om-carte__modele::after {
  animation: om-allumer var(--om-entree-interro) backwards;
  animation-delay: calc(var(--om-entree-paquet-retard) + var(--om-entree-paquet-duree));
}

.om-racine .om-carte__legende {
  position: absolute;
  left: var(--om-carte-modele-cote);
  right: var(--om-carte-modele-cote);
  top: calc(
    var(--om-carte-modele-haut) + (var(--om-carte-largeur) - 2 * var(--om-carte-modele-cote)) +
      var(--om-carte-nom-ecart)
  );
}

.om-racine .om-carte__nom {
  font-family: "Big Shoulders", "Inter", sans-serif;
  font-size: 22px;
  font-weight: 600;
  line-height: 102%;
  letter-spacing: 0em;
  text-align: center;
  text-transform: uppercase;
  color: var(--om-grey-96);
}

.om-racine .om-carte__compteur {
  margin-top: var(--om-carte-compteur-ecart);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 10px;
  font-weight: 500;
  line-height: 120%;
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
  color: var(--om-grey-88);
}

/* ---- Carte mystère ---- */
.om-racine .om-carte--mystere {
  border-color: transparent;
  transition: box-shadow var(--om-devoilement);
}

.om-racine .om-carte--mystere:not(.om-carte--verrouillee) {
  box-shadow:
    0 4px 15px 4px #ffe60029,
    0 -13px 24px 4px #ffe6001a;
}


/* Deux couches : le conteneur porte le masque qui ne laisse qu'un filet, son
   `::before` porte le dégradé et tourne — les fusionner ferait tourner le masque.
   `border-image` n'anime ni son dégradé ni un angle, et `@property` avec un
   `conic-gradient` n'arrive qu'en Safari 16.4 pour un plancher à 16. */
.om-racine .om-carte__bordure {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  /* Épaisseur du filet : le masque ne garde que cette couronne. La maquette
     donne 0,6px, mais sous le pixel le filet se délave sur un écran non-Retina —
     1px le rend franc partout sans l'épaissir visiblement. */
  padding: 1px;
  /* Différence entre la boîte entière et sa content-box. `xor` est la forme que
     comprend Safari, `exclude` la forme normalisée. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

.om-racine .om-carte__bordure::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  /* Carré tournant autour de son centre : son côté doit dépasser la diagonale
     de la carte (200 × 280 → 344px) pour qu'aucun angle ne se découvre. */
  width: calc(var(--om-carte-largeur) * 1.8);
  height: calc(var(--om-carte-largeur) * 1.8);
  background: linear-gradient(
    180deg,
    rgba(255, 230, 0, 0.7) 0%,
    rgba(255, 230, 0, 0) 100%
  );
  animation: om-bordure-tourne 4s linear infinite;
}

/* Verrouillée, la carte doit porter le contour gris commun aux autres. Il est
   posé au bout du dégradé, pas en `border-color` sous le filet : superposés, le
   jaune à 70 % se serait éclairci sur le gris au lieu de rester sur le noir. */
.om-racine .om-carte--mystere.om-carte--verrouillee .om-carte__bordure::before {
  background: linear-gradient(
    180deg,
    rgba(255, 230, 0, 0.7) 0%,
    var(--om-grey-30) 100%
  );
}

@keyframes om-bordure-tourne {
  from {
    transform: translate(-50%, -50%) rotate(0deg);
  }
  to {
    transform: translate(-50%, -50%) rotate(360deg);
  }
}

/* Une rotation perpétuelle est exactement ce que ce réglage demande d'arrêter.
   Le dégradé reste, figé sur sa position d'origine. */
@media (prefers-reduced-motion: reduce) {
  .om-racine .om-carte__bordure::before {
    animation: none;
  }
}

.om-racine .om-carte--mystere:not(.om-carte--verrouillee) .om-carte__deco--cercle {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 6'%3E%3Ccircle cx='3' cy='3' r='2.75' fill='none' stroke='%23FFE600' stroke-width='0.5'/%3E%3C/svg%3E");
}

.om-racine .om-carte--mystere:not(.om-carte--verrouillee) .om-carte__deco--fleche {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 7'%3E%3Cpath d='M5.66151 2.98991L2.88373 0.212128L0.105957 2.98991M2.88373 0.212128V6.32324' fill='none' stroke='%23FFE600' stroke-width='0.5'/%3E%3C/svg%3E");
}

/* Voile flouté. Un `backdrop-filter` n'agit que sur ce qui le précède : le voile
   suit le modèle, tout ce qui doit rester net vient après lui.
   Toujours dans le DOM, à flou nul quand la matière est gagnée : c'est le rayon
   qui est animé, pas l'opacité — un fondu d'opacité sur un `backdrop-filter` fait
   clignoter le compositeur, même piège que l'annonce. */
.om-racine .om-carte__voile {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition: backdrop-filter var(--om-devoilement), -webkit-backdrop-filter var(--om-devoilement);
  pointer-events: none;
}

.om-racine .om-carte--verrouillee {
  /* Elle n'ouvre rien : son étiquette de curseur dit pourquoi. */
  cursor: default;
}

.om-racine .om-carte--verrouillee .om-carte__voile {
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

/* Le flou vaut dès la première image, distribution comprise : la carte mystère
   doit se distinguer des autres dans le paquet comme sur la map. Sa transition
   d'une seconde ne sert qu'au dévoilement, quand les dix matières sont vues. */

.om-racine .om-carte__deco {
  position: absolute;
}

/* Tracé SVG et non bordure CSS : sous 1 device pixel, les navigateurs relèvent
   une bordure à 1 device pixel opaque, ce qui la rendait plus grasse que la
   flèche. Un stroke SVG s'affiche en antialiasing à son épaisseur réelle.
   Couleur figée faute de variable utilisable dans une URL : à tenir à jour avec
   --om-grey-60 (#8F8FA3). */
.om-racine .om-carte__deco--cercle {
  width: 5.5px;
  height: 5.5px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 6'%3E%3Ccircle cx='3' cy='3' r='2.75' fill='none' stroke='%238F8FA3' stroke-width='0.5'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

.om-racine .om-carte__deco--fleche {
  display: block;
  width: 5.5px;
  height: 6px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 7'%3E%3Cpath d='M5.66151 2.98991L2.88373 0.212128L0.105957 2.98991M2.88373 0.212128V6.32324' fill='none' stroke='%238F8FA3' stroke-width='0.5'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

/* Déplacement latéral, posé quand la disposition déborde du conteneur. Placé
   après les règles des cartes : `.om-carte` fixe déjà un curseur, et les
   sélecteurs ont la même spécificité. */
.om-racine .om-cartes--deplacable {
  cursor: grab;
}

.om-racine--deplacement .om-cartes,
.om-racine--deplacement .om-carte {
  cursor: grabbing;
}

/* Un glissement qui passe sur un nom de matière le sélectionnerait, et la
   sélection resterait posée après le relâchement. */
.om-racine--deplacement .om-cartes {
  user-select: none;
  -webkit-user-select: none;
}

/* ---- Carte ouverte ---- */
.om-racine .om-voile-detail {
  position: absolute;
  inset: 0;
  z-index: var(--om-z-voile);
  background: var(--om-fond-verre);
}

/* Voile plus opaque qu'en desktop : la carte occupe presque tout l'écran, la map
   derrière n'a plus à se deviner. Posé ici et non sur `--om-fond-verre`, que la
   barre du haut et la mention légale partagent. */
.om-racine--compact .om-voile-detail {
  background: #000000e5;
}

.om-racine .om-carte-ouverte {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: var(--om-z-detail);
  cursor: pointer;
  width: var(--om-detail-largeur);
  height: var(--om-detail-hauteur);
  /* Portée par un ancêtre du pivot : sur le pivot lui-même elle n'aurait aucun
     effet. 1200px est la valeur de la Comet Card (`perspective-distant`). */
  perspective: 1200px;
  /* Le maintien sert à la pencher : laisser le navigateur interpréter le geste
     lui faisait lâcher la carte au premier déplacement du doigt, pour partir en
     défilement ou en sélection. */
  touch-action: none;
  /* Le facteur agit après le centrage et autour du centre de l'élément : la
     carte reste centrée quelle que soit sa réduction. Un `scale` plutôt qu'une
     hauteur en pourcentage, pour que rayon d'angle, bordure et ombres se
     réduisent avec le reste au lieu de s'épaissir. */
  transform: translate(-50%, -50%) scale(var(--om-detail-echelle));
}

/* Étiquette accrochée au curseur sur la carte ouverte.

   À la racine et non dans la carte, qui s'incline et s'agrandit au survol :
   l'étiquette y serait déformée. Sa position est calculée depuis le pointeur.
   Elle passe au-dessus de tout : c'est un curseur. */
.om-racine .om-curseur {
  position: absolute;
  z-index: var(--om-z-curseur);
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 20px 24px;
  border-radius: 20px;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(60px);
  -webkit-backdrop-filter: blur(60px);
  box-shadow: 0 0 24px 0 rgba(255, 255, 255, 0.35);
  white-space: nowrap;

  /* Style de texte L/ui-1 */
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 480;
  line-height: 105%;
  letter-spacing: 0em;
  text-align: center;
  color: var(--om-grey-96);

  /* Ni cliquable ni visible au repos. L'apparition est fondue, la position ne
     l'est pas : le moindre lissage se lirait comme un retard.
     `transform-origin` côté curseur : elle croît depuis lui. */
  pointer-events: none;
  opacity: 0;
  transform: scale(0.92);
  transform-origin: 0 0;
  transition:
    opacity 260ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.om-racine .om-curseur--visible {
  opacity: 1;
  transform: scale(1);
}

.om-racine .om-curseur__fleche {
  display: block;
  width: 8px;
  height: 8px;
  flex: none;
  color: var(--om-blanc);
}

/* Couche d'inclinaison : elle suit le curseur pendant que le pivot, en dessous,
   joue le retournement. Deux transformations ne peuvent pas cohabiter sur un
   même élément, d'où ce niveau supplémentaire. `preserve-3d` doit être posé sur
   toute la chaîne, sinon la scène s'aplatit dès le premier maillon manquant. */
.om-racine .om-carte-ouverte__inclinaison {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

/* Le composant d'origine fusionne en `overlay`, qui vaut 2 × base × lueur, donc
   zéro sur du noir — sa démo porte une photo, notre carte est noire. D'où `screen`,
   et une opacité bien plus basse : sur du noir il restitue la lueur telle quelle,
   à 0,6 le halo brûlait la carte.
   Jamais dans une couche en `preserve-3d` : les deux ne cohabitent pas, la scène
   s'aplatirait et le mélange déborderait sur le voile. */
.om-racine .om-carte-ouverte__lueur {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: calc(var(--om-detail-lueur-opacite) * var(--om-lueur-survol, 0));
  mix-blend-mode: screen;
  pointer-events: none;
}

.om-racine .om-carte-ouverte__pivot {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

.om-racine .om-carte-ouverte__face {
  position: absolute;
  inset: 0;
  /* Sans cela, on verrait la face arrière par transparence pendant la
     rotation. */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  /* Contient la surbrillance, dont le dégradé déborde largement des bords, et
     isole sa fusion du reste de la page. */
  overflow: hidden;
  isolation: isolate;

  border-radius: var(--om-detail-rayon);
  background: #000000;
  border: 1px solid #ffffff4d;
  box-shadow:
    5px 4px 18px 1px #ffffff1f inset,
    3.31px 0 16.54px 1.1px #ffffff29,
    0 10px 54px 2px #ffffff0d;
}

/* Halo jaune sur son calque dans le pivot : il tourne avec la carte.
   Porté par les faces, l'éteindre aurait demandé d'interpoler toute la liste
   d'ombres — coûteux à repeindre sur un flou de 90px. Isolé, une opacité suffit
   et le GPU la compose. Pas de `backface-visibility: hidden` ici : le halo doit
   rester visible carte retournée. */
.om-racine .om-carte-ouverte__glow {
  position: absolute;
  inset: 0;
  border-radius: var(--om-detail-rayon);
  box-shadow: 0 4px 90px 0 #ffe6002b;
  pointer-events: none;
}

.om-racine .om-carte-ouverte__face--verso {
  transform: rotateY(180deg);
}

.om-racine .om-detail__face-contenu {
  position: absolute;
  inset: 0;
}

.om-racine .om-detail__secrets {
  position: absolute;
  inset: var(--om-detail-secrets-marge);
  background: var(--om-fond);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 17px;
  box-shadow: 4px 4px 10px 0 rgba(255, 255, 255, 0.23);
}

.om-racine .om-detail__picto {
  position: absolute;
  left: 50%;
  bottom: var(--om-detail-picto-bas);
  transform: translateX(-50%);
  display: block;
  width: 64px;
  height: 64px;
}

.om-racine .om-detail__secrets-corps {
  position: absolute;
  top: calc(42px - var(--om-detail-secrets-marge));
  left: calc(42px - var(--om-detail-secrets-marge));
  right: calc(42px - var(--om-detail-secrets-marge));
  display: flex;
  flex-direction: column;
  gap: var(--om-detail-secrets-gap);
}

.om-racine .om-detail__recit {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.om-racine .om-detail__accent {
  color: var(--om-jaune-zinc);
}

/* Style de texte L/subtitle-lg */
.om-racine .om-detail__secrets-titre {
  font-family: "Nunito Sans", "Inter", system-ui, sans-serif;
  font-size: 34px;
  font-weight: 500;
  line-height: 115%;
  letter-spacing: 0em;
  color: var(--om-grey-96);
}

.om-racine .om-detail__secrets-texte {
  display: flex;
  flex-direction: column;
  gap: 1em;
  font-family: "Inter", system-ui, sans-serif;
  font-size: 16px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  color: var(--om-grey-88);
}

/* ---- Articles « pour aller plus loin » ---- */
.om-racine .om-detail__articles {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Style de texte L/body-2 */
.om-racine .om-detail__articles-titre {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  color: var(--om-grey-96);
}

.om-racine .om-detail__article {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 8px 20px 8px 8px;
  border-radius: 12px;
  background: #1f1f1f;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition:
    background-color var(--om-fondu),
    color var(--om-fondu);
}

.om-racine .om-detail__article:hover {
  background: var(--om-jaune-zinc);
}

.om-racine .om-detail__article:hover .om-detail__article-titre,
.om-racine .om-detail__article:hover .om-detail__article-meta {
  color: var(--om-fond);
}

.om-racine .om-detail__article:hover .om-detail__article-date {
  color: rgba(0, 0, 0, 0.55);
}

.om-racine .om-detail__article:hover .om-detail__article-bouton {
  background: var(--om-fond);
  color: var(--om-blanc);
}

.om-racine .om-detail__article-image {
  display: block;
  width: 90px;
  height: 90px;
  flex: none;
  border-radius: 8px;
  object-fit: cover;
}

.om-racine .om-detail__article-textes {
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex: 1;
  min-width: 0;
}

.om-racine .om-detail__article-titre {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 13px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  color: var(--om-blanc);
  transition: color var(--om-fondu);

  /* Deux lignes au plus, la coupe finissant par des points de suspension. Les
     titres viennent d'un CMS et ne sont pas calibrés : une troisième ligne
     pousserait la méta hors de l'encart. La forme préfixée reste la seule
     interopérable, `line-clamp` n'étant pas partout. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.om-racine .om-detail__article-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: "Inter", system-ui, sans-serif;
  font-size: 11px;
  font-weight: 480;
  line-height: 105%;
  letter-spacing: 0em;
  color: var(--om-grey-96);
  transition: color var(--om-fondu);
}

.om-racine .om-detail__article-date {
  color: var(--om-grey-60);
  transition: color var(--om-fondu);
}

.om-racine .om-detail__article-bouton {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 12px;
  background: var(--om-grey-88);
  color: var(--om-fond);
  transition:
    background-color var(--om-fondu),
    color var(--om-fondu);
}

.om-racine .om-detail__article-bouton svg {
  display: block;
  width: 12px;
  height: 12px;
}

/* ---- Intérieur de la carte ouverte ---- */
.om-racine .om-detail__interieur {
  position: absolute;
  top: var(--om-detail-interieur-haut);
  left: var(--om-detail-interieur-cote);
  right: var(--om-detail-interieur-cote);
}

.om-racine .om-detail__trace {
  display: block;
  width: 100%;
  height: auto;
  position: relative;
}

.om-racine .om-detail__entete {
  position: absolute;
  top: calc(var(--om-detail-interieur-haut) + var(--om-detail-entete-ecart));
  left: var(--om-detail-entete-gauche);
  right: var(--om-detail-entete-droite);
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}

/* Style de texte L/display-stat */
.om-racine .om-detail__titre {
  font-family: "Big Shoulders", "Inter", sans-serif;
  font-size: var(--om-detail-titre-taille);
  font-weight: 800;
  line-height: 102%;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--om-grey-96);
  /* La maquette donne une `box-shadow` : sur du texte, Figma nomme ainsi ce qui
     est en CSS une `text-shadow`. Une box-shadow dessinerait un halo autour de
     la boîte du titre, pas autour de ses lettres. */
  text-shadow: 0 0 4.41px #ffe60080;
}

/* Style de texte L/ui-2 */
.om-racine .om-detail__verticale {
  width: 85px;
  flex: none;
  /* Remonte le texte pour l'aligner optiquement sur le titre : les deux polices
     n'ont pas la même hauteur de boîte au-dessus de leur ligne de base. */
  margin-top: -2px;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  font-weight: 500;
  line-height: 120%;
  letter-spacing: 0.08em;
  text-align: right;
  text-transform: uppercase;
  color: var(--om-grey-88);
}

.om-racine .om-detail__modele {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: var(--om-detail-modele-haut);
  width: var(--om-detail-modele-cote);
  height: var(--om-detail-modele-cote);
}

.om-racine .om-detail__modele canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.om-racine .om-detail__bas {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--om-detail-bas-hauteur);
}

.om-racine .om-detail__encart {
  position: absolute;
  left: 0;
  right: 0;
  top: var(--om-detail-encart-haut);
  height: var(--om-detail-encart-hauteur);
}

.om-racine .om-detail__pied {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--om-detail-encart-haut) + var(--om-detail-encart-hauteur));
  bottom: 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 26px 18px 18px;
}

.om-racine .om-detail__point-fort {
  display: flex;
  align-items: stretch;
  height: 100%;
  gap: 8.8px;
  padding: 4.4px 4.4px 4.4px 11px;
  border-radius: 8.82px;
  background: var(--om-grey-96);
}

/* Style de texte Tiny5 */
.om-racine .om-detail__point-fort-libelle,
.om-racine .om-detail__point-fort-valeur {
  font-family: "Tiny5", monospace;
  font-size: 16px;
  font-weight: 400;
  line-height: 100%;
  letter-spacing: -0.04em;
  text-align: center;
  text-transform: uppercase;
}

.om-racine .om-detail__point-fort-libelle {
  align-self: center;
  color: var(--om-fond);
}

.om-racine .om-detail__point-fort-valeur {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 12px;
  border-radius: 6.62px;
  /* Fond non spécifié par la maquette : sans lui, un texte gris 96 sur un
     parent gris 96 serait illisible. */
  background: var(--om-fond);
  color: var(--om-grey-96);
}

.om-racine .om-detail__rarete {
  display: flex;
  align-items: center;
  gap: 15px;
}

/* Style de texte L/ui-2 */
.om-racine .om-detail__rarete-libelle {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  font-weight: 500;
  line-height: 120%;
  letter-spacing: 0.08em;
  text-align: center;
  text-transform: uppercase;
  color: var(--om-grey-96);
}

.om-racine .om-detail__etoiles {
  display: flex;
  align-items: center;
  gap: 8.8px;
}

.om-racine .om-detail__etoile {
  display: block;
  width: 12px;
  height: 12px;
  color: var(--om-jaune-zinc-20);
}

.om-racine .om-detail__etoile--acquise {
  color: var(--om-jaune-zinc);
}

.om-racine .om-detail__textes {
  position: absolute;
  left: var(--om-detail-shapes-cote);
  right: var(--om-detail-shapes-cote);
  top: var(--om-detail-textes-haut);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--om-detail-textes-gap);
  padding-right: var(--om-detail-textes-retrait);
}

.om-racine .om-detail__badge {
  position: relative;
  display: inline-flex;
  align-items: center;
  height: var(--om-detail-badge-hauteur);
  padding: 0 8px;
  border: 0;
  border-radius: 8.82px;
  background: var(--om-jaune-zinc);
  cursor: default;
}

.om-racine .om-detail__badge-lueur {
  position: absolute;
  top: -3px;
  left: 1px;
  display: block;
  width: 17.5px;
  height: 30px;
  pointer-events: none;
}

/* Style de texte L/ui-2 */
.om-racine .om-detail__badge-texte {
  position: relative;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
  font-weight: 500;
  line-height: 120%;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--om-fond);
  /* La maquette donne une `box-shadow` : sur du texte, Figma nomme ainsi ce qui
     est en CSS une `text-shadow`. */
  text-shadow: 0 2.21px 4.41px #ffffffb2;
}

/* Style de texte L/body-3 */
.om-racine .om-detail__talent {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 19px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  color: var(--om-blanc);
}

/* Style de texte L/body-2 */
.om-racine .om-detail__accroche {
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  color: var(--om-grey-88);
}

.om-racine .om-detail__shapes {
  position: absolute;
  left: var(--om-detail-shapes-cote);
  right: var(--om-detail-shapes-cote);
  bottom: var(--om-detail-shapes-bas);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.om-racine .om-detail__shapes-groupe {
  display: flex;
  align-items: center;
}

.om-racine .om-detail__dot-arrow {
  display: block;
  width: 34px;
  height: 13px;
}

.om-racine .om-detail__fond {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  display: block;
  width: 100%;
  height: auto;
  opacity: 0.8;
  pointer-events: none;
}

.om-racine .om-carte-ouverte__copie {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: 0 0;
  border-radius: inherit;
  overflow: hidden;
}

.om-racine .om-carte--copie {
  position: static;
  left: auto;
  top: auto;
  z-index: auto;
  cursor: default;
}

/* ---- Carte ouverte : gabarit mobile ----

   315 × 460 au lieu de 552 × 806. Le rapport des deux gabarits est le même sur
   les deux axes (0,571), mais la typographie ne le suit pas : réduite d'autant
   elle deviendrait illisible, la DA la remonte donc au cas par cas — le titre
   perd 41 %, l'accroche 13 %, le titre d'article rien du tout. D'où ce bloc
   d'écrasements plutôt qu'un facteur unique.

   Les valeurs données par la DA sont reprises telles quelles ; celles qui ne
   relèvent que de l'espacement sont déduites du rapport 0,571 et arrondies au
   pixel. */
.om-racine.om-racine--compact {
  --om-detail-largeur: 315px;
  --om-detail-hauteur: 460px;
  --om-detail-marge-y: 142px;
  --om-detail-marge-x: 57px;
  /* 30px sous la barre de commandes, sa hauteur, puis 20px avant la carte.
     Recalculé en JS d'après la barre mesurée, dont la hauteur suit les libellés
     et les polices ; cette valeur n'est que le repli d'avant la mesure. Elle doit
     rester une longueur simple : `_gabarit()` la lit avec `parseFloat`, et une
     propriété personnalisée en `calc()` est substituée sans être évaluée. */
  --om-detail-bas: 164px;
  --om-detail-variante: "mobile";
  --om-detail-rayon: 11px;
  --om-detail-titre-taille: 38px;
  --om-detail-interieur-haut: 12px;
  --om-detail-interieur-cote: 12px;
  /* Le titre se pose à 8px du tracé intérieur, en haut comme à gauche : l'écart
     d'entête est donc ce retrait, que le tracé lui-même soit à 12px du bord. */
  --om-detail-entete-ecart: 8px;
  --om-detail-entete-gauche: 20px;
  /* La verticale, elle, reste alignée sur le bord du tracé, comme en desktop où
     les deux valent 18px. Rapportée au retrait du titre, qui passe de 24 à 8px
     du tracé, elle vaut toujours zéro. */
  --om-detail-entete-droite: 12px;
  --om-detail-modele-haut: 57px;
  --om-detail-modele-cote: 186px;
  --om-detail-bas-hauteur: 167px;
  --om-detail-encart-haut: 13px;
  --om-detail-encart-hauteur: 99px;
  --om-detail-shapes-cote: 21px;
  --om-detail-shapes-bas: 7px;
  --om-detail-textes-gap: 6px;
  --om-detail-textes-retrait: 34px;
  /* Badge à 274px du haut de la carte. L'encart s'ouvre à 306 (460 - 167 de bas,
     + 13), le groupe remonte donc de 32. Seul lui bouge : le pied garde son
     ancrage sur l'encart, point fort et rareté ne suivent pas. */
  --om-detail-textes-haut: -32px;
  --om-detail-badge-hauteur: 16px;
  --om-detail-secrets-marge: 10px;
  --om-detail-secrets-gap: 20px;
  --om-detail-picto-bas: 18px;
}

/* -- Recto -- */
.om-racine--compact .om-detail__entete {
  gap: 9px;
}

.om-racine--compact .om-detail__titre {
  text-shadow: 0 0 2.5px #ffe60080;
}

.om-racine--compact .om-detail__verticale {
  width: 49px;
  margin-top: -1px;
  font-size: 7px;
}

.om-racine--compact .om-detail__pied {
  padding: 12px;
}

.om-racine--compact .om-detail__point-fort {
  gap: 5px;
  padding: 2.5px 2.5px 2.5px 6px;
  border-radius: 5px;
}

.om-racine--compact .om-detail__point-fort-libelle,
.om-racine--compact .om-detail__point-fort-valeur {
  font-size: 10px;
}

.om-racine--compact .om-detail__point-fort-valeur {
  max-width: 120px;
  padding: 0 7px;
  border-radius: 4px;
}

.om-racine--compact .om-detail__rarete {
  gap: 9px;
}

.om-racine--compact .om-detail__rarete-libelle {
  font-size: 8px;
}

.om-racine--compact .om-detail__etoiles {
  gap: 5px;
}

.om-racine--compact .om-detail__etoile {
  width: 6px;
  height: 6px;
}

.om-racine--compact .om-detail__badge {
  padding: 0 5px;
  border-radius: 5px;
}

.om-racine--compact .om-detail__badge-lueur {
  top: -2px;
  left: 0.5px;
  width: 10px;
  height: 17px;
}

.om-racine--compact .om-detail__badge-texte {
  font-size: 8px;
  text-shadow: 0 1.3px 2.5px #ffffffb2;
}

.om-racine--compact .om-detail__talent {
  font-size: 14px;
}

.om-racine--compact .om-detail__accroche {
  font-size: 13px;
}

.om-racine--compact .om-detail__dot-arrow {
  width: 16.47px;
  height: 6.04px;
}

/* Le numéro atomique retrouve les mesures qu'il a sur les cartes de la map :
   l'agrandissement du détail n'a plus lieu d'être à ce gabarit. */
.om-racine--compact .om-detail__shapes .om-numero {
  --om-numero-taille: 12px;
  --om-numero-cercle: 5.5px;
}

/* -- Verso -- */
.om-racine--compact .om-detail__secrets {
  border-radius: 10px;
  box-shadow: 2px 2px 6px 0 rgba(255, 255, 255, 0.23);
}

.om-racine--compact .om-detail__secrets-corps {
  top: calc(24px - var(--om-detail-secrets-marge));
  left: calc(24px - var(--om-detail-secrets-marge));
  right: calc(24px - var(--om-detail-secrets-marge));
}

.om-racine--compact .om-detail__recit {
  gap: 17px;
}

.om-racine--compact .om-detail__secrets-titre {
  font-size: 22px;
  letter-spacing: 0.01em;
}

/* `gap: 0` et non un `<p>` unique : la DA demande de passer de deux retours à la
   ligne à un seul, pas de souder les paragraphes. Le découpage du JSON est donc
   conservé, c'est l'interligne qui disparaît. */
.om-racine--compact .om-detail__secrets-texte {
  gap: 0;
  font-size: 13px;
  line-height: 130%;
}

.om-racine--compact .om-detail__articles {
  gap: 11px;
}

.om-racine--compact .om-detail__articles-titre {
  font-size: 14px;
}

.om-racine--compact .om-detail__article {
  gap: 11px;
  padding: 5px 11px 5px 5px;
  border-radius: 7px;
}

/* Un seul article au verso : place manquante à ce gabarit. La sélection se fait
   par frère précédent plutôt que par `nth-of-type` — l'article est un `<a>` ou un
   `<div>` selon qu'il porte une URL, le type ne peut donc pas servir de repère. */
.om-racine--compact .om-detail__article ~ .om-detail__article {
  display: none;
}

.om-racine--compact .om-detail__article-image {
  width: 70px;
  height: 70px;
  border-radius: 5px;
}

.om-racine--compact .om-detail__article-textes {
  gap: 7px;
}

.om-racine--compact .om-detail__article-meta {
  gap: 7px;
  font-size: 8px;
}

.om-racine--compact .om-detail__article-bouton {
  width: 20px;
  height: 20px;
  border-radius: 7px;
}

.om-racine--compact .om-detail__article-bouton svg {
  width: 8px;
  height: 8px;
}

.om-racine--compact .om-detail__picto {
  width: 24px;
  height: 24px;
}

/* ---- Barre du haut : logo centré, bouton de fermeture à droite ---- */
/* Pas de `z-index` sur la barre : il en ferait un contexte d'empilement, et son
   bouton de fermeture ne pourrait plus passer sous le voile de la carte ouverte.
   Le rang est donc porté par chaque élément, qui reçoit pour cela un
   `position: relative`. */
.om-racine .om-top {
  position: absolute;
  top: var(--om-marge-haut);
  left: var(--om-marge-cote);
  right: var(--om-marge-cote);

  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;

  padding-bottom: 200px;

  /* La barre couvre une large bande de l'écran mais ne doit rien intercepter :
     le survol et le clic doivent atteindre la scène 3D en dessous. Seul le
     bouton de fermeture rétablit les interactions. */
  pointer-events: none;
}

/* Le voile est porté par un pseudo-élément plutôt que par la barre elle-même :
   il doit filer jusqu'aux bords de l'écran, alors que la barre est en retrait
   de 30px en haut et 40px sur les côtés. Sans cela, le dégradé s'arrêterait net
   à 0,9 d'opacité sur trois arêtes visibles. */
.om-racine .om-top::before {
  content: "";
  position: absolute;
  inset:
    calc(-1 * var(--om-marge-haut))
    calc(-1 * var(--om-marge-cote))
    0;
  /* Au-dessus des cartes, qui montent jusqu'au rang 11, mais sous le voile : la
     bande s'efface donc avec le reste du décor quand une carte s'ouvre. */
  z-index: var(--om-z-decor);
  pointer-events: none;
  background: linear-gradient(
    0deg,
    rgba(3, 6, 14, 0) 0%,
    rgba(3, 6, 14, 0.082482) 17.93%,
    rgba(3, 6, 14, 0.165219) 33.58%,
    rgba(3, 6, 14, 0.247411) 47.11%,
    rgba(3, 6, 14, 0.328258) 58.67%,
    rgba(3, 6, 14, 0.40696) 68.42%,
    rgba(3, 6, 14, 0.482716) 76.5%,
    rgba(3, 6, 14, 0.554728) 83.08%,
    rgba(3, 6, 14, 0.622195) 88.31%,
    rgba(3, 6, 14, 0.684316) 92.33%,
    rgba(3, 6, 14, 0.740293) 95.32%,
    rgba(3, 6, 14, 0.789325) 97.42%,
    rgba(3, 6, 14, 0.830611) 98.78%,
    rgba(3, 6, 14, 0.863352) 99.56%,
    rgba(3, 6, 14, 0.886749) 99.92%,
    rgba(3, 6, 14, 0.9) 100%
  );
}

.om-racine .om-top__logo {
  grid-column: 2;
  display: block;
  width: 128px;
  height: auto;
  position: relative;
  z-index: var(--om-z-interface);
}

/* Toujours dans la grille, jamais en `display: none` : le fondu doit se jouer
   dans les deux sens, et `display` ne s'interpole pas. Sa place réservée ne coûte
   rien — la barre n'a pas de fond propre et n'intercepte rien. `visibility` bascule
   à la fin du fondu, pour qu'il sorte aussi du parcours au clavier. */
.om-racine .om-top__retour {
  grid-column: 1;
  justify-self: start;
  display: inline-flex;
  position: relative;
  z-index: var(--om-z-interface);

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--om-fondu),
    visibility 0s var(--om-fondu-duree);
}

.om-racine--detail .om-top__retour {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition:
    opacity var(--om-fondu),
    visibility 0s;
}

.om-racine .om-bouton-retour {
  align-items: center;
  gap: 20px;
  padding: 8px;
  border: 0;
  background: transparent;
  color: var(--om-grey-96);
  cursor: pointer;

  /* Style de texte L/ui-1 */
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 480;
  line-height: 105%;
  letter-spacing: 0em;
  text-align: center;
}

/* Le libellé court ne sert qu'au gabarit mobile, où le long ne tiendrait pas. */
.om-racine .om-bouton-retour__court {
  display: none;
}

.om-racine .om-bouton-retour__icone {
  display: block;
  width: 12px;
  height: 12px;
  flex: none;
}

.om-racine .om-bouton-retour:focus-visible {
  outline: 2px solid var(--om-grey-96);
  outline-offset: 2px;
}

.om-racine .om-top__fermer {
  grid-column: 3;
  justify-self: end;
  pointer-events: auto;
  position: relative;
  z-index: var(--om-z-interface);
}

/* Son passage sous le voile carte ouverte est réglé plus bas, avec celui des deux
   blocs des angles bas : les trois se tiennent, et l'ordre entre l'état de repos
   et l'état `--detail` doit rester lisible d'un seul endroit. */

/* -- Barre du haut : gabarit mobile --
   Placé après les règles de base : les sélecteurs ont la même spécificité, c'est
   l'ordre qui tranche. */

/* Le retrait passe du positionnement au padding, pour que la hauteur de la barre
   colle à son contenu — la bande de 200px n'existait que pour étaler le dégradé,
   qui disparaît ici.

   Deux lignes : logo et fermeture se font face, le retour vient dessous quand une
   carte est ouverte. Hors détail il reste masqué, la grille retombe alors sur sa
   seule première ligne et l'entre-deux ne s'applique pas. */
.om-racine--compact .om-top {
  top: 0;
  left: 0;
  right: 0;
  padding: 30px 20px;

  display: grid;
  grid-template-columns: auto auto;
  justify-content: space-between;
  align-items: center;
  row-gap: 20px;
}

.om-racine--compact .om-top::before {
  display: none;
}

.om-racine--compact .om-top__logo {
  grid-area: 1 / 1;
  width: 115px;
  height: 36px;
}

/* Le bouton mesure 97 × 33,75 : bordures, 8px de retrait, « Fermer » en 15px et
   sa croix de 12. Un facteur d'échelle plutôt que des mesures reprises une à une,
   pour que le retrait, le rayon et l'écart se réduisent ensemble. Origine à
   droite : il reste collé au bord du retrait de la barre. */
.om-racine--compact .om-top__fermer {
  grid-area: 1 / 2;
  justify-self: end;
  transform: scale(0.89);
  transform-origin: right center;
}

.om-racine--compact .om-top__retour {
  grid-area: 2 / 1;
  justify-self: start;
}

/* Style de texte S/ui-1 — seule la taille change, le reste de L/ui-1 tient. */
.om-racine--compact .om-bouton-retour {
  font-size: 13px;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.om-racine--compact .om-bouton-retour__long {
  display: none;
}

.om-racine--compact .om-bouton-retour__court {
  display: inline;
}

/* ---- Bouton de fermeture ---- */
.om-racine .om-bouton-fermer {
  display: inline-flex;
  align-items: center;
  gap: 20px;

  padding: 8px;
  border: 1px solid var(--om-grey-40);
  border-radius: 4px;
  background: var(--om-fond-verre);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);

  color: var(--om-grey-96);
  cursor: pointer;

  /* Style de texte L/ui-1 */
  font-family: "Inter", system-ui, sans-serif;
  font-size: 15px;
  font-weight: 480;
  line-height: 105%;
  /* 0em et non 0 : `letter-spacing: 0` retombe sur `normal`, qui laisse le
     moteur appliquer son propre crénage. */
  letter-spacing: 0em;
  text-align: center;
}

.om-racine .om-bouton-fermer__icone {
  display: block;
  width: 12px;
  height: 12px;
  flex: none;
}

.om-racine .om-bouton-fermer:focus-visible {
  outline: 2px solid var(--om-grey-96);
  outline-offset: 2px;
}

/* ---- Barre de commandes de la carte ouverte ----

   Le groupe des trois blocs — matière précédente, matière ouverte, suivante —
   sert les deux gabarits : passer d'une matière à l'autre sans repasser par la
   map n'a rien de propre au tactile.

   Le bouton de retournement, lui, n'existe qu'en tactile. Au curseur, le clic sur
   la carte et l'étiquette qui l'annonce s'en chargent déjà, et rien ne change de
   ce côté. */
.om-racine .om-nav {
  position: absolute;
  left: 50%;
  bottom: 26px;
  width: 420px;
  /* Centrage par la marge, comme la map : un `translateX` créerait un contexte
     d'empilement dont la barre n'a pas besoin. */
  margin-left: -210px;
  z-index: var(--om-z-nav);

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;

  /* La barre couvre toute sa largeur mais ne doit rien intercepter en dehors de
     ses boutons : la map reste manipulable derrière. */
  pointer-events: none;

  /* Même fondu que le bouton de retour, dont elle est le pendant bas. */
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--om-fondu),
    visibility 0s var(--om-fondu-duree);
}

.om-racine--detail .om-nav {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--om-fondu),
    visibility 0s;
}

/* Toute la largeur disponible entre les marges, et le bouton de retournement
   au-dessus. */
.om-racine--compact .om-nav {
  left: 20px;
  right: 20px;
  bottom: 30px;
  width: auto;
  margin-left: 0;
}

.om-racine .om-nav__bascule {
  display: none;
}

.om-racine--compact .om-nav__bascule {
  display: inline-flex;
}

.om-racine .om-nav__groupe {
  display: flex;
  align-items: stretch;
  gap: 10px;
  width: 100%;
}

/* Les trois blocs partagent la même boîte et se répartissent la largeur à parts
   égales — `flex: 1 1 0` et non `1 1 auto`, qui les aurait dimensionnés sur leur
   contenu, donc trois libellés de longueurs différentes.

   L'écart de 40px de la maquette est repris comme un minimum, pas comme une
   consigne : à un tiers d'un écran de 375px chaque bloc n'offre que 87px utiles,
   où 40px de vide ne tiennent pas. C'est `space-between` qui distribue le reste. */
.om-racine .om-nav__pas,
.om-racine .om-nav__courant {
  flex: 1 1 0;
  min-width: 0;

  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;

  padding: 8px;
  border: 1px solid var(--om-grey-40);
  border-radius: 4px;
  background: var(--om-fond);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);

  color: var(--om-grey-96);

  /* Style de texte S/body-3 */
  font-family: "Inter", system-ui, sans-serif;
  font-size: 16px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  text-align: center;
}

.om-racine .om-nav__pas {
  pointer-events: auto;
  cursor: pointer;
}

.om-racine .om-nav__pas[disabled] {
  opacity: 0.4;
  cursor: default;
}

.om-racine .om-nav__pas:focus-visible {
  outline: 2px solid var(--om-grey-96);
  outline-offset: 2px;
}

.om-racine .om-nav__pas-fleche {
  display: block;
  width: 12px;
  height: 12px;
  flex: none;
  color: var(--om-grey-96);
}

/* Une matière au nom long déborderait de son tiers de largeur : elle se coupe
   plutôt que de pousser la flèche hors du bloc. */
.om-racine .om-nav__pas-nom,
.om-racine .om-nav__nom {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.om-racine .om-nav__point {
  width: 10px;
  height: 10px;
  flex: none;
  border-radius: 50%;
  background: var(--om-jaune-zinc);
}

/* Le nombre et le nom partagent leur ligne de base, comme deux mots d'une même
   phrase. C'est le seul alignement qui ne dépende pas des métriques déclarées
   par les polices : centrer les boîtes obligeait à rattraper à la marge le vide
   que TINY 5x3 laisse sous ses chiffres (0,591 / 0,409 pour des glyphes qui
   n'occupent que 0,48em), et le moindre écart de rendu — une police de repli, un
   ajustement du navigateur — faisait retomber le nombre sous le nom.

   Le point, lui, n'a pas de ligne de base : il se centre à part. */
.om-racine .om-nav__courant {
  align-items: baseline;
}

.om-racine .om-nav__point {
  align-self: center;
}

.om-racine .om-nav__isotope {
  flex: none;
  font-family: "TINY 5x3", monospace;
  font-size: 26px;
  font-weight: 200;
  line-height: 1;
  /* Seule la marge basse subsiste : elle ne déplace pas la ligne de base, elle
     évite au vide sous les chiffres de creuser la hauteur de la barre. */
  margin-bottom: -0.399em;
  letter-spacing: 0em;
  text-transform: uppercase;
}

/* Deux filets superposés en bas du bloc courant, qui marquent la matière ouverte
   à la façon d'un onglet actif. Hors du flux, ils ne comptent pas dans le
   `space-between`.

   Le dégradé est porté par le fond d'un élément de 1px et non par un
   `border-image` comme l'exporte Figma : à hauteur nulle, la boîte du filet ne
   fait qu'un pixel, et le découpage en neuf régions du `border-image-slice` n'a
   plus de quoi s'appliquer. Le rendu est le même — un trait dont l'alpha suit le
   dégradé de gauche à droite. */
.om-racine .om-nav__ligne,
.om-racine .om-nav__lueur {
  position: absolute;
  /* Un élément absolu se cale sur la boîte de padding de son conteneur, pas sur
     sa boîte de bordure : à `bottom: 0` les filets se posaient sur le bord
     intérieur de la bordure basse, donc un pixel au-dessus du trait. */
  bottom: -1px;
  left: 50%;
  transform: translateX(-50%);
  display: block;
  height: 1px;
  pointer-events: none;
}

.om-racine .om-nav__ligne {
  width: 20px;
  background: radial-gradient(
    50% 203.87% at 50% 0%,
    rgba(255, 230, 0, 0.56) 0%,
    rgba(255, 230, 0, 0) 100%
  );
}

.om-racine .om-nav__lueur {
  width: 80px;
  background: radial-gradient(
    50% 200.3% at 50% 0%,
    rgba(255, 230, 0, 0.8) 0%,
    rgba(255, 230, 0, 0) 100%
  );
  box-shadow: 0 -1px 5px 0 #14b071;
}

/* Sans `display` ici : c'est le gabarit qui décide de sa présence, plus haut. */
.om-racine .om-nav__bascule {
  align-items: center;
  gap: 20px;
  pointer-events: auto;

  padding: 16px 24px;
  border: 0;
  border-radius: 20px;
  background: #00000066;
  backdrop-filter: blur(60px);
  -webkit-backdrop-filter: blur(60px);
  box-shadow:
    0 0 24px 0 #ffffff40,
    2px 3px 24px 0 #ffffff26;

  color: var(--om-grey-96);
  cursor: pointer;

  /* Style de texte S/body-2 */
  font-family: "Inter", system-ui, sans-serif;
  font-size: 14px;
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
  text-align: center;
}

.om-racine .om-nav__bascule:focus-visible {
  outline: 2px solid var(--om-grey-96);
  outline-offset: 2px;
}

/* Les deux libellés occupent la même cellule : le bouton prend la largeur du plus
   long et ne bouge plus d'un état à l'autre. Remplacer le texte l'aurait fait
   sauter au milieu du retournement, et rien n'aurait pu se fondre. */
.om-racine .om-nav__bascule-libelles {
  display: grid;
  white-space: nowrap;
}

/* Roulement plutôt que fondu croisé : le libellé sortant s'élève et s'efface
   pendant que l'entrant monte depuis le bas. Les deux se déplacent du même côté,
   ce qu'un fondu seul ne dit pas — et au retour ils redescendent ensemble, l'état
   caché de chacun servant à la fois de sortie et d'entrée. */
.om-racine .om-nav__bascule-texte {
  grid-area: 1 / 1;
  transition:
    opacity var(--om-nav-roulement),
    transform var(--om-nav-roulement);
}

.om-racine .om-nav__bascule-texte--verso {
  opacity: 0;
  transform: translateY(0.6em);
}

/* `display: block` obligatoire : le remplacement du libellé le déplace, et une
   transformation ne s'applique pas à un élément inline non remplacé. Les autres
   libellés de la barre sont des éléments flex, donc déjà blockifiés. */
.om-racine .om-nav__bascule-valeur {
  display: block;
}

.om-racine .om-nav__bascule--verso .om-nav__bascule-texte--recto {
  opacity: 0;
  transform: translateY(-0.6em);
}

.om-racine .om-nav__bascule--verso .om-nav__bascule-texte--verso {
  opacity: 1;
  transform: none;
}

/* Le chevron fait demi-tour avec la carte : il annonce une action réversible,
   pas une progression. */
.om-racine .om-nav__bascule-fleche {
  display: block;
  width: 8px;
  height: 8px;
  flex: none;
  color: var(--om-blanc);
  transition: transform var(--om-nav-roulement);
}

.om-racine .om-nav__bascule--verso .om-nav__bascule-fleche {
  transform: rotate(180deg);
}

/* Plus rien à cliquer sur la carte elle-même : la barre du bas a repris ses deux
   actions.

   Et elle ne se centre plus dans le conteneur mais dans la bande laissée libre
   entre la barre du haut et le bouton de retournement, dont les deux bornes sont
   relevées en JS. `100%` se résout sur la hauteur du conteneur, le milieu de la
   bande se calcule donc sans rien mesurer de plus. */
.om-racine--compact .om-carte-ouverte {
  cursor: default;
  top: calc(
    var(--om-detail-marge-y) +
      (100% - var(--om-detail-marge-y) - var(--om-detail-bas)) / 2
  );
}

/* ---- Bouton rond ---- */
.om-racine .om-bouton-rond {
  display: inline-flex;
  align-items: center;
  justify-content: center;

  width: 40px;
  height: 40px;
  min-width: 40px;
  max-width: 40px;
  min-height: 40px;
  max-height: 40px;
  flex: none;

  padding: 0;
  gap: 0;
  border: 0;
  border-radius: 20px;
  background: var(--om-fond-bouton-rond);
  backdrop-filter: blur(60px);
  -webkit-backdrop-filter: blur(60px);
  cursor: pointer;
  /* Les flèches défilent au maintien : sans cela, tenir le bouton et bouger d'un
     rien ferait défiler la page hôte à la place de la map. */
  touch-action: none;

  /* Même fondu que celui du groupe des flèches. Sans lui, l'extinction d'un
     bouton sautait d'un coup à côté de flèches qui, elles, se fondent. */
  transition: opacity var(--om-fondu);
}

/* Un seul cran de zoom : à chaque niveau, un des deux boutons n'a plus d'objet.
   Il reste en place plutôt que de disparaître — la paire est un repère, et la
   croix perdrait sa forme. */
.om-racine .om-bouton-rond[disabled] {
  opacity: 0.35;
  cursor: default;
}

.om-racine .om-bouton-rond__icone {
  display: block;
  flex: none;
}

.om-racine .om-bouton-rond:focus-visible {
  outline: 2px solid var(--om-blanc);
  outline-offset: 2px;
}

/* ---- Contrôles de navigation, en bas à gauche ----

   Les deux groupes occupent la même cellule et se croisent. L'écart dégage le
   centre de la croix, et ne varie pas : les boutons de zoom doivent rester au
   même endroit selon que les flèches sont là ou non, or le groupe vertical est
   centré dans sa cellule — un écart plus petit les aurait rapprochés de 8px
   chacun. */
.om-racine .om-size {
  --om-size-ecart: 32px;

  position: absolute;
  left: 0;
  bottom: 0;
  z-index: var(--om-z-interface);

  display: grid;
  padding: 40px 40px 40px 20px;
}

.om-racine .om-size__groupe {
  grid-area: 1 / 1;
  place-self: center;

  display: flex;
  gap: var(--om-size-ecart);
}

.om-racine .om-size__groupe--vertical {
  flex-direction: column;
}

/* Les flèches restent dans le flux même quand elles ne servent pas : les en
   retirer réduisait la boîte de la croix à la largeur d'un seul bouton, et le
   groupe vertical, centré dedans, se déplaçait de 36px vers la gauche. */
.om-racine .om-size__groupe--horizontal {
  flex-direction: row;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--om-fondu),
    visibility 0s var(--om-fondu-duree);
}

.om-racine .om-size--croix .om-size__groupe--horizontal {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition:
    opacity var(--om-fondu),
    visibility 0s;
}

.om-racine .om-bouton-rond__icone--miroir {
  transform: scaleX(-1);
}

/* ---- Mention légale, en bas à droite ---- */
.om-racine .om-mention {
  position: absolute;
  right: 20px;
  bottom: 20px;
  z-index: var(--om-z-interface);

  width: var(--om-mention-largeur);
  padding: var(--om-mention-padding);
  border: 1px solid var(--om-blanc-20);
  border-radius: 8px;
  background: var(--om-fond-verre);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);

  color: var(--om-grey-96);

  /* Style de texte L/body-1, dont seule la taille cède sur un conteneur étroit. */
  font-family: "Inter", system-ui, sans-serif;
  font-size: var(--om-mention-taille);
  font-weight: 480;
  line-height: 145%;
  letter-spacing: 0em;
}

/* Carte ouverte : les deux blocs des angles bas passent sous le voile. Ils
   servent la map, pas la fiche, et flottaient par-dessus l'assombrissement comme
   s'ils restaient actifs. La barre du haut, elle, garde son rang : elle porte le
   retour à l'expérience.

   Le fondu est porté par leur propre opacité, et non laissé au voile qui les
   recouvre : à la fermeture le voile s'efface au moment même où leur rang
   remonte, il n'en reste alors aucun pour les révéler progressivement. Les
   durées suivent celles du voile, l'effet est donc symétrique.

   Le rang, lui, n'est pas une valeur qu'on interpole : sa transition ne sert
   qu'à retarder sa bascule jusqu'à la fin du fondu de retour, pour qu'ils ne
   repassent pas devant le voile en cours de route.

   Les deux états se suivent ici, l'état de repos d'abord : les sélecteurs ont la
   même spécificité, et si le repos passait en second ses délais s'appliqueraient
   aussi à l'ouverture. */
.om-racine .om-size,
.om-racine .om-mention,
.om-racine .om-top__fermer {
  transition:
    opacity var(--om-detail-sortie) ease-in,
    z-index 0s var(--om-detail-sortie);
}

.om-racine--detail .om-size,
.om-racine--detail .om-mention,
.om-racine--detail .om-top__fermer {
  z-index: var(--om-z-decor);
  opacity: 0;
  /* Une opacité nulle ne suffit pas à rendre un élément inerte. */
  pointer-events: none;
  transition:
    opacity var(--om-detail-entree) ease-out,
    z-index 0s;
}

/* ---- Focus au clavier ----

   Les boutons portent déjà le leur. Ceux-ci n'en avaient aucun : la carte de la
   map et la carte ouverte ne sont pas des `<button>`, et l'article est un lien
   dont le style de survol ne se déclenche pas au clavier.

   `outline` et non `box-shadow` : l'anneau doit rester visible en contraste
   forcé, où les ombres sont supprimées. Le décalage le sort du bord de la carte,
   qui a son propre liseré clair. */
.om-racine .om-carte:focus-visible,
.om-racine .om-detail__article:focus-visible {
  outline: 2px solid var(--om-jaune-zinc);
  outline-offset: 3px;
}

/* Le dialogue n'est pas dans le parcours au tabulateur : il ne reçoit le focus
   que par programme, à l'ouverture, pour être annoncé. Un anneau y désignerait
   une cible qui n'existe pas — et il s'allumait à chaque flèche, puisque
   `:focus-visible` retient seulement que le clavier vient de servir. */
.om-racine .om-carte-ouverte:focus {
  outline: none;
}

/* Focus rendu par programme, au retour d'une carte fermée. Même raison : Échap
   met le navigateur en mode clavier, et la vignette s'allumait sous la souris.
   La classe part au premier `blur` — une tabulation la ramène et l'anneau
   revient, cette fois à bon droit. */
.om-racine .om-focus-discret:focus-visible {
  outline: none;
}

/* L'article se distingue au survol par sa teinte : au clavier, il faut que le
   même état s'applique, sinon seul l'anneau bouge. */
.om-racine .om-detail__article:focus-visible .om-detail__article-titre,
.om-racine .om-detail__article:focus-visible .om-detail__article-meta {
  color: var(--om-grey-96);
}

/* ---- Entrée en scène ----

   `backwards` et non `both` : l'état de départ ne vaut que pendant le retard, et
   la propriété est rendue à sa valeur de base une fois l'animation finie. Avec
   `both`, l'opacité resterait figée à 1 et la mention légale comme la croix de
   déplacement ne pourraient plus s'effacer sous la carte ouverte. */
@keyframes om-allumer {
  from {
    opacity: 0;
  }
}

/* Distribution : les cartes partent d'un paquet posé en bas de l'écran et
   rejoignent leur place l'une après l'autre, comme on les distribuerait.

   Le trajet est propre à chaque carte, `cards.js` le mesure et le pose en
   propriétés. Il passe par `translate` et `rotate`, les propriétés à part :
   `transform` appartient à l'inclinaison au survol, qui l'écraserait dès qu'on
   effleure une carte en cours de route. Les trois se composent, une carte
   survolée pendant sa distribution vole et penche à la fois. */
@keyframes om-distribuer {
  from {
    translate: var(--om-carte-depart-x, 0px) var(--om-carte-depart-y, 0px);
    rotate: var(--om-carte-depart-rotation, 0deg);
  }
}

/* L'autre entrée : les cartes s'allument sur place en montant de quelques
   pixels. Plus sobre que la distribution, et sans rien à mesurer — la montée est
   la même pour toutes. Même remarque sur `translate` que ci-dessus. */
@keyframes om-allumer-carte {
  from {
    opacity: 0;
    translate: 0 var(--om-entree-cartes-montee);
  }
}

/* Le rang est celui de l'empilement, posé par `cards.js` : les cartes se posent
   donc dans l'ordre où elles se recouvrent, chacune sur la précédente.

   L'animation n'existe que sous la classe du groupe, retirée à la fin : sans
   quoi une carte reconstruite se remettrait à voler. */
.om-racine .om-cartes--distribution .om-carte {
  /* Deux animations, sur des propriétés disjointes : l'opacité pour l'allumage du
     paquet, `translate` et `rotate` pour le vol. Elles ne se gênent donc pas, et
     surtout elles peuvent être retenues séparément — voir la règle du paquet. */
  animation:
    om-distribuer var(--om-entree-carte) backwards,
    om-allumer var(--om-entree-paquet) backwards;
  animation-delay:
    calc(
      var(--om-entree-cartes-depart) + var(--om-entree-rang, 0) * var(--om-entree-cartes-pas)
    ),
    var(--om-entree-paquet-retard);
}

/* Seule l'image-clé change : durée, courbe, retard et remplissage sont ceux de
   la règle ci-dessus, dont celle-ci ne fait que dévier la partition. Le rang y
   est spatial et non l'empilement, `cards.js` le repose en conséquence. */
.om-racine .om-cartes--distribution.om-cartes--entree-fondu .om-carte {
  /* La carte s'allume déjà en montant : le second fondu n'a plus lieu d'être, il
     ne ferait que multiplier deux opacités. La place reste occupée dans la liste,
     dont l'ordre commande celui de `animation-play-state`. */
  animation-name: om-allumer-carte, none;
}

/* Paquet tenu : l'animation est en pause à son instant zéro, donc dans son
   retard, où le remplissage `backwards` impose l'image de départ. Les cartes
   restent empilées en bas de l'écran tant que le décor n'est pas prêt — polices
   substituées, sonde des modèles rendue, première matière chargée. */
.om-racine .om-cartes--paquet .om-carte {
  /* Seule la distribution est retenue. L'allumage, lui, doit se jouer pendant
     l'attente — c'est tout l'objet. Les deux valeurs suivent l'ordre de la liste
     d'animations ci-dessus. */
  animation-play-state: paused, running;
}

/* Le logo et la fermeture s'allument, pas la barre : son dégradé est porté par un
   pseudo-élément, qu'une opacité posée sur elle emporterait avec le reste. Il
   fait partie du fond, il est là dès la première image — comme le noir. */
.om-racine .om-top__logo,
.om-racine .om-top__fermer,
.om-racine .om-size,
.om-racine .om-mention {
  animation: om-allumer var(--om-entree-interface) backwards;
}

@media (prefers-reduced-motion: reduce) {
  .om-racine::before,
  .om-racine::after,
  /* Même spécificité que la règle de distribution, qui compte un sélecteur de
     plus : à égalité de poids, celle-ci passerait après sans jamais gagner. La
     variante en fondu en compte un de plus encore, d'où la seconde ligne. */
  .om-racine .om-cartes--distribution .om-carte,
  .om-racine .om-cartes--distribution.om-cartes--entree-fondu .om-carte,
  .om-racine .om-top__logo,
  .om-racine .om-top__fermer,
  .om-racine .om-size,
  .om-racine .om-mention {
    animation: none;
  }
}

/* Aucune transition le temps du montage. Les états initiaux — croix de
   déplacement, crans de zoom atteignables, cartes en place — sont posés après des
   mesures qui forcent une résolution de style : chacun partirait donc en fondu,
   au moment où son propre état est calculé, et jamais au même que les autres.

   `!important` assumé : la règle doit battre toutes les transitions du composant,
   et elle est retirée à la deuxième image. */
.om-racine--montage * {
  transition: none !important;
}

/* ---- Annonce du déverrouillage de la carte mystère ---- */
.om-racine .om-revelation {
  position: absolute;
  inset: 0;
  z-index: var(--om-z-revelation);
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000000cc;
  pointer-events: none;
}

/* Pas de `backdrop-filter` ici : un fondu d'opacité sur un parent qui en contient
   un fait perdre des images au compositeur, sur Safari comme sur Chrome.
   L'ellipse du SVG porte sa propre couleur. */
.om-racine .om-revelation__lueur {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 1106px;
  height: 625px;
  max-width: 100%;
  background: url("../assets/img/light.svg") center / contain no-repeat;
}

/* Style L/heading-2. Pleine largeur, à 50px des bords : la coupure voulue vient
   du `\n` du contenu, et les suivantes du navigateur quand la ligne ne tient
   plus. La taille suit alors la largeur — à 82px, la première ligne demande à
   elle seule près de 1100px. */
.om-racine .om-revelation__texte {
  position: relative;
  width: 100%;
  margin: 0;
  padding: 0 50px;
  font-family: "Big Shoulders", Impact, sans-serif;
  font-size: clamp(40px, 7vw, 82px);
  font-weight: 900;
  line-height: 96%;
  text-align: center;
  text-transform: uppercase;
  color: var(--om-grey-96);
}

.om-racine .om-revelation__accent {
  color: var(--om-jaune-zinc);
}
