/* Portail de la suite : mêmes fondations que Lestrade (aplats, ombres plates décalées sans flou, Poppins). */

@font-face {
  font-family: Poppins;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/poppins-latin-400-normal.woff2') format('woff2');
}

@font-face {
  font-family: Poppins;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/poppins-latin-600-normal.woff2') format('woff2');
}

/* Titre de la suite : capitales romaines ornées, dans l'esprit victorien du décor (OFL, fonts/OFL-CinzelDecorative.txt). */
@font-face {
  font-family: 'Cinzel Decorative';
  font-weight: 700;
  font-display: swap;
  src: url('fonts/cinzel-decorative-latin-700-normal.woff2') format('woff2');
}

:root {
  color-scheme: light;
  --color-brand: #245dae;
  --color-brand-strong: #173b6f;
  --color-brand-hover: #1d4c8f;
  --color-brand-subtle: #dce7f7;
  --color-teal: #2ec4b6;
  --color-coral: #ff6b6b;
  --color-yellow: #ffc845;
  --color-bg: #f4f6fa;
  --color-surface: #ffffff;
  --color-text: #1b2a41;
  --color-text-muted: #5b6b80;
  --color-border: #e3e8ef;
  --color-ok-text: #0e7a70;
  --color-ok-subtle: #e0f7f5;
  --color-critical-text: #c0392b;
  --color-critical-subtle: #ffeded;
  --color-warning-text: #8a6100;
  --color-warning-subtle: #fff6dc;
  /* Nuit et or du décor (illustration de Londres, avatars de la suite). */
  --color-night: #0d1522;
  --color-night-soft: #16223a;
  --color-on-night-muted: #b8c3d4;
  --color-gold: #d4a64a;
  --color-gold-bright: #ffd98a;
  --color-gold-deep: #7a5418;
  --color-gold-line: rgba(212, 166, 74, 0.45);
  --color-copper: #c8773e;
  --color-verdigris: #5fb3a1;
  --color-parchment: #e6dcc6;
  --color-parchment-muted: #b9ad94;
  /* Plaques du portail (cartes, étiquette du serveur) : laiton sombre sur la nuit du décor. */
  --color-plate: linear-gradient(180deg, rgba(31, 42, 64, 0.96), rgba(16, 24, 39, 0.98));
  --shadow-plate: 0 4px 0 rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 217, 138, 0.14);
  --font-sans: Poppins, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 999px;
  --shadow-flat: 0 4px 0 rgba(27, 42, 65, 0.1);
}

* {
  box-sizing: border-box;
}

/* Accueil : barre de portraits à gauche, cinématique sur tout le reste du champ. Jamais de défilement horizontal ;
   le défilement vertical n'apparaît que si le contenu ne tient pas (écran bas, texte agrandi : WCAG 1.4.4). Dans le
   poste tactile, la barre disparaît (il a la sienne) et la colonne auto se referme. */
body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  overflow-x: clip;
  background: var(--color-night);
  color: var(--color-text);
  font-family: var(--font-sans);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* --- Barre de portraits ------------------------------------------------------------------------------------- */

/* Acajou sombre, baguette de laiton à droite, médaillons cerclés d'or (maquette B du poste). Cachée par défaut :
   plateforme.js la montre (.is-browser, posée dans <head> avant la première image ; cartes.js refait la détection en
   secours) hors du poste, qui a sa propre barre. Placée après
   la cinématique dans le document (ordre de tabulation), la grille la remet à gauche. */
.rail {
  position: sticky;
  top: 0;
  grid-row: 1;
  grid-column: 1;
  display: none;
  flex-direction: column;
  gap: 8px;
  width: 104px;
  height: 100vh;
  height: 100dvh;
  /* Six portraits et l'engrenage tiennent en 768 px de haut (écran du salon) ; plus bas, la barre défile. */
  padding: 10px 0;
  overflow-y: auto;
  border-right: 3px solid #7b5b23;
  background: linear-gradient(180deg, #21130c, #1a0f09);
  box-shadow: inset -1px 0 0 rgba(255, 217, 138, 0.22);
}

.is-browser .rail {
  display: flex;
}

/* Poste tactile : sa propre barre remplace celle-ci, dont il ne garde que l'engrenage des réglages du visiteur (seules
   commandes de la pause du décor et du son, WCAG 2.2.2). Il flotte en haut à droite de l'accueil, loin de la barre du
   poste à gauche et du titre en bas, cible de 88 × 96 px au doigt. */
.is-poste .rail {
  position: fixed;
  top: 12px;
  right: 12px;
  z-index: 3;
  display: flex;
  width: auto;
  height: auto;
  padding: 0;
  overflow: visible;
  border: 0;
  background: none;
  box-shadow: none;
}

.is-poste .rail__nav,
.is-poste .rail__check {
  display: none;
}

.is-poste .rail__foot {
  margin: 0;
  padding: 0;
}

/* Sur le décor, sans l'acajou de la barre : fond sombre translucide pour rester lisible. */
.is-poste .rail__item--reglages {
  background: rgba(26, 15, 9, 0.72);
}

/* Sans JavaScript, le message occupe toute la largeur. */
noscript {
  grid-column: 1 / -1;
}

.rail__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0 6px;
  list-style: none;
}

.rail__item {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: center;
  width: 88px;
  min-height: 96px;
  /* 10 px en haut : le liseré d'état déborde de 6 px au-dessus du médaillon et reste dans l'icône. */
  padding: 10px 2px 6px;
  border: 2px solid transparent;
  border-radius: var(--radius-lg);
  background: transparent;
  color: var(--color-parchment);
  font: inherit;
  cursor: pointer;
}

.rail__item:hover {
  background: rgba(255, 217, 138, 0.07);
}

.rail__item:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 2px;
}

/* Brain Attic : l'accueil, toujours actif (cadre de cuivre). */
.rail__item--home,
.rail__item--home:hover {
  border-color: var(--color-copper);
  background: #452817;
}

