/* =========================================================================
   UST — vocabulaire des composants
   Aucune valeur littérale : tout vient de tokens.css. Aucun survol non gardé.
   Aucune translation, aucun changement d'échelle au survol (§8).
   ========================================================================= */

/* =========================================================================
   1. LA GRADUATION — l'élément signature (ARBITRAGE §7)
   Une piste tracée au seuil de visibilité (--filet-haut), un segment allumé
   dont la longueur est √(minutes), un index de 2 px --lampe posé sur la
   valeur lue, et LES SEUILS IMPRIMÉS SOUS LA PISTE (G4).

   Elle ne mesure QUE du temps d'intégration, jamais autre chose.

   Deux instruments, deux axes, jamais confondus (C4). L'épaisseur de piste
   les sépare avant même la lecture : 3 px pour l'objet, 6 px pour l'appareil.

   L'état vide est un état de premier rang : piste tracée jusqu'au bout,
   segment de longueur nulle, index parqué à zéro en --gradue. Jamais une
   ligne masquée, jamais une carte absente, jamais une astérisque.
   ========================================================================= */
.grad {
  --grad-piste: var(--grad-piste-objet);
  --grad-boite: var(--grad-boite-objet);
  display: flex; flex-direction: column; gap: var(--e-2);
  /* La graduation est son propre contexte de requête (voir .grad__seuils).
     Le confinement en ligne annule sa largeur intrinsèque : sans ce
     width:100%, une graduation posée en enfant d'une rangée flex se
     réduirait à zéro — une piste invisible, exactement ce que §7 interdit.
     Mesuré : 0 px sans, la largeur disponible avec.                       */
  container-type: inline-size;
  width: 100%; min-width: 0;
}
.grad--appareil {
  --grad-piste: var(--grad-piste-appareil);
  --grad-boite: var(--grad-boite-appareil);
}

/* La valeur, en toutes lettres : la forme annonce, le texte confirme.
   Ni l'un ni l'autre ne porte seul l'information (§7). */
.grad__ligne {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: baseline; gap: var(--e-1) var(--e-3);
  max-width: none;
}
.grad__valeur { color: var(--texte); font-weight: var(--g-mono-fort); letter-spacing: 0; }
.grad__note   { color: var(--label); letter-spacing: 0; }

.grad__piste {
  position: relative; display: block; width: 100%;
  height: var(--grad-boite);
}
/* La piste, tracée jusqu'au bout — y compris à zéro. */
.grad__piste::before {
  content: ""; position: absolute; left: 0; right: 0;
  top: calc((var(--grad-boite) - var(--grad-piste)) / 2);
  height: var(--grad-piste);
  background: var(--filet-haut); border-radius: 1px;
}
/* Le segment allumé : longueur = √(min) / √(axe), calculée à la construction
   et servie en --part. Le scaleX n'est qu'une animation : la géométrie est
   déjà juste au premier rendu, sans JavaScript. */
.grad__lu {
  position: absolute; left: 0;
  top: calc((var(--grad-boite) - var(--grad-piste)) / 2);
  height: var(--grad-piste);
  width: calc(var(--part, 0) * 100%);
  background: var(--texte); border-radius: 1px;
  transform-origin: left center;
  transition: transform var(--t-pose) var(--courbe) var(--t-retard-pose);
}
/* Les traits de seuil (G4) : ils sont sous la piste ET traversent le puits. */
.grad__seuil {
  position: absolute; top: 0; height: var(--grad-boite); width: 1px;
  left: calc(var(--x) * 100%);
  transform: translateX(calc(var(--x) * -100%));
  background: var(--gradue);
}
/* L'index : 2 px de lampe posés sur la valeur lue, avec son halo. */
.grad__index {
  position: absolute; top: 0; height: var(--grad-boite); width: var(--grad-index);
  left: calc(var(--part, 0) * 100%);
  transform: translateX(calc(var(--part, 0) * -100%));
  background: var(--lampe); box-shadow: var(--ombre-index);
  transition: opacity var(--t-ui) ease var(--t-retard-index);
}
/* Les valeurs des seuils, imprimées sous la piste. L'ancrage glisse de
   gauche à droite avec la position : aucune étiquette ne déborde du puits. */
.grad__seuils {
  position: relative; display: block; width: 100%;
  min-height: var(--grad-ligne);
  color: var(--gradue); max-width: none;
}
.grad__seuils span {
  position: absolute; top: 0; white-space: nowrap;
  left: calc(var(--x) * 100%);
  transform: translateX(calc(var(--x) * -100%));
}
/* Sur l'échelle objet, les deux premiers seuils sont à 11,9 % et 23,8 % :
   sous ~455 px de piste, « 15 min » et « 1 h » se toucheraient. Aucune valeur
   n'est pour autant retirée — l'axe se lit alors sur deux lignes, chaque
   étiquette restant sous son trait. Mesuré : plus aucun chevauchement à 300 px
   de piste, la largeur d'une carte à 390.                                   */
@container (max-width: 30rem) {
  .grad--objet .grad__seuils { min-height: calc(2 * var(--grad-ligne)); }
  .grad--objet .grad__seuils span:nth-child(2n) { top: var(--grad-ligne); }
}
/* Garde-fou. Sous 15 rem de piste aucune étiquette intermédiaire ne tient,
   même sur deux lignes : seule l'extrémité de l'axe reste imprimée. Les traits
   restent tracés et la légende porte l'axe en toutes lettres, donc rien n'est
   perdu — mais une graduation n'a pas sa place dans une colonne aussi étroite.
   Ce n'est pas une mise en page prévue, c'est un filet de sécurité.         */
@container (max-width: 15rem) {
  .grad__seuils span:not(:last-child) { display: none; }
  .grad--objet .grad__seuils { min-height: var(--grad-ligne); }
  .grad--objet .grad__seuils span { top: 0; }
}
/* L'axe écrit en toutes lettres (C4, C5). */
.grad__axe { color: var(--label); max-width: none; }

/* --- L'état vide, état de premier rang ---------------------------------- */
.grad.est-vide .grad__lu    { background: var(--filet-haut); }
.grad.est-vide .grad__index { background: var(--gradue); box-shadow: none; }
.grad.est-vide .grad__valeur{ color: var(--label); font-weight: var(--g-mono); }

/* --- Le tracé : 400 ms, 120 ms de retard ; l'index : 200 ms, 420 ms ------
   Conditionné à .js ET à [data-grad]. Sans JavaScript, sans observateur, ou
   sur une graduation déclarée immobile (le hero), tout est peint d'emblée. */