/* Icône dont la carte est ouverte (ou engrenage dont le panneau est ouvert) : fond chaud et fine bordure dorée pleine
   (::after, 1 px). Quand les mouvements sont permis, la bordure respire doucement et un reflet doré passe sur le fond
   (bloc no-preference) ; le décor en pause les arrête. Le focus clavier reste distinct : contour jaune de 3 px, à
   l'extérieur. Le reflet passe sous le médaillon et son liseré d'état. */
.rail__item[aria-expanded='true'] {
  background: #392615;
}

.rail__item[aria-expanded='true']::after {
  content: '';
  position: absolute;
  inset: 0;
  border: 1px solid var(--color-gold);
  border-radius: inherit;
  pointer-events: none;
}

.rail__medal {
  position: relative;
  display: block;
  width: 60px;
  height: 60px;
  border: 3px solid var(--color-gold-bright);
  border-radius: 50%;
  background: var(--color-night);
  box-shadow: 0 0 0 1px var(--color-gold-deep), 0 3px 0 rgba(0, 0, 0, 0.45);
}

.rail__avatar {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}

.rail__name {
  max-width: 100%;
  font-family: 'Cinzel Decorative', Georgia, serif;
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1.15;
  text-align: center;
  /* Sur une ligne tant qu'il tient ; sinon à la ligne, jamais en débord (Camden House mesure 88 px en Cinzel
     10 px pour 80 de place : deux lignes plutôt qu'un corps de 9 px, trop petit). */
  overflow-wrap: anywhere;
}

/* Liseré d'état autour du médaillon, rappel visuel de l'état lu en texte (rail__state) : vert sombre connecté (il
   scintille quand les mouvements sont permis, bloc no-preference), orange sombre dégradé, rouge perdu, gris argent non
   connecté. Tous sont continus, à la demande de Laurent (le pointillé du rouge a été retiré) : seule la teinte les
   distingue, et l'état reste lu en texte (rail__state). */
.rail__ring {
  position: absolute;
  inset: -9px;
  border: 3px solid #b4b8bd;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18), 0 0 4px rgba(200, 205, 212, 0.35);
  pointer-events: none;
}

.rail__ring--connecte {
  border-color: #1f6b3a;
  box-shadow: inset 0 0 0 1px rgba(120, 220, 150, 0.3), 0 0 7px rgba(46, 160, 90, 0.65);
}

.rail__ring--degrade {
  border-color: #b5620e;
  box-shadow: inset 0 0 0 1px rgba(255, 190, 120, 0.25), 0 0 7px rgba(214, 120, 30, 0.6);
}

.rail__ring--perdu {
  border-color: #b8322a;
  box-shadow: inset 0 0 0 1px rgba(255, 150, 140, 0.25), 0 0 7px rgba(220, 60, 50, 0.6);
}

/* Engrenage « Son et décor » : médaillon de laiton sans portrait, la roue au centre. */
.rail__medal--engrenage {
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 50% 40%, #3b2416, #1a0f09);
  color: var(--color-gold-bright);
}

.rail__engrenage {
  display: block;
}

/* Pied de la barre : l'engrenage, puis l'heure de la dernière vérification (rappel ; le texte lu est celui du pied de
   page). */
.rail__foot {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  margin: auto 0 0;
  padding: 0 6px;
}

.rail__check {
  margin: 0;
  color: #a99784;
  font-size: 0.75rem;
}

/* État en texte, pour les lecteurs d'écran : hors champ, mais lu avec le nom du module. */
.rail__state {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Son et décor : réglages du visiteur (reglages.js) --------------------------------------------------------- */

/* Carte de laiton à droite du pied de la barre ; navigateur seulement (le poste a ses propres réglages). */
.reglages {
  position: fixed;
  bottom: 14px;
  left: 116px;
  z-index: 20;
  width: min(300px, calc(100vw - 132px));
  padding: 12px 16px 14px;
  border: 2px solid var(--color-gold);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, #2a1810, #1d110a);
  box-shadow: 0 0 0 1px var(--color-gold-deep), 0 10px 28px rgba(0, 0, 0, 0.55);
  color: var(--color-parchment);
}

.reglages[hidden] {
  display: none;
}

/* Poste : le panneau s'ouvre sous l'engrenage, en haut à droite. */
.is-poste .reglages {
  top: 128px;
  right: 12px;
  bottom: auto;
  left: auto;
}

.reglages__tete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.reglages__titre {
  margin: 0;
  color: var(--color-gold-bright);
  font-family: 'Cinzel Decorative', Georgia, serif;
  font-size: 1rem;
}

.reglages__fermer {
  width: 44px;
  height: 44px;
  margin-right: -10px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--color-parchment);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.reglages__fermer::before {
  content: '\00d7';
}

.reglages__fermer:hover,
.reglages__interrupteur:not(:disabled):hover {
  background: rgba(255, 217, 138, 0.07);
}

.reglages__fermer:focus-visible,
.reglages__interrupteur:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 2px;
}

.reglages__liste,
.reglages__legende {
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
}

/* Interrupteur : libellé à gauche, glissière de laiton à droite (curseur à droite et vert quand il est coché). */
.reglages__interrupteur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 44px;
  padding: 6px 4px;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.875rem;
  text-align: left;
  cursor: pointer;
}

.reglages__interrupteur::after {
  content: '';
  flex: none;
  width: 42px;
  height: 24px;
  border: 2px solid var(--color-gold-deep);
  border-radius: var(--radius-full);
  background: radial-gradient(circle at 11px 50%, var(--color-parchment) 0 7px, transparent 8px), #4a3a2a;
}

.reglages__interrupteur[aria-checked='true']::after {
  border-color: var(--color-gold);
  background: radial-gradient(circle at 27px 50%, #fff6e2 0 7px, transparent 8px), #1f6b3a;
}

.reglages__interrupteur:disabled {
  cursor: default;
  opacity: 0.55;
}

.reglages__note {
  margin: 0 4px 4px;
  color: #c9b79c;
  font-size: 0.75rem;
}

.reglages__soustitre {
  margin: 12px 0 0;
  color: var(--color-gold-bright);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.reglages__legende li {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 26px;
  font-size: 0.8125rem;
}

/* Légende : le liseré en miniature, mêmes couleurs que .rail__ring. */
.reglages__lisere {
  flex: none;
  width: 16px;
  height: 16px;
  border: 3px solid #b4b8bd;
  border-radius: 50%;
}

.reglages__lisere--connecte {
  border-color: #1f6b3a;
  box-shadow: 0 0 5px rgba(46, 160, 90, 0.65);
}

.reglages__lisere--degrade {
  border-color: #b5620e;
}

.reglages__lisere--perdu {
  border-color: #b8322a;
}

.reglages__verif {
  margin: 10px 0 0;
  color: #a99784;
  font-size: 0.75rem;
}

/* --- Cinématique -------------------------------------------------------------------------------------------- */

/* Toute la hauteur de l'écran ; si le contenu ne tient pas, elle s'allonge et la page défile (rien n'est coupé).
   Toujours dans la seconde colonne : barre masquée (poste), la première, auto, se referme à zéro. */
.masthead {
  position: relative;
  grid-column: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-width: 0;
  min-height: 100vh;
  min-height: 100dvh;
  overflow: hidden;
  background: var(--color-night);
  color: #ffffff;
}

/* Le décor couvre le bandeau ; l'image et les circuits partagent le même cadrage (cover / slice, calés en haut :
   sur un écran large, c'est le bas, fondu dans la page, qui est rogné, pas le ciel). */
.masthead__scene {
  position: absolute;
  inset: 0;
  transform: scale(1.04);
  transform-origin: 60% 55%;
}

.masthead__scene > * {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.masthead__art {
  background: var(--color-night);
}

/* Calques : chaque élément tourne, flotte ou vire autour de son propre centre. */
.decor__engrenage,
.decor__aerostat,
.decor__vol,
.decor__aeronef,
.decor__cheminee circle {
  transform-box: fill-box;
  transform-origin: center;
}

/* Sans mouvement (mouvements réduits), seul le dirigeable reste, à sa place dans l'illustration : les trois autres
   appareils n'existent que dans le ballet, qui les sort de l'image (bloc prefers-reduced-motion plus bas). Pas
   d'opacité ici : vol-triplan ne la règle pas. */
.decor__vol--triplan,
.decor__vol--cargo,
.decor__vol--helico {
  display: none;
}

/* Pales : elles tournent autour du moyeu, origine de leur groupe (un groupe à trois pales n'est pas centré sur sa
   boîte). */
.decor__pales {
  transform-box: view-box;
  transform-origin: 0 0;
}

/* Hélices et rotors : pales de bronze sombre à peine liserées, comme celles de l'illustration. */
.decor__pales use {
  fill: url('#decor-pale');
  stroke: #6e4c2b;
  stroke-width: 0.5;
}

.decor__moyeu {
  fill: #3a2818;
  stroke: #b07a40;
  stroke-width: 0.6;
}

/* Flou de rotation : seulement quand les pales tournent (mouvements admis, décor pas en pause, voir plus bas). */
.decor__helice-flou {
  fill: url('#decor-helice-flou');
  opacity: 0;
}

.decor__arbre {
  stroke: #3a2a1c;
  stroke-width: 3;
  stroke-linecap: round;
}

.decor__tuyere {
  fill: url('#decor-tuyere');
  mix-blend-mode: screen;
}

.decor__cheminee circle {
  fill: url('#decor-bouffee');
  opacity: 0;
}

/* Vidéo : invisible tant qu'elle ne joue pas ; ensuite elle remplace l'image et ses calques (même cadrage). */
.masthead__video {
  object-fit: cover;
  object-position: center top;
  opacity: 0;
  pointer-events: none;
}

.masthead__scene.is-video .masthead__video {
  opacity: 1;
}

/* Sous la vidéo, les calques s'arrêtent (ils restent posés pour que le fondu d'entrée ne laisse pas de trou). */
.masthead__scene.is-video .masthead__art *,
.masthead__scene.is-video .masthead__smoke {
  animation-play-state: paused;
}

.masthead__scene.is-video .decor__eau {
  filter: none;
}

.masthead__scene.is-video .masthead__smoke,
.masthead__scene.is-video .masthead__glow {
  opacity: 0;
}

/* Le scintillement anime l'opacité de la lueur : il doit céder pour qu'elle s'éteigne. */
.masthead__scene.is-video .masthead__glow {
  animation: none;
}

/* Le zoom lent de la scène s'arrête là où il est quand la vidéo prend le relais : redimensionner la vidéo à chaque
   image la fait trembler (rééchantillonnage d'une échelle qui change), et elle porte déjà son propre mouvement. */
.masthead__scene.is-video {
  animation-play-state: paused;
}

/* Au-dessus de la vidéo, les impulsions des circuits continuent sans leur halo : un drop-shadow sur un tracé animé
   oblige à repeindre à chaque image, en plus du décodage vidéo. */
.masthead__scene.is-video .circuits__pulses path {
  filter: none;
}

/* Décor en pause (interrupteur « Décor animé » du panneau Son et décor) : toutes les animations CSS de la scène
   s'arrêtent là où elles sont ; la vidéo et l'ondulation SVG sont arrêtées par decor.js. */
.masthead__scene.is-paused,
.masthead__scene.is-paused * {
  animation-play-state: paused;
}

/* Plaques des usines (.masthead__usines) : hors de la scène (qui est aria-hidden), elles en reprennent le cadrage et
   le zoom lent (scene-drift, arrêté comme celui de la scène quand la vidéo joue ou que le décor est en pause). Seuls
   les liens captent l'appui : ailleurs, il tombe sur la scène (le ciel, qui ferme la carte). */
.masthead__usines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: scale(1.04);
  transform-origin: 60% 55%;
  pointer-events: none;
}

.masthead__scene.is-video ~ .masthead__usines,
.masthead__scene.is-paused ~ .masthead__usines,
.masthead__scene.is-paused ~ .masthead__usines * {
  animation-play-state: paused;
}

.usine {
  cursor: pointer;
  pointer-events: auto;
}

/* Sans module jumeau au catalogue (pas d'adresse), ou usine absente du jumeau : la plaque reste un décor. */
.usine:not([href]) {
  pointer-events: none;
}

/* Plaque rognée par le cadrage, ou sous le titre, la carte ou le pied de page (usines.js) : ni vue, ni focus, ni
   appui. L'enseigne peinte reste dans le décor, vierge. */
.usine.is-hors-champ,
.usine.is-masquee {
  visibility: hidden;
}

.usine:focus {
  outline: none;
}

.usine__cible {
  fill: transparent;
}

.usine:focus-visible .usine__cible {
  stroke: #ffffff;
  stroke-dasharray: 4 3;
  stroke-width: 2;
}

/* Nom peint sur l'enseigne, à l'échelle du panneau (taille dans l'attribut font-size) : lettres adoucies comme la
   peinture, ombre de gravure dessous (filtre fixe #usine-peint), sans halo. */
.usine__enseigne {
  fill: var(--usine-peinture);
  filter: url('#usine-peint');
  font-family: 'Cinzel Decorative', Georgia, 'Times New Roman', serif;
  font-weight: 700;
  letter-spacing: 0.04em;
  opacity: 0.82;
  text-anchor: middle;
}

/* Cartouche de laiton au-dessus de l'enseigne : le nom en grand, au survol et au focus seulement. Jamais cible
   d'appui : caché, il ne doit pas capter le clic destiné à la plaque voisine (celui de ROC déborde sur GRU) ; montré,
   il n'est que l'étiquette de la cible survolée ou du lien qui a le focus. */
.usine__plaque {
  opacity: 0;
  pointer-events: none;
}

.usine[href]:hover .usine__plaque,
.usine:focus-visible .usine__plaque {
  opacity: 1;
}

.usine__cadre {
  fill: url('#usine-laiton');
  stroke: #3b2610;
  stroke-width: 0.6;
}

.usine:focus-visible .usine__cadre {
  stroke: #ffffff;
  stroke-width: 1.6;
}

.usine__email {
  fill: var(--usine-email);
}

.usine__logo {
  fill: none;
  stroke: #f0c870;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 0.9;
}

.usine__rivets {
  fill: #ffe7a8;
}

/* Nom du cartouche : lettres à la couleur de lueur de la société (contraste AA sur l'émail), 13,5 unités (plus de
   12 px en 1366 × 768). */
.usine__nom {
  fill: var(--usine-lueur);
  font-family: 'Cinzel Decorative', Georgia, 'Times New Roman', serif;
  font-size: 13.5px;
  font-weight: 700;
}

/* Couleurs des sociétés, reprises des enseignes du jumeau (brandSigns.ts) : émail sombre, lueur des lettres ; et
   peinture du nom sur l'enseigne du décor, claire sur les panneaux sombres, sombre sur les panneaux clairs. */
.usine--gru {
  --usine-email: #163a5c;
  --usine-lueur: #c2e1ff;
  --usine-peinture: #2a1a0c;
}

.usine--omc {
  --usine-email: #234a35;
  --usine-lueur: #a6f0c4;
  --usine-peinture: #2a1a0c;
}

.usine--roc {
  --usine-email: #1d4677;
  --usine-lueur: #a8d4ff;
  --usine-peinture: #33200f;
}

.usine--mad {
  --usine-email: #6e3119;
  --usine-lueur: #ffb47e;
  --usine-peinture: #cfae6c;
}

.usine--dal {
  --usine-email: #5e1b32;
  --usine-lueur: #ffd99a;
  --usine-peinture: #d9b874;
}

/* Coin bas gauche : il couvre toute la largeur du bandeau. Seuls le titre et la carte captent l'appui ; ailleurs, il
   atteint les plaques ou tombe sur le ciel. Les plaques le suivent dans le document (ordre de tabulation) : il passe
   au-dessus d'elles, une plaque ne recouvre jamais la carte. */
.masthead__corner {
  z-index: 1;
  pointer-events: none;
}

.masthead__heading,
.modules > li,
.notice {
  pointer-events: auto;
}

/* Lueur des becs de gaz et des ateliers, sur les quais à droite. */
.masthead__glow {
  background: radial-gradient(ellipse 38% 22% at 74% 68%, rgba(255, 170, 70, 0.28), transparent 70%);
  mix-blend-mode: screen;
  opacity: 0.8;
}

/* Fumées : nappes floues deux fois plus larges que le bandeau, qui dérivent vers la gauche. */
.masthead__smoke {
  width: 200%;
  filter: blur(18px);
  pointer-events: none;
}

.masthead__smoke--far {
  background:
    radial-gradient(ellipse 9% 14% at 20% 18%, rgba(60, 52, 48, 0.55), transparent 70%),
    radial-gradient(ellipse 12% 10% at 38% 10%, rgba(70, 60, 54, 0.45), transparent 70%),
    radial-gradient(ellipse 9% 14% at 70% 18%, rgba(60, 52, 48, 0.55), transparent 70%),
    radial-gradient(ellipse 12% 10% at 88% 10%, rgba(70, 60, 54, 0.45), transparent 70%);
  opacity: 0.7;
}

.masthead__smoke--near {
  background:
    radial-gradient(ellipse 14% 9% at 30% 30%, rgba(90, 78, 70, 0.35), transparent 70%),
    radial-gradient(ellipse 14% 9% at 80% 30%, rgba(90, 78, 70, 0.35), transparent 70%);
  opacity: 0.6;
}

.masthead__circuits {
  overflow: visible;
}

.circuits__traces path {
  fill: none;
  stroke: var(--color-gold);
  stroke-opacity: 0.35;
  stroke-width: 1.5;
}

.circuits__pulses path {
  fill: none;
  visibility: hidden;
  stroke: var(--color-gold-bright);
  stroke-linecap: round;
  stroke-width: 2.5;
  filter: drop-shadow(0 0 4px var(--color-gold));
}

.circuits__nodes circle {
  fill: var(--color-night);
  stroke: var(--color-gold);
  stroke-width: 1.5;
}

/* Voile : assombrit la gauche pour le titre et fond le bas du bandeau dans la page. */
.masthead__veil {
  background:
    linear-gradient(180deg, rgba(13, 21, 34, 0) 50%, rgba(13, 21, 34, 0.75) 82%, var(--color-night) 100%),
    linear-gradient(90deg, rgba(13, 21, 34, 0.7) 0%, rgba(13, 21, 34, 0.25) 40%, rgba(13, 21, 34, 0) 65%);
}

/* Coin bas gauche : titre, puis la carte ouverte ; le titre remonte au-dessus de la carte. */
.masthead__corner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 96px 48px 0;
}

/* Le titre n'occupe que sa largeur : à sa droite, un appui tombe sur le ciel (et ferme la carte). Il passe devant la
   carte : pendant son glissement (titre.js), il part de la place que la carte vient de prendre ; à l'arrêt, ils ne se
   chevauchent pas. */
.masthead__heading {
  position: relative;
  z-index: 1;
  align-self: flex-start;
  max-width: 100%;
}

.masthead__kicker {
  margin: 0 0 8px;
  color: var(--color-gold-bright);
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Titre en laiton : dégradé doré découpé dans le texte ; l'ombre passe par filter, text-shadow ne suit pas un texte
   transparent. */
.masthead__title {
  margin: 0;
  background: linear-gradient(180deg, #fff1c4 0%, var(--color-gold-bright) 35%, var(--color-gold) 62%, #9c6a24 100%);
  background-clip: text;
  -webkit-background-clip: text;
  color: transparent;
  font-family: 'Cinzel Decorative', Georgia, 'Times New Roman', serif;
  font-size: clamp(2.5rem, 6.5vw, 5rem);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.05;
  /* Place du trait doré (16 px d'écart + 3 px), qui n'est plus dans le titre : boîte du titre inchangée (masquage
     des plaques). */
  padding-bottom: 19px;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.6));
}

/* Carte ouverte : le titre se fait plus petit pour lui laisser la place. */
.has-card .masthead__title {
  font-size: clamp(2rem, 4.6vw, 3.6rem);
}

/* Trait doré sous le titre : hors du titre, il ne suit pas son échelle pendant le glissement (titre.js le fait
   glisser à part, sans échelle) ; posé au bas du bloc, dans la place réservée par le titre. */
.masthead__trait {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 72px;
  height: 3px;
  border-radius: var(--radius-full);
  background: var(--color-gold);
}

/* Signature en haut à gauche du bandeau : « Une solution » et le logo Solutyo (blanc, fond sombre). */
.masthead__top {
  position: absolute;
  top: 0;
  left: 0;
  padding: 24px 32px 0;
}

.masthead__editor {
  display: inline-flex;
  gap: 10px;
  align-items: center;
  margin: 0;
  color: #ffffff;
  font-size: 0.875rem;
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.7));
}