.js [data-grad] .grad__lu    { transform: scaleX(0); }
.js [data-grad] .grad__index { opacity: 0; }
.js [data-grad].vu .grad__lu    { transform: none; }
.js [data-grad].vu .grad__index { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .js [data-grad] .grad__lu    { transform: none; }
  .js [data-grad] .grad__index { opacity: 1; }
}

/* --- Filet de surtitre ---------------------------------------------------
   LE MÊME TRAIT ÉTAIT DESSINÉ DEUX FOIS, SOUS DEUX NOMS, DANS DEUX FEUILLES.
   .integ-marque vivait ici, .filet-brow dans pages.css : trois rem sur un
   pixel de --filet, à l'identique, et vingt poses réparties entre les deux
   noms au hasard des lots. C'est la faute de .bande, recommencée.
   LE NOM RETENU EST .filet-brow, parce qu'il dit ce que la chose est — le
   filet du surtitre — quand .integ nommait la graduation d'intégration
   qu'elle n'est plus. .integ et .integ-marque restent en alias tant que les
   gabarits les portent ; aucune règle nouvelle ne doit les employer.
   TRANSITION : .integ / .integ-marque étaient la signature de la direction
   précédente. Une graduation ne décore jamais et ne mesure rien d'autre que
   du temps (§7) : ces marques ne sont plus qu'un filet. À remplacer par
   <Graduation> là où il y a une donnée, par .filet-brow partout ailleurs. */
.integ, .integ-marque, .filet-brow {
  display: block; height: 1px; background: var(--filet); border: 0;
}
.integ-marque, .filet-brow { width: 3rem; flex: none; }

/* =========================================================================
   2. EN-TÊTE
   C7 : aucun backdrop-filter. Le fond est tokenisé et n'apparaît qu'à 80 px
   de défilement — un seul écouteur passif, 300 ms (§8).
   ========================================================================= */
.nav {
  position: sticky; top: 0; z-index: 50;
  background: transparent; border-bottom: 1px solid transparent;
  transition: background-color var(--t-panneau) ease, border-color var(--t-panneau) ease;
}
.nav[data-collee="oui"] {
  background: var(--voile-entete);
  border-bottom-color: var(--filet);
}
/* LE BLOC DE MARQUE RESPIRE. La barre portait padding 0 et min-height
   4,5rem pour un sceau de 64 px : 4 px au-dessus, 4 en dessous, le logo
   collé au filet. L'air est posé ICI, sur la barre, et non sur .marque :
   le bouton et les liens sont centrés sur la même boîte, ils gagnent le
   même air et la barre reste un seul rythme.
   La hauteur ne saute pas à la bascule data-collee : elle vaut --h-entete
   dans les deux états, et le filet du bas est déjà là au repos, en
   transparent. Seuls le fond et la couleur du filet changent, en 300 ms. */
.nav__barre {
  display: flex; align-items: center; gap: var(--e-4);
  padding-block: var(--air-entete);
  min-height: var(--h-entete); max-width: var(--contenu); margin-inline: auto;
}
.nav__liens { display: flex; align-items: center; gap: var(--e-4); margin-left: auto; }
.nav__lien {
  position: relative; display: inline-block;
  font-size: var(--d-petit); font-weight: var(--g-corps-moyen);
  text-decoration: none; color: var(--champ);
  padding-block: var(--e-2); min-height: 1.5rem;
  transition: color var(--t-micro) ease;
}
/* Le soulignement de nav est le seul autre emploi de la lampe dans le chrome. */
.nav__lien::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .15rem; height: 2px;
  background: var(--lampe); transform: scaleX(0); transform-origin: left;
  transition: transform var(--t-ui) var(--courbe);
}
.nav__lien[aria-current="page"] { color: var(--texte); }
.nav__lien[aria-current="page"]::after, .nav__lien:focus-visible::after { transform: scaleX(1); }
@media (hover: hover) {
  .nav__lien:hover { color: var(--texte); }
  .nav__lien:hover::after { transform: scaleX(1); }
}
/* LA PORTE DU FORUM. Un buste, pas un mot : la barre n'a que quelques
   pixels de marge au large, et un septième libellé la casserait. L'icône est
   décorative, le nom accessible est le .u-sr qu'elle porte.
   ELLE EST LE DERNIER ENFANT DE LA BARRE, ET AUCUNE PROPRIÉTÉ order NE LA
   DÉPLACE : l'ordre du clavier suit donc l'ordre de l'oeil aux deux tailles,
   ce qu'order aurait cassé au large (l'icône aurait été atteinte avant les
   six liens tout en s'affichant après eux). Au large, .nav__liens prend la
   marge automatique et l'icône la suit, à l'extrémité droite après le bouton ;
   au serré, .nav__liens quitte le flux, c'est le burger qui prend la marge et
   l'icône le suit.
   La cible fait 2,25rem au large et 2,75rem au serré, la taille du burger :
   au-dessus des 24 px de la 2.5.8 partout, à 44 px là où on vise du doigt. */
.nav__compte {
  display: grid; place-items: center;
  width: 2.25rem; height: 2.25rem;
  color: var(--champ);
  transition: color var(--t-micro) ease;
}
.nav__buste { width: 1.375rem; height: 1.375rem; }
@media (hover: hover) { .nav__compte:hover { color: var(--texte); } }

/* LA BANDE ÉTROITE, 62 à 72rem. Mesuré : l'icône ajoutée, le bloc de marque
   se comprime et son nom passe de deux à trois lignes entre 992 et 1040 px,
   la barre gagnant 7 px. Ce n'est pas l'icône qu'il faut retirer, c'est
   l'écart qui est trop large quand sept éléments se disputent la ligne : il
   descend d'un cran, ce qui rend 8 px sur chacun des six intervalles, et la
   marque cesse de se plier. Elle ne se comprime plus du tout, de surcroît :
   le nom de la marque est ce qui nomme le site, il passe avant un écart. */
@media (min-width: 62rem) and (max-width: 71.99rem) {
  .nav__barre, .nav__liens { gap: var(--e-3); }
}
.marque { flex: none; }

.nav__burger { display: none; }