.masthead__logo {
  display: block;
  width: auto;
  height: 28px;
}

/* Étiquette du serveur, dans le pied de page : petite plaque de laiton, comme les boutons du décor. */
.foot__site {
  margin: 0;
  padding: 2px 10px;
  border: 1px solid var(--color-gold);
  border-radius: var(--radius-full);
  background: rgba(13, 21, 34, 0.72);
  box-shadow: inset 0 1px 0 rgba(255, 217, 138, 0.18);
  color: var(--color-gold-bright);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.foot__fallback {
  color: var(--color-gold-bright);
  font-size: 0.75rem;
}

.foot__fallback:hover {
  color: #ffffff;
}

.foot__fallback:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 2px;
}

/* --- Carte -------------------------------------------------------------------------------------------------- */

.page {
  width: 100%;
  max-width: 460px;
}

/* Pied de page, en bas à droite de la cinématique : serveur, secours, heure de vérification. */
.foot {
  position: relative;
  display: flex;
  justify-content: flex-end;
  padding: 16px 24px 20px;
}

.foot [hidden] {
  display: none;
}

/* Bas à droite : serveur de secours, serveur qui répond, heure de vérification. */
.foot__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  align-items: center;
  justify-content: flex-end;
  margin-left: auto;
}

/* Heure de la dernière vérification : discrète. */
.page__check {
  margin: 0;
  color: var(--color-on-night-muted);
  font-size: 0.75rem;
  text-align: right;
  opacity: 0.8;
}