@media (max-width: 61.99rem) {
  .nav__liens {
    /* Le panneau s'ouvre SOUS la barre : il lit sa hauteur, il ne la recopie
       plus. Avec 4,5rem en dur il recouvrait le bas de l'en-tête dès que la
       barre gagnait un pixel. */
    position: fixed; inset: var(--h-entete) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--ciel); border-bottom: 1px solid var(--filet);
    padding: var(--e-3) var(--marge) var(--e-5);
    clip-path: inset(0 0 100% 0); transition: clip-path var(--t-pose) ease-in-out;
  }
  body:has(.nav__burger[aria-expanded="true"]) .nav__liens { clip-path: inset(0 0 0 0); }
  body:has(.nav__burger[aria-expanded="true"]) { overflow: hidden; }
  .nav__lien { padding-block: var(--e-3); border-bottom: 1px solid var(--filet); font-size: 1rem; }
  .nav__lien::after { display: none; }
  .nav__liens .btn { margin-top: var(--e-3); justify-content: center; }
  .nav__compte { width: 2.75rem; height: 2.75rem; color: var(--texte); }
  .nav__burger {
    display: grid; place-items: center; margin-left: auto;
    width: 2.75rem; height: 2.75rem; color: var(--texte);
  }
  .nav__burger span, .nav__burger span::before, .nav__burger span::after {
    content: ""; display: block; width: 20px; height: 1.5px;
    background: currentColor; transition: transform var(--t-panneau) ease, opacity var(--t-ui) ease;
  }
  .nav__burger span::before { transform: translateY(-6px); }
  .nav__burger span::after  { transform: translateY(4.5px); }
  .nav__burger[aria-expanded="true"] span { background: transparent; }
  .nav__burger[aria-expanded="true"] span::before { transform: rotate(45deg); }
  .nav__burger[aria-expanded="true"] span::after  { transform: rotate(-45deg) translateY(0); }
}

/* --- Marque -------------------------------------------------------------- */
/* LE MÉDAILLON N'ÉCRIT PAS « UST », ET C'EST POUR CELA QUE LE NOM EST ÉCRIT À
   CÔTÉ. Son troisième glyphe est le pied de la lunette, sans barre horizontale :
   on y lit « USI », à 48 comme à 1254 px. Le nom en toutes lettres n'est donc
   pas un confort de lecture, c'est ce qui nomme la marque ; le sigle, lui, n'est
   répété nulle part. Voir sceau() dans inc/composants.php et MEMOIRE.md n° 6. */
/* LE SCEAU N'A NI CERNE NI HALO, ET C'EST UNE DÉCISION MESURÉE.
   Le champ marine du médaillon vaut #000714 en haut : 1,02:1 sur --nuit et
   1,25:1 sur --ciel au maître, et l'intérieur du disque une fois rendu reste
   entre 1,08 et 1,17:1 sur --nuit à toutes les tailles servies. Le haut du
   disque est donc bien indistinguable du fond, et la tentation est d'ajouter un
   cerne or ou un halo pour lui rendre sa silhouette. On ne le fait pas, pour
   trois raisons chiffrées.
   D'abord ce n'est pas la marque qui disparaît, c'est le vide autour d'elle :
   l'anneau, lui, tient. MESURE REFAITE À LA RECETTE, sur les pixels que le
   navigateur compose et non sur un hexadécimal supposé, médiane du pic radial
   relevé sur 1 440 angles, le tube blanc qui traverse l'anneau écarté :
                        48     64     80     96    132
     sur --nuit, d1    3,77   4,40   8,07   9,03   9,74
     sur --nuit, d2    8,69   9,34  11,10  11,35  11,20
   Le seuil d'un objet graphique est 3:1 : l'anneau le tient partout où le site
   le pose. La pose annonçait 8,10:1 à 64 px en densité 1 ; c'est le 99e centile
   de l'anneau, pas sa médiane, qui vaut 4,40:1. La conclusion ne bouge pas, le
   chiffre si. Ce qu'on perdrait à cerner, c'est la seule chose qui marche déjà.
   Ensuite un cerne demanderait un or, et il n'y a pas d'or dans la palette :
   les dix jetons sont mesurés sur 219 029 pixels des astrophotos et l'or
   « reste à l'intérieur de son médaillon » (MEMOIRE.md, décision 4). En poser
   un ici, ce serait écrire une valeur littérale et changer la DA pour le logo.
   Enfin un halo n'a qu'une couleur disponible, la lampe : un halo rose derrière
   le sceau du client, c'est l'accent qui commente la marque. Non.
   Le sceau est donc posé nu, et le seul geste porté sur lui est le détourage. */
.marque { display: inline-flex; align-items: center; gap: var(--e-2); text-decoration: none; }
.marque--pied { flex-direction: column; align-items: flex-start; gap: var(--e-3); }
.marque__nom {
  font-family: var(--f-display); font-weight: var(--g-display);
  font-size: 1.0625rem; line-height: 1.12; letter-spacing: -.01em; color: var(--texte);
}
.marque--pied .marque__nom { font-size: 1.375rem; }
@media (max-width: 34rem) { .marque__nom { font-size: .9375rem; } }

/* =========================================================================
   3. BOUTONS — trois niveaux, un seul accent
   ========================================================================= */
.btn {
  display: inline-flex; align-items: center; gap: var(--e-1);
  padding: .7rem 1.2rem; border-radius: var(--r-pill);
  font-size: var(--d-petit); font-weight: var(--g-corps-fort); text-decoration: none;
  border: 1px solid transparent; white-space: nowrap;
  transition: background-color var(--t-ui) ease, color var(--t-ui) ease,
              border-color var(--t-ui) ease, box-shadow var(--t-ui) ease;
}
/* LE BOUTON PRINCIPAL EST BLANC SUR UN FOND DÉDIÉ, ET C'EST UNE CONTRAINTE
   D'ACCESSIBILITÉ, PAS UN GOÛT. Son libellé fait 14 à 15 px en graisse 600 :
   ce n'est pas du grand texte au sens WCAG, le seuil est 4,5:1, et le blanc
   sur --lampe ne donne que 3,51:1. Le fond descend donc à --lampe-bouton
   (blanc 5,60:1), la lampe elle-même ne bouge nulle part ailleurs.
   LE FILET N'EST PAS UN ORNEMENT. --lampe-bouton sur une carte --ciel ne vaut
   que 2,88:1, sous le plancher 3:1 de WCAG 1.4.11 : posé sur un panneau
   d'appel ou une carte produit, l'aplat seul ne suffirait plus à dire qu'il y
   a là un bouton. Le filet de 1 px, déjà réservé par .btn, prend la lampe et
   porte la limite : 5,61:1 sur --nuit, 4,58:1 sur --ciel. La boîte ne bouge
   pas d'un pixel, la bordure était déjà là, en transparent. */
.btn--1 {
  background: var(--lampe-bouton); color: var(--encre-bouton);
  border-color: var(--lampe); box-shadow: var(--ombre-bouton);
}
.btn--2 { border-color: var(--filet-haut); color: var(--texte); }
.btn--3 { padding-inline: 0; color: var(--lampe); }
@media (hover: hover) {
  /* Le survol monte la lampe, mais d'un pas borné : --lampe-bouton-haute est
     un pas du fond vers l'accent, et il s'arrête là où le blanc tomberait
     sous 4,5:1. Mesuré : 4,86:1. L'ancien survol --lampe-haute #FFB3BF
     aurait donné 1,69:1 en blanc, soit un bouton illisible. */
  .btn--1:hover { background: var(--lampe-bouton-haute); box-shadow: var(--ombre-bouton-haute); }
  .btn--2:hover { border-color: var(--texte); }
  .btn--3:hover { color: var(--lampe-haute); }
}
.btn__fleche { width: 14px; height: 14px; flex: none; }

/* =========================================================================
   4. CARTES
   Parade du §5 : la hiérarchie ne repose jamais sur le seul contraste de
   surface (1,22:1). Chaque carte porte EN PLUS un filet 1 px et un
   espacement franc.
   ========================================================================= */
/* LE FILET AU REPOS EST À --filet-haut, ET CE N'EST PAS UN CHOIX D'HUMEUR.
   La carte entière est une cible de lien (.carte__lien::after { inset: 0 }) :
   sa limite est fonctionnelle, pas décorative, et relève du plancher 3:1 de
   WCAG 1.4.11. Mesure : les vignettes du catalogue, même recadrées, plafonnent
   au niveau du fond de page (liserés relevés entre 1,0 et 6,5 pour un --nuit à
   13). La carte ne peut donc pas se détacher par ses propres pixels, et le
   contour doit venir d'ici. --filet #353032 sur --nuit vaut 1,52:1, soit la
   moitié du plancher ; --filet-haut #534648 vaut 2,19:1.
   IL RESTE SOUS 3:1, et ce n'est pas caché : seul --gradue #867678 (4,58:1)
   l'atteindrait, mais un filet de ce poids sur quarante cartes change
   l'équilibre de la page. Cet arbitrage appartient au donneur d'ordre.
   Conséquence obligatoire, les deux règles bougent ensemble : --filet-haut
   était la couleur de survol ; le survol monte donc à --gradue, sans quoi
   l'état de survol cesse d'exister. Aucune valeur littérale n'est ajoutée. */
.carte {
  position: relative;
  background: var(--ciel); border: 1px solid var(--filet-haut);
  border-radius: var(--r); overflow: hidden;
  display: flex; flex-direction: column;
  transition: border-color var(--t-ui) ease, box-shadow var(--t-ui) ease;
}
/* G7 : le cadre garde le format de son capteur. La carte reçoit le ratio
   natif de la prise via --ratio ; aucun recadrage n'est décidé par le CSS. */
.carte__media {
  position: relative; overflow: hidden; background: var(--nuit);
  aspect-ratio: var(--ratio, 3 / 2);
}
.carte__media img, .carte__media picture { width: 100%; height: 100%; }
.carte__media img { object-fit: cover; }
.carte__corps { padding: var(--e-4); display: flex; flex-direction: column; gap: var(--e-2); flex: 1; }
.carte__titre {
  font-family: var(--f-corps); font-size: var(--d-h4);
  font-weight: var(--g-corps-fort); letter-spacing: -.01em; line-height: var(--i-titre);
}
.carte__lien::after { content: ""; position: absolute; inset: 0; }
@media (hover: hover) {
  .carte:has(a:hover) { border-color: var(--gradue); box-shadow: var(--ombre-carte); }
}
.carte:has(a:focus-visible) {
  outline: var(--focus-l) solid var(--focus-couleur); outline-offset: var(--focus-o);
}

/* -------------------------------------------------------------------------
   4 bis. LE PACKSHOT ET LA CARTE DE CATALOGUE

   LA BOÎTE CARRÉE EST L'EXCEPTION ASSUMÉE À LA RÈGLE DE .cadre. Partout
   ailleurs sur ce site, une image garde le format de son capteur et rien
   n'est recadré par le CSS. Le catalogue mélange neuf appareils
   photographiés par cinq marques : des carrés 2000 x 2000, des portraits
   1066 x 2000, des paysages. À format libre, les neuf cartes ne s'alignent
   plus et la page se lit comme une liste d'accidents.

   La réserve est donc commune, et l'image posée dedans en `contain` :
   centrée, jamais recadrée, jamais déformée. C'est le contraire de
   .carte__media, qui recadre en `cover` parce qu'une astrophotographie est
   un plein cadre et qu'un packshot détouré est un objet dans du vide.

   Le fond est un dégradé de --ciel vers --nuit, tokens seuls : la
   profondeur d'un fond de studio, sans une seule surface claire (loi n° 3).
   ------------------------------------------------------------------------- */
.pack {
  position: relative; display: block;
  aspect-ratio: var(--pack, 1 / 1); overflow: hidden;
  background: radial-gradient(115% 92% at 50% 8%, var(--ciel), var(--nuit) 76%);
  border-bottom: 1px solid var(--filet);
}
/* La réserve est posée en absolu, et c'est ce qui la rend HAUTE de façon
   définie. En flux, `height: 100 %` sur le <picture> ne résout rien : le
   parent n'a pas de hauteur définie pour lui, l'image reprend son format
   natif, et un portrait 1066 x 2000 déborde d'une boîte carrée au lieu d'y
   tenir. Posée en `inset`, elle hérite d'une largeur ET d'une hauteur, et
   `contain` fait enfin son travail. Le retrait remplace le padding. */
.pack picture { position: absolute; inset: var(--e-4); display: block; }
.pack img {
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center;
}
/* Un visuel de constructeur pris de jour est le seul du lot à porter du
   ciel clair. Il ne se pose pas nu sur la page : il reste dans sa boîte,
   qui l'encadre, et il est le seul à recevoir un filet complet. */
.pack[data-fond="clair"] { border: 1px solid var(--filet); border-radius: var(--r-s); }

.carte-produit .carte__corps { gap: var(--e-3); }
.carte-produit__marque { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2); }
.carte-produit__vitre { display: block; }
.carte-produit__lien {
  color: var(--texte); text-decoration: none;
  transition: color var(--t-ui) ease;
}
@media (hover: hover) { .carte-produit__lien:hover { color: var(--lampe); } }

/* LE PRIX EST LA DEUXIÈME CHOSE QU'ON LIT APRÈS LE NOM. Martian Mono porte
   la donnée, et un prix en est une ; il monte à l'échelle du titre parce
   qu'un catalogue qui cache son prix ne vend pas. */
.carte-produit__prix {
  font-size: var(--d-h3); font-weight: var(--g-mono-fort); color: var(--texte);
  letter-spacing: -.01em;
}
/* La nuance de prix : offre de lancement, déstockage, pack. Elle reste
   sourde, mais elle reste, entière. */
.carte-produit__nuance { color: var(--label); max-width: none; }
.carte-produit__phrase { color: var(--champ); font-size: var(--d-petit); }
/* LE RELEVÉ ET LE PIED SONT POUSSÉS EN BAS ENSEMBLE, et c'est le relevé qui
   porte la poussée : neuf cartes de longueurs inégales alignent ainsi leurs
   neuf filets et leurs neuf boutons, au lieu de laisser le relevé flotter au
   milieu d'une carte courte. */