/* --- Modules ------------------------------------------------------------------------------------------------ */

.modules {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Carte : plaque de laiton sombre liserée d'or ; la tranche gauche prend le métal propre à chaque module. */
.module {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 20px 20px 24px;
  border: 1px solid var(--color-gold-line);
  border-left: 6px solid var(--module-accent, var(--color-gold));
  border-radius: var(--radius-lg);
  background: var(--color-plate);
  box-shadow: var(--shadow-plate);
  color: var(--color-parchment);
}

/* Une seule carte à la fois : les autres restent cachées (hidden, que display: flex écraserait sinon). Sans
   mouvement admis, elle apparaît en fondu, sans glissement (le glissement est dans le bloc no-preference). */
.module[hidden] {
  display: none;
}

.module:not([hidden]) {
  animation: card-fade 160ms ease-out both;
}

.module:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 3px;
}

/* Fermeture : pictogramme ✕ (texte de remplacement vide, le nom vient d'aria-label). */
.module__close {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: -6px -6px 0 0;
  padding: 0;
  border: 1px solid var(--color-gold-line);
  border-radius: 50%;
  background: rgba(13, 21, 34, 0.55);
  color: var(--color-gold-bright);
  font: inherit;
  cursor: pointer;
}

.module__close::before {
  content: '✕';
  content: '✕' / '';
  font-size: 1rem;
  line-height: 1;
}

.module__close:hover {
  border-color: var(--color-gold-bright);
}

.module__close:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 2px;
}

.module--mes {
  --module-accent: var(--color-gold);
}

.module--jumeau {
  --module-accent: var(--color-verdigris);
}

.module--bi {
  --module-accent: var(--color-copper);
}

.module__head {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  justify-content: space-between;
}

/* Portrait du module : médaillon rond cerclé d'or, comme les avatars de la suite. */
.module__avatar {
  flex-shrink: 0;
  width: 64px;
  height: 64px;
  border: 2px solid var(--color-gold);
  border-radius: 50%;
  background: var(--color-night);
  box-shadow: 0 3px 0 rgba(0, 0, 0, 0.45);
  object-fit: cover;
}