.carte-produit__releve {
  margin-top: auto;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2);
  color: var(--label); max-width: none;
  padding-top: var(--e-3); border-top: 1px solid var(--filet);
}
.carte-produit__pied {
  padding-top: var(--e-1);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2) var(--e-4);
}
/* La carte de catalogue n'a pas de lien étendu : elle porte deux actions
   distinctes, et le halo de focus doit désigner celle qui a le focus, pas
   la carte entière. */
.carte-produit:has(a:focus-visible) { outline: none; }

/* =========================================================================
   5. DONNÉES
   ========================================================================= */
.donnee {
  display: flex; justify-content: space-between; gap: var(--e-3);
  padding-block: var(--e-1); border-bottom: 1px solid var(--filet);
}
.donnee:last-child { border-bottom: 0; }
.donnee dt { color: var(--label); font-size: var(--d-petit); }
.donnee dd {
  font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
  text-align: right; font-variant-numeric: tabular-nums;
}
/* Grille de caractéristiques : le label en mono, la valeur en corps. */
.specs {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--e-2) var(--e-3); margin: 0; padding-top: var(--e-3);
  border-top: 1px solid var(--filet);
}
.specs div { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.specs dt {
  font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
  color: var(--label); text-transform: uppercase; letter-spacing: .09em;
}
.specs dd { font-size: var(--d-petit); font-weight: var(--g-corps-moyen); }

/* --- Puce ---------------------------------------------------------------- */
.puce {
  /* align-self : sans lui, la puce s'étire sur toute la largeur dès qu'elle est
     enfant d'une pile flex (align-items: stretch l'emporte sur inline-flex). */
  display: inline-flex; align-self: flex-start; align-items: center; gap: .375rem;
  padding: .1875rem .625rem; border-radius: var(--r-pill);
  border: 1px solid var(--lampe-bord); color: var(--lampe);
  font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
  text-transform: uppercase; letter-spacing: .1em;
}
.puce--sourde { border-color: var(--filet-haut); color: var(--label); }

/* --- En-tête de section -------------------------------------------------- */
.tete { display: flex; flex-direction: column; gap: var(--e-3); max-width: 62ch; align-items: flex-start; }
.tete__brow { display: flex; align-items: center; gap: var(--e-3); }
.tete p { color: var(--champ); font-size: var(--d-lead); line-height: 1.55; max-width: 44ch; }

/* =========================================================================
   6. ACCORDÉON
   ========================================================================= */
.acc { border-top: 1px solid var(--filet); }
.acc__item { border-bottom: 1px solid var(--filet); }
.acc__q {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-4); padding: var(--e-4) 0; text-align: left;
  font-size: var(--d-h4); font-weight: var(--g-corps-moyen); color: var(--texte);
}
.acc__plus { position: relative; width: 16px; height: 16px; flex: none; }
.acc__plus::before, .acc__plus::after {
  content: ""; position: absolute; inset: 50% 0 auto 0; height: 1.5px;
  background: var(--lampe); transition: transform var(--t-ui) ease;
}
.acc__plus::after { transform: rotate(90deg); }
.acc__q[aria-expanded="true"] .acc__plus::after { transform: rotate(0deg); }
.acc__r { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--t-panneau) var(--courbe); }
.acc__q[aria-expanded="true"] + .acc__r { grid-template-rows: 1fr; }
.acc__r > div { overflow: hidden; }
.acc__r p { padding-bottom: var(--e-4); color: var(--champ); }

/* =========================================================================
   7. PANNEAU D'APPEL — le bord porte le palier sombre de la lampe, décor seul
   ========================================================================= */
.appel {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-4) var(--e-5);
  background: var(--ciel); border: 1px solid var(--lampe-nuit);
  border-radius: var(--r); padding: clamp(1.5rem, 1rem + 2vw, 2.25rem);
}
/* L'APPEL FINAL RESPIRE DES DEUX CÔTÉS. La section qui le porte écrivait son
   air en style en ligne, par le bas seulement : padding-bottom --section,
   padding-top 0. Le panneau se collait donc au bas de la bande qui le
   précède, et tout le vide partait sous lui.
   LA RÈGLE EST ICI, PAS DANS LES GABARITS, et c'est délibéré : l'accueil est
   réécrit en parallèle par un autre lot, et le même panneau ferme aussi
   /telescopes/. Une seule règle les traite tous les deux, et le jour où le
   style en ligne disparaîtra du gabarit elle fournira les deux côtés au lieu
   d'un seul. Le style en ligne, tant qu'il est là, gagne sur le bas et pose
   exactement la même valeur : rien à forcer, aucun !important. */
.section:has(> .contenu > .appel),
.enveloppe:has(> .contenu > .appel) { padding-block: var(--section); }

.appel p { color: var(--champ); max-width: 46ch; }
.appel strong { color: var(--texte); font-weight: var(--g-corps-moyen); }

/* =========================================================================
   8. PIED DE PAGE
   ========================================================================= */
/* L'AIR AU-DESSUS DU PIED ÉTAIT COMPTÉ DEUX FOIS, SUR LES 61 PAGES.
   Chaque page se termine par une section qui porte déjà --section de padding
   bas (150 px à 1440). Le pied ajoutait --section de marge haute par-dessus :
   300 px mesurés entre le dernier bouton et le filet du pied, sur l'accueil
   comme sur À propos. C'est « l'énorme espace entre le CTA et le footer ».
   La marge tombe donc à zéro : l'air appartient à la section qui précède, une
   fois, et le rythme entre le dernier bloc et le pied redevient le rythme
   commun de --section. */
.pied {
  border-radius: var(--r-pied);
  border-top: 1px solid var(--filet);
  margin-top: 0;
}
.pied__grille { display: grid; gap: var(--e-6); grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); }
.pied__titre {
  font-family: var(--f-data); font-size: var(--d-mono); font-weight: var(--g-mono);
  text-transform: uppercase; letter-spacing: var(--l-brow);
  color: var(--label); margin-bottom: var(--e-3);
}
.pied ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: .25rem; }
.pied a {
  font-size: var(--d-petit); text-decoration: none; color: var(--champ);
  transition: color var(--t-micro) ease;
  /* WCAG 2.2 (2.5.8) : 24 px de cible minimum. Un lien de pied à 21 px échoue. */
  display: inline-flex; align-items: center; min-height: 1.5rem; padding-block: 2px;
}
@media (hover: hover) { .pied a:hover { color: var(--texte); } }
.pied__bas {
  display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-5);
  justify-content: space-between; align-items: center;
  padding-top: var(--e-5); margin-top: var(--e-7); border-top: 1px solid var(--filet);
  color: var(--label);
}

/* --- Média encadré ------------------------------------------------------- */
.cadre { border-radius: var(--r-media); overflow: hidden; background: var(--nuit); border: 1px solid var(--filet); }
.cadre img { width: 100%; }

/* =========================================================================
   9. L'ENTRÉE EN TROIS TEMPS (demande client du 14/09/2026, carnet §2.1, piste 1)
   « Le logo est frappé par des étoiles qui s'accélèrent de plus en plus, et
   finit sur l'éclipse totale. » Une fois par session, 3 000 ms, dur. Toutes
   les durées, courbes et tailles sont les jetons du bloc ENTRÉE de tokens.css,
   la dérogation nommée au barème ; ici aucune valeur littérale, et les seuls
   nombres sont ceux de la partition elle-même (une échelle, une opacité, un
   pourcentage d'image-clé).

   TROIS ÉTATS DU DOCUMENT, décidés par le script de tête d'inc/entete.php
   avant que cette feuille ne soit lue :
     html sans classe   sans JavaScript. Le calque n'existe pas : la règle de
                        base est display: none, la page est là.
     html.entree-non    session déjà entrée, ou prefers-reduced-motion. Le
                        calque n'existe pas non plus, et PAS UN OCTET de sprite
                        n'est demandé : le calque du HTML est un div vide, la
                        scène n'est construite (inc/pied.php) que sous
                        .entree-joue. Aucun preload, nulle part.
     html.entree-joue   la scène est construite en JavaScript : le sceau (le
                        même fichier que sceau() sert), les étoiles, les éclats,
                        le disque. Tout est décoratif (alt vide, aria-hidden
                        sur le calque entier), rien n'est focalisable, rien ne
                        prend le pointeur, le calque ne porte aucun texte.

   TEMPS 1, 0 -> 1 600 : quatorze étoiles partent de --x / --y, à 140 % de la
   demi-diagonale de la fenêtre, et convergent sur le centre du médaillon en
   ease-in PUR : la vitesse monte jusqu'au contact. Échelle .5 -> 1, opacité
   montée sur les 15 premiers pour cent, mort au contact sur --entree-mort.
   Le sceau ne tourne pas : il encaisse trois fois (--c1, --c2, --c3, les
   instants des trois gros impacts), 1 -> .985 -> 1.008 -> 1 en --entree-choc,
   et rien d'autre.
   TEMPS 2, 1 450 -> 2 350 : gerbes ancrées par leur bord gauche au point de
   choc (elles poussent puis s'éteignent), brisures et étincelles qui partent
   selon la normale sur --dx / --dy et tournent de --r ; toutes DÉCÉLÈRENT,
   à l'inverse des étoiles : l'énergie est dépensée.
   TEMPS 3, 2 250 -> 3 000, RÉÉCRIT LE 15/09/2026 : le disque, une photographie
   de l'auteur (éclipse totale du 12/08/2026), à 155 % du médaillon, entre par
   la gauche, FREINE en --entree-courbe-eclat et S'ARRÊTE centré sur le sceau
   à --entree-t3-arret ; il y tient --entree-t3-tenue à pleine opacité. C'est
   pendant cette tenue, et seulement là, que le FOND du calque (le
   pseudo-élément ::before) et le SCEAU s'effacent : le disque, lui, ne fond
   pas, et la page se découvre AUTOUR d'un cercle noir immobile. Le disque
   seul s'efface ensuite en --entree-eclipse-fin, et le script retire le
   calque à --entree-duree. L'ancienne version (traversée continue, fondu du
   calque entier de 2 700 à 2 900) ne montrait l'éclipse totale que 12 ms, et
   à 13 % d'opacité : mesuré, refusé, remplacé. Le pourquoi et les nombres
   sont dans le bloc ENTRÉE de tokens.css.
   LE FOND EST UN PSEUDO-ÉLÉMENT ET NON LE FOND DU CALQUE parce qu'il doit
   s'effacer sans emporter le disque ; et un pseudo-élément et non un nœud du
   script parce qu'il est peint dès la première image, avant que le script
   n'ait construit quoi que ce soit : pas un éclair de page nue.

   LES DÉCALAGES ET LES POSITIONS SONT DES PROPRIÉTÉS PERSONNALISÉES POSÉES EN
   LIGNE PAR LE SCRIPT : --d (le départ), --x / --y (la position), --a
   (l'angle), --dx / --dy / --r (la course d'un éclat), --c1..3 (les chocs),
   comme --i pour la cadence du site. Les tailles rendues sont les attributs
   width et height des images (la moitié du natif, densité 2). L'invisibilité
   avant le départ n'est pas un fill-mode backwards, qui montrerait la
   première image-clé à sa position de départ : c'est l'opacité de base, 0,
   que la première animation prend en charge dès son départ.

   L'INTERRUPTION (clavier, pointeur, toucher, défilement) est un état du
   calque, .entree-fin : tout à zéro en --t-ui, puis le script le retire.
   ========================================================================= */
#rideau { display: none; }
.entree-joue #rideau {
  display: block; position: fixed; inset: 0; z-index: 900;
  overflow: hidden; pointer-events: none;
}
.entree-joue #rideau::before {
  content: ""; position: absolute; inset: 0; background: var(--nuit);
  animation: entree-meurt var(--entree-t3-tenue) var(--courbe) var(--entree-t3-arret) forwards;
}
.entree-joue #rideau.entree-fin { transition: opacity var(--t-ui) var(--courbe); opacity: 0; }
/* Tout est posé au centre du calque, qui est le centre du médaillon ; la
   propriété translate centre la boîte, la propriété transform est laissée
   entière aux images-clés. */
#rideau .entree__sceau, #rideau .entree__astre, #rideau .entree__eclat,
#rideau .entree__gerbe, #rideau .entree__eclipse {
  position: absolute; top: 50%; left: 50%; translate: -50% -50%; max-width: none;
}
/* Le sceau encaisse trois fois, puis s'efface sous le disque arrêté : les
   trois premières animations tiennent la transformation, la dernière tient
   l'opacité, elles ne se disputent rien. */
#rideau .entree__sceau {
  width: var(--entree-medaillon);
  animation:
    entree-encaisse var(--entree-choc) var(--courbe) var(--c1) none,
    entree-encaisse var(--entree-choc) var(--courbe) var(--c2) none,
    entree-encaisse var(--entree-choc) var(--courbe) var(--c3) none,
    entree-meurt var(--entree-t3-tenue) var(--courbe) var(--entree-t3-arret) forwards;
}
#rideau .entree__astre {
  opacity: 0;
  animation:
    entree-vole var(--entree-vol) var(--entree-courbe-choc) var(--d) forwards,
    entree-meurt var(--entree-mort) linear calc(var(--d) + var(--entree-vol) - var(--entree-mort)) forwards;
}
/* La gerbe est ancrée par son bord gauche : c'est lui qui est au point de
   choc, et c'est autour de lui qu'elle pousse et tourne. */