.module__titles {
  flex: 1;
  min-width: 0;
  align-self: center;
}

.module__titles .status {
  margin-top: 6px;
}

.module__role {
  margin: 0 0 2px;
  color: var(--color-gold);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Nom du module : capitales ornées et dorées, comme le titre de la suite. */
.module__name {
  margin: 0;
  color: var(--color-gold-bright);
  font-family: 'Cinzel Decorative', Georgia, serif;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.module__description {
  flex: 1;
  margin: 0;
  color: var(--color-parchment);
  font-size: 0.9375rem;
}

/* Bouton d'ouverture : laiton poli, encre de nuit ; au moins 44 px de haut, même en carte compacte. */
.module__open {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  min-height: 44px;
  padding: 10px 18px;
  border-radius: var(--radius-md);
  background: linear-gradient(180deg, #e6bf6c, #c4923a);
  box-shadow: 0 3px 0 var(--color-gold-deep), inset 0 1px 0 rgba(255, 240, 200, 0.5);
  color: var(--color-night);
  font-weight: 600;
  text-decoration: none;
}

.module__open:hover {
  background: linear-gradient(180deg, #f2d088, #d4a64a);
}

.module__open:focus-visible {
  outline: 3px solid var(--color-yellow);
  outline-offset: 2px;
}

.module__external {
  margin: 0;
  color: var(--color-parchment-muted);
  font-size: 0.8125rem;
}

/* Poste tactile : une appli publiée ailleurs (Hudson, Myacroft) s'y ouvre dans sa vue, pas dans un onglet. */
.is-poste .module__external {
  display: none;
}

/* Fiche de présentation (Watson) : où voir l'application, en laiton, à la place du bouton « Ouvrir ». */
.module__mention {
  margin: 0;
  color: var(--color-gold-bright);
  font-size: 0.875rem;
  font-weight: 600;
}

/* --- État --------------------------------------------------------------------------------------------------- */

.status {
  display: inline-flex;
  flex-shrink: 0;
  gap: 6px;
  align-items: center;
  padding: 4px 10px;
  border: 1px solid currentColor;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.06);
  color: var(--color-parchment-muted);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

.status::before {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  content: '';
}

/* États sur fond de nuit : teinte claire et voile de la même couleur (vert-de-gris, or, cuivre rouge). */
.status--ok {
  background: rgba(95, 179, 161, 0.14);
  color: #8fdcca;
}

.status--degraded {
  background: rgba(255, 200, 69, 0.12);
  color: var(--color-gold-bright);
}

.status--down {
  background: rgba(255, 107, 107, 0.14);
  color: #ffa39a;
}

.notice {
  margin: 0;
  padding: 16px 20px;
  border: 1px solid var(--color-gold-line);
  border-radius: var(--radius-md);
  background: rgba(212, 166, 74, 0.12);
  color: var(--color-gold-bright);
}

/* Écran assez haut : carte compacte, la cinématique garde le plus de champ possible. */
@media (min-width: 720px) and (min-height: 600px) {
  .module {
    gap: 8px;
    padding: 14px 16px 16px 20px;
  }

  .module__description {
    font-size: 0.875rem;
    line-height: 1.4;
  }

  .module__open {
    padding: 8px 16px;
  }
}

/* Écran étroit : la barre passe en bande horizontale sous la cinématique, la carte s'affiche au-dessus d'elle. La
   cinématique prend la hauteur que la bande lui laisse (quelle que soit celle de la bande), pour que tout tienne
   sans défiler quand c'est possible ; elle s'allonge si son contenu ne tient pas. */
@media (max-width: 719.98px) {
  body {
    display: flex;
    flex-direction: column;
  }

  .rail {
    position: static;
    flex-direction: row;
    width: auto;
    height: auto;
    padding: 6px 8px;
    overflow-x: auto;
    border-top: 3px solid #7b5b23;
    border-right: 0;
    box-shadow: inset 0 1px 0 rgba(255, 217, 138, 0.22);
  }

  .rail__list {
    flex-direction: row;
    gap: 4px;
    margin: 0 auto;
    padding: 0;
  }

  .rail__item {
    width: 80px;
    min-height: 90px;
    padding: 6px 2px;
  }

  .rail__medal {
    width: 50px;
    height: 50px;
  }

  .rail__check {
    display: none;
  }

  .masthead {
    flex: 1 0 auto;
    min-height: 0;
  }

  .masthead__corner {
    padding: 80px 16px 0;
  }

  .masthead__top {
    padding: 16px 16px 0;
  }

  .foot {
    padding: 12px 16px 16px;
  }

  /* L'illustration est rognée sur les côtés : les enseignes des usines sortent du champ. */
  .masthead__usines {
    visibility: hidden;
  }
}

@media (prefers-reduced-motion: no-preference) {
  /* Liseré connecté : il scintille (respiration de l'opacité) et un reflet en fait le tour. Le décor en pause l'arrête
     aussi. */
  .rail__ring--connecte {
    animation: lisere-scintille 2.6s ease-in-out infinite;
  }

  .rail__ring--connecte::after {
    content: '';
    position: absolute;
    inset: -3px;
    border-radius: 50%;
    background: conic-gradient(from 0deg, transparent 0 68%, rgba(190, 255, 205, 0.95) 82%, transparent 94%);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
    mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
    animation: lisere-tourne 3.2s linear infinite;
  }

  :root:has(.masthead__scene.is-paused) .rail__ring--connecte,
  :root:has(.masthead__scene.is-paused) .rail__ring--connecte::after {
    animation-play-state: paused;
  }

  /* Carte ouverte : la bordure dorée respire, un reflet doré discret traverse le fond (sous le médaillon). Le reflet
     est rogné au cadre de la carte ; la bordure (::after, en inset 0) reste dans ce cadre, le contour du focus dehors. */
  .rail__item[aria-expanded='true'] {
    overflow: hidden;
  }

  .rail__item[aria-expanded='true']::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(105deg, transparent 35%, rgba(255, 217, 138, 0.14) 50%, transparent 65%);
    pointer-events: none;
    animation: carte-reflet 5s ease-in-out infinite;
  }

  .rail__item[aria-expanded='true']::after {
    animation: carte-bordure 3.2s ease-in-out infinite;
  }

  :root:has(.masthead__scene.is-paused) .rail__item[aria-expanded='true']::before,
  :root:has(.masthead__scene.is-paused) .rail__item[aria-expanded='true']::after {
    animation-play-state: paused;
  }

  .module__open {
    transition: background-color 120ms ease-out;
  }

  /* Décor : zoom lent, fumées qui dérivent, lueurs qui vacillent, impulsions le long des circuits. */
  .masthead__scene {
    animation: scene-drift 48s ease-in-out infinite alternate;
  }

  .masthead__smoke--far {
    animation: smoke-drift 90s linear infinite;
  }

  .masthead__smoke--near {
    animation: smoke-drift 55s linear infinite;
  }

  .masthead__glow {
    animation: glow-flicker 7s steps(1, end) infinite;
  }

  /* Calques de l'illustration : quatre aéronefs se relaient dans le ciel, les aérostats flottent, l'engrenage
     tourne, les cheminées fument, la Tamise ondule. Les quatre vols partagent la durée et le décalage du ballet ;
     le décalage place le dirigeable dans le ciel dès l'ouverture. */
  .decor__vol {
    animation-duration: 90s;
    animation-timing-function: linear;
    animation-delay: -10s;
    animation-iteration-count: infinite;
  }

  .decor__vol--dirigeable {
    animation-name: vol-dirigeable;
  }

  .decor__vol--triplan,
  .decor__vol--cargo,
  .decor__vol--helico {
    display: inline;
  }

  .decor__vol--triplan {
    animation-name: vol-triplan;
  }

  .decor__vol--cargo {
    animation-name: vol-cargo;
  }

  .decor__vol--helico {
    animation-name: vol-helico;
  }

  .decor__aeronef {
    animation: float-bob 7s ease-in-out infinite;
  }

  .decor__vol--triplan .decor__aeronef {
    animation-duration: 3.5s;
  }

  .decor__pales {
    animation: gear-turn 0.7s linear infinite;
  }

  .decor__pales--lent {
    animation-duration: 0.8s;
  }

  .decor__pales--rotor {
    animation-duration: 0.45s;
  }

  .decor__pales--rotor-queue {
    animation-duration: 0.3s;
  }

  .decor__helice-flou {
    opacity: 1;
  }

  /* Grand disque du rotor de l'hélico : plus léger, sinon il pèse plus lourd que l'appareil. */
  .decor__helice-flou--rotor {
    opacity: 0.55;
  }

  /* Décor en pause : pales arrêtées, donc plus de flou. */
  .masthead__scene.is-paused .decor__helice-flou {
    opacity: 0;
  }

  .decor__tuyere {
    animation: exhaust-flicker 0.18s steps(2, end) infinite alternate;
  }

  .decor__tuyere--2 {
    animation-delay: -0.09s;
  }

  .decor__aerostat--1 {
    animation: float-bob 5.5s ease-in-out infinite;
  }

  .decor__aerostat--2 {
    animation: float-bob 6.5s ease-in-out -2.5s infinite;
  }

  .decor__engrenage {
    animation: gear-turn 24s linear infinite;
  }

  .decor__cheminee circle {
    animation: smoke-puff 6s ease-out infinite;
  }

  .decor__cheminee circle:nth-child(2) {
    animation-delay: -2s;
  }

  .decor__cheminee circle:nth-child(3) {
    animation-delay: -4s;
  }

  .decor__cheminee:nth-child(2n) circle {
    animation-duration: 7s;
  }

  .decor__cheminee:nth-child(3n) circle {
    animation-duration: 5s;
  }

  .decor__eau {
    filter: url('#decor-eau');
  }

  .masthead__video,
  .masthead__smoke,
  .masthead__glow {
    transition: opacity 1.2s ease-out;
  }

  .circuits__pulses path {
    visibility: visible;
    stroke-dasharray: 8 200;
    animation: circuit-pulse 6s cubic-bezier(0.45, 0, 0.55, 1) infinite;
  }

  .circuits__pulses path:nth-child(2) {
    animation-duration: 4.5s;
    animation-delay: -1.5s;
  }

  .circuits__pulses path:nth-child(3) {
    animation-duration: 7s;
    animation-delay: -3s;
  }

  .circuits__pulses path:nth-child(4) {
    animation-duration: 8s;
    animation-delay: -5s;
  }

  .circuits__pulses path:nth-child(5) {
    animation-duration: 5s;
    animation-delay: -2s;
  }

  .circuits__nodes circle {
    animation: node-blink 6s ease-in-out infinite;
  }

  .circuits__nodes circle:nth-child(2n) {
    animation-delay: -2s;
  }

  .circuits__nodes circle:nth-child(3n) {
    animation-delay: -4s;
  }

  /* Entrée : titre puis cartes, posées par portal.js, en cascade. */
  .masthead__heading {
    animation: rise-in 900ms cubic-bezier(0.22, 1, 0.36, 1) both;
  }

  /* Titre et surtitre glissent vers leur nouvelle place à l'ouverture et à la fermeture de la carte (titre.js : FLIP,
     transform seulement, origine en haut à gauche). La taille change en continu pendant le trajet ; à l'arrivée, un
     rebond discret (4 % du trajet) puis l'arrêt sans transformation, texte net. */
  .masthead__kicker.is-glisse,
  .masthead__title.is-glisse,
  .masthead__trait.is-glisse {
    transform-origin: 0 0;
    animation: titre-glisse 680ms both;
  }

  /* Carte : elle monte en glissant et en fondu, et redescend de même (durée de sortie = CLOSE_MS de cartes.js). */
  .module:not([hidden]) {
    animation: card-in 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
  }

  .module.is-closing {
    animation: card-out 220ms ease-in both;
  }

  /* Plaques : même zoom lent que la scène. */
  .masthead__usines {
    animation: scene-drift 48s ease-in-out infinite alternate;
  }
}

@keyframes card-fade {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes card-in {
  from {
    opacity: 0;
    transform: translateY(24px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes card-out {
  from {
    opacity: 1;
    transform: none;
  }

  to {
    opacity: 0;
    transform: translateY(24px);
  }
}

@keyframes scene-drift {
  from {
    transform: scale(1.04) translate(0, 0);
  }

  to {
    transform: scale(1.12) translate(-1.5%, -1%);
  }
}

/* Ballet des aéronefs (90 s, unités de l'illustration 1536 × 864). Chacun attend hors de l'image, la traverse dans
   son créneau, puis ressort ; les retours à la position de départ se font hors de l'image ou à opacité nulle, jamais
   sous les yeux du visiteur, quel que soit le recadrage.
   0-30 %   le dirigeable (image en x 796-1132) traverse de droite à gauche et sort par la gauche ;
   35-47 %  le triplan (x 663-873, haut dans le ciel pour passer au-dessus du titre) file de droite à gauche en prenant un peu d'altitude ;
   51-76 %  le dirigeable cargo (x 661-874), vu de trois quarts arrière, entre à droite et s'éloigne vers le fond à
            gauche, jusqu'à s'y fondre ;
   77-99 %  l'hélico (x 683-853) sort du fond, au-dessus des usines, grossit en approchant et sort par la droite. */
@keyframes vol-dirigeable {
  0% {
    opacity: 1;
    transform: translate(760px, 20px);
  }

  30% {
    opacity: 1;
    transform: translate(-1160px, -10px);
  }

  31%,
  99% {
    opacity: 0;
    transform: translate(-1160px, -10px);
  }

  99.5% {
    opacity: 0;
    transform: translate(760px, 20px);
  }

  100% {
    opacity: 1;
    transform: translate(760px, 20px);
  }
}

@keyframes vol-triplan {
  0%,
  35% {
    transform: translate(900px, 10px);
  }

  41% {
    transform: translate(0, -6px);
  }

  47%,
  100% {
    transform: translate(-900px, -30px);
  }
}

@keyframes vol-cargo {
  0%,
  51% {
    opacity: 1;
    transform: translate(950px, 0) scale(1);
  }

  70% {
    opacity: 0.9;
    transform: translate(-380px, 90px) scale(0.42);
  }

  76% {
    opacity: 0;
    transform: translate(-520px, 110px) scale(0.3);
  }

  77%,
  99% {
    opacity: 0;
    transform: translate(950px, 0) scale(1);
  }

  100% {
    opacity: 1;
    transform: translate(950px, 0) scale(1);
  }
}

@keyframes vol-helico {
  0%,
  77% {
    opacity: 0;
    transform: translate(-150px, -60px) scale(0.2);
  }

  80% {
    opacity: 1;
    transform: translate(-7px, -72px) scale(0.32);
  }

  99% {
    opacity: 1;
    transform: translate(900px, -150px) scale(1.1);
  }

  100% {
    opacity: 0;
    transform: translate(900px, -150px) scale(1.1);
  }
}

@keyframes exhaust-flicker {
  from {
    opacity: 0.65;
  }

  to {
    opacity: 1;
  }
}

@keyframes float-bob {
  0%,
  100% {
    transform: translateY(0) rotate(0deg);
  }

  50% {
    transform: translateY(-6px) rotate(-0.6deg);
  }
}

@keyframes gear-turn {
  to {
    transform: rotate(360deg);
  }
}

/* Une bouffée naît au sommet de la cheminée, grossit, monte en dérivant vers la gauche et se dissipe. */
@keyframes smoke-puff {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.6);
  }

  15% {
    opacity: 0.9;
  }

  100% {
    opacity: 0;
    transform: translate(-46px, -120px) scale(3.2);
  }
}

@keyframes smoke-drift {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(-50%);
  }
}

@keyframes glow-flicker {
  0%,
  100% {
    opacity: 0.8;
  }

  12% {
    opacity: 0.65;
  }

  14% {
    opacity: 0.85;
  }

  47% {
    opacity: 0.7;
  }

  49% {
    opacity: 0.9;
  }

  78% {
    opacity: 0.75;
  }
}

@keyframes circuit-pulse {
  from {
    stroke-dashoffset: 8;
  }

  to {
    stroke-dashoffset: -100;
  }
}

@keyframes node-blink {
  0%,
  100% {
    stroke: var(--color-gold);
  }

  50% {
    stroke: var(--color-gold-bright);
  }
}

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }

  to {
    opacity: 1;
    transform: none;
  }
}

/* Bec de gaz derrière les lettres des plaques : paliers brefs de baisse, comme glow-flicker. */

/* Liseré connecté de la barre : scintillement et reflet qui tourne (bloc no-preference). */
@keyframes lisere-scintille {
  0%,
  100% {
    opacity: 1;
  }

  40% {
    opacity: 0.7;
  }

  55% {
    opacity: 1;
  }

  75% {
    opacity: 0.85;
  }
}

@keyframes lisere-tourne {
  to {
    transform: rotate(360deg);
  }
}

/* Carte ouverte dans la barre : reflet qui traverse le fond, puis repos ; bordure dorée qui respire (bloc
   no-preference). */
@keyframes carte-reflet {
  0% {
    transform: translateX(-110%);
  }

  55%,
  100% {
    transform: translateX(110%);
  }
}

@keyframes carte-bordure {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.55;
  }
}

/* Glissement du titre (bloc no-preference, titre.js) : de l'ancienne place (--glisse-dx, --glisse-dy, --glisse-s)
   vers la nouvelle, courbe douce, léger dépassement à 78 % (4 % du trajet et de l'écart de taille), puis pose. */
@keyframes titre-glisse {
  0% {
    transform: translate(var(--glisse-dx), var(--glisse-dy)) scale(var(--glisse-s));
    animation-timing-function: cubic-bezier(0.33, 0, 0.2, 1);
  }

  78% {
    transform: translate(calc(var(--glisse-dx) * -0.04), calc(var(--glisse-dy) * -0.04))
      scale(calc(1 + (1 - var(--glisse-s)) * 0.04));
    animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
  }

  100% {
    transform: none;
  }
}