#rideau .entree__gerbe {
  opacity: 0; translate: 0 -50%; transform-origin: left center;
  animation:
    entree-gerbe var(--entree-gerbe-pousse) var(--entree-courbe-eclat) var(--d) forwards,
    entree-meurt var(--entree-gerbe-eteint) linear calc(var(--d) + var(--entree-gerbe-pousse)) forwards;
}
#rideau .entree__eclat {
  opacity: 0;
  animation: entree-eclate var(--entree-eclat-vol) var(--entree-courbe-eclat) var(--d) forwards;
}
/* Le disque attend hors champ, à gauche, jusqu'à --entree-t3-debut ; il court,
   freine, s'arrête sur le sceau, tient, puis s'efface seul. Les deux
   animations portent l'une la position, l'autre l'opacité. */
#rideau .entree__eclipse {
  width: calc(var(--entree-medaillon) * var(--entree-eclipse));
  transform: translateX(var(--entree-eclipse-de));
  animation:
    entree-passe var(--entree-t3-course) var(--entree-courbe-eclat) var(--entree-t3-debut) forwards,
    entree-meurt var(--entree-eclipse-fin) var(--courbe) calc(var(--entree-t3-arret) + var(--entree-t3-tenue)) forwards;
}
@keyframes entree-vole {
  from { opacity: 0; transform: translate(var(--x), var(--y)) rotate(var(--a)) scale(.5); }
  15%  { opacity: 1; }
  to   { opacity: 1; transform: translate(0, 0) rotate(var(--a)) scale(1); }
}
@keyframes entree-meurt { from { opacity: 1; } to { opacity: 0; } }
@keyframes entree-encaisse {
  from { transform: scale(1); }
  33%  { transform: scale(.985); }
  66%  { transform: scale(1.008); }
  to   { transform: scale(1); }
}
@keyframes entree-gerbe {
  from { opacity: 1; transform: translate(var(--x), var(--y)) rotate(var(--a)) scaleX(.2); }
  to   { opacity: 1; transform: translate(var(--x), var(--y)) rotate(var(--a)) scaleX(1); }
}
@keyframes entree-eclate {
  from { opacity: 1; transform: translate(var(--x), var(--y)) rotate(var(--a)); }
  70%  { opacity: 1; }
  to   { opacity: 0; transform: translate(calc(var(--x) + var(--dx)), calc(var(--y) + var(--dy))) rotate(calc(var(--a) + var(--r))); }
}
/* L'arrivée du disque est translateX(0) : le zéro n'est pas un réglage, c'est
   le centre du médaillon, où la boîte est déjà posée. */
@keyframes entree-passe {
  from { transform: translateX(var(--entree-eclipse-de)); }
  to   { transform: translateX(0); }
}

/* =========================================================================
   10. LA VUE — une photographie de l'auteur, et sa légende
   Aucune valeur littérale : hauteurs, gouttières et couleurs viennent des
   jetons. Aucune photo en fond, aucune opacité réduite : la loi n° 3 et les
   onze contrastes de la recette sont calculés contre --nuit et --ciel PLEINS.

   AUCUN FORMAT N'EST IMPOSÉ AU CADRE. Ce qui est borné, c'est la HAUTEUR
   RENDUE, par l'idiome déjà employé sur .planche : la largeur suit le format
   du capteur, l'image reste entière, on change l'échelle et jamais le
   cadrage. Sans cette borne, le portrait 1 800 × 4 000 servait 2 808 px de
   haut, mesurés au navigateur. Sur un paysage, la borne calculée dépasse
   toujours la colonne : elle n'a aucun effet, et c'est voulu.
   ========================================================================= */
.vue {
  --vue-h: clamp(30rem, 20rem + 30vw, 40rem);   /* 480 -> 640 px */
  margin: 0;
  max-width: calc(var(--vue-h) * var(--ratio, 3 / 2));
}
.vue .cadre { display: block; }
.vue picture { display: block; }
.vue img { display: block; width: 100%; height: auto; }

.vue__legende { margin-top: var(--e-3); display: flex; flex-direction: column; gap: var(--e-1); }
.vue__ligne {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  gap: var(--e-1) var(--e-3); max-width: none;
}
.vue__sujet  { color: var(--texte); }
.vue__date   { color: var(--champ); font-variant-numeric: tabular-nums; }
.vue__source { color: var(--label); max-width: none; }

/* Le bandeau. Un panorama de 8 640 px n'a pas à tenir dans une colonne de
   lecture : il prend toute la largeur du contenu, et sa borne de hauteur
   saute avec lui. */
.vue--bandeau { max-width: none; }

/* Deux vues de formats différents sur une rangée. Chacune reçoit une part de
   largeur proportionnelle à SON format (--fr, servi par vue()), ce qui leur
   donne exactement la même hauteur sans qu'aucune ne soit recadrée : mesuré
   1 009 × 454 et 203 × 451 à 1 440 px. Sous 48 rem elles s'empilent, chacune
   reprenant sa borne de hauteur propre. */
.vues-deux { display: grid; gap: var(--gouttiere); align-items: start; }
@media (min-width: 48rem) {
  .vues-deux { display: flex; align-items: flex-start; }
  .vues-deux > .vue { flex: var(--fr, 1) 1 0; min-width: 0; max-width: none; }
}

/* =========================================================================
   11. L'ABSENCE D'IMAGE, DESSINÉE
   Même grammaire que la graduation vide, et les mêmes jetons : le cadre est
   tracé jusqu'au bout en --filet-haut, rien ne le remplit, l'index reste
   parqué à zéro en --gradue. Jamais une section escamotée, jamais une
   astérisque, et surtout jamais la photo d'un autre appareil pour meubler.

   Ce n'est pas une graduation : une graduation ne mesure que du temps
   d'intégration (§7). C'est un emplacement d'image, vide, à la place et à la
   taille qu'aurait prises la galerie.
   ========================================================================= */
.vide {
  display: grid; align-content: space-between; gap: var(--e-6);
  min-height: clamp(11rem, 6rem + 14vw, 17rem);
  padding: var(--e-5);
  background: var(--nuit);
  border: 1px solid var(--filet-haut); border-radius: var(--r-media);
}
.vide__mot { color: var(--label); max-width: none; }
.vide__piste { position: relative; display: block; width: 100%; height: var(--grad-boite-appareil); }
.vide__piste::before {
  content: ""; position: absolute; left: 0; right: 0;
  top: calc((var(--grad-boite-appareil) - var(--grad-piste-appareil)) / 2);
  height: var(--grad-piste-appareil);
  background: var(--filet-haut); border-radius: 1px;
}
.vide__index {
  position: absolute; left: 0; top: 0;
  width: var(--grad-index); height: var(--grad-boite-appareil);
  background: var(--gradue);
}

/* =========================================================================
   12. LA RONDE DES CONSTRUCTEURS
   Les cinq marques dont UST est revendeur agréé, sur une orbite qui tourne
   (demande client du 14/09/2026 : « en cercle, en mouvement »). Posée en
   tête de la section des télescopes de l'accueil par ronde_constructeurs().

   LA GÉOMÉTRIE VIENT DE DEUX JETONS ET DE LA DONNÉE. --r-ronde est le rayon
   et la largeur de référence d'un logo ; chaque poste reçoit son angle (--a,
   72° entre voisins) et son facteur d'égalisation (--f, facteurStrict de
   donnees/logos-constructeurs.json) par son attribut style, écrits par le
   composant depuis la donnée, jamais ici. Le poste est placé par une
   transformation FIXE : au centre, tourné de --a, poussé d'un rayon, remis
   droit. L'orbite tourne ; le lien tourne à l'envers à la même vitesse, et
   le logo reste droit. Deux animations, une seule durée, --t-ronde, la
   dérogation nommée dans tokens.css.

   LA BOÎTE DU LIEN EST CENTRÉE SUR LA LIGNE D'ORBITE (le centre du logo est 12 à 13 px au-dessus, le libellé compte dans la boîte), et le lien porte le fond de la
   page : la ligne passe derrière lui, jamais entre ses lettres.

   LA PAUSE EST UN ÉTAT DE LA RONDE, data-pause, posé par le script du pied
   (WCAG 2.2.2) : elle fige l'orbite ET les cinq contre-rotations, sans quoi
   les logos se coucheraient. Sous prefers-reduced-motion il n'y a rien à
   figer : aucune animation, et le bouton n'est pas rendu. Sans JavaScript
   non plus : un bouton qui ne fait rien n'est pas rendu.

   LE CENTRE NE PREND PAS LES CLICS : il couvre la scène, et les logos qui
   entrent dans son carré resteraient sous lui.
   ========================================================================= */
.ronde { display: flex; flex-direction: column; align-items: center; gap: var(--e-3); }
/* La place des logos qui débordent l'orbite, en haut et en bas (le libellé
   pend sous le logo du bas). */
.ronde__scene {
  position: relative;
  width: calc(2 * var(--r-ronde)); height: calc(2 * var(--r-ronde));
  margin-block: var(--e-4) var(--e-7);
}
.ronde__orbite {
  position: absolute; inset: 0; margin: 0; padding: 0; list-style: none;
  border: 1px solid var(--filet); border-radius: 50%;
  animation: ronde-tourne var(--t-ronde) linear infinite;
}
/* LE POSTE N'A PAS DE TAILLE, ET C'EST MESURÉ. Un poste qui aurait la boîte
   de son lien tournerait avec l'orbite, et sa boîte tournée déborderait de
   son lien resté droit : à 390 de large la page mesurait 396 px de
   défilement horizontal, pour une boîte invisible. Le poste est donc un
   point sur l'orbite ; c'est le lien, posé sur ce point, qui a une boîte.
   Le lien se centre sur le point par la propriété translate, qui se compose
   AVANT la rotation de son animation : il tourne autour de son propre
   centre, puis se pose. Sa largeur est celle de son contenu (max-content) :
   dans un point de 0 px de large, l'ajustement automatique l'écraserait. */
/* contain: layout N'EST PAS UN CONFORT, C'EST MESURÉ. Le lien est droit à
   l'écran, mais le navigateur calcule le débordement du poste en boîte
   englobante à chaque étage : le lien contre-tourné dans le poste, puis le
   poste dans l'orbite tournée. Deux englobantes d'un rectangle tourné, et
   une boîte invisible dépasse la fenêtre : à 390 de large, orbite figée à
   quatre angles, la page mesurait 439, 423, 413 et 450 px de défilement.
   Avec le confinement de mise en page, le débordement du poste est traité
   comme de l'encre : 390 aux quatre angles, logos visibles et cliquables. */
.ronde__poste {
  position: absolute; top: 50%; left: 50%; width: 0; height: 0;
  contain: layout;
  transform: rotate(var(--a)) translate(var(--r-ronde)) rotate(calc(-1 * var(--a)));
}
.ronde__lien {
  position: absolute; top: 0; left: 0; width: max-content; translate: -50% -50%;
  display: flex; flex-direction: column; align-items: center; gap: var(--e-1);
  padding: var(--e-1); border-radius: var(--r-s);
  background: var(--nuit); color: var(--texte); text-decoration: none;
  animation: ronde-contre var(--t-ronde) linear infinite;
}
.ronde__logo {
  display: block; width: calc(var(--r-ronde) * var(--f)); height: auto;
  transition: transform var(--t-ui) var(--courbe);
}
/* DwarfLab n'a pas de couleur propre (fill=currentColor, posé en ligne) : il
   prend l'encre de la page, 16,21:1 sur --nuit. */
.ronde__logo--encre { color: var(--texte); }
.ronde__nom { font-size: var(--d-petit); line-height: var(--i-titre); color: var(--champ); white-space: nowrap; }
.ronde__centre {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; max-width: none; pointer-events: none;
  font-size: var(--d-mono); line-height: var(--i-mono); color: var(--champ);
}
.ronde__centre .nombre { color: var(--texte); font-weight: var(--g-mono-fort); }
.ronde__fait { display: block; }
.ronde__mention { max-width: none; text-align: center; font-size: var(--d-petit); color: var(--champ); }
.ronde__pause { display: none; }
.js .ronde__pause { display: inline-flex; }
.ronde[data-pause="oui"] .ronde__orbite,
.ronde[data-pause="oui"] .ronde__lien { animation-play-state: paused; }
@keyframes ronde-tourne { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes ronde-contre { from { transform: rotate(360deg); } to { transform: rotate(0deg); } }
/* Le survol est léger et ne touche que le logo survolé : une échelle de 4 %,
   en --t-ui. Ni couleur, ni opacité : un logo ne se teinte pas. */
@media (hover: hover) {
  .ronde__lien:hover .ronde__logo { transform: scale(1.04); }
}
@media (prefers-reduced-motion: reduce) {
  .ronde__orbite, .ronde__lien { animation: none; }
  .js .ronde__pause { display: none; }
}
