/* ===================================================
   DASHBOARD — Thème sombre façon moi
   =================================================== */
:root {
  --bg: #0f1115;
  --bg-soft: #151821;
  --bg-softer: #1c2030;
  --bg-card: #1a1e2b;
  --bg-card-hover: #222738;
  --border: #262b3b;
  /*  LE CONTOUR DES CHAMPS DE SAISIE — posé le 09/09/2026.
   *
   *  `--border` sert à SÉPARER (cartes, listes, filets) : à 1,34 contre le
   *  fond il est volontairement discret, et c'est très bien pour ça.
   *
   *  Mais il servait AUSSI à dessiner le contour des champs de formulaire,
   *  et là ce n'est plus une séparation, c'est la seule chose qui dit « on
   *  écrit ici ». Le champ (#1c2030) sur la page (#0f1115) fait 1,17 : de
   *  loin, ou avec une vue basse, un formulaire ressemblait à du texte. La
   *  règle WCAG 1.4.11 demande 3:1 pour un élément d'interface, et c'est une
   *  des rares règles d'accessibilité qui se voit à l'œil nu.
   *
   *  #666f8c donne 3,25 contre le champ et 3,79 contre la page. On ne touche
   *  pas à `--border` : les filets doivent rester des filets. */
  --border-champ: #666f8c;
  --text: #e6e8ef;
  /* ------------------------------------------------------------------ *
   *  --text-muted : REMIS AU PLANCHER QUE LE PROJET S'EST DONNÉ
   * ------------------------------------------------------------------ *
   *  Le projet exige 5.0:1 sur ce jeton — plus que les 4.5:1 de la norme, et
   *  c'est une bonne règle : il porte la moitié du texte explicatif du site.
   *
   *  Il ne la tenait plus. Mesuré le 29/08 sur les pages rendues : 6.01:1 sur
   *  le fond nu, mais 4.45:1 sur les cartes teintées de la PRODUCTION — le
   *  fond y est ambré, donc plus clair, et l'écart se referme. Le plancher
   *  était respecté dans le monde où l'on regarde, et pas dans l'autre.
   *
   *  #9ba1b3 tient 5.21:1 au pire endroit mesuré, et 7.33:1 sur le fond nu. */
  --text-muted: #9ba1b3;
  /* ------------------------------------------------------------------ *
   *  --text-dim : ÉCLAIRCI LE 29/08, PARCE QU'IL PORTAIT DU VRAI TEXTE
   * ------------------------------------------------------------------ *
   *  Il valait #5a6078 : 2.67:1 sur une carte, 3.04:1 sur le fond nu. La
   *  norme demande 4.5:1. Un jeton « atténué » n'a pas vocation à être
   *  illisible — il a vocation à passer au second plan, ce qui n'est pas la
   *  même chose. Or il porte des phrases qu'on doit pouvoir lire : « ce sont
   *  des sessions, pas des personnes », les mentions de bas de page, les
   *  compteurs « 0/4 » des tableaux de droits.
   *
   *  #8087a6 donne 4.69:1 sur une carte et 5.34:1 sur le fond. Il reste
   *  nettement en retrait de --text (#e6e8ef) : la hiérarchie ne bouge pas,
   *  seule la lisibilité change. */
  /*  RELEVÉ À #9aa1bd le 29/08 : #8087a6 tenait 4.69:1 sur une carte du
   *  public, mais 3.79:1 sur les cartes teintées de la PRODUCTION. Comme
   *  --text-muted, il tenait le plancher dans un seul des deux mondes.
   *  #9aa1bd donne 5.25:1 au pire endroit mesuré. */
  --text-dim: #9aa1bd;
  /* ------------------------------------------------------------------ *
   *  --sur-couleur : L'ENCRE POSÉE SUR UNE PASTILLE PLEINE
   * ------------------------------------------------------------------ *
   *  Vert, rouge, orange et or sont des couleurs CLAIRES. Du blanc dessus
   *  donne 1.44:1 à 2.99:1 — mesuré sur les pastilles d'état du site, où
   *  « OK » en blanc sur le vert plafonne à 1.77:1. Une encre sombre donne
   *  6.15:1 à 12.74:1 sur les mêmes fonds.
   *
   *  Elle ne suit pas le monde, et c'est voulu : ces quatre couleurs sont
   *  claires dans les deux mondes, donc l'encre y est sombre dans les deux.
   *  Un jeton qui changerait ici ne ferait que déplacer le problème. */
  --sur-couleur: #12141d;
  /* ------------------------------------------------------------------ *
   *  LA COULEUR DIT DANS QUEL MONDE ON EST
   * ------------------------------------------------------------------ *
   *  Public / normal = VIOLET.  Production = JAUNE.
   *
   *  Ce n'est pas de la décoration : c'est le signal le plus rapide pour
   *  savoir où l'on est avant de cliquer. Se tromper de monde n'est pas une
   *  petite erreur, et personne ne lit une étiquette avant d'agir — alors
   *  qu'une teinte se remarque sans effort.
   *
   *  Le jeu de jetons ci-dessous est celui du PUBLIC. La bascule vers le
   *  jaune se fait plus bas, sur [data-monde="prod"], posé sur <html> par
   *  partials/head.ejs. Rien d'autre à changer : tout le site lit ces
   *  variables.
   *
   *  --accent-rgb existe pour les fonds translucides : rgba() ne sait pas
   *  manger un #hex. Sans lui, chaque « rgba(var(--accent-rgb), .1) » resterait
   *  violet en prod — et il y en a une cinquantaine.
   *
   *  --accent-texte est la couleur du texte posé SUR l'accent. Elle doit
   *  changer avec lui : du blanc sur violet passe (4.35:1), du blanc sur
   *  jaune non (1.53:1, illisible). Le seuil lisible est 4.5:1.
   */
  --accent: #7c5cff;
  --accent-rgb: 124, 92, 255;
  /* --accent-plein : la teinte des surfaces REMPLIES (boutons, onglet actif).
     Elle existe parce que #7c5cff échoue des deux côtés : du blanc dessus
     donne 4.35:1, du noir 4.23:1, et le seuil lisible est 4.5:1. La couleur
     de marque ne bouge pas — elle reste celle des liens, des bordures et des
     halos, où elle atteint un contraste correct sur le fond sombre. Seul le
     fond des boutons descend d'un cran, assez peu pour que l'œil n'y voie
     que du violet, assez pour que le texte blanc passe (4.85:1). */
  --accent-plein: #7454f5;
  --accent-texte: #ffffff;
  --accent-hover: #8f72ff;
  --accent-dim: #4d3ba8;
  /* ------------------------------------------------------------------ *
   *  --accent-lisible : L'ACCENT QUAND IL SERT DE TEXTE
   * ------------------------------------------------------------------ *
   *  MESURÉ LE 29/08, sur les pages rendues : le violet de marque #7c5cff,
   *  employé comme couleur de TEXTE sur le fond des cartes, donne 3.82:1.
   *  La norme demande 4.5:1 pour du texte courant. Cinquante-quatre endroits
   *  du site écrivaient donc en violet sous la norme — liens de tableaux,
   *  petites étiquettes, valeurs mises en avant.
   *
   *  ET LE DÉFAUT N'EXISTAIT QU'AU PUBLIC. En production, le même jeton vaut
   *  #facc15 : 10.84:1, très au-dessus. Deux mondes, un seul code, et un seul
   *  des deux illisible — c'est exactement le genre de chose qu'on ne voit
   *  jamais en regardant, parce qu'on regarde toujours le monde où l'on
   *  travaille.
   *
   *  #9b85ff est le même violet éclairci jusqu'à 5.69:1. Il reste la couleur
   *  de la marque : on le reconnaît, il se lit. `--accent` continue de servir
   *  aux FONDS, aux bordures et aux boutons, où il n'a aucun problème. */
  --accent-lisible: #9b85ff;
  --success: #3ddc97;
  --warning: #fb923c;
  --danger: #ff5b7a;
  /* ------------------------------------------------------------------ *
   *  --note : L'OR DES ÉTOILES, ET RIEN D'AUTRE
   * ------------------------------------------------------------------ *
   *  Relevé le 29/08 : les notes et les moyennes étaient peintes en
   *  `#ffd700` écrit à la main, trente-six fois, dans huit vues. Faute d'un
   *  jeton pour ça, certaines pages retombaient sur `--warning` — et une note
   *  de 5/5 s'affichait alors dans la couleur d'un avertissement.
   *
   *  L'or n'est PAS l'orange d'alerte : il dit « bien noté », pas « attention ».
   *  Deux sens différents méritent deux jetons, sinon l'un finit par emprunter
   *  la couleur de l'autre — et c'est arrivé.
   *
   *  Il ne suit pas le monde : une étoile est dorée partout, comme sur
   *  n'importe quel site. C'est justement ce qui la rend lisible d'un coup
   *  d'œil au milieu du violet comme au milieu du jaune. */
  --note: #ffd166;
  /* ------------------------------------------------------------------ *
   *  LE PREMIUM SUIT LE MONDE — violet/bleu en public, jaune en prod
   * ------------------------------------------------------------------ *
   *  DEMANDÉ LE 28/08 : « harmonise les thématiques premium en violet/bleu,
   *  et en jaune pour la prod ».
   *
   *  L'or #ffe9a8 servait à TROIS choses à la fois : marquer le premium,
   *  border les encadrés « 💡 comment ça marche », et signaler une opération
   *  en cours. Trois sens, une seule valeur, écrite en clair à une vingtaine
   *  d'endroits — donc impossible de recolorer le premium sans repeindre les
   *  encadrés d'aide au passage. On sépare les sens avant de changer la
   *  couleur : --premium ci-dessous, --aide plus bas. C'est la seule raison
   *  pour laquelle ce changement tient en trois lignes au lieu de vingt.
   *
   *  --premium-2 existe pour les dégradés : « violet/bleu » demande DEUX
   *  teintes. En public le dégradé va du violet au bleu ; en prod, de l'or au
   *  jaune. Le même dégradé, la même intention, deux mondes reconnaissables.
   *
   *  CONTRASTES MESURÉS sur --bg (#0f1115) : violet-bleu 9.5:1, or 15.7:1.
   *  Et sous du texte sombre (#12141d), 9.2:1 et 15.3:1 — les deux servent
   *  aussi bien d'encre que de fond, ce que l'or faisait déjà.
   */
  --premium: #b3b0ff;
  --premium-rgb: 179, 176, 255;
  --premium-2: #7ab8ff;
  --premium-2-rgb: 122, 184, 255;
  /* --aide : les encadrés « comment ça marche » et les avis passagers. Elle
     NE suit PAS le monde : c'est un repère de lecture, pas une marque. */
  --aide: #ffe9a8;
  /* --prod : la couleur qui VEUT DIRE « production ». Ce n'est pas l'accent :
     elle ne change pas d'un monde à l'autre, justement pour que la tuile et
     le bouton qui mènent en prod se reconnaissent depuis le site public. */
  --prod: #facc15;
  --prod-rgb: 250, 204, 21;
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 10px 30px -10px rgba(0,0,0,0.5);
  --shadow-lg: 0 20px 60px -10px rgba(0,0,0,0.7);
}

/* ------------------------------------------------------------------------ *
 *  LE MONDE PROD : le même site, en jaune.
 * ------------------------------------------------------------------------ *
 *  Seuls les jetons d'accent changent. Les fonds, les bordures et les
 *  couleurs de signal restent identiques : c'est le MÊME site, pas un autre
 *  thème — on doit reconnaître ses repères, juste savoir où l'on est.
 *
 *  --accent-texte repasse en sombre : c'est obligatoire, pas esthétique.
 *  Du blanc sur ce jaune tombe à 1.53:1 et devient illisible.
 */
html[data-monde="prod"] {
  --accent: #facc15;
  --accent-rgb: 250, 204, 21;
  /* En jaune, la couleur de marque tient telle quelle sous du texte sombre
     (11.99:1) : pas besoin d'assombrir le fond des boutons. */
  --accent-plein: #facc15;
  --accent-texte: #12141d;
  --accent-hover: #fde047;
  --accent-dim: #a16207;
  /*  En production, l'accent atteint déjà 10.84:1 comme texte : le jeton
   *  lisible n'a rien à éclaircir, il EST l'accent. */
  --accent-lisible: #facc15;
  /* Le premium en prod : le jaune demandé. C'est l'or d'origine, donc rien ne
     bouge visuellement de ce côté — c'est le site public qui change. */
  --premium: #ffe9a8;
  --premium-rgb: 255, 233, 168;
  --premium-2: #facc15;
  --premium-2-rgb: 250, 204, 21;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  /* ------------------------------------------------------------------ *
   *  LA PAGE NE DÉFILE JAMAIS SUR LES CÔTÉS
   * ------------------------------------------------------------------ *
   *  Symptôme observé : une bande NOIRE à droite, sous la barre du haut.
   *  L'explication : la barre est collante, elle ne peint que la largeur de
   *  la fenêtre. Dès qu'un seul élément dépasse et rend la page défilable
   *  horizontalement, tout ce qui est au-delà se retrouve sans fond.
   *
   *  `clip` et non `hidden` : `hidden` crée un conteneur de défilement, ce
   *  qui casse `position: sticky` — la barre du haut cesserait de coller.
   *  `clip` empêche le défilement sans rien créer.
   *
   *  Ce n'est pas un tapis sous lequel on pousse la poussière : ce qui a
   *  vraiment besoin d'être large a déjà son propre défilement
   *  (`.table-defilante` pour les tableaux, `pre` ci-dessous pour le code).
   */
  overflow-x: clip;
}
/* Le code ne pousse plus la page : il défile dans son cadre. */
pre { overflow-x: auto; max-width: 100%; }

html, body {
  background: var(--bg);
  color: var(--text);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-lisible); text-decoration: none; }
a:hover { color: var(--accent-hover); }
code { background: var(--bg-softer); padding: 2px 6px; border-radius: 4px; font-size: 0.9em; color: var(--premium); }

/* ======= NAVBAR ======= */
.navbar {
  position: sticky; top: 0; z-index: 100;
  background: rgba(15, 17, 21, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.navbar-inner {
  /* 1600, et non 1280 comme le contenu des pages : mesurée dans un vrai
     navigateur, la barre complète réclame 1560 px. Les deux contraintes n'ont
     aucune raison d'être identiques — une barre porte bien plus d'éléments
     qu'un paragraphe n'a besoin de largeur. Alignées à 1280, les éléments se
     chevauchaient : le lien « Staff » passait par-dessus le bouton PROD. */
  max-width: 1600px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 24px; gap: clamp(10px, 1.6vw, 24px);
}
.brand { display: flex; align-items: center; gap: 10px; color: var(--text); font-weight: 700; font-size: 1.2rem; }
/* La marque est passée d'un émoji à une image : il lui faut une taille fixe,
   sinon la barre saute pendant le chargement. `display:block` supprime le
   petit espace sous les images en ligne, qui décalait le nom vers le haut. */
.brand-logo { display: block; width: 42px; height: 42px; flex: 0 0 auto; }

/* --- Le bouton des deux mondes ------------------------------------------
 * Il doit se remarquer sans crier : on est soit sur le site normal, soit sur
 * la production, et se tromper de monde n'est pas une petite erreur. D'où la
 * pastille de couleur, lisible d'un coup d'œil même sans lire le texte. */
.monde-bascule {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-softer);
  color: var(--text-muted); font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.4px; text-transform: uppercase;
  transition: border-color 0.15s, color 0.15s;
}
.monde-bascule:hover { border-color: var(--accent); color: var(--text); }
.monde-point {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--text-dim); flex: 0 0 auto;
}
.monde-bascule.en-prod { border-color: var(--danger); color: var(--danger); }
/* « Prod ? » : le droit d'entrer existe, la configuration non. En pointillé
   pour qu'on voie tout de suite que ce n'est pas un bouton comme les autres. */
.monde-bascule.a-configurer { border-style: dashed; color: var(--text-dim); }
.monde-bascule.a-configurer .monde-point { background: var(--warning); }
.monde-bascule.en-prod .monde-point { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
.monde-lecture {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(251, 146, 60, 0.14); color: var(--warning);
  font-size: 0.75rem; font-weight: 600;
}
/* ------------------------------------------------------------------------ *
 *  LA BARRE DU HAUT NE DÉBORDE JAMAIS
 * ------------------------------------------------------------------------ *
 *  Mesuré dans un vrai navigateur : à 1400 px la barre débordait de 141 px,
 *  à 1100 px de 283 px. Comme elle est collante, elle ne peint que la largeur
 *  de la fenêtre — en défilant vers la droite on tombait sur du NOIR sous
 *  « Déconnexion ». C'était ça, la bande noire.
 *
 *  Entre 1200 et 1500 px on retire seulement du confort : le mot du bouton,
 *  puis le pseudo (l'avatar reste, on sait qui est connecté). En dessous de
 *  1200, le menu « ☰ » prend le relais et TOUT redevient atteignable —
 *  cacher « Déconnexion » n'aurait jamais été acceptable.
 */
/* Les seuils viennent de mesures, pas d'estimations. Besoin de la barre :
   1560 px complète · 1426 sans le pseudo ni le badge · 1353 sans le mot du
   bouton. Chaque seuil est posé un cran AVANT que la place manque. */
@media (max-width: 1620px) {
  .navbar .username { display: none; }
  .navbar .badge { display: none; }
  .monde-lecture { display: none; }
}
@media (max-width: 1500px) {
  .monde-nom { display: none; }
  .monde-bascule { padding: 5px 8px; }
}

/* ---- Mode compact : la barre écrite tient jusqu'au téléphone -------------
 * Plutôt que de replier la navigation dans un « ☰ » dès 1400 px, on la
 * resserre. Les liens restent lisibles et surtout VISIBLES : sur un
 * ordinateur, cacher la navigation derrière un bouton est une régression.
 * On rogne dans cet ordre : les espaces, la taille du texte, les rembourrages
 * — jamais le nombre de liens. */
@media (max-width: 1400px) {
  .navbar-inner { padding: 12px 16px; gap: 10px; }
  .nav-links { gap: 14px; }
  .nav-links a { font-size: 0.92rem; }
  .staff-link { padding: 5px 9px !important; }
  .navbar .btn { padding: 8px 14px; font-size: 0.9rem; }
  /* Sélecteur de langue en menu déroulant (drapeau + nom) — 14/09/2026 */
.lang-picker { position: relative; }
.lang-picker > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.04); font-size: .9rem; font-weight: 500; color: inherit; }
.lang-picker > summary::-webkit-details-marker, .lang-picker > summary::marker { display: none; content: ""; }
.lang-picker .lang-chevron { font-size: .7rem; opacity: .7; transition: transform .15s; }
.lang-picker[open] .lang-chevron { transform: rotate(180deg); }
.lang-menu { position: absolute; top: calc(100% + 6px); right: 0; min-width: 168px; background: #161a26; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; padding: 6px; box-shadow: 0 12px 34px rgba(0,0,0,.45); z-index: 200; }
.lang-opt { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px; color: inherit; text-decoration: none; font-size: .9rem; }
.lang-opt:hover { background: rgba(var(--accent-rgb), .15); }
.lang-opt.active { color: var(--accent); font-weight: 600; }
.lang-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); margin-left: auto; }

.lang-switch { transform: scale(0.92); }
}
@media (max-width: 1220px) {
  .nav-links { gap: 10px; }
  .nav-links a { font-size: 0.86rem; }
  .navbar .btn { padding: 7px 11px; font-size: 0.85rem; }
  .navbar .avatar { width: 26px; height: 26px; }
  /* Le raccourci « Mes licences » : un confort, pas une porte. Il reste
     atteignable depuis la page Premium. */
  .navbar-inner .nav-actions > a[href*="mes-licences"] { display: none; }
}
@media (max-width: 1180px) {
  .nav-links { gap: 8px; }
  .nav-links a { font-size: 0.82rem; }
  .staff-link { padding: 4px 8px !important; }
  .navbar .btn { padding: 6px 10px; font-size: 0.82rem; }
  .lang-switch { transform: scale(0.85); }
  .brand { font-size: 1.02rem; }
  .brand-logo { width: 34px; height: 34px; }
}
@media (max-width: 1080px) {
  .nav-links { gap: 6px; }
  .nav-links a { font-size: 0.78rem; }
  .navbar-inner { padding: 10px 10px; gap: 8px; }
  .navbar .avatar { width: 24px; height: 24px; }
}
/* --- La barre du haut tient sur une ligne ---------------------------------
 * L'ajout du bouton des deux mondes a fait déborder la barre : « Mes bots »
 * se coupait en deux lignes et le bouton sortait de l'écran à gauche.
 *
 * Trois réglages, chacun pour une raison :
 *   - nowrap sur les liens : un intitulé de menu ne doit jamais se couper au
 *     milieu, c'est illisible et ça déforme la hauteur de la barre ;
 *   - un espacement qui rétrécit quand la place manque (clamp) au lieu de
 *     pousser le contenu dehors ;
 *   - min-width:0 sur les groupes, sans quoi un enfant flex refuse de
 *     rétrécir sous sa largeur naturelle et déborde en silence.
 */
.nav-links { display: flex; gap: clamp(10px, 1.6vw, 24px); }
.nav-links a { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.nav-links a:hover, .nav-links a.active { color: var(--text); }
.nav-actions { display: flex; align-items: center; gap: clamp(6px, 0.8vw, 12px); min-width: 0; flex-wrap: nowrap; }
.nav-actions > * { flex: 0 0 auto; }
.username { white-space: nowrap; }
.user-menu { display: flex; align-items: center; gap: 10px; }
.avatar { width: 32px; height: 32px; border-radius: 50%; border: 2px solid var(--border); }
.username { color: var(--text); font-weight: 500; }

/* ======= BUTTONS ======= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 18px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font-weight: 600; cursor: pointer; font-size: 0.95rem; font-family: inherit;
  transition: all 0.2s ease; text-decoration: none;
  /*  `.btn` NE DÉCLARAIT AUCUNE COULEUR DE TEXTE.
   *
   *  Les variantes en posent une — `btn-primary`, `btn-danger`, `btn-ghost` —
   *  mais un bouton nu, `class="btn btn-sm"`, retombait sur la couleur par
   *  défaut du navigateur pour un `<button>` : du NOIR. Sur le fond sombre du
   *  site, mesuré à 1.5:1 sur les filtres d'avis de la page Tickets.
   *
   *  Deux cent quatre-vingts boutons du projet n'ont pas de variante. Ils
   *  étaient tous dans ce cas, et ça ne se voyait pas : le noir sur gris très
   *  sombre se lit encore un peu, juste assez pour qu'on croie à un choix. */
  color: var(--text);
  background: transparent;
}
.btn-primary { background: var(--accent-plein); color: var(--accent-texte); }
.btn-primary:hover { background: var(--accent-hover); color: var(--accent-texte); transform: translateY(-1px); box-shadow: 0 10px 20px -5px rgba(var(--accent-rgb), 0.5); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn-ghost:hover { background: var(--bg-card); border-color: var(--accent); color: var(--text); }
.btn-sm { padding: 6px 12px; font-size: 0.85rem; }
.btn-lg { padding: 14px 28px; font-size: 1.05rem; }
.btn-danger { background: var(--danger); color: var(--sur-couleur); }

/* `btn-ghost` sert à deux choses différentes dans les gabarits :
   · avec `btn` — une cinquantaine d'endroits : un vrai bouton secondaire,
     encadré (« Annuler », « Se connecter », les filtres du panel staff) ;
   · seule — trois endroits, tous dans `partials/navbar.ejs` (Mes bots,
     ✨ licences, Déconnexion) : un lien discret, sans cadre.
   Une seconde définition de `.btn-ghost` retirait bordure et remplissage pour
   obtenir le second aspect. Écrite plus bas, elle gagnait sur la première :
   les cinquante vrais boutons ressemblaient donc à du texte inerte, sur
   téléphone comme sur ordinateur. Le `:not(.btn)` sépare les deux cas sans
   toucher un seul gabarit. */
.btn-ghost:not(.btn) {
  background: transparent; color: var(--text-muted);
  padding: 6px 10px; border: none; font-weight: 500;
}
.btn-ghost:not(.btn):hover { color: var(--text); text-decoration: underline; }

/* ======= LANDING / HERO ======= */
/*  `position: relative` sert de repère à la cloche, posée en absolu par-dessus
    le hero. Voir la note de `.cloche` : c'est ce qui remplace le flottant. */
.landing { overflow-x: hidden; position: relative; }
.hero { position: relative; padding: 100px 24px 80px; overflow: hidden; }
.hero-inner { max-width: 900px; margin: 0 auto; text-align: center; position: relative; z-index: 2; }
/*  LA PASTILLE DU HERO — corrigée le 29/08, sur capture.
 *
 *  Elle posait du texte en `--accent` sur un fond `rgba(--accent, .15)` :
 *  le fond était une teinte de l'encre. Tant que l'accent était le violet de
 *  la marque et le fond de page presque noir, ça passait. Pendant Halloween,
 *  la pastille s'est retrouvée en orange sur orange, posée en plus sur un
 *  halo orange du hero — et elle s'est lue comme une tache brune avec
 *  quelque chose d'écrit dedans.
 *
 *  LE CALCUL DISAIT POURTANT 6,3:1. C'est la leçon de cette correction : le
 *  ratio était bon parce qu'il compare une LUMINANCE, et il ne dit rien du
 *  fait que l'encre et le fond soient la même couleur. Deux tons du même
 *  orange peuvent passer WCAG et rester illisibles à l'œil.
 *
 *  DONC : un fond de carte, opaque et neutre, et la couleur de l'événement
 *  garde la bordure — c'est elle qui porte l'identité, pas le remplissage.
 *  La pastille est ainsi la même sur un halo clair ou sur du noir. */
.hero-badge {
  display: inline-block; padding: 6px 14px; border-radius: 100px;
  background: var(--bg-card); color: var(--text);
  font-size: 0.85rem; font-weight: 600; margin-bottom: 24px;
  border: 1px solid rgba(var(--accent-rgb), 0.55);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.08);
}
.hero h1 { font-size: clamp(2.2rem, 5vw, 4rem); font-weight: 800; line-height: 1.1; margin-bottom: 24px; }
/*  LE TITRE : du dégradé de l'accent vers la SECONDE teinte du décor, et
    non vers un rose écrit en dur. Pendant Halloween, « comme un boss »
    partait en orange et finissait en rose bonbon — deux couleurs qui n'ont
    aucun rapport, sur le mot le plus regardé de la page. */
.gradient { background: linear-gradient(135deg, var(--accent) 0%, rgb(var(--fond-aurore-2)) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero-sub { font-size: 1.2rem; color: var(--text-muted); max-width: 650px; margin: 0 auto 36px; }
.hero-cta { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; margin-bottom: 56px; }
.hero-stats { display: flex; gap: 48px; justify-content: center; flex-wrap: wrap; }
.hero-stats div { display: flex; flex-direction: column; align-items: center; }
.hero-stats strong { font-size: 2.2rem; color: var(--text); font-weight: 800; }
.hero-stats span { color: var(--text-muted); font-size: 0.9rem; }

/*  LES TROIS HALOS DU HERO — repris le 29/08.
 *
 *  DEUX D'ENTRE EUX NE SUIVAIENT RIEN. `.orb-2` était rose (#ff6bcb) et
 *  `.orb-3` verte (#3ddc97), écrites en dur bien avant que le site ait deux
 *  mondes et des événements. Résultat visible sur la capture d'Halloween :
 *  un ciel orange, un halo rose à droite et une tache VERTE en bas — trois
 *  couleurs qui ne se parlent pas. Ce n'est pas une question de goût : ces
 *  deux halos-là ignoraient aussi la production, où tout le reste est ambre.
 *
 *  LE TROISIÈME SUIVAIT L'ACCENT, ET C'ÉTAIT PIRE. Son opacité de 0,4 avait
 *  été réglée pour le violet de la marque. L'orange d'Halloween est deux fois
 *  et demie plus lumineux à opacité égale : le même réglage donnait un aplat
 *  orange sur tout le tiers gauche. C'est exactement la marche que le projet
 *  avait déjà rencontrée entre le violet et l'ambre de la production.
 *
 *  Ils prennent donc les teintes du DÉCOR (--fond-teinte / --fond-aurore-2),
 *  qui suivent déjà le monde ET l'événement, et leur force est un jeton :
 *  --orbe-force, plus faible en production comme tout le reste. */
.hero-decoration { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
/*  LE FLOU EST DESCENDU DE 80 À 55 px — signalé le 29/08 : « je trouve que
    c'est un peu flou en fond ». À 80 px sur des cercles de 300 à 400, il ne
    restait plus de forme du tout : trois taches sans bord, qui se lisent
    comme un voile sale posé sur la page plutôt que comme des lumières. À
    55 px les halos ont de nouveau un centre et une chute — c'est ce qui fait
    la différence entre « éclairé » et « embué ». */
.orb { position: absolute; border-radius: 50%; filter: blur(55px); opacity: var(--orbe-force); }
.orb-1 { width: 400px; height: 400px; background: rgb(var(--fond-teinte)); top: -100px; left: -100px; }
/*  LE HALO DE DROITE EST PLUS FAIBLE QUE LES DEUX AUTRES, et c'est mesuré :
    il porte la SECONDE teinte, qui est la plus claire des deux par
    construction (un bleu à côté du violet, un violet à côté de l'orange).
    À force égale, c'est lui qui faisait tomber l'accueil public à 4,92:1 —
    la seule page des six à échouer, parce que c'est la seule qui a des
    halos de hero. */
.orb-2 { width: 300px; height: 300px; background: rgb(var(--fond-aurore-2)); top: 100px; right: -50px;
  opacity: calc(var(--orbe-force) * 0.62); }
/*  UN FLOU COUPÉ NET N'EST PLUS UN FLOU : C'EST UN RECTANGLE.
 *
 *  `.hero` porte `overflow: hidden` — il le faut, sinon les halos débordent
 *  sur le reste de la page. Les trois orbes sont donc découpées par ses
 *  bords. Pour deux d'entre elles c'est sans conséquence : elles sortent en
 *  haut à gauche et à droite, c'est-à-dire aux BORDS DE L'ÉCRAN, où personne
 *  ne voit la coupure.
 *
 *  La troisième sortait par le BAS, avec `bottom: -50px` — donc en plein
 *  milieu de la page, là où l'œil se pose. Additionné aux 55 px de flou, ça
 *  faisait 105 px de halo tranchés d'un trait horizontal net : un rectangle
 *  clair, aux quatre coins vifs, posé derrière le bouton « Ajouter à
 *  Discord ». C'est la première chose qu'on voit en arrivant sur le site.
 *
 *  RÈGLE : une surface floutée de N pixels doit rester à N pixels au moins à
 *  l'intérieur du cadre qui la découpe. Ici : 250 px de haut, 55 px de flou,
 *  donc au moins 55 px de marge basse. On en met 60. */
.orb-3 { width: 250px; height: 250px; background: rgb(var(--fond-teinte)); bottom: 60px; left: 40%;
  opacity: calc(var(--orbe-force) * 0.6); }

/* ======= FEATURES PREVIEW ======= */
.features-preview { padding: 80px 24px; max-width: 1280px; margin: 0 auto; }
.features-preview h2 { font-size: 2.2rem; text-align: center; margin-bottom: 48px; }
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
.feature-card {
  background: var(--bg-card); padding: 28px; border-radius: var(--radius);
  border: 1px solid var(--border); transition: all 0.3s ease;
}
.feature-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); transform: translateY(-2px); }
.feature-icon { font-size: 2.4rem; margin-bottom: 12px; }
.feature-card h3 { font-size: 1.25rem; margin-bottom: 8px; }
.feature-card p { color: var(--text-muted); font-size: 0.95rem; }
/*  LA CARTE EST DEVENUE UN LIEN — elle mène à la fiche du module sur la page
 *  Fonctionnalités. Un `<a>` hérite du bleu et du soulignement du navigateur :
 *  sans ces deux lignes, les huit cartes de la page d'arrivée se seraient
 *  affichées en bleu souligné, ce qui n'aurait échappé à personne — mais
 *  autant ne pas le laisser arriver. */
.feature-card { display: block; text-decoration: none; color: inherit; }
/*  LE PALIER SE DIT, ET C'EST L'ARGUMENT LE PLUS FORT DE LA PAGE : la plupart
 *  de ces modules s'ouvrent en gratuit, et personne ne le sait avant de
 *  l'avoir lu. Discret, mais présent. */
.feature-palier {
  display: inline-block;
  margin-top: 14px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--accent-lisible);
}

/* ======= CTA BAND ======= */
/*  LE BANDEAU DE FIN — dernier rose écrit en dur, retiré le 29/08.
 *
 *  Il partait de l'accent et finissait sur `rgba(255,107,203,.1)`, le même
 *  rose que les halos du hero et le titre. Sur une capture de l'événement
 *  « Été », ça se voyait tout de suite : un ciel bleu, des cartes bleues, et
 *  le grand bandeau du bas qui vire au violet. Ce n'est pas une faute de
 *  goût isolée — c'est le même défaut que les orbes, dans un troisième
 *  endroit : des couleurs de marque figées avant que le site ait deux mondes
 *  et des événements. Chaque fois qu'on en trouve une, il faut chercher les
 *  autres.
 *
 *  Il va maintenant de l'accent à la SECONDE teinte du décor : violet→bleu
 *  d'ordinaire, bleu→ambre en été, vert→rouge à Noël. */
.cta-band {
  text-align: center; padding: 80px 24px;
  background: linear-gradient(135deg,
    rgba(var(--accent-rgb), 0.15), rgba(var(--fond-aurore-2), 0.12));
  margin-top: 60px;
}
.cta-band h2 { font-size: 2rem; margin-bottom: 12px; }
.cta-band p { color: var(--text-muted); margin-bottom: 28px; }

/* ======= CONTAINER ======= */
.container { max-width: 1280px; margin: 0 auto; padding: 40px 24px; }
.center-column { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 70vh; }
.page-head { margin-bottom: 32px; }
.page-head.center { text-align: center; max-width: 700px; margin-left: auto; margin-right: auto; }
.page-head h1 { font-size: 2rem; margin-bottom: 8px; }
.page-head p { color: var(--text-muted); }

/* ======= SERVER GRID ======= */
.server-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.server-card {
  display: flex; align-items: center; gap: 16px;
  background: var(--bg-card); padding: 20px; border-radius: var(--radius);
  border: 1px solid var(--border); color: var(--text);
  transition: all 0.2s ease;
}
.server-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); color: var(--text); transform: translateX(4px); }
.server-icon { width: 48px; height: 48px; border-radius: 50%; }
.server-icon-placeholder {
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--bg-softer); display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 1.2rem; color: var(--text-muted);
}
.server-info { flex: 1; }
.server-name { font-weight: 600; font-size: 1.05rem; }
.server-role { margin-top: 4px; }
.server-arrow { color: var(--text-dim); font-size: 1.3rem; }
.server-card-add { border-style: dashed; }

.tag {
  display: inline-block; padding: 2px 8px; border-radius: 4px;
  font-size: 0.75rem; font-weight: 600;
}
.tag-owner { background: rgba(var(--premium-rgb), 0.15); color: var(--premium); }
.tag-admin { background: rgba(var(--accent-rgb), 0.15); color: var(--accent-lisible); }

.muted { color: var(--text-muted); font-size: 0.9rem; }

/* ======= DASHBOARD LAYOUT (avec sidebar) ======= */
.dashboard-layout { display: grid; grid-template-columns: 260px 1fr; min-height: calc(100vh - 72px); }
.sidebar {
  background: var(--bg-soft); padding: 24px; border-right: 1px solid var(--border);
  position: sticky; top: 72px; height: calc(100vh - 72px); overflow-y: auto;
}
.sidebar-server { display: flex; flex-direction: column; align-items: center; gap: 10px; padding-bottom: 24px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.sidebar-server-icon, .sidebar-server-icon-placeholder { width: 64px; height: 64px; border-radius: 50%; }
.sidebar-server-icon-placeholder { background: var(--bg-softer); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.5rem; color: var(--text-muted); }
.sidebar-server-name { font-weight: 600; text-align: center; }
.sidebar-nav { display: flex; flex-direction: column; gap: 4px; }
.sidebar-nav a { padding: 10px 12px; border-radius: var(--radius-sm); color: var(--text-muted); font-weight: 500; transition: all 0.15s; }
.sidebar-nav a:hover { background: var(--bg-card); color: var(--text); }
.sidebar-nav a.active { background: var(--accent-plein); color: var(--accent-texte); }
/*  LA PASTILLE DU MENU STAFF — combien de demandes attendent.
 *
 *  Elle existe parce qu'un onglet sans signal ne se visite pas : la file des
 *  demandes de suppression d'avis est restée invisible tant qu'il fallait
 *  cliquer dessus pour savoir si elle était vide.
 *
 *  ELLE EST DIMENSIONNÉE EN `em`, PAS EN PIXELS : posée dans un lien dont la
 *  taille de police suit celle du menu, une pastille en pixels grossit ou
 *  rétrécit de travers dès que le menu change d'échelle.
 *
 *  Le lien passe en `flex` pour que le nombre aille à droite sans dépendre
 *  d'une largeur devinée ; `min-width` garde le rond ROND à un chiffre et le
 *  laisse s'allonger à deux ou trois. La couleur ne vient pas de l'accent :
 *  sur le lien actif, l'accent devient le fond, et une pastille de la même
 *  couleur y disparaîtrait — c'est pour ça qu'elle a sa propre paire. */
.sidebar-nav a:has(.sf-pastille) { display: flex; align-items: center; gap: 8px; }
.sf-pastille {
  margin-left: auto;
  min-width: 1.55em;
  padding: 0 0.42em;
  height: 1.55em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  /* #d64a63 mesurait 4,18:1 contre le blanc — sous le seuil de 4,5:1, et
     mesuré, pas supposé : verif-pastille-avis.js calcule le contraste. Un
     nombre qu'on ne lit pas ne sert à rien, et une pastille est faite pour
     être lue d'un coup d'œil. #c9304b donne 5,24:1. */
  background: #c9304b;
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Sur le lien actif le fond est déjà l'accent : la pastille se détache par
   un contour clair plutôt que par sa couleur, qui s'y noierait. */
.sidebar-nav a.active .sf-pastille { box-shadow: 0 0 0 1.5px var(--accent-texte, #fff); }

.sidebar-section { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--text-dim); margin: 16px 8px 4px; }
.dashboard-content { padding: 40px; max-width: 1000px; }

/* ======= STATS / MODULES ======= */
.stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; margin-bottom: 32px; }
.stat-card { background: var(--bg-card); padding: 20px; border-radius: var(--radius); border: 1px solid var(--border); }
.stat-label { display: block; color: var(--text-muted); font-size: 0.85rem; margin-bottom: 4px; }
.stat-value { display: block; font-size: 1.8rem; font-weight: 700; }

.section-title { font-size: 1.4rem; margin: 24px 0 16px; }
.module-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.module-card {
  background: var(--bg-card); padding: 20px; border-radius: var(--radius);
  border: 1px solid var(--border); text-align: center; color: var(--text);
  transition: all 0.2s ease;
}
.module-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); color: var(--text); transform: translateY(-2px); }
.module-card.is-on { border-color: var(--success); box-shadow: 0 0 0 1px rgba(61, 220, 151, 0.2); }
.module-icon { font-size: 2.4rem; margin-bottom: 8px; }
.module-name { font-weight: 600; margin-bottom: 4px; }
.module-status { font-size: 0.85rem; color: var(--text-muted); }
.module-card.is-on .module-status { color: var(--success); }

.premium-cta {
  margin-top: 32px; padding: 24px; border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.15), rgba(var(--premium-2-rgb), 0.15));
  border: 1px solid rgba(var(--premium-rgb), 0.3);
  display: flex; justify-content: space-between; align-items: center; gap: 20px;
  flex-wrap: wrap;
}

/* ======= FORMS ======= */
.form { display: flex; flex-direction: column; gap: 24px; }
.form-section { background: var(--bg-card); padding: 28px; border-radius: var(--radius); border: 1px solid var(--border); }
.form-section h2 { font-size: 1.2rem; margin-bottom: 16px; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-bottom: 16px; }
.form-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.form-field label { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; }

/* --- Étiquettes des formulaires : au-dessus du champ, pas à côté ----------
 *
 *  Une <label> est `inline` par défaut. Les formulaires écrits en
 *  `<div><label>…</label><input></div>` — il y en a beaucoup, d'antiraid à
 *  modération — affichaient donc « Nom [champ] » sur la même ligne, avec un
 *  champ à la largeur du navigateur et une colonne d'étiquettes en escalier.
 *
 *  `.switch` est exclu : ces étiquettes-là ENTOURENT une case à cocher et
 *  doivent rester en ligne. Les 11 étiquettes portant un style inline gagnent
 *  de toute façon sur cette règle.
 */
.form-section label:not(.switch):not(.switch-label) {
  display: block;
  margin-bottom: 6px;
  color: var(--text-muted); font-size: 0.9rem; font-weight: 500;
}
.form-section input:not([type="checkbox"]):not([type="radio"]),
.form-section select,
.form-section textarea { width: 100%; }

/* Un tableau à cinq colonnes de champs ne rentre pas sur un portable : on le
   laisse défiler dans son coin plutôt que de faire déborder toute la page. */
.table-defilante { overflow-x: auto; }
.table-defilante > table { min-width: 560px; }

/* Style global pour TOUS les inputs/selects/textareas du dashboard ET staff (cohérence visuelle) */
.dashboard-content input[type="text"],
.dashboard-content input[type="number"],
.dashboard-content input[type="password"],
.dashboard-content input[type="url"],
.dashboard-content input[type="email"],
.dashboard-content input[type="date"],
.dashboard-content input[type="time"],
.dashboard-content input[type="datetime-local"],
.dashboard-content input:not([type]),
.dashboard-content select,
.dashboard-content textarea,
.container input[type="text"],
.container input[type="number"],
.container input[type="password"],
.container input[type="url"],
.container input[type="email"],
.container input[type="date"],
.container input[type="time"],
.container input[type="datetime-local"],
.container input:not([type]),
.container select,
.container textarea,
.form-field input, .form-field select, .form-field textarea {
  background: var(--bg-softer); border: 1px solid var(--border-champ);
  color: var(--text); padding: 10px 12px; border-radius: var(--radius-sm);
  font-family: inherit; font-size: 0.95rem;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.dashboard-content input:focus,
.dashboard-content select:focus,
.dashboard-content textarea:focus,
.container input:focus,
.container select:focus,
.container textarea:focus,
.form-field input:focus, .form-field select:focus, .form-field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.2);
}
.dashboard-content input::placeholder,
.dashboard-content textarea::placeholder,
.container input::placeholder,
.container textarea::placeholder {
  color: var(--text-dim);
}
.dashboard-content input[type="checkbox"],
.container input[type="checkbox"] {
  accent-color: var(--accent);
}
.select-multiple { min-height: 200px; }

.form-actions { display: flex; gap: 12px; align-items: center; justify-content: flex-end; }

/* switch */
.switch { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; cursor: pointer; }
.switch input { display: none; }
.switch-slider {
  width: 44px; height: 24px; background: var(--bg-softer); border-radius: 100px;
  position: relative; transition: all 0.2s; flex-shrink: 0; border: 1px solid var(--border);
}
.switch-slider::after {
  content: ''; position: absolute; width: 18px; height: 18px; border-radius: 50%;
  background: var(--text-muted); top: 2px; left: 2px; transition: all 0.2s;
}
.switch input:checked + .switch-slider { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-slider::after { background: white; left: 22px; }
.switch-label { color: var(--text); font-weight: 500; }

/* ======= ROLES GRID (loup-garou) ======= */
.roles-filter { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.roles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.role-card {
  display: flex; gap: 12px; padding: 16px; border-radius: var(--radius-sm);
  background: var(--bg-softer); border: 1px solid var(--border); cursor: pointer;
  transition: all 0.15s; align-items: flex-start;
}
.role-card:hover { border-color: var(--accent-dim); }
.role-card input[type="checkbox"] { margin-top: 3px; flex-shrink: 0; accent-color: var(--accent); }
.role-card:has(input:checked) { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.1); }
.role-info { flex: 1; }
.role-name { font-weight: 600; margin-bottom: 2px; }
.role-camp { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
.role-desc { font-size: 0.85rem; color: var(--text-muted); }
.role-loup .role-camp { color: var(--danger); }
.role-solo .role-camp { color: var(--premium); }
.role-card.locked { opacity: 0.5; cursor: not-allowed; background: var(--bg); }
.role-card.locked:hover { border-color: var(--border); }
.role-premium-badge { display: inline-block; font-size: 0.65rem; padding: 2px 6px; border-radius: 4px; background: rgba(var(--premium-rgb), 0.15); color: var(--premium); font-weight: 600; margin-left: 6px; }

.game-snapshot { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.game-snapshot > div { background: var(--bg-softer); padding: 12px; border-radius: var(--radius-sm); }

/* ======= ALERTS ======= */
.alert { padding: 16px 20px; border-radius: var(--radius); margin-bottom: 16px; display: flex; align-items: center; gap: 12px; }
.alert-success { background: rgba(61, 220, 151, 0.1); border: 1px solid rgba(61, 220, 151, 0.3); color: var(--success); }
.alert-warning { background: rgba(255, 181, 71, 0.1); border: 1px solid rgba(255, 181, 71, 0.3); color: var(--warning); }
.alert-danger { background: rgba(255, 91, 122, 0.1); border: 1px solid rgba(255, 91, 122, 0.3); color: var(--danger); }

.badge { display: inline-block; padding: 4px 10px; border-radius: 100px; font-size: 0.8rem; font-weight: 600; }
.badge-premium { background: rgba(var(--premium-rgb), 0.15); color: var(--premium); }

/* ======= PRICING ======= */
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; max-width: 1100px; margin: 0 auto; }
.pricing-card {
  background: var(--bg-card); padding: 32px; border-radius: var(--radius);
  border: 1px solid var(--border); display: flex; flex-direction: column; gap: 20px;
}
.pricing-card.highlighted {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 20px 60px -20px rgba(var(--accent-rgb), 0.4);
  transform: scale(1.03);
}
.pricing-card h2 { font-size: 1.5rem; }
.pricing-price { font-size: 2rem; font-weight: 800; color: var(--accent-lisible); margin-top: 8px; }
.pricing-features { list-style: none; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.pricing-features li { color: var(--text-muted); }

.faq { margin-top: 60px; max-width: 800px; margin-left: auto; margin-right: auto; }
.faq h2 { margin-bottom: 20px; }
.faq details {
  background: var(--bg-card); padding: 16px 20px; border-radius: var(--radius-sm);
  margin-bottom: 8px; border: 1px solid var(--border);
}
.faq summary { cursor: pointer; font-weight: 600; }
.faq details p { color: var(--text-muted); margin-top: 10px; }

/* ======= TABLE ======= */
.table { width: 100%; border-collapse: collapse; background: var(--bg-card); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border); }
.table th, .table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); }
.table th { background: var(--bg-softer); color: var(--text-muted); font-weight: 600; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; }
.table tr:last-child td { border-bottom: none; }

/* ======= ERROR PAGE ======= */
.error-page { text-align: center; }
/*  Même raison que le bandeau ci-dessus : le grand numéro d'erreur finissait
    sur un rose de marque, quel que soit le monde ou la fête. */
.error-code { font-size: 6rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), rgb(var(--fond-aurore-2))); -webkit-background-clip: text; background-clip: text; color: transparent; }
.error-page h1 { margin-bottom: 24px; color: var(--text-muted); font-weight: 500; }

/* --- Le detail technique d'une erreur, et l'astuce qui va avec -------------- *
 *  Ajoutes le 28/08 : la page d'authentification affichait son propre balisage
 *  en toutes lettres, parce que la vue echappe le message (a raison) et que la
 *  route y mettait du HTML. La mise en forme revient donc ici, ou elle doit
 *  etre, et le texte reste du texte. */
.error-detail-titre {
  margin: 0 0 6px; text-align: left; font-size: 0.78rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); opacity: .8;
}
.error-detail {
  margin: 0 auto 18px; max-width: 640px; text-align: left;
  padding: 12px 14px; border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.10);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem; line-height: 1.5; color: var(--text-muted);
  /*  Une erreur d'API tient sur une ligne de trois cents caracteres : sans
   *  ces deux regles elle deborde de l'ecran et on n'en lit que le debut. */
  white-space: pre-wrap; word-break: break-word;
}
.error-astuce {
  margin: 0 auto 20px; max-width: 640px; text-align: left;
  padding: 12px 14px; border-radius: 8px;
  background: rgba(255, 216, 107, 0.08);
  border: 1px solid rgba(255, 216, 107, 0.25);
  font-size: 0.9rem; line-height: 1.6;
}
.error-actions { display: flex; gap: 12px; justify-content: center; }

/* ======= EMPTY STATE ======= */
.empty-state { text-align: center; padding: 80px 24px; background: var(--bg-card); border-radius: var(--radius); border: 1px dashed var(--border); }
.empty-state h2 { margin-bottom: 12px; }
.empty-state p { color: var(--text-muted); margin-bottom: 24px; }

/* ======= FOOTER ======= */
.footer { border-top: 1px solid var(--border); margin-top: 60px; background: var(--bg-soft); }
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 40px 24px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 24px; align-items: center; }
.footer-links { display: flex; gap: 24px; }
.footer-links a { color: var(--text-muted); }
.footer-copy { color: var(--text-dim); font-size: 0.9rem; }

/* ======= RESPONSIVE =======
   Tout le comportement des petits écrans est regroupé en fin de fichier
   (section « TÉLÉPHONES & TABLETTES »). Il doit venir après les règles
   d'animation ajoutées plus bas, sinon celles-ci le récrivent. */

/* ======= STAFF ======= */
.staff-link {
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.15), rgba(var(--premium-2-rgb), 0.15));
  padding: 6px 12px !important;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--premium-rgb), 0.3);
  color: var(--premium) !important;
  font-weight: 600;
}
.staff-link:hover, .staff-link.active {
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.25), rgba(var(--premium-2-rgb), 0.25));
  color: var(--text) !important;
}

/* ===========================================================
   Chip picker — sélection click-to-toggle pour rôles & salons
   =========================================================== */
.chip-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  max-height: 280px;
  overflow-y: auto;
}

.chip-picker.compact { max-height: 180px; }

.chip-picker .chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text, #ddd);
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
  white-space: nowrap;
}

.chip-picker .chip:hover {
  background: rgba(var(--accent-rgb), 0.15);
  border-color: rgba(var(--accent-rgb), 0.4);
}

.chip-picker .chip.selected {
  background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.6), rgba(255, 91, 122, 0.6));
  border-color: rgba(255, 255, 255, 0.4);
  color: var(--accent-texte);
  box-shadow: 0 2px 8px rgba(var(--accent-rgb), 0.3);
}

.chip-picker .chip.selected::after {
  content: '✓';
  margin-left: 4px;
  font-weight: bold;
}

.chip-picker .chip-color {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}

.chip-picker .chip-empty {
  color: rgba(255, 255, 255, 0.4);
  font-style: italic;
  padding: 8px;
}

.chip-picker-toolbar {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
  align-items: center;
}

.chip-picker-toolbar input.search {
  flex: 1;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  color: var(--text, #ddd);
  font-size: 13px;
}

.chip-picker-toolbar .picker-count {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  white-space: nowrap;
}

.chip-picker-toolbar button.mini {
  padding: 4px 10px;
  font-size: 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text, #ddd);
  border-radius: 6px;
  cursor: pointer;
}

.chip-picker-toolbar button.mini:hover {
  background: rgba(var(--accent-rgb), 0.2);
}

/* === Language switcher (navbar) === */
.lang-switch {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  margin-right: 0.5rem;
  padding: 0.15rem 0.35rem;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.lang-switch .lang-flag {
  display: inline-block;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.2rem 0.35rem;
  border-radius: 6px;
  text-decoration: none;
  filter: grayscale(50%);
  opacity: 0.65;
  transition: all 0.15s ease;
}
.lang-switch .lang-flag:hover { filter: none; opacity: 1; background: rgba(var(--accent-rgb), 0.15); }
.lang-switch .lang-flag.active { filter: none; opacity: 1; background: rgba(var(--accent-rgb), 0.25); }

/* ============================================================
   ANIMATIONS & MICRO-INTERACTIONS — Polish Vaeron
   ============================================================ */

/* ============================================================
   LA TRANSITION DOUCE — SUR CE QUI RÉAGIT, PAS SUR TOUT
   ============================================================
 *  ELLE ÉTAIT POSÉE SUR `*`. Mesuré le 29/08, sur la page « Membres du
 *  staff » : 1 882 éléments, tous porteurs de la règle, soit 9 788 propriétés
 *  déclarées animables — pour une page où il y a peut-être quarante choses
 *  qui réagissent à la souris. Chaque `<td>`, chaque `<span>`, chaque
 *  `<option>` payait une transition qu'aucun œil ne verra jamais.
 *
 *  ET ELLE MENTAIT AUX OUTILS. Une couleur lue juste après avoir été posée
 *  rend l'ANCIENNE valeur pendant 180 ms — la transition est en cours. Une
 *  vérification de palette a conclu que `--danger` « ne peignait rien »,
 *  alors que le jeton était parfaitement défini : elle mesurait le début de
 *  l'animation. Un décor qui trompe les instruments finit par cacher un vrai
 *  défaut.
 *
 *  On garde exactement la même douceur, aux mêmes durée et courbe, sur ce qui
 *  répond au survol, au focus ou au clic. Le reste du document ne bouge pas,
 *  et n'a jamais eu de raison de bouger. */
a,
button,
input,
select,
textarea,
summary,
label,
.btn,
.card, .module-card, .server-card, .feature-card, .pricing-card, .form-section,
.stat-card, .badge, .etat-pill, .badge-status, .switch-slider,
.table tbody tr, .rg-table tbody tr, .reg-panneau, .alert,
.sidebar a, .navbar a, .tk-capacite, .bp-bouton {
  transition-property: background-color, border-color, color, opacity, transform, box-shadow;
  transition-duration: 0.18s;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* On respecte la preference utilisateur reduce-motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* === Page fade-in à l'entrée === */
@keyframes vaeron-page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
main {
  animation: vaeron-page-in 0.3s ease-out;
}

/* === Cartes : float au survol === */
.feature-card,
.module-card,
.server-card,
.pricing-card,
.form-section {
  position: relative;
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.2s ease;
}
.feature-card:hover,
.module-card:hover,
.server-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px -8px rgba(var(--accent-rgb), 0.3);
  border-color: rgba(var(--accent-rgb), 0.4);
}

/* ==========================================================================
 *  CARTES DE TARIFS — l'animation appartient a la FORMULE, pas au surlignage
 * ==========================================================================
 *  CE QUI ETAIT FAUX. Le balayage tournant etait accroche a
 *  `.pricing-card.highlighted`, c'est-a-dire a « la carte mise en avant ».
 *  Or `highlighted` porte deja trois choses : la bordure d'accent, l'ombre
 *  portee et l'agrandissement — bref, « c'est celle-ci qu'on recommande ».
 *
 *  Tant qu'une seule carte devait bouger ET etre recommandee, la confusion
 *  ne se voyait pas. Le 28/08 elle s'est vue : « enleve l'anim de Max, mets-la
 *  sur Ultra ». Deplacer `highlighted: true` de Max vers Ultra aurait bien
 *  deplace l'animation — en emportant AUSSI la recommandation, la bordure et
 *  l'agrandissement, que personne n'avait demande de deplacer. Et l'inverse
 *  etait impossible : on ne pouvait pas animer Premium sans le declarer
 *  recommande, donc sans recommander deux formules a la fois.
 *
 *  DONC : l'animation se branche desormais sur `data-formule`, pose par la
 *  vue depuis l'identifiant de la formule (voir views/premium.ejs). Une
 *  seule source — l'id du plan dans src/plans.js — et deux decisions
 *  independantes : qui est recommande (highlighted, cote route) et qui
 *  respire (data-formule, ici).
 *
 *  Le mouvement reste DERRIERE le contenu (z-index 0 contre 1) et
 *  `pointer-events: none` : une carte de tarifs doit rester cliquable, et un
 *  reflet qui intercepte le clic sur « S'abonner » coute une vente.
 *  Le reglage systeme « moins d'animations » neutralise les deux (bloc
 *  prefers-reduced-motion plus bas).
 * ========================================================================== */

/* --- ULTRA : le balayage tournant (celui qui etait sur Max) --------------- */
.pricing-card[data-formule="ultra"] {
  position: relative;
  overflow: hidden;
}
.pricing-card[data-formule="ultra"]::before {
  content: '';
  position: absolute;
  inset: -50%;
  background: conic-gradient(from 0deg, transparent 0%, rgba(var(--accent-rgb), 0.15) 25%, transparent 50%);
  animation: vaeron-shimmer 4s linear infinite;
  pointer-events: none;
  z-index: 0;
}
.pricing-card[data-formule="ultra"] > * { position: relative; z-index: 1; }

@keyframes vaeron-shimmer {
  to { transform: rotate(360deg); }
}

/* --- PREMIUM : un reflet qui traverse, dans l'or de la formule ------------ *
 *  Volontairement d'une AUTRE nature que celui d'Ultra : l'un tourne en
 *  continu, l'autre passe puis disparait. Deux cartes qui bougent de la meme
 *  façon se lisent comme un seul niveau ; ici on voit du premier coup d'oeil
 *  que ce ne sont pas les memes.
 *
 *  Il se deplace en `transform` et non en `left` : la premiere est prise en
 *  charge par le compositeur, la seconde reflowe la carte soixante fois par
 *  seconde. Sur la page des tarifs, ou six cartes coexistent, la difference
 *  se sent sur une machine modeste.
 *
 *  Deux tiers du cycle sont un temps mort (opacite 0, hors cadre) : un reflet
 *  qui repasse sans arret devient un clignotant, et on finit par regarder le
 *  reflet au lieu du prix. */
.pricing-card[data-formule="premium"] {
  position: relative;
  overflow: hidden;
}
.pricing-card[data-formule="premium"]::before {
  content: '';
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: 0;
  width: 38%;
  background: linear-gradient(100deg,
    transparent 0%,
    rgba(var(--premium-2-rgb), 0.10) 35%,
    rgba(var(--premium-rgb), 0.20) 50%,
    rgba(var(--premium-2-rgb), 0.10) 65%,
    transparent 100%);
  filter: blur(6px);
  animation: vaeron-reflet-premium 6s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}
.pricing-card[data-formule="premium"] > * { position: relative; z-index: 1; }

@keyframes vaeron-reflet-premium {
  0%   { transform: translateX(-120%) rotate(6deg); opacity: 0; }
  8%   { opacity: 1; }
  42%  { opacity: 1; }
  50%  { transform: translateX(320%) rotate(6deg); opacity: 0; }
  100% { transform: translateX(320%) rotate(6deg); opacity: 0; }
}

/* === Boutons : glow sur primary === */
.btn-primary {
  position: relative;
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.btn-primary::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.4s ease, height 0.4s ease;
  pointer-events: none;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 20px -4px rgba(var(--accent-rgb), 0.5);
}
.btn-primary:active {
  transform: translateY(0);
}
.btn-primary:active::after {
  width: 200%;
  height: 200%;
  transition: width 0s, height 0s;
}

/* === Toggle switch : smooth === */
.switch-slider, .slider {
  transition: background-color 0.25s ease, transform 0.25s ease !important;
}

/* === Inputs : ring focus === */
input[type="text"], input[type="number"], input[type="password"],
input[type="email"], input[type="url"], textarea, select {
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
input[type="text"]:focus, input[type="number"]:focus, input[type="password"]:focus,
input[type="email"]:focus, input[type="url"]:focus, textarea:focus, select:focus {
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.6) !important;
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}

/* === Liens nav : underline animée === */
.navbar .nav-links a {
  position: relative;
}
.navbar .nav-links a::after {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.navbar .nav-links a:hover::after,
.navbar .nav-links a.active::after {
  width: 100%;
}

/* === Sidebar items : indicateur slide === */
.sidebar-nav a {
  position: relative;
  transition: background-color 0.15s ease, color 0.15s ease, padding-left 0.2s ease;
}
.sidebar-nav a::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--accent), var(--accent-hover));
  transform: scaleY(0);
  transition: transform 0.2s ease;
  border-radius: 0 3px 3px 0;
}
.sidebar-nav a:hover {
  padding-left: 14px;
}
.sidebar-nav a.active::before,
.sidebar-nav a:hover::before {
  transform: scaleY(1);
}

/* === Badges : pulse pour STAFF === */
.badge-premium {
  animation: vaeron-pulse 2.4s ease-in-out infinite;
}
@keyframes vaeron-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.4); }
  50%      { box-shadow: 0 0 0 6px rgba(var(--accent-rgb), 0); }
}

/* === Alertes : slide-down === */
.alert {
  animation: vaeron-alert-in 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes vaeron-alert-in {
  from { opacity: 0; transform: translateY(-8px); max-height: 0; }
  to   { opacity: 1; transform: translateY(0); max-height: 200px; }
}

/* === Hero orbs : float souple === */
.orb {
  animation: vaeron-orb-float 7s ease-in-out infinite;
}
.orb-2 { animation-delay: -2s; animation-duration: 9s; }
.orb-3 { animation-delay: -4s; animation-duration: 11s; }
@keyframes vaeron-orb-float {
  0%, 100% { transform: translate(0, 0); }
  33%      { transform: translate(20px, -25px); }
  66%      { transform: translate(-15px, 15px); }
}

/* === Stats card : nombres comptent (visual cue) === */
.stat-card .stat-value,
.stats-row .stat-value {
  background: linear-gradient(135deg, #fff 0%, var(--accent-hover) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 800;
}

/* === Lang switch : drapeau flip === */
.lang-switch .lang-flag {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.15s, opacity 0.15s, background 0.15s;
}
.lang-switch .lang-flag:hover {
  transform: scale(1.15) rotate(-5deg);
}
.lang-switch .lang-flag.active {
  transform: scale(1.1);
}

/* === Avatars : zoom subtil au hover === */
.user-menu .avatar,
.sidebar-server-icon,
.server-card img {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.user-menu:hover .avatar,
.server-card:hover img {
  transform: scale(1.08);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.3);
}

/* === Transition entre pages (link click) === */
a:not(.btn):not(.brand):not(.lang-flag):not([target="_blank"]) {
  transition: color 0.15s ease;
}

/* === Spinner moderne (à utiliser dans le checkout) === */
.vaeron-spinner {
  display: inline-block;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(var(--accent-rgb), 0.2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: vaeron-spin 0.7s linear infinite;
}
@keyframes vaeron-spin {
  to { transform: rotate(360deg); }
}

/* === Scroll smooth === */
html { scroll-behavior: smooth; }

/* === Selection : couleur Vaeron === */
::selection {
  background: rgba(var(--accent-rgb), 0.4);
  color: var(--accent-texte);
}

/* === Scrollbar customisée === */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.02); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.5), rgba(var(--accent-rgb), 0.3));
  border-radius: 5px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.7), rgba(var(--accent-rgb), 0.5));
  background-clip: padding-box;
}

/* === Form-section : entry stagger === */
.form-section {
  animation: vaeron-fade-up 0.4s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}
.form-section:nth-child(1) { animation-delay: 0.05s; }
.form-section:nth-child(2) { animation-delay: 0.1s; }
.form-section:nth-child(3) { animation-delay: 0.15s; }
.form-section:nth-child(4) { animation-delay: 0.2s; }
.form-section:nth-child(5) { animation-delay: 0.25s; }
@keyframes vaeron-fade-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* === Confetti pour la page success premium === */
@keyframes vaeron-confetti {
  0%   { transform: translateY(-20vh) rotate(0deg);   opacity: 0; }
  10%  { opacity: 1; }
  100% { transform: translateY(120vh) rotate(720deg); opacity: 0; }
}

/* === Tab focus visible (accessibilité) === */
*:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), 0.7);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ============================================================
 * Mission 3 — Animations stylées (discrètes, 200-400ms)
 * ============================================================ */
html { scroll-behavior: smooth; }

.module-card,
.stat-card,
.server-card,
.plugin-card {
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.22s;
}
.module-card:hover,
.stat-card:hover,
.server-card:hover,
.plugin-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 28px rgba(var(--accent-rgb), 0.22),
              0 2px 6px rgba(0, 0, 0, 0.18);
}

.badge-premium,
.badge-pro,
.badge-active {
  animation: vaeron-badge-pulse 3.4s ease-in-out infinite;
}
@keyframes vaeron-badge-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--premium-rgb), 0.4); }
  50%      { box-shadow: 0 0 0 6px rgba(var(--premium-rgb), 0); }
}

.dashboard-sidebar {
  animation: vaeron-slide-in-left 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes vaeron-slide-in-left {
  from { opacity: 0; transform: translateX(-18px); }
  to   { opacity: 1; transform: translateX(0); }
}

.btn,
button.btn,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-success {
  transition: transform 0.15s ease,
              box-shadow 0.2s ease,
              background-color 0.2s ease,
              border-color 0.2s ease,
              filter 0.15s ease;
}
.btn:hover,
button.btn:hover {
  transform: translateY(-1px);
  filter: brightness(1.05);
}
.btn:active,
button.btn:active {
  transform: translateY(0) scale(0.98);
  filter: brightness(0.95);
}

.switch input + .switch-slider,
.switch-slider {
  transition: background-color 0.25s ease,
              box-shadow 0.25s ease;
}
.switch-slider::before {
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.25s ease;
}

form[aria-busy="true"] .btn[type="submit"],
form.is-submitting .btn[type="submit"] {
  position: relative;
  pointer-events: none;
  opacity: 0.85;
  color: transparent !important;
}
form[aria-busy="true"] .btn[type="submit"]::after,
form.is-submitting .btn[type="submit"]::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: vaeron-spin 0.7s linear infinite;
}

input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="url"],
select,
textarea {
  transition: border-color 0.2s ease,
              box-shadow 0.2s ease,
              background-color 0.2s ease;
}
input:focus,
select:focus,
textarea:focus {
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
}

.navbar a,
.dashboard-sidebar a {
  transition: color 0.18s ease, background-color 0.18s ease;
}

table tbody tr {
  transition: background-color 0.18s ease;
}
table tbody tr:hover {
  background-color: rgba(var(--accent-rgb), 0.06);
}

.alert {
  animation: vaeron-fade-up 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Tables : surlignage de la ligne survolée */
table tbody tr {
  transition: background-color 0.18s ease;
}
table tbody tr:hover {
  background-color: rgba(var(--accent-rgb), 0.06);
}

/* Alerts : entrée descendante douce (au cas où vaeron-alert-in pas applique) */
.alert {
  animation: vaeron-fade-up 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}

/* Respect prefers-reduced-motion : désactive tout */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Petit script JS optionnel : auto-ajout de .is-submitting sur form submit
 * (CSS ne peut pas le faire seul). On le branche depuis un partial JS si besoin. */

/* ============================================================================
   TÉLÉPHONES & TABLETTES
   ============================================================================
   Cette section est volontairement la DERNIÈRE du fichier. Les blocs
   d'animation au-dessus redéfinissent `transform`, `padding-left`, etc. avec la
   même spécificité ; placées plus haut, les règles mobiles se faisaient récrire
   sans qu'on comprenne pourquoi.

   Trois seuils :
     · 1024px — on resserre, la mise en page reste la même
     · 900px  — bascule : la barre latérale et le menu haut deviennent des
                menus dépliants
     · 600px  — téléphone : une colonne, textes et marges réduits

   Aucun JavaScript n'est utilisé pour les menus : une case à cocher masquée
   porte l'état ouvert/fermé, et le CSS montre ou cache le panneau. Elle se
   remet à zéro à chaque changement de page, ce qui est exactement le
   comportement voulu (on ouvre, on clique un lien, le menu se referme).
   ========================================================================== */

/* ---- Grilles : ne jamais être plus large que l'écran ----------------------
   `minmax(320px, 1fr)` impose une colonne de 320px même dans un conteneur de
   300px : la page déborde alors sur la droite et tout part de travers. Le
   `min(…, 100%)` autorise la colonne à rétrécir sous la largeur idéale quand
   l'écran est plus étroit qu'elle. C'est la cause n°1 du défilement horizontal
   parasite sur téléphone. */
/* Un élément de grille refuse par défaut de descendre sous la largeur minimale
   de son contenu (`min-width: auto`). Un seul tableau un peu large dans la
   colonne de droite élargissait donc TOUTE la colonne, et la page entière
   partait sur la droite : cartes coupées, menu qui dépasse. `min-width: 0`
   autorise la colonne à s'en tenir à la largeur de l'écran ; c'est alors le
   tableau qui défile tout seul, à l'intérieur de son cadre. */
.dashboard-layout > * { min-width: 0; }

.server-grid   { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.feature-grid  { grid-template-columns: repeat(auto-fit,  minmax(min(280px, 100%), 1fr)); }
.pricing-grid  { grid-template-columns: repeat(auto-fit,  minmax(min(280px, 100%), 1fr)); }
.roles-grid    { grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); }
.form-grid     { grid-template-columns: repeat(auto-fit,  minmax(min(240px, 100%), 1fr)); }
.module-grid   { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }
.stats-row     { grid-template-columns: repeat(auto-fit,  minmax(min(140px, 100%), 1fr)); }
.game-snapshot { grid-template-columns: repeat(auto-fit,  minmax(min(140px, 100%), 1fr)); }

/* ---- Boutons des menus dépliants : invisibles sur ordinateur ---- */
.nav-burger, .sidebar-toggle,
.nav-burger-label, .sidebar-toggle-label { display: none; }

/* ==========================================================================
   ≤ 1024px — on resserre
   ========================================================================== */
@media (max-width: 1024px) {
  .navbar-inner { padding: 14px 18px; gap: 16px; }
  .nav-links { gap: 16px; }
  .dashboard-layout { grid-template-columns: 220px 1fr; }
  .dashboard-content { padding: 32px 24px; }
  .hero { padding: 72px 20px 64px; }
}

/* ==========================================================================
   ≤ 900px — bascule en menus dépliants
   ========================================================================== */
/* Le menu « ☰ » est réservé aux petits écrans : téléphone et petite tablette.
   Sur un ordinateur, on veut le menu ÉCRIT — un burger sur grand écran cache
   la navigation sans raison. Au-dessus de ce seuil, la barre tient grâce au
   mode compact plus bas, pas en se repliant.
   Le seuil vient d'une mesure, pas d'une habitude : en dessous de 1140 px la
   barre écrite ne rentre plus, même compactée au maximum. Au-dessus, elle
   tient — donc pas de burger. 1140 px, c'est une petite tablette ; aucun
   ordinateur n'y tombe, sauf en zoom fort.
   Les règles de la barre LATÉRALE restent à 900 px : elle a encore la place
   à 1140, et un seul seuil pour deux problèmes différents ne marche pas. */
@media (max-width: 1140px) {


  /* ---- Barre du haut ---------------------------------------------------
     Avant, `.nav-links { display: none }` faisait simplement disparaître
     Fonctionnalités / Commandes / Premium / Support : depuis un téléphone, ces
     pages n'étaient plus atteignables du tout. Elles reviennent, dans un
     panneau qu'on ouvre avec le « ☰ ». */
  .navbar-inner { flex-wrap: wrap; padding: 10px 14px; gap: 10px; }
  .brand { flex: 1; min-width: 0; font-size: 1.1rem; }
  .brand-logo { width: 34px; height: 34px; }

  .nav-burger, .sidebar-toggle {
    display: block; position: absolute;
    width: 1px; height: 1px; margin: 0; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%);
  }

  .nav-burger-label {
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    width: 44px; height: 44px;          /* 44px : la cible tactile minimale */
    border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--bg-soft);
    color: var(--text); font-size: 1.3rem; line-height: 1;
    cursor: pointer; user-select: none;
  }
  .nav-burger:checked ~ .nav-burger-label { border-color: var(--accent); color: var(--accent-lisible); }
  .nav-burger:focus-visible ~ .nav-burger-label { outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 2px; }

  .nav-links, .nav-actions { display: none; }
  .nav-burger:checked ~ .nav-links,
  .nav-burger:checked ~ .nav-actions {
    display: flex; flex-direction: column; align-items: stretch;
    flex-basis: 100%; width: 100%; gap: 2px;
  }
  .nav-burger:checked ~ .nav-actions {
    gap: 10px; padding-top: 12px; margin-top: 4px;
    border-top: 1px solid var(--border);
  }
  .nav-burger:checked ~ .nav-links a {
    padding: 12px 10px; border-radius: var(--radius-sm);
  }
  .nav-burger:checked ~ .nav-links a:hover,
  .nav-burger:checked ~ .nav-links a.active { background: var(--bg-card); }
  /* Le soulignement animé du menu de bureau n'a plus de sens en colonne. */
  .navbar .nav-links a::after { display: none; }

  .nav-burger:checked ~ .nav-actions .lang-switch { align-self: flex-start; margin-right: 0; }
  .nav-burger:checked ~ .nav-actions > .btn { width: 100%; }
  /* Les liens discrets (Mes bots, ✨ licences, Déconnexion) prennent la même
     hauteur que les autres entrées du menu : 6 px de remplissage, ça ne se
     vise pas au doigt. */
  .nav-burger:checked ~ .nav-actions .btn-ghost:not(.btn) {
    padding: 12px 10px; border-radius: var(--radius-sm);
  }
  .nav-burger:checked ~ .nav-actions .user-menu {
    flex-wrap: wrap; gap: 8px; padding-top: 10px;
    border-top: 1px solid var(--border);
  }
  .nav-burger:checked ~ .nav-actions .username { overflow-wrap: anywhere; }

  /* ---- Des cibles qu'un doigt peut viser -------------------------------
     Mesuré sur iPhone 13, Pixel 5 et Galaxy S9+ : dans le menu ouvert, cinq
     éléments faisaient moins de 34 px de haut — le drapeau de langue ne
     faisait que 20 px. On vise un doigt, pas un curseur : 44 px est la taille
     recommandée, et en dessous de 40 on rate sa cible une fois sur trois. */
  .nav-burger:checked ~ .nav-actions .monde-bascule,
  .nav-burger:checked ~ .nav-links a,
  .nav-burger:checked ~ .nav-actions .btn,
  .nav-burger:checked ~ .nav-actions .btn-ghost {
    /* `.nav-links a` et non seulement `.staff-link` : mesurés à 43 px, les
       cinq liens ordinaires passaient juste sous la barre. Un pixel, mais du
       mauvais côté du seuil. */
    min-height: 44px; min-width: 44px;
    display: flex; align-items: center; justify-content: center;
  }
  /* On ANNULE ici le scale(0.85) appliqué plus haut aux barres étroites.
     Il servait à gagner de la place sur une seule ligne ; dans le menu
     déplié il n'y a plus de contrainte de largeur, et il ne faisait que
     rapetisser les cibles tactiles : 44 px × 0.85 = 37 px, soit en dessous
     du seuil où l'on rate sa cible au doigt. */
  .nav-burger:checked ~ .nav-actions .lang-switch { gap: 8px; transform: none; }
  /* `.lang-switch .lang-flag` est plus spécifique que `.lang-flag` seul :
     ma première version était ignorée pour l'un des deux drapeaux. On reprend
     la même spécificité plutôt que d'empiler un !important. */
  .nav-burger:checked ~ .nav-actions .lang-switch .lang-flag {
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.25rem; padding: 0;
  }
  /* Le mot du bouton revient dans le menu : la place ne manque plus, et une
     pastille seule ne dit rien quand elle n'est plus dans la barre. */
  .nav-burger:checked ~ .nav-actions .monde-nom { display: inline; }
}

@media (max-width: 900px) {
  /* ---- Barre latérale du tableau de bord ----------------------------------
     Elle passe au-dessus du contenu quand il n'y a plus deux colonnes. Avec
     une trentaine de liens, la page demandée se retrouvait à un écran et demi
     plus bas. On la replie, en gardant le nom du serveur visible. */
  .dashboard-layout { grid-template-columns: 1fr; min-height: 0; }
  .sidebar {
    position: static; height: auto; overflow: visible;
    padding: 12px 16px;
    border-right: none; border-bottom: 1px solid var(--border);
  }
  .sidebar-toggle-label {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    width: 100%; padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--bg-card); border: 1px solid var(--border);
    color: var(--text); font-weight: 600; cursor: pointer; user-select: none;
  }
  .sidebar-toggle-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sidebar-toggle-label::after { content: '▾'; flex-shrink: 0; color: var(--text-muted); }
  .sidebar-toggle:checked ~ .sidebar-toggle-label { border-color: var(--accent); }
  .sidebar-toggle:checked ~ .sidebar-toggle-label::after { transform: rotate(180deg); }
  .sidebar-toggle:focus-visible ~ .sidebar-toggle-label { outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 2px; }

  .sidebar-body { display: none; }
  .sidebar-toggle:checked ~ .sidebar-body {
    display: block; margin-top: 12px;
    max-height: 65vh; overflow-y: auto;   /* le menu ne mange jamais tout l'écran */
  }
  /* Replié, l'entête « icône + nom du serveur » ferait doublon avec le bouton. */
  .sidebar-body .sidebar-server { padding-bottom: 16px; margin-bottom: 12px; }
  .sidebar-nav a { padding: 12px; }      /* liens plus hauts = plus faciles à viser */
  .dashboard-sidebar { animation: none; }

  .dashboard-content { padding: 24px 16px 48px; max-width: 100%; }
  .container { padding: 28px 16px 48px; }
}

/* ==========================================================================
   ≤ 700px — téléphone
   ========================================================================== */
@media (max-width: 700px) {

  /* ---- Champs de saisie : 16px obligatoires -------------------------------
     Sous 16px, Safari iOS zoome automatiquement à la mise au point du champ,
     et l'utilisateur se retrouve avec une page décalée qu'il doit repincer.
     Les sélecteurs reprennent la forme `[type]` de la règle d'origine : à
     spécificité égale, c'est la dernière écrite qui gagne — pas de !important. */
  .dashboard-content input[type], .dashboard-content input:not([type]),
  .dashboard-content select, .dashboard-content textarea,
  .container input[type], .container input:not([type]),
  .container select, .container textarea,
  .form-field input, .form-field select, .form-field textarea,
  input, select, textarea { font-size: 16px; }

  /* ---- Tableaux ------------------------------------------------------------
     Un tableau de 5 colonnes ne rentre pas dans 360px : sans ça, c'est la page
     ENTIÈRE qui se met à défiler de côté, entête et menu compris. En bloc avec
     `overflow-x`, seul le tableau glisse, le reste de la page ne bouge plus. */
  .table, .dashboard-content table, .container table {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  .table th, .table td { padding: 10px 12px; }
  .table th { font-size: 0.72rem; letter-spacing: 0.5px; }
  .table td { font-size: 0.9rem; }

  /* ---- Grilles écrites en clair dans les gabarits --------------------------
     Une trentaine de pages portent un `style="grid-template-columns:1fr 1fr"`
     directement sur la balise. Deux colonnes dans 328px donnent des champs de
     150px, inutilisables. Un style en ligne ne peut se récrire qu'avec
     `!important` — c'est le seul endroit du fichier où on s'y résout. */
  .dashboard-content [style*="grid-template-columns"],
  .container [style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }

  /* ---- Formulaires ---- */
  .form-section { padding: 18px 16px; }
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions .btn, .form-actions button { width: 100%; }
  .chip-picker-toolbar { flex-wrap: wrap; }
  .chip-picker-toolbar input.search { min-width: 140px; }
  .chip-picker { max-height: 220px; }
  .select-multiple { min-height: 160px; }

  /* ---- Rien ne doit dépasser sur la droite ---- */
  .page-head h1, .hero h1, .section-title,
  .sidebar-server-name, .server-name, .username, .role-name, code {
    overflow-wrap: anywhere;
  }
  pre { overflow-x: auto; }
}

/* ==========================================================================
   ≤ 600px — petits téléphones
   ========================================================================== */
@media (max-width: 600px) {
  .hero { padding: 56px 16px 48px; }
  .hero h1 { font-size: 2rem; }
  .hero-sub { font-size: 1.05rem; margin-bottom: 28px; }
  .hero-cta { flex-direction: column; align-items: stretch; margin-bottom: 40px; }
  .hero-cta .btn { width: 100%; }
  .hero-stats { gap: 20px 28px; }
  .hero-stats strong { font-size: 1.8rem; }
  /* Les halos flous coûtent cher à dessiner et sortent du cadre sur un petit
     écran ; on les réduit plutôt que de les supprimer. */
  .orb-1 { width: 260px; height: 260px; }
  .orb-2 { width: 200px; height: 200px; }
  .orb-3 { display: none; }

  .features-preview { padding: 48px 16px; }
  .features-preview h2 { font-size: 1.6rem; margin-bottom: 28px; }
  .feature-card { padding: 20px; }
  .cta-band { padding: 48px 16px; margin-top: 40px; }
  .cta-band h2 { font-size: 1.5rem; }

  .page-head { margin-bottom: 24px; }
  .page-head h1 { font-size: 1.55rem; }
  .section-title { font-size: 1.2rem; }

  .server-card { padding: 16px; gap: 12px; }
  .pricing-card { padding: 24px 20px; }
  /* Une carte agrandie de 3 % déborde du cadre quand il n'y a plus de marge. */
  .pricing-card.highlighted { transform: none; }
  .premium-cta { flex-direction: column; align-items: flex-start; padding: 20px; }
  .empty-state { padding: 48px 20px; }

  .error-code { font-size: 4rem; }
  .error-actions { flex-wrap: wrap; justify-content: center; }

  .alert { padding: 14px 16px; align-items: flex-start; }
  .footer-inner { padding: 28px 16px; flex-direction: column; align-items: flex-start; gap: 14px; }
  .footer-links { flex-wrap: wrap; gap: 16px; }
}

/* ==========================================================================
   Écrans tactiles
   ==========================================================================
   Sans souris, `:hover` reste collé après le doigt : la carte touchée garde son
   décalage vers le haut jusqu'au prochain appui ailleurs. On rend donc les
   effets de survol inertes quand il n'y a pas de vrai pointeur. */
@media (hover: none) {
  .feature-card:hover, .module-card:hover, .server-card:hover,
  .stat-card:hover, .plugin-card:hover, .pricing-card:hover {
    transform: none; box-shadow: var(--shadow);
  }
  .btn:hover, button.btn:hover, .btn-primary:hover { transform: none; box-shadow: none; }
  .sidebar-nav a:hover { padding-left: 12px; }
  .lang-switch .lang-flag:hover { transform: none; }
  .user-menu:hover .avatar, .server-card:hover img { transform: none; box-shadow: none; }
}

/* ==========================================================================
 *  SOCLE — cohérence des états et du mouvement
 * ==========================================================================
 *
 *  Ajouté en fin de feuille, volontairement : à spécificité égale la dernière
 *  règle gagne, donc ce socle harmonise l'existant sans qu'il faille réécrire
 *  les 1250 lignes au-dessus, ni ajouter un seul !important.
 *
 *  Ce qui manquait, constaté sur la feuille d'origine :
 *    - :disabled n'était stylé nulle part. Un bouton désactivé — celui de
 *      création de backup quand le quota est atteint, par exemple — avait
 *      exactement l'apparence d'un bouton actif. On clique, rien ne se passe,
 *      et rien à l'écran n'explique pourquoi.
 *    - :focus-visible n'apparaissait que 3 fois contre 22 :focus. Résultat :
 *      un contour au clic de souris (inesthétique) et rien de fiable au
 *      clavier (inaccessible). :focus-visible règle les deux d'un coup.
 *    - :active n'était présent que 4 fois : la plupart des boutons ne
 *      réagissaient pas à l'enfoncement, ce qui donne une impression de
 *      page morte.
 *    - 10 animations définies, utilisées une ou deux fois chacune.
 *
 *  Parti pris : discret. C'est un tableau de bord qu'on garde ouvert des
 *  heures, pas une page d'accueil qu'on visite une fois. Les durées restent
 *  courtes (120 à 320 ms) et rien ne bouge en boucle.
 * ========================================================================== */

:root {
  --duree-rapide: 120ms;
  --duree: 200ms;
  --duree-entree: 320ms;
  --courbe: cubic-bezier(0.22, 0.61, 0.36, 1);   /* démarre franc, finit doux */
  --anneau-focus: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* --- Réactivité au survol, au clavier et à l'enfoncement ------------------ */

.btn, button, select, input, textarea, .bk-card, .bk-opt, .card {
  transition:
    background-color var(--duree) var(--courbe),
    border-color     var(--duree) var(--courbe),
    color            var(--duree) var(--courbe),
    box-shadow       var(--duree) var(--courbe),
    transform        var(--duree-rapide) var(--courbe);
}

.btn:hover:not(:disabled),
button:hover:not(:disabled) { transform: translateY(-1px); }

.btn:active:not(:disabled),
button:active:not(:disabled) {
  transform: translateY(1px) scale(0.99);
  transition-duration: 60ms;   /* l'enfoncement doit être immédiat */
}

/* Un seul anneau de focus pour tout le site, et seulement au clavier :
 * :focus-visible ne se déclenche pas sur un clic de souris. */
:where(a, button, .btn, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  box-shadow: var(--anneau-focus);
  border-radius: var(--radius-sm);
}

/* --- États désactivés : ils doivent se voir ------------------------------ */

.btn:disabled, button:disabled,
input:disabled, select:disabled, textarea:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.4);
  transform: none;
  box-shadow: none;
}

/* --- Champs de saisie ---------------------------------------------------- */

input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
}

input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  cursor: pointer;
}

/* Une case cochée doit se distinguer sans avoir à la chercher. */
.bk-opt:has(input:checked) {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-card));
}
.bk-opt:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }

/* --- Cartes -------------------------------------------------------------- */

.bk-card:hover, .card:hover {
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
  box-shadow: var(--shadow);
}

/* --- Entrée en scène ----------------------------------------------------- *
 *
 *  Les sélecteurs visent les classes réellement utilisées dans les vues :
 *  .page-head, .form-section, .stat-card, .module-card, .server-card,
 *  .feature-card, .pricing-card, .plugin-card, .role-card, .alert, .table.
 *  Cibler .card ou .content ne servait à rien, ces classes n'existent pas.
 *
 *  Distance portée à 16 px et durée à 380 ms : la version d'origine faisait
 *  8 px en 300 ms, assez discret pour passer complètement inaperçu. Le but
 *  n'est pas d'attirer l'attention mais qu'on sente la page se poser. */

main, .dashboard-content {
  animation: vaeron-page-in var(--duree-entree) var(--courbe) both;
}

.page-head {
  animation: vaeron-fade-up 420ms var(--courbe) both;
}

.form-section,
.stat-card, .module-card, .server-card, .feature-card,
.pricing-card, .plugin-card, .role-card,
.alert, .table, .bk-card, .bk-opt {
  animation: vaeron-fade-up 380ms var(--courbe) both;
}

/* Décalage entre voisins : l'œil suit l'arrivée au lieu de subir un bloc
 * entier d'un coup. Plafonné au huitième — au-delà l'attente devient
 * perceptible et l'effet se retourne contre lui. */
.form-section:nth-child(1),  .stat-card:nth-child(1),  .module-card:nth-child(1),
.server-card:nth-child(1),   .bk-card:nth-child(1),    .bk-opt:nth-child(1)   { animation-delay: 0ms; }
.form-section:nth-child(2),  .stat-card:nth-child(2),  .module-card:nth-child(2),
.server-card:nth-child(2),   .bk-card:nth-child(2),    .bk-opt:nth-child(2)   { animation-delay: 50ms; }
.form-section:nth-child(3),  .stat-card:nth-child(3),  .module-card:nth-child(3),
.server-card:nth-child(3),   .bk-card:nth-child(3),    .bk-opt:nth-child(3)   { animation-delay: 100ms; }
.form-section:nth-child(4),  .stat-card:nth-child(4),  .module-card:nth-child(4),
.server-card:nth-child(4),   .bk-card:nth-child(4),    .bk-opt:nth-child(4)   { animation-delay: 150ms; }
.form-section:nth-child(5),  .stat-card:nth-child(5),  .module-card:nth-child(5),
.server-card:nth-child(5),   .bk-card:nth-child(5),    .bk-opt:nth-child(5)   { animation-delay: 200ms; }
.form-section:nth-child(6),  .stat-card:nth-child(6),  .module-card:nth-child(6),
.server-card:nth-child(6),   .bk-card:nth-child(6),    .bk-opt:nth-child(6)   { animation-delay: 250ms; }
.form-section:nth-child(n+7), .stat-card:nth-child(n+7), .module-card:nth-child(n+7),
.server-card:nth-child(n+7), .bk-card:nth-child(n+7),  .bk-opt:nth-child(n+7) { animation-delay: 300ms; }

/* La barre latérale entre par la gauche, ses entrées se posent ensuite. */
.dashboard-sidebar, .sidebar {
  animation: vaeron-slide-in-left 380ms var(--courbe) both;
}

/* --- Survol : les éléments cliquables doivent se soulever ---------------- */

.stat-card:hover, .module-card:hover, .server-card:hover,
.feature-card:hover, .pricing-card:hover, .plugin-card:hover,
.role-card:hover, .bk-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  box-shadow: var(--shadow);
}

.sidebar-nav a, .nav-links a {
  transition: color var(--duree) var(--courbe),
              background-color var(--duree) var(--courbe),
              padding-left var(--duree) var(--courbe);
}
.sidebar-nav a:hover, .nav-links a:hover { padding-left: 4px; }

/* --- Retours visuels d'état ---------------------------------------------- */

/* Un texte de statut qui change doit attirer l'œil une fois, pas clignoter. */
#optionsStatut:not(:empty),
#copyStatus:not(:empty),
#createStatus:not(:empty) {
  animation: vaeron-fade-up var(--duree) var(--courbe) both;
}

/* --- Respect du réglage système ------------------------------------------ *
 *
 *  Dernier bloc de la feuille, et c'est délibéré : il neutralise TOUT ce qui
 *  précède, y compris les animations déclarées plus haut dans le fichier.
 *  Les trois blocs prefers-reduced-motion d'origine ne couvraient qu'une
 *  partie des règles ; celui-ci ne laisse rien passer.
 *
 *  Le mouvement n'est pas qu'une question de goût : il déclenche des nausées
 *  et des migraines chez une partie des utilisateurs. Quand le système le
 *  signale, on obéit. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover, button:hover, .btn:active, button:active { transform: none !important; }
}

/* ==========================================================================
 *  GRAPHIQUES — courbes et barres (voir public/js/graphiques.js)
 * ==========================================================================
 *  Le texte ne porte jamais la couleur d'une série : les valeurs et les
 *  libellés restent en encre neutre, une pastille colorée à côté porte
 *  l'identité. Sans ça, un daltonien lit un chiffre dont il ne peut pas
 *  déduire à quelle courbe il appartient.
 * ========================================================================== */

.gr-hote { position: relative; width: 100%; }
.gr-svg  { width: 100%; height: auto; display: block; overflow: visible; }
.gr-tick { fill: var(--text-dim); font-size: 11px; font-family: inherit; }
.gr-vide { color: var(--text-muted); font-size: 14px; padding: 24px 0; text-align: center; }

.gr-puce {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  margin-right: 6px; vertical-align: -1px;
}

.gr-legende {
  display: flex; gap: 18px; flex-wrap: wrap;
  margin-top: 10px; font-size: 13px; color: var(--text-muted);
}

.gr-infobulle {
  position: absolute; top: 0; left: 0; pointer-events: none; opacity: 0;
  background: var(--bg-softer); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 10px;
  font-size: 13px; color: var(--text); box-shadow: var(--shadow);
  transition: opacity var(--duree-rapide) var(--courbe); white-space: nowrap; z-index: 5;
}
.gr-infobulle .gr-date { color: var(--text-muted); font-size: 12px; margin-bottom: 4px; }

/* --- Barres horizontales -------------------------------------------------- */

.gr-barres { display: flex; flex-direction: column; gap: 8px; }

.gr-barre {
  display: grid; grid-template-columns: minmax(90px, 30%) 1fr auto;
  gap: 12px; align-items: center;
}
.gr-barre-nom {
  font-size: 13px; color: var(--text); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.gr-barre-piste {
  height: 14px;                       /* mark fin : on ne remplit pas la ligne */
  background: var(--bg-softer); border-radius: 4px; overflow: hidden;
}
.gr-barre-fill {
  display: block; height: 100%; background: var(--accent);
  /* Bout arrondi côté valeur, carré contre la ligne de base. */
  border-radius: 0 4px 4px 0;
  transition: width 420ms var(--courbe);
}
.gr-barre:hover .gr-barre-fill { background: var(--accent-hover); }
.gr-barre-val {
  font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums;
  min-width: 3.5ch; text-align: right;
}

/* --- Tuiles de synthèse --------------------------------------------------- */

.gr-tuiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px; margin-bottom: 20px;
}
.gr-tuile {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px;
}
.gr-tuile-label { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.gr-tuile-valeur {
  font-size: 28px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.gr-tuile-detail { font-size: 12px; color: var(--text-dim); margin-top: 2px; }

/* --- Panneau Staff : les pages utilisent .container, pas .dashboard-content -
 *
 *  Les vues du panneau ont été écrites sans barre latérale et centrent leur
 *  contenu avec .container. Maintenant qu'elles vivent dans .dashboard-layout,
 *  ce centrage les décalerait vers la droite : on le neutralise seulement dans
 *  ce contexte, sans toucher aux pages publiques qui l'utilisent aussi. */

.dashboard-layout > main.container {
  flex: 1;
  min-width: 0;              /* sans ça un tableau large déborde de la grille */
  margin: 0;
  max-width: none;
  padding: 24px 28px;
  animation: vaeron-page-in var(--duree-entree) var(--courbe) both;
}

/* ======= EN-TÊTE AVEC ACTIONS =======
 *
 * Le titre à gauche, l'état et le bouton à droite. En dessous de 640 px la
 * ligne se casse : sur un écran étroit, un titre et un bouton côte à côte se
 * disputent la largeur et le titre se retrouve haché sur trois lignes.
 */
.entete-ligne {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.entete-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.entete-fraicheur {
  font-size: 0.85rem;
  white-space: nowrap;
}
@media (max-width: 640px) {
  .entete-ligne { flex-direction: column; }
  .entete-actions { width: 100%; justify-content: space-between; }
}

/* ============================================================
 *  TICKETS — éditeur de catégories et réglages par formule
 * ============================================================
 *  Le principe visuel : ce qui est fermé reste LISIBLE. Un bloc hors formule
 *  est atténué et barré d'un cadenas, pas retiré de la page — sinon personne
 *  ne peut savoir ce qu'il obtiendrait en changeant de formule, et un réglage
 *  dont on a entendu parler semble simplement ne pas exister.
 *
 *  L'atténuation reste au-dessus du seuil de lisibilité (0.55, pas 0.3) :
 *  un texte qu'on doit deviner ne remplit pas cette fonction.
 */

/* Le bandeau de tête : ce que la formule du serveur ouvre, d'un coup d'œil. */
.tk-formule { border-left: 3px solid var(--accent); }

.tk-formule-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
}

.tk-capacites {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px; margin-top: 12px;
}
.tk-capacite {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: rgba(255, 255, 255, 0.02);
  font-size: 0.88rem;
}
.tk-capacite-etat { flex-shrink: 0; }
.tk-capacite-nom { flex: 1; }
.tk-capacite.tk-oui { border-color: rgba(61, 220, 151, 0.35); }
.tk-capacite.tk-oui .tk-capacite-etat { color: #3ddc97; }
.tk-capacite.tk-non { opacity: 0.62; }
.tk-capacite-formule {
  font-size: 0.75rem; padding: 2px 8px; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.18); color: var(--accent-lisible); white-space: nowrap;
}

/* ---- le cadenas ---- */
/*  (Definition de .tk-verrou retiree le 19/09/2026 : elle etait ecrite
    DEUX fois, a l'identique, et la seconde gagnait. Deux copies d'une
    meme regle, c'est une correction sur deux qui ne se voit pas.) */


/* ---- blocs de réglages ---- */
.tk-section {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 14px; margin-top: 12px; background: rgba(255, 255, 255, 0.015);
}
.tk-section-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.tk-section-tete h4 { margin: 0; font-size: 0.98rem; }
.tk-section-corps { margin-top: 10px; }
.tk-section.tk-ferme { border-style: dashed; }
.tk-section.tk-ferme .tk-section-corps { opacity: 0.55; }
.form-field.tk-ferme { opacity: 0.62; }

.tk-aide { color: var(--text-muted); font-size: 0.82rem; margin: 4px 0 0; }
.tk-etiquette { display: block; font-size: 0.85rem; margin-bottom: 4px; }
.tk-apercu {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem; color: var(--text-muted); margin: 6px 0 0;
}

.tk-champ { display: block; margin-top: 10px; }
.tk-champ input[type="text"], .tk-champ input[type="number"],
.tk-champ select, .tk-champ textarea { width: 100%; }
.tk-champ-case { display: flex; align-items: center; gap: 8px; margin-top: 26px; }
.tk-champ-case input { width: auto; }

.tk-ligne-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tk-ligne-4 { display: grid; grid-template-columns: 90px 1.4fr 1fr auto; gap: 12px; align-items: end; }
@media (max-width: 760px) {
  .tk-ligne-2, .tk-ligne-4 { grid-template-columns: 1fr; }
}

/* ---- listes de rôles à cocher ----
 * Des cases plutôt qu'un <select multiple> : la sélection multiple native
 * exige un Ctrl+clic que rien n'annonce, et un clic simple efface tout.
 */
.tk-cases {
  max-height: 150px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 8px; padding: 6px;
  background: rgba(0, 0, 0, 0.18);
}
.tk-case {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: 6px; font-size: 0.88rem; cursor: pointer;
}
.tk-case:hover { background: rgba(255, 255, 255, 0.05); }
.tk-case input { width: auto; margin: 0; }
.tk-pastille {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-muted); flex-shrink: 0;
}

/* ---- une catégorie ---- */
.tk-liste { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.tk-cat {
  border: 1px solid var(--border); border-radius: 10px;
  background: rgba(255, 255, 255, 0.03); overflow: hidden;
}
.tk-cat[open] { border-color: var(--accent-dim); }
.tk-cat-tete {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; cursor: pointer; list-style: none; user-select: none;
}
.tk-cat-tete::-webkit-details-marker { display: none; }
.tk-cat-tete::before {
  content: '▸'; color: var(--text-muted); transition: transform 160ms var(--courbe);
}
.tk-cat[open] > .tk-cat-tete::before { transform: rotate(90deg); }
.tk-cat-tete:hover { background: rgba(255, 255, 255, 0.04); }
.tk-cat-titre { flex: 1; font-weight: 600; }
.tk-tag-off { background: rgba(255, 255, 255, 0.1); color: var(--text-muted); }
.tk-cat-corps { padding: 0 14px 14px; }
.tk-cat-pied { margin-top: 14px; text-align: right; }
/*  #ef4444 sur une carte : 4.07:1, sous la norme. Le jeton, lui, est
 *  calibré pour ce fond. */
.tk-supprimer { color: var(--danger); }
.tk-supprimer:hover { background: rgba(239, 68, 68, 0.12); }

/* ---- questions du formulaire ---- */
.tk-questions { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.tk-question {
  display: grid; grid-template-columns: 1.6fr 1.4fr auto auto auto;
  gap: 8px; align-items: center;
}
@media (max-width: 760px) { .tk-question { grid-template-columns: 1fr 1fr; } }
.tk-mini { display: flex; align-items: center; gap: 5px; font-size: 0.8rem; white-space: nowrap; }
.tk-mini input { width: auto; margin: 0; }

/* ---- horaires ---- */
.tk-jour {
  display: flex; align-items: center; gap: 12px;
  padding: 7px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.tk-jour:last-of-type { border-bottom: none; }
.tk-jour-nom { width: 100px; flex-shrink: 0; font-size: 0.88rem; }
.tk-jour-plages { display: flex; gap: 10px; flex-wrap: wrap; flex: 1; align-items: center; }
.tk-plage {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 6px; border: 1px solid var(--border); border-radius: 8px;
}
.tk-plage input[type="time"] { width: auto; padding: 4px 6px; }
/* Une fin avant le début : signalée tout de suite, pas écartée en silence
 * à l'enregistrement. */
.tk-plage-fausse { border-color: #ef4444; }
.tk-fleche { color: var(--text-muted); }

#categories-compteur.tk-plein { background: rgba(217, 89, 38, 0.2); color: #ffab7d; }

/* ============================================================
 *  TARIFS — la longue liste, repliée
 * ============================================================
 *  Cent quinze lignes par carte : les boutons d'achat se retrouvaient à des
 *  écrans de défilement, et on ne voyait jamais deux formules en même temps,
 *  donc on ne pouvait pas les comparer.
 *
 *  C'est un <details> natif : ça se déplie SANS JavaScript. Une page de
 *  tarifs qui cache ses prix quand un script tombe est pire que la liste
 *  longue.
 */
.pricing-plus { margin-top: 4px; }
.pricing-plus > summary {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; list-style: none;
  padding: 8px 10px; border-radius: 8px;
  font-size: 0.86rem; color: var(--text-muted);
  border: 1px dashed var(--border);
}
/* Safari pose son propre triangle : on le retire pour ne pas en avoir deux. */
.pricing-plus > summary::-webkit-details-marker { display: none; }
.pricing-plus > summary:hover { color: var(--text); border-color: var(--accent-dim); }
.pricing-plus-fleche { transition: transform 0.15s ease; }
.pricing-plus[open] > summary > .pricing-plus-fleche { transform: rotate(180deg); }
/* Le repli s'ouvre sur cent lignes : on lui donne sa propre barre plutôt que
 * d'allonger la carte — sinon on a juste déplacé le problème. */
.pricing-plus-corps { max-height: 320px; overflow-y: auto; margin-top: 8px; }
.pricing-plus-titre {
  margin: 12px 0 2px; font-size: 0.78rem; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}
.pricing-plus-titre:first-child { margin-top: 0; }

/* ============================================================
 *  ÉDITEUR DE LIGNES — boutique, mur des messages, réputation
 * ============================================================
 *  Réutilise le socle des tickets (.tk-cat, .tk-champ, .tk-mini) : même
 *  grammaire visuelle, un seul endroit à corriger. Seule la grille des
 *  champs d'une ligne est propre à l'éditeur.
 *
 *  `auto-fit` plutôt qu'un nombre de colonnes fixe : la boutique en a cinq,
 *  la réputation deux. Écrire le nombre ici, c'est une règle par module —
 *  et une de plus à chaque module suivant.
 */
.ed-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px 14px;
  align-items: start;
}
.ed-grille .tk-champ { margin-top: 0; }
.ed-grille .tk-champ label { display: block; font-size: 0.85rem; margin-bottom: 4px; }
.ed-grille .tk-champ small { display: block; margin-top: 4px; font-size: 0.75rem; }
/* Un champ fermé par la formule est GRISÉ, jamais caché : on doit pouvoir
 * voir ce qu'on obtiendrait en changeant de formule. */
.ed-grille .tk-champ [disabled] { opacity: 0.55; cursor: not-allowed; }
/* Le compteur vire à l'orange au plafond — le même signal que sur les
 * catégories de tickets, mais sans être accroché à un seul identifiant. */
.tk-mini.tk-plein {
  padding: 2px 8px; border-radius: 999px;
  background: rgba(217, 89, 38, 0.2); color: #ffab7d;
}

/* ============================================================
 *  RÔLES EN LIBRE-SERVICE — éditeur de panneaux
 * ============================================================
 *  Réutilise le socle des tickets (.tk-*) : même grammaire visuelle pour les
 *  blocs, les cadenas et les listes à cocher. Seul ce qui est propre aux
 *  panneaux est défini ici.
 */

/* Le barème des limites : il doit se lire d'un coup d'œil, c'est son seul
 * intérêt. La ligne de la formule en cours est marquée. */
.rr-bareme {
  width: 100%; border-collapse: collapse; margin: 12px 0 8px;
  font-size: 0.9rem;
}
.rr-bareme th, .rr-bareme td {
  text-align: left; padding: 7px 12px; border-bottom: 1px solid var(--border);
}
.rr-bareme th { color: var(--text-muted); font-weight: 600; font-size: 0.82rem; }
.rr-bareme tr:last-child td { border-bottom: none; }
.rr-bareme .rr-moi { background: rgba(var(--accent-rgb), 0.1); }
.rr-bareme .rr-moi td { font-weight: 600; }

/* Un panneau replié */
.rr-panneau .tk-cat-titre { font-weight: 600; }
.rr-publie { background: rgba(61, 220, 151, 0.18); color: #3ddc97; }

/* Choix de l'affichage et du mode : des cartes, pas une liste déroulante.
 * Le texte d'explication est ce qui permet de choisir sans se tromper. */
.rr-choix {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
}
.rr-option {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
  cursor: pointer; background: rgba(255, 255, 255, 0.02);
  transition: border-color 160ms var(--courbe), background 160ms var(--courbe);
}
.rr-option:hover { border-color: var(--accent-dim); }
.rr-option input { margin-top: 3px; flex-shrink: 0; }
.rr-option input:checked ~ div .rr-option-nom { color: var(--accent-lisible); }
.rr-option:has(input:checked) { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.08); }
.rr-option-nom { font-weight: 600; font-size: 0.92rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rr-option-fermee { opacity: 0.62; cursor: not-allowed; }
.rr-option-fermee input { cursor: not-allowed; }

/* La grille des rôles : une ligne par rôle, colonnes alignées avec l'en-tête. */
.rr-entete, .rr-ligne {
  display: grid;
  grid-template-columns: 28px 70px minmax(140px, 1.4fr) minmax(120px, 1.2fr) minmax(120px, 1.2fr) 68px 68px 34px 34px;
  gap: 8px; align-items: center;
}
.rr-entete {
  font-size: 0.75rem; color: var(--text-muted);
  padding: 0 4px 6px; border-bottom: 1px solid var(--border); margin-bottom: 8px;
}
.rr-roles { display: flex; flex-direction: column; gap: 6px; }
.rr-ligne {
  padding: 4px; border-radius: 8px;
  transition: background 140ms var(--courbe);
}
.rr-ligne:hover { background: rgba(255, 255, 255, 0.03); }
.rr-rang { color: var(--text-muted); font-size: 0.8rem; text-align: center; }
.rr-emoji { text-align: center; font-size: 1.15em; }
.rr-ligne input, .rr-ligne select { width: 100%; min-width: 0; }
.rr-petit { text-align: center; }
/* Ces deux-là n'ont pas de style propre — elles servent de repère dans le
 * balisage et de point d'accroche pour l'adaptation aux petits écrans. */
.rr-libelle, .rr-desc { min-width: 0; }
.rr-fleche { padding: 4px 8px; }

/* Un champ obligatoire encore vide : signalé pendant la saisie, pas découvert
 * à l'enregistrement quand la ligne a déjà été écartée en silence. */
.rr-manque { border-color: #d95926 !important; }

.rr-roles-pied {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 10px; flex-wrap: wrap;
}
.rr-plein { color: #ffab7d; }

.rr-actions {
  display: flex; align-items: center; gap: 10px;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.rr-actions .tk-aide { flex: 1; margin: 0; }

@media (max-width: 900px) {
  .rr-entete { display: none; }
  .rr-ligne {
    grid-template-columns: 44px 1fr 1fr 34px;
    padding: 10px; border: 1px solid var(--border); border-radius: 8px;
  }
  .rr-ligne .rr-role { grid-column: span 3; }
}

/* L'état d'un panneau, annoncé dès son ouverture. Un panneau configuré mais
 * jamais publié n'existe pas sur Discord — ça doit se voir sans chercher. */
.rr-etat {
  padding: 10px 12px; border-radius: 8px; margin-bottom: 14px;
  font-size: 0.88rem; line-height: 1.45;
}
.rr-etat-ok {
  background: rgba(61, 220, 151, 0.12); color: #7fe9c0;
  border: 1px solid rgba(61, 220, 151, 0.3);
}
.rr-etat-attente {
  background: rgba(217, 89, 38, 0.14); color: #ffab7d;
  border: 1px solid rgba(217, 89, 38, 0.35);
}
/* Un module éteint alors que des panneaux existent : le cas où tout paraît
 * en ordre et où rien ne marche. */
.rr-alerte-forte { border-left: 4px solid var(--danger, #ef4444); }

/* Deux boutons de fin de formulaire : celui qui publie est le principal,
 * parce que c'est ce que les gens veulent faire neuf fois sur dix. */
.rr-enregistrer { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rr-enregistrer .tk-aide { flex-basis: 100%; margin: 0; }

/* ==========================================================================
 *  L'ASSISTANCE EN DIRECT — la bulle en bas à droite
 * ==========================================================================
 *  Elle n'apparaît que sur le site de production. La couleur suit le monde
 *  comme tout le reste : elle est donc jaune, puisque la prod est jaune.
 *  Rien n'est écrit en dur ici — si la bulle passait un jour sur le site
 *  public, elle deviendrait violette toute seule.
 */
.assist {
  position: fixed; right: 18px; bottom: 18px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
}
.assist-bouton {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 48px; padding: 0 18px;
  border: none; border-radius: 999px; cursor: pointer;
  background: var(--accent-plein); color: var(--accent-texte);
  font-size: 0.95rem; font-weight: 600; font-family: inherit;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.assist-bouton:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45); }
.assist-bouton:focus-visible { outline: 3px solid rgba(var(--accent-rgb), 0.85); outline-offset: 3px; }
.assist-ouvert .assist-bouton-texte { display: none; }

.assist-panneau {
  width: 340px; max-width: calc(100vw - 36px);
  max-height: min(560px, calc(100vh - 120px));
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-card); color: var(--text);
  border: 1px solid var(--border, #262b3a); border-radius: 16px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
}
.assist-panneau[hidden] { display: none; }

.assist-tete {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border, #262b3a);
  background: rgba(var(--accent-rgb), 0.10);
}
.assist-fermer {
  background: none; border: none; cursor: pointer; font-family: inherit;
  color: var(--text-dim, #9aa3b8); font-size: 1.5rem; line-height: 1;
  /* 44 px : on vise un doigt, pas un curseur. Même règle que le menu du
     téléphone — une croix de 16 px se rate une fois sur trois. */
  min-width: 44px; min-height: 44px;
}
.assist-fermer:hover { color: var(--text); }

.assist-fil {
  flex: 1; overflow-y: auto; padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.assist-msg {
  padding: 10px 12px; border-radius: 12px;
  font-size: 0.9rem; line-height: 1.5;
  /* Une réponse peut contenir une commande longue ou un chemin : elle doit
     revenir à la ligne dans la bulle, pas l'élargir hors de l'écran. */
  overflow-wrap: anywhere;
}
.assist-msg-bot { background: var(--bg-softer); align-self: stretch; }
.assist-msg-moi {
  background: var(--accent-plein); color: var(--accent-texte);
  align-self: flex-end; max-width: 85%;
}
.assist-attente { opacity: 0.6; font-style: italic; }

.assist-lien, .assist-piste {
  display: block; margin-top: 8px; font-size: 0.85rem;
  color: var(--accent-lisible); text-decoration: none;
}
.assist-lien:hover { text-decoration: underline; }
.assist-piste { color: var(--text-dim, #9aa3b8); }
.assist-note {
  display: block; margin-top: 8px; font-size: 0.78rem;
  color: var(--warning, #fb923c);
}
.assist-action {
  display: block; margin-top: 10px; width: 100%; min-height: 40px;
  border: 1px solid var(--accent); border-radius: 10px; cursor: pointer;
  background: transparent; color: var(--accent-lisible);
  font-family: inherit; font-size: 0.85rem;
}
.assist-action:hover:not(:disabled) { background: rgba(var(--accent-rgb), 0.12); }
.assist-action:disabled { opacity: 0.5; cursor: default; }

/* Reformuler est le geste qu'on veut voir d'abord : il ne derange personne et
   il marche souvent. Il reste plus discret que le ticket — un cadre plein
   attirerait l'oeil au meme titre, alors que les deux n'ont pas le meme
   cout pour l'equipe. */
.assist-action-doux {
  border-color: var(--border, #262b3a);
  color: var(--text-dim, #9aa3b8);
}
.assist-action-doux:hover:not(:disabled) {
  border-color: var(--accent); color: var(--accent-lisible);
}

/* « Cette reponse ne me convient pas » : volontairement discret. Il doit etre
   trouvable sans concurrencer la reponse elle-meme — on ne pousse pas les
   gens a rejeter une reponse juste parce que le bouton est gros. */
.assist-avis {
  display: block; margin-top: 10px; padding: 0; width: auto;
  border: 0; background: none; cursor: pointer;
  color: var(--text-dim, #9aa3b8);
  font-family: inherit; font-size: 0.78rem; text-align: left;
  text-decoration: underline; text-underline-offset: 3px;
}
.assist-avis:hover:not(:disabled) { color: var(--accent-lisible); }
.assist-avis:disabled { opacity: 0.5; cursor: default; }

/* « Effacer » : discret, a cote de la croix. La conversation se garde d'une
   page a l'autre, il faut donc pouvoir la remettre a zero sans fermer
   l'onglet — mais ce n'est pas le geste courant, il ne doit pas sauter aux
   yeux plus que le contenu. */
.assist-effacer {
  margin-left: auto; margin-right: 6px; padding: 2px 8px;
  border: 0; border-radius: 8px; background: none; cursor: pointer;
  color: var(--text-dim, #9aa3b8);
  font-family: inherit; font-size: 0.75rem;
}
.assist-effacer:hover { color: var(--accent-lisible); background: rgba(var(--accent-rgb), 0.1); }

.assist-saisie {
  display: flex; gap: 8px; padding: 12px;
  border-top: 1px solid var(--border, #262b3a);
}
.assist-saisie input {
  flex: 1; min-width: 0; min-height: 40px; padding: 0 12px;
  border: 1px solid var(--border, #262b3a); border-radius: 10px;
  background: var(--bg, #12141d); color: var(--text);
  font-family: inherit; font-size: 0.9rem;
}
.assist-saisie input:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 1px;
}
.assist-saisie button {
  min-width: 44px; min-height: 40px;
  border: none; border-radius: 10px; cursor: pointer;
  background: var(--accent-plein); color: var(--accent-texte);
  font-family: inherit; font-size: 1.1rem;
}

/* Sur téléphone, la bulle prend toute la largeur : un panneau de 340 px sur
   un écran de 360 laisse deux marges de dix pixels et se lit mal. */
@media (max-width: 480px) {
  .assist { right: 10px; bottom: 10px; left: 10px; align-items: stretch; }
  .assist-panneau { width: auto; max-height: calc(100vh - 100px); }
  .assist-bouton { align-self: flex-end; }
}

/* Le texte réservé aux lecteurs d'écran. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
 *  LES TUTOS VIDÉO
 * ========================================================================== */
.tutos-grille {
  display: grid; gap: 18px;
  /* auto-fill plutôt qu'auto-fit : avec une seule vidéo, auto-fit l'étirerait
     sur toute la largeur et le lecteur ferait 900 px de haut. */
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.tuto {
  border: 1px solid var(--border, #262b3a); border-radius: 14px;
  overflow: hidden; background: var(--bg-softer);
  display: flex; flex-direction: column;
}
/* Le cadre 16/9 : sans lui, l'iframe prend une hauteur arbitraire et la
   grille sautille au chargement de chaque vidéo. */
.tuto-video { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.tuto-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tuto-corps { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.tuto-corps h3 { margin: 0; font-size: 1rem; }
.tuto-corps p { margin: 0; font-size: 0.88rem; line-height: 1.5; }
.tuto-meta { font-size: 0.78rem; color: var(--text-dim, #9aa3b8); }
.tuto details summary { min-height: 32px; display: flex; align-items: center; }

/* ==========================================================================
 *  LE CODE À SIX CHIFFRES — voir public/js/champ-code.js
 * ==========================================================================
 *  LA VRAIE BOÎTE DE TEXTE EST TOUJOURS LÀ, posée par-dessus les cases, encre
 *  et curseur transparents. Les cases ne sont que du décor qui recopie ce
 *  qu'elle contient : c'est ce qui laisse marcher le collage, le remplissage
 *  automatique du code reçu, et les lecteurs d'écran.
 *
 *  Sans le script, rien de tout ça n'existe : `.cc-saisie` n'est jamais posé,
 *  le champ garde son apparence ordinaire, et la page est celle d'avant.
 * ========================================================================== */

.cc { position: relative; margin: 6px 0 2px; }

/*  LA RANGÉE S'ALIGNE SUR LE RESTE DU FORMULAIRE, elle n'est pas centrée.
 *
 *  PREMIÈRE VERSION, CENTRÉE, ET ÇA SE VOYAIT : le libellé au-dessus et le
 *  bouton en dessous sont calés à gauche ; six cases centrées au milieu
 *  donnaient une rangée qui flotte, décalée d'une quarantaine de pixels vers
 *  la droite. Ce n'est pas un choix esthétique contre un autre — dans une
 *  colonne alignée à gauche, un élément centré se lit comme une erreur de
 *  mise en page. Signalé sur une capture d'écran. */
.cc-rangee {
  display: flex; gap: 10px; justify-content: flex-start; flex-wrap: wrap;
  transition: opacity var(--duree) var(--courbe);
}

.cc-case {
  flex: 0 0 auto;
  width: 48px; height: 62px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-softer);
  /* LE PIVOT EST SOUS LA RANGÉE, ET C'EST TOUT LE GESTE. Les cartes tournent
     autour d'un point commun placé bien plus bas qu'elles : c'est ce qui fait
     une main de cartes qui s'ouvre plutôt que six carrés penchés chacun dans
     son coin. */
  transform-origin: 50% 320%;
  transition: border-color var(--duree-rapide) var(--courbe),
              background var(--duree-rapide) var(--courbe),
              box-shadow var(--duree-rapide) var(--courbe);
}

.cc-chiffre {
  font-size: 1.7rem; font-weight: 600; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.cc-case.cc-remplie { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }

/* La case courante porte le même anneau que n'importe quel champ au focus :
   le curseur est invisible, c'est elle qui dit où l'on écrit. */
.cc-case.cc-active {
  border-color: var(--accent);
  box-shadow: var(--anneau-focus);
}

/* La boîte de texte : présente, cliquable, mais transparente. On garde une
   encre transparente PLUTÔT QUE `opacity: 0` — un champ à opacité nulle est
   sauté par certains lecteurs d'écran et refuse le focus sur d'autres. */
/*  LE SÉLECTEUR EST VOLONTAIREMENT LOURD, et c'est une correction, pas un
 *  tic. `.cc-saisie` seul (0,1,0) perdait contre `.form-field input` et
 *  `.container input[type="text"]` (0,2,1) : le champ gardait le fond opaque
 *  et la bordure du site, et repeignait par-dessus les six cases. Vu sur une
 *  capture — la rangée était là, invisible, sous une grande boîte grise.
 *  On monte donc à la même arme. `!important` aurait marché aussi ; il aurait
 *  surtout caché la raison. */
.cc input.cc-saisie,
.cc-saisie {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; padding: 0; margin: 0;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  text-align: center;
  /* L'écart entre les chiffres n'a pas à coller aux cases : le texte est
     invisible. Il sert seulement à ce qu'une sélection au clavier tombe à peu
     près en face de la bonne case. */
  letter-spacing: 3rem;
  font-size: 1.7rem;
  cursor: text;
  border-radius: 0;
  box-shadow: none;
}
.cc input.cc-saisie:focus,
.cc-saisie:focus { outline: none; box-shadow: none; border: 0; background: transparent; }
/* La sélection resterait visible et baverait par-dessus le décor. */
.cc-saisie::selection { background: transparent; }

/* --- L'anneau de vérification ------------------------------------------- *
 *  Il n'apparaît qu'au moment de l'envoi, quand les cases se rassemblent.
 *  Il occupe un vrai aller-retour serveur : sans lui, on regarde un écran
 *  figé en se demandant si le clic a été pris. */
.cc-anneau {
  position: absolute; left: 50%; top: 50%;
  width: 46px; height: 46px; margin: -23px 0 0 -23px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-top-color: var(--accent);
  opacity: 0; pointer-events: none;
  transition: opacity var(--duree) var(--courbe);
}
.cc-verifie .cc-anneau { opacity: 1; animation: cc-tourne 720ms linear infinite; }
.cc-verifie .cc-saisie { cursor: default; }

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

/* Un code refusé se dit LÀ OÙ IL A ÉTÉ TAPÉ, pas seulement en haut de page :
   c'est là que les yeux sont au moment où l'on revient. */
.cc-refuse .cc-case { border-color: color-mix(in srgb, #ff5b7a 55%, var(--border)); }

/* --- Petits écrans -------------------------------------------------------- *
 *  Six cases à 48px plus les écarts dépassent la largeur d'un téléphone
 *  étroit. On rétrécit plutôt que de laisser la rangée déborder — une case
 *  hors de l'écran est une case qu'on ne peut pas remplir. */
@media (max-width: 420px) {
  .cc-case { width: 40px; height: 54px; border-radius: 10px; }
  .cc-chiffre { font-size: 1.4rem; }
  .cc-rangee { gap: 7px; }
  .cc-saisie { letter-spacing: 2.4rem; font-size: 1.4rem; }
}

/* ==========================================================================
 *  LE DÉCOR DE FOND — demandé le 27/08 : « tout noir c'est joli mais… »
 * ==========================================================================
 *  CE QU'IL FAUT SAVOIR AVANT DE TOUCHER À CE BLOC.
 *
 *  1. IL SUIT LE MONDE, ET CE N'EST PAS DÉCORATIF.
 *     Violet sur le site public, ambre sur la production. La teinte est le
 *     seul repère qui fonctionne SANS LIRE : c'est elle qui dit, avant le
 *     premier clic, qu'on manipule les vraies données. Un décor identique
 *     dans les deux mondes détruirait ce repère — il ne rendrait pas la prod
 *     moins reconnaissable, il ajouterait un grand aplat commun qui la ferait
 *     ressembler au site public. Le coût : une suppression de masse lancée
 *     dans le mauvais monde parce que l'œil a cru reconnaître le public.
 *     D'où `rgba(var(--fond-teinte), …)` : la teinte bascule avec le monde
 *     comme tout le reste du site, sur `html[data-monde="prod"]`.
 *
 *  2. IL RESTE PLUS SOMBRE QU'UNE CARTE.
 *     Ce site est un tableau de bord : des tableaux, des formulaires, des
 *     étiquettes. Le point le PLUS CLAIR du décor est réglé pour rester au
 *     niveau de --bg-card (#1a1e2b), la surface sur laquelle le site pose
 *     déjà ses libellés en --text-muted. Autrement dit : un texte posé sur le
 *     décor n'est jamais moins lisible qu'un texte posé sur une carte, et ce
 *     seuil-là est déjà celui que le site assume partout. Un fond qui rend
 *     une étiquette de formulaire difficile à lire est un fond raté, même
 *     s'il est beau. verif-fond.js mesure ce point le plus clair EN PIXELS,
 *     dans un vrai navigateur, et exige 4,5:1 pour --text-muted.
 *
 *  3. IL NE COÛTE RIEN, ET C'EST UNE CONTRAINTE, PAS UN LUXE.
 *     Un seul pseudo-élément, six couches de dégradés, zéro image, zéro nœud
 *     ajouté au DOM, zéro JavaScript. Même vocabulaire et même technique que
 *     le ciel du règlement (.rg-ciel) : la poussière est faite de deux
 *     dégradés RÉPÉTÉS, pas de trois cents éléments. Le tableau de bord doit
 *     s'ouvrir sur un vieux téléphone ; trois cents nœuds pour un décor que
 *     personne ne regarde, c'est le faire ramer pour rien.
 *
 *  4. IL BOUGE — DEPUIS LE 29/08 — ET LA RAISON DE NE PAS BOUGER RESTE VRAIE.
 *     Ce bloc disait « il ne bouge pas, volontairement », et l'argument était
 *     juste : ce n'est pas une page d'accueil qu'on traverse, c'est un outil
 *     qu'on regarde des heures, et un mouvement continu en fond d'écran est
 *     exactement ce qui déclenche nausées et migraines.
 *
 *     La demande du 29/08 — « le fond n'est pas terrible, il faudrait des
 *     animations, en restant dans la thématique » — ne rend pas cet argument
 *     faux. Elle oblige à le SATISFAIRE AUTREMENT, et c'est ce que fait la
 *     couche `body::after` plus bas :
 *
 *       · EN CSS, JAMAIS EN JAVASCRIPT. C'était le coeur de l'objection :
 *         `@media (prefers-reduced-motion)` ne peut RIEN contre un
 *         `element.animate()`. Une animation CSS, elle, s'éteint pour de bon
 *         — et la feuille porte déjà une coupure générale sur `*::after`.
 *         Qui demande moins de mouvement retrouve exactement le décor d'hier.
 *
 *       · UN CYCLE DE 96 SECONDES, quelques pour cent d'amplitude. Ce qui
 *         donne la nausée, c'est le mouvement qu'on PERÇOIT du coin de l'oeil
 *         pendant qu'on lit. À cette vitesse-là — de l'ordre du tiers de pixel
 *         par seconde — on ne voit pas bouger : on trouve juste que l'écran
 *         n'est plus le même qu'il y a deux minutes. C'est la différence entre
 *         une aurore et un économiseur d'écran.
 *
 *       · LA COUCHE DU BAS NE BOUGE PAS. Le sol, la poussière et les trois
 *         halos restent posés (`body::before`). Une étoile qui dérive, l'oeil
 *         l'attrape ; c'est le point net qui trahit le mouvement, pas la
 *         brume. Seule la brume dérive.
 *
 *       · LE PLAFOND DE CLARTÉ TIENT À CHAQUE INSTANT DU CYCLE, pas seulement
 *         à l'arrêt. C'est la seule chose qu'une animation pouvait casser en
 *         silence : un décor conforme au repos et trop clair à la seconde 40.
 *         verif-fond.js parcourt donc le cycle et mesure au pire moment.
 *
 *  5. IL NE PASSE JAMAIS DEVANT.
 *     `z-index: -1` le range sous TOUT contenu positionné ou non — donc sous
 *     les cadres modaux (z-index 1000), sous la barre du haut (100) et sous
 *     les messages. `pointer-events: none` fait le reste : un décor qui
 *     avalerait un clic transformerait un bouton en décoration.
 */

/*  Les jetons du décor. Ils sont à part de --accent : l'accent sert au texte
    et aux bordures, où il doit être FRANC ; ici on veut une brume. Les
    séparer permet de rendre le décor plus discret sans délaver les boutons. */
:root {
  /* La teinte du monde public : le violet de la marque. */
  --fond-teinte: 124, 92, 255;
  /*  TROIS FORCES DIFFÉRENTES, ET C'EST VOULU : un halo haut plus présent,
      un halo latéral en rappel, un halo bas qui empêche le bas de l'écran de
      retomber au noir plat quand on a défilé. */
/*  REMONTÉ LE 27/08, APRÈS AVOIR REGARDÉ LA PAGE.
 *
 *  Les premières valeurs (0,115 / 0,085 / 0,090 côté public) avaient été
 *  choisies pour rester SOUS la clarté d'une carte — un raisonnement juste sur
 *  le papier, et un décor invisible à l'écran : sur une page de tableau de
 *  bord, qui est pleine de cartes sombres, on ne voyait rien. La demande était
 *  « tout noir c'est joli mais... », et la page restait toute noire.
 *
 *  Cent quatre-vingt-quatorze mesures disaient « le décor est peint ». Elles
 *  avaient raison, et elles répondaient à côté de la question — « est-il
 *  peint » n'est pas « le voit-on ». C'est une capture d'écran qui a tranché.
 *
 *  Les valeurs ci-dessous restent bornées par la même règle : le point le plus
 *  clair du décor ne dépasse pas la clarté d'une carte, sinon un texte posé
 *  dessus deviendrait moins lisible qu'ailleurs. La vérification le mesure
 *  page par page, dans les deux mondes. */
/*  BAISSÉS LE 29/08, PARCE QUE L'AURORE S'AJOUTE PAR-DESSUS.
 *  Ces trois halos et l'aurore peignent au même endroit : les garder à leur
 *  valeur d'hier aurait fait passer le point le plus clair AU-DESSUS d'une
 *  carte, et c'est la seule règle que ce décor n'a pas le droit d'enfreindre
 *  — un fond plus clair qu'une carte rend les étiquettes de formulaire moins
 *  lisibles que partout ailleurs. Ils gardent leur rôle propre : c'est eux, et
 *  pas l'aurore, qui garantissent que le bas de l'écran reste teinté quel que
 *  soit le moment du cycle. */
  --fond-halo-haut: 0.058;
  --fond-halo-cote: 0.050;
  --fond-halo-bas: 0.090;
  /*  LES TROIS ARRÊTS DU CIEL. Ils sont des couleurs pleines et non des
      opacités : un dégradé de ciel se règle en regardant les couleurs, pas en
      empilant des transparences dont on ne sait plus laquelle fait quoi.
      --ciel-bas est PLUS SOMBRE que --bg : c'est le sol. */
  --ciel-haut: #16172a;
  --ciel-milieu: #111320;
  --ciel-bas: #0a0b0f;
  --fond-voile: #121420;
  /* La poussière : des points, pas des étoiles. Assez pour que le fond ait
     du grain, assez peu pour qu'on ne les compte pas. */
  --fond-poussiere: 210, 200, 255;
  --fond-poussiere-force: 0.10;
  /*  L'AURORE — la couche qui dérive (voir body::after).
   *  DEUX teintes et pas une : une brume d'une seule couleur se lit comme un
   *  filtre posé sur l'écran, deux teintes qui se croisent se lisent comme de
   *  la profondeur. Le bleu est celui de la formule Premium (--premium-2) :
   *  le site n'apprend pas une couleur de plus. */
  --fond-aurore-2: 122, 184, 255;
  --fond-aurore-force: 0.066;
  /* La trame : voir body::before. Elle suit le monde comme le reste — les
     hachures sont peintes dans la couleur de la poussière, pas en blanc. */
  --fond-trame: 0.026;
  /*  LES ÉTOILES. Leur opacité est un jeton parce qu'elle doit baisser en
      production : l'ambre est trois fois plus lumineux que le violet à
      opacité égale, et un ciel deux fois plus clair là où on lit des chiffres
      réels, c'est exactement ce qu'on ne veut pas. */
  --ciel-etoiles-loin: 0.46;
  --ciel-etoiles-moyen: 0.52;
  --ciel-etoiles-pres: 0.58;
  --ciel-filante-force: 0.9;
  /*  LA FORCE DES FORMES DE FÊTE, à part de celle des étoiles : une forme de
      dix pixels ne pèse pas comme un point de un. Ce qui décide de la
      lisibilité d'un libellé, c'est la clarté MOYENNE sous lui — donc la
      surface autant que l'opacité. Réglée fête par fête, dans les deux
      mondes, sur des fenêtres de la taille d'une étiquette. */
  --fete-force: 0.16;
  --fete-force-2: 0.12;
  /* Les halos du hero — voir .orb. */
  --orbe-force: 0.30;
}

/*  LA PRODUCTION EST AMBRE — et l'ambre est BEAUCOUP plus lumineux que le
    violet à opacité égale (#facc15 pèse 12,34:1 sur le fond, #7c5cff 4,35:1).
    Reprendre les mêmes valeurs donnerait un décor deux fois plus clair côté
    prod, et c'est précisément là qu'on lit des chiffres réels. On baisse donc
    les forces d'à peu près moitié : à l'œil les deux décors ont le même
    poids, et la mesure de contraste tient des deux côtés. */
html[data-monde="prod"] {
  --fond-teinte: 250, 204, 21;
  /*  Deux fois plus faible qu'en public, et ce n'est pas un oubli : le jaune
   *  de la production pèse 12,3:1 là où le violet pèse 4,35:1. À opacité
   *  égale, la production serait deux fois plus claire que le site public. */
  --fond-halo-haut: 0.038;
  --fond-halo-cote: 0.034;
  --fond-halo-bas: 0.048;
  --ciel-haut: #221d0c;
  --ciel-milieu: #16140b;
  --ciel-bas: #0a0a07;
  --fond-voile: #16150f;
  --fond-poussiere: 255, 238, 190;
  --fond-poussiere-force: 0.09;
  /*  Même raison qu'au-dessus : l'ambre pèse trois fois le violet à opacité
   *  égale. La seconde teinte reste chaude — un bleu dans une aurore ambre
   *  ferait ressembler la production au site public, et c'est précisément le
   *  repère qu'on ne veut pas brouiller. */
  --fond-aurore-2: 255, 176, 74;
  --fond-aurore-force: 0.040;
  --fond-trame: 0.017;
  --ciel-etoiles-loin: 0.42;
  --ciel-etoiles-moyen: 0.40;
  --ciel-etoiles-pres: 0.55;
  --ciel-filante-force: 0.75;
  /*  Deux tiers de la valeur du public, pour la raison habituelle : l'ambre
      de la production pèse trois fois le violet à opacité égale. Sans ça, la
      Saint-Valentin en production tombait à 4,97:1 — mesuré, pas estimé. */
  --fete-force: 0.11;
  --fete-force-2: 0.085;
  --orbe-force: 0.20;
}

/*  LE CORPS DE PAGE DEVIENT TRANSPARENT.
 *  Sans cette ligne, rien de ce qui suit ne se voit : `html, body` peint
 *  --bg en opaque, et le fond de `body` est peint APRÈS la couche z-index
 *  négative — il recouvrirait donc intégralement le décor. Le sol sombre
 *  reste posé par `html`, qui le propage au canevas : le rebond élastique en
 *  haut et en bas de page reste noir, il ne devient pas blanc. */
body { background: transparent; }

body::before {
  content: '';
  /*  FIXE, PAS ABSOLU. Le décor doit couvrir l'écran, pas la page : sur une
      page de module qui fait six écrans de haut, un décor `absolute` serait
      étiré sur toute la hauteur et l'on ne verrait qu'une tranche de dégradé
      — arrivé en bas, le fond serait redevenu noir plat. */
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--bg);
  /* ======================================================================
   *  LE DÉGRADÉ DE CIEL — refait le 29/08
   * ======================================================================
   *  « Fais un dégradé de ciel ou autre, un truc clean, ils n'aiment pas ce
   *   fond d'écran. »
   *
   *  CE QUI FAISAIT SALE, ET CE N'ÉTAIT PAS LA COULEUR. Deux choses :
   *
   *   1. UNE TRAME DE HACHURES. Elle avait été posée pour donner du grain à
   *      la bande morte du milieu. Vu de près c'est du grain ; vu de loin,
   *      sur un grand écran, c'est un quadrillage régulier posé sur toute la
   *      page — et un motif régulier sur un fond, ça ne se lit pas comme de
   *      la matière, ça se lit comme un écran sale. Elle est retirée.
   *
   *   2. TROIS HALOS SANS DIRECTION. Un en haut à gauche, un en haut à
   *      droite, un en bas au milieu : trois taches qui se contredisent. Un
   *      ciel a un HAUT et un BAS. C'est cette verticale-là qui manquait, et
   *      c'est elle qui fait « propre » — pas la quantité de lumière.
   *
   *  CE QUI REMPLACE : un vrai dégradé vertical, de l'indigo profond en haut
   *  vers le noir en bas, avec une lueur d'horizon basse. La bande la plus
   *  claire est TOUT EN HAUT, là où il n'y a que la barre de navigation ;
   *  elle s'éteint avant le premier tiers, c'est-à-dire avant le premier
   *  tableau. C'est ce qui permet d'avoir un ciel visible sans toucher au
   *  plafond de lisibilité : on met la lumière là où personne ne lit.
   * ====================================================================== */
  background-image:
    /*  LA POUSSIÈRE FIXE. Les étoiles qui BOUGENT sont dans .ciel ; celles-ci
        ne bougent pas, et c'est exprès : un fond entièrement mobile ne donne
        à l'oeil aucun repère immobile pour mesurer le mouvement des autres.
        Deux trames premières entre elles (173 et 251 px) pour que le motif
        combiné ne se répète qu'au bout de plusieurs écrans. */
    radial-gradient(1px 1px at 34px 51px,
      rgba(var(--fond-poussiere), var(--fond-poussiere-force)), transparent),
    radial-gradient(1.4px 1.4px at 148px 97px,
      rgba(var(--fond-poussiere), calc(var(--fond-poussiere-force) * 0.7)), transparent),
    /*  LA LUEUR D'HORIZON. Très large, très basse, très faible : c'est elle
        qui empêche le bas de l'écran de retomber au noir plat quand on a
        défilé. Un ciel dont le bas est noir n'est pas un ciel, c'est un mur. */
    radial-gradient(150% 42% at 50% 118%,
      rgba(var(--fond-teinte), var(--fond-halo-bas)), transparent 70%),
    /*  DEUX LUEURS HAUTES, DÉCENTRÉES. Elles donnent au ciel son épaisseur —
        un dégradé purement vertical est propre mais plat. Décentrées exprès :
        une lueur centrée se lit comme une tache, une lueur décentrée se lit
        comme une lumière qui vient de quelque part. */
    radial-gradient(1100px 620px at 12% -18%,
      rgba(var(--fond-teinte), var(--fond-halo-haut)), transparent 62%),
    radial-gradient(900px 520px at 92% -10%,
      rgba(var(--fond-aurore-2), var(--fond-halo-cote)), transparent 58%),
    /*  LE CIEL LUI-MÊME. Quatre arrêts, et chacun a une raison :
          0%    l'indigo le plus profond, derrière la barre du haut ;
          22%   il s'éteint déjà : c'est là que commence le contenu ;
          55%   la couleur de fond du site, nette — la bande des tableaux ;
          100%  un cran PLUS SOMBRE que --bg, pour que le bas ait un sol.
        Descendre sous --bg en bas plutôt que remonter en haut, c'est ce qui
        donne de la profondeur sans coûter un point de contraste. */
    linear-gradient(180deg,
      var(--ciel-haut) 0%,
      var(--ciel-milieu) 22%,
      var(--bg) 55%,
      var(--ciel-bas) 100%);
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat, no-repeat;
  background-size: 173px 173px, 251px 251px, auto, auto, auto, auto;
}

/* ==========================================================================
 *  LE CIEL — le décor animé (voir views/partials/ciel.ejs)
 * ==========================================================================
 *  DEMANDÉ LE 29/08, DEUX FOIS. La première version dérivait déjà, mais si
 *  lentement et de si peu que la réponse a été « je ne vois rien qui a
 *  changé ». C'est un verdict sur le résultat, pas sur l'intention : un décor
 *  qu'on doit prouver avec un chronomètre n'est pas un décor animé.
 *
 *  CE QUI CLOCHAIT VRAIMENT, ET CE N'ÉTAIT PAS LA VITESSE. Tout tenait sur
 *  deux pseudo-éléments, donc sur deux `transform` — et un élément n'en porte
 *  qu'un. Tout ce qui bougeait bougeait donc EN BLOC. Or un décor qui glisse
 *  d'un seul tenant ne se lit pas comme de la profondeur, il se lit comme une
 *  image qu'on fait coulisser : l'oeil ne trouve aucun repère fixe pour
 *  mesurer le mouvement, et conclut qu'il n'y en a pas. C'est la PARALLAXE qui
 *  se voit — deux plans qui avancent à des vitesses différentes — pas la
 *  vitesse absolue.
 *
 *  D'OÙ QUATRE PLANS, ET C'EST TOUTE LA DIFFÉRENCE :
 *    · l'aurore, trois nappes de couleur qui se déforment (46 s) ;
 *    · les étoiles LOINTAINES, petites et pâles, qui dérivent lentement (150 s) ;
 *    · les étoiles PROCHES, plus grosses, deux fois plus vite (78 s), qui
 *      scintillent ;
 *    · les filantes, deux traits qui traversent toutes les 17 et 23 secondes.
 *
 *  LES FILANTES SONT LÀ POUR UNE RAISON PRÉCISE. Un mouvement continu, on
 *  cesse de le voir au bout d'une minute — c'est ainsi que l'oeil fonctionne.
 *  Un ÉVÉNEMENT, non : il se remarque à chaque fois. Deux traits qui passent
 *  de temps en temps donnent au décor la seule chose qu'aucune dérive ne peut
 *  donner — un instant où il se passe quelque chose. Et parce que c'est bref
 *  et rare, ça ne fatigue pas : on ne peut pas s'habituer à ce qui n'est pas
 *  là la plupart du temps.
 *
 *  CE QUI N'A PAS CHANGÉ, ET NE CHANGERA PAS :
 *    · LA TEINTE SUIT LE MONDE. Violet et bleu en public, ambre en prod. Ce
 *      n'est pas décoratif : c'est le seul repère qui dit « tu manipules les
 *      vraies données » sans qu'on ait à lire.
 *    · LE PLAFOND DE LISIBILITÉ. Le point le plus clair du décor reste sous
 *      la barre qui garantit 5:1 aux étiquettes de formulaire — et il est
 *      mesuré À TOUS LES MOMENTS du cycle, pas au repos.
 *    · ZÉRO IMAGE, ZÉRO JAVASCRIPT, et rien qui ne s'anime autrement qu'en
 *      `transform` ou `opacity`. Le tableau de bord doit s'ouvrir sur un vieux
 *      téléphone.
 *    · LE RÉGLAGE SYSTÈME « MOINS D'ANIMATIONS » arrête tout. C'est la
 *      condition qui a permis d'animer : en CSS, il suffit ; en JavaScript, il
 *      ne peut rien.
 * ========================================================================== */

.ciel {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /*  Il découpe ce qui déborde : les couches intérieures sont volontairement
      plus grandes que l'écran pour que rotations et translations ne montrent
      jamais un bord. Sans ce découpage, elles créeraient une barre de
      défilement horizontale sur toutes les pages du site. */
  overflow: hidden;
  /*  `contain` dit au navigateur que rien de ce qui se passe là-dedans ne peut
      affecter la mise en page du reste. Sur une page qui contient un tableau
      de six cents lignes, ça évite de recalculer quoi que ce soit à chaque
      image de l'animation. */
  contain: strict;
}
.ciel > * { position: absolute; }

/* --- 1. L'AURORE : trois nappes qui se déforment ------------------------- */
/*  Les trois nappes sont à des distances différentes du centre de rotation :
    une rotation de six degrés les déplace donc de trois quantités différentes,
    et elles passent l'une devant l'autre au lieu de glisser ensemble. C'est ce
    croisement qui donne l'impression de volume. */
.ciel-aurore {
  inset: -35%;
  background-image:
    radial-gradient(32% 26% at 22% 26%,
      rgba(var(--fond-teinte), var(--fond-aurore-force)),
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.35)) 45%, transparent 66%),
    radial-gradient(28% 22% at 76% 18%,
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.9)),
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.3)) 45%, transparent 66%),
    radial-gradient(40% 26% at 58% 84%,
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.8)),
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.28)) 45%, transparent 68%),
    radial-gradient(58% 20% at 42% 52%,
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.45)), transparent 72%);
  animation: ciel-aurore 46s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes ciel-aurore {
  0%   { transform: translate3d(-4%, -2%, 0) scale(1.02) rotate(-3deg); }
  35%  { transform: translate3d(3%, 2.5%, 0) scale(1.14) rotate(2deg); }
  70%  { transform: translate3d(-2%, 4%, 0)  scale(1.06) rotate(4deg); }
  100% { transform: translate3d(4%, -3%, 0)  scale(1.10) rotate(-1.5deg); }
}

/* --- 2. LES ÉTOILES : deux plans, deux vitesses -------------------------- */
/*  LE BOUCLAGE SANS SAUT. Le motif se répète toutes les T pixels ; la couche
    est agrandie de T dans chaque direction et translatée d'EXACTEMENT T. À la
    fin du cycle, l'image est donc pixel pour pixel celle du début, et la
    boucle ne se voit pas. Une valeur approchée ferait un à-coup toutes les
    deux minutes — le genre de défaut qu'on remarque sans savoir le nommer. */
.ciel-etoiles {
  /*  LA MARGE COUVRE LA PLUS GRANDE DES TROIS TUILES (420 px) : sous-estimer
      ne se verrait qu'au bout d'un cycle, sur un bord, quelques secondes. */
  inset: -440px -440px auto auto;
  width: calc(100% + 880px);
  height: calc(100% + 880px);
  top: -440px; right: -440px;
  will-change: transform;
}
/*  TROIS PAS DE TUILE DIFFÉRENTS — 300, 340 et 420 px — ET C'EST LE POINT.
    Avec la même tuile pour les trois plans, les trois grilles se
    superposaient exactement : on voyait des colonnes d'étoiles régulières,
    et un ciel dont on devine la grille n'est plus un ciel. À trois pas
    différents, le motif combiné ne se répète qu'au bout de plusieurs mètres
    d'écran — c'est-à-dire jamais.

    LES POSITIONS SONT TIRÉES UNE FOIS ET ÉCRITES EN CLAIR. Un semis
    recalculé à chaque chargement ferait sauter les étoiles d'une page à
    l'autre, ce qui se remarque beaucoup plus qu'on ne croit. */
.ciel-etoiles-loin {
  background-image:
    radial-gradient(1.2px 1.2px at 171px 83px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 280px 54px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 265px 115px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 220px 41px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 36px 69px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 209px 31px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 154px 220px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 163px 292px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 196px 55px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1.2px 1.2px at 111px 260px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 238px 191px, rgba(var(--fond-poussiere), 0.35), transparent),
    radial-gradient(1px 1px at 130px 47px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 181px 235px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1.2px 1.2px at 66px 268px, rgba(var(--fond-poussiere), 0.35), transparent),
    radial-gradient(1.2px 1.2px at 83px 256px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 291px 166px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 239px 41px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 39px 37px, rgba(var(--fond-poussiere), 0.28), transparent);
  background-size: 300px 300px;
  opacity: var(--ciel-etoiles-loin);
  animation: ciel-derive-loin 150s linear infinite;
}

/*  LE PLAN DU MILIEU — demandé le 29/08 : « des étoiles par-ci par-là ».
    Deux plans donnaient deux vitesses ; trois donnent une profondeur
    qu'on n'obtient pas en ajoutant des points aux deux autres. */
.ciel-etoiles-moyen {
  background-image:
    radial-gradient(1.3px 1.3px at 201px 176px, rgba(var(--fond-aurore-2), 0.5), transparent),
    radial-gradient(1.1px 1.1px at 301px 151px, rgba(var(--fond-aurore-2), 0.6), transparent),
    radial-gradient(1.3px 1.3px at 301px 10px, rgba(var(--fond-aurore-2), 0.7), transparent),
    radial-gradient(1.1px 1.1px at 85px 82px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 137px 323px, rgba(var(--fond-poussiere), 0.7), transparent),
    radial-gradient(1.6px 1.6px at 306px 22px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 43px 190px, rgba(var(--fond-poussiere), 0.38), transparent),
    radial-gradient(1.3px 1.3px at 290px 15px, rgba(var(--fond-poussiere), 0.7), transparent),
    radial-gradient(1.6px 1.6px at 97px 33px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 323px 19px, rgba(var(--fond-aurore-2), 0.5), transparent),
    radial-gradient(1.6px 1.6px at 35px 160px, rgba(var(--fond-poussiere), 0.5), transparent),
    radial-gradient(1.3px 1.3px at 53px 214px, rgba(var(--fond-aurore-2), 0.38), transparent),
    radial-gradient(1.3px 1.3px at 123px 17px, rgba(var(--fond-poussiere), 0.5), transparent);
  background-size: 340px 340px;
  opacity: var(--ciel-etoiles-moyen);
  animation: ciel-derive-moyen 108s linear infinite,
             ciel-scintille-lent 13s ease-in-out infinite;
}

.ciel-etoiles-pres {
  background-image:
    radial-gradient(1.8px 1.8px at 405px 154px, rgba(var(--fond-teinte), 0.9), transparent),
    radial-gradient(2.6px 2.6px at 163px 222px, rgba(var(--fond-poussiere), 0.8), transparent),
    radial-gradient(1.5px 1.5px at 377px 104px, rgba(var(--fond-poussiere), 1), transparent),
    radial-gradient(2.6px 2.6px at 119px 323px, rgba(var(--fond-poussiere), 0.9), transparent),
    radial-gradient(2.6px 2.6px at 49px 369px, rgba(var(--fond-poussiere), 1), transparent),
    radial-gradient(2.6px 2.6px at 268px 327px, rgba(var(--fond-poussiere), 0.8), transparent),
    radial-gradient(1.8px 1.8px at 111px 406px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(2.2px 2.2px at 94px 323px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(1.5px 1.5px at 395px 194px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(2.2px 2.2px at 49px 248px, rgba(var(--fond-teinte), 1), transparent);
  background-size: 420px 420px;
  opacity: var(--ciel-etoiles-pres);
  animation: ciel-derive-pres 78s linear infinite,
             ciel-scintille 9s ease-in-out infinite;
}

/*  LE DÉPLACEMENT VAUT EXACTEMENT UNE TUILE — 300 px, comme
    `background-size`. À la fin du cycle l'image est pixel pour pixel celle du
    début, et la boucle ne se voit pas. Une valeur approchée ferait un à-coup
    toutes les deux minutes : le genre de défaut qu'on remarque sans savoir le
    nommer. Si la tuile change, ces trois nombres changent avec elle. */
@keyframes ciel-derive-loin {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-300px, 300px, 0); }
}
@keyframes ciel-derive-moyen {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(340px, 340px, 0); }
}
@keyframes ciel-derive-pres {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-420px, 420px, 0); }
}
@keyframes ciel-scintille-lent {
  0%, 100% { opacity: calc(var(--ciel-etoiles-moyen) * 0.7); }
  50%      { opacity: var(--ciel-etoiles-moyen); }
}
@keyframes ciel-scintille {
  0%, 100% { opacity: calc(var(--ciel-etoiles-pres) * 0.75); }
  50%      { opacity: var(--ciel-etoiles-pres); }
}

/* --- 3. LES FILANTES ----------------------------------------------------- */
/*  DEMANDÉ LE 29/08 : « les étoiles filantes c'est stylé mais il n'y en a pas
 *  assez, il faut aussi qu'il y en ait partout ».
 *
 *  ELLES ÉTAIENT DEUX, ET CANTONNÉES AU HAUT DE L'ÉCRAN. Le raisonnement de
 *  la veille était : la bande basse est celle des tableaux et des
 *  formulaires, un trait lumineux qui traverse une colonne de chiffres
 *  distrait pendant qu'on lit. Le raisonnement tient toujours — mais il
 *  s'appliquait à un trait BRILLANT. On change donc l'objet plutôt que la
 *  règle : six filantes, sur toute la hauteur, et celles qui passent BAS sont
 *  plus fines et plus discrètes que celles du haut. Un ciel n'a pas de raison
 *  d'avoir des météores seulement dans son tiers supérieur.
 *
 *  SIX, ET PAS VINGT. Chacune est visible environ cinq pour cent de son
 *  cycle ; avec des périodes premières entre elles (11, 13, 17, 19, 23, 29
 *  secondes) elles ne repassent presque jamais ensemble, et il s'en passe une
 *  toutes les trois à quatre secondes en moyenne. Vingt filantes ne feraient
 *  pas « plus de ciel », elles feraient une pluie de météores — c'est-à-dire
 *  un décor qu'on regarde au lieu de regarder la page.
 *
 *  LES PÉRIODES SONT PREMIÈRES ENTRE ELLES pour une raison précise : avec des
 *  durées rondes (10, 20, 30) elles se synchroniseraient toutes les trente
 *  secondes et partiraient en salve. Des nombres premiers ne retombent jamais
 *  ensemble, et le ciel n'a pas de rythme perceptible.
 */
/*  LA BASE COMMUNE. `top` et `left` ne servent plus qu'à poser l'origine :
    c'est la keyframe qui décide où chaque passage a lieu. */
.ciel-filante {
  top: 0;
  left: 0;
  width: 200px;
  height: 2px;
  border-radius: 2px;
  /*  La traîne est plus longue que la tête : c'est ce qui donne le sens du
      mouvement. Une barre symétrique se lit comme un trait, pas comme un
      objet qui file. */
  background: linear-gradient(90deg,
    transparent, rgba(var(--fond-poussiere), calc(var(--ciel-filante-force) * 0.25)) 45%,
    rgba(var(--fond-poussiere), var(--ciel-filante-force)) 88%, transparent);
  opacity: 0;
  will-change: transform, opacity;
  animation: ciel-filante 17s linear infinite;
}
.ciel-filante-b { width: 150px; height: 1px; animation: ciel-filante-b 13s linear infinite; animation-delay: 0.1s; }
.ciel-filante-c { width: 130px; height: 2px; animation: ciel-filante-c 17s linear infinite; animation-delay: 10.3s; }
.ciel-filante-d { width: 150px; height: 1.5px; animation: ciel-filante-d 19s linear infinite; animation-delay: 4.0s; }
.ciel-filante-e { width: 170px; height: 2px; animation: ciel-filante-e 23s linear infinite; animation-delay: 4.8s; }
.ciel-filante-f { width: 150px; height: 1.5px; animation: ciel-filante-f 29s linear infinite; animation-delay: 1.5s; }
.ciel-filante-g { width: 150px; height: 1px; animation: ciel-filante-g 31s linear infinite; animation-delay: 8.5s; }
.ciel-filante-h { width: 90px; height: 2px; animation: ciel-filante-h 37s linear infinite; animation-delay: 2.3s; }
.ciel-filante-i { width: 90px; height: 1.5px; animation: ciel-filante-i 41s linear infinite; animation-delay: 8.5s; }
.ciel-filante-j { width: 150px; height: 1px; animation: ciel-filante-j 43s linear infinite; animation-delay: 8.2s; }

/* ==========================================================================
 *  CHAQUE FILANTE PASSE À TROIS ENDROITS DIFFÉRENTS — corrigé le 29/08
 * ==========================================================================
 *  « Les animations sont toujours au même endroit, il faudrait que ça change
 *   un peu. » C'était vrai, et c'était un défaut de conception, pas un
 *  réglage : chaque filante avait UNE hauteur, UN angle, UNE trajectoire,
 *  répétés à l'identique toutes les dix-sept secondes. Au bout de deux
 *  minutes l'oeil a appris le motif — et un mouvement qu'on prévoit ne se
 *  remarque plus. C'est le même piège que la première version du décor, sous
 *  une autre forme : ce n'est pas la quantité de mouvement qui compte, c'est
 *  qu'il reste imprévisible.
 *
 *  LE CSS NE SAIT PAS TIRER AU HASARD, et c'est tout le problème. On ne peut
 *  pas changer la trajectoire à chaque passage — mais on peut en mettre
 *  PLUSIEURS dans un même cycle. Chaque filante fait donc trois passages, à
 *  trois hauteurs, trois angles et trois vitesses, et se replace entre les
 *  deux pendant qu'elle est invisible.
 *
 *  LE PALIER ENTRE DEUX PASSAGES N'EST PAS DÉCORATIF. Sans une image-clé qui
 *  TIENT la position jusqu'au passage suivant, le navigateur interpolerait
 *  tout le temps mort : la couche dériverait doucement en travers de l'écran
 *  pendant vingt-cinq secondes, invisible, et le passage suivant partirait
 *  d'ailleurs que prévu. On ne l'aurait pas vu — on aurait seulement trouvé
 *  que « ça saute ».
 *
 *  DIX FILANTES × TROIS PASSAGES = TRENTE TRAJECTOIRES, sur des cycles
 *  premiers entre eux (11 à 43 s). Le motif complet ne se répète qu'au bout
 *  de leur plus petit commun multiple — des milliers d'heures. Autant dire
 *  jamais.
 *
 *  ELLES RESTENT DISCRÈTES EN BAS. La bande basse est celle des tableaux et
 *  des formulaires : les passages qui y descendent sont plus fins et moins
 *  lumineux. C'est la seule contrainte qui n'a pas bougé — un trait brillant
 *  en travers d'une colonne de chiffres distrait pendant qu'on lit.
 */
/* ==========================================================================
 *  L'ANGLE D'UNE FILANTE SE DÉDUIT DE SA TRAJECTOIRE — corrigé le 01/09/2026
 * ==========================================================================
 *  « Corrige le sens des étoiles filantes, ça fait bizarre. » C'était vrai, et
 *  mesurable : sur les 30 passages, 23 étaient à plus de 5° de leur propre
 *  trajectoire et 13 penchaient carrément à l'ENVERS — le trait montait
 *  pendant que l'étoile descendait. Une traîne qui ne suit pas le chemin ne se
 *  lit pas comme de la vitesse : elle se lit comme un trait posé de travers.
 *
 *  LE PIÈGE, ET IL EST DISCRET : `vw` et `vh` ne valent PAS la même chose.
 *  Un passage qui va de `-32vw, 50vh` à `120vw, 22vh` semble monter beaucoup —
 *  28 unités de haut pour 152 de large — et l'angle avait été écrit là-dessus
 *  (-23,7°). En pixels sur un écran 16/9, c'est 302 px de haut pour 2918 px de
 *  large, donc **-5,9°**. Quatre fois moins. Toutes les valeurs étaient
 *  fausses de la même façon, et celles dont le signe l'était aussi donnaient
 *  la comète qui vole en marche arrière.
 *
 *  CHAQUE ANGLE EST MAINTENANT `atan2(dy, dx)` DE SON PROPRE PASSAGE, calculé
 *  en pixels sur une référence 16/9. Il reste juste sur tout écran de forme
 *  voisine, et approché sur un téléphone tenu debout — le décor y est de toute
 *  façon presque invisible.
 *
 *  LE SENS DE LA TÊTE, LUI, ÉTAIT BON : tous les passages vont de la gauche
 *  vers la droite, et le dégradé met le point brillant à droite (88 %). La
 *  tête ouvre la voie, la traîne suit. On n'y a pas touché.
 * ========================================================================== */
@keyframes ciel-filante {
  4.62% { transform: translate3d(-32vw, 50vh, 0) rotate(-5.9deg); opacity: 0; }
  6.01% { opacity: 0.89; }
  9.50% { opacity: 0; }
  10.17% { transform: translate3d(120vw, 22vh, 0) rotate(-5.9deg); opacity: 0; }
  29.82% { transform: translate3d(120vw, 22vh, 0) rotate(-5.9deg); opacity: 0; }
  29.84% { transform: translate3d(-16vw, 4vh, 0) rotate(6.7deg); opacity: 0; }
  31.23% { opacity: 0.5; }
  34.72% { opacity: 0; }
  35.39% { transform: translate3d(138vw, 36vh, 0) rotate(6.7deg); opacity: 0; }
  56.85% { transform: translate3d(138vw, 36vh, 0) rotate(6.7deg); opacity: 0; }
  56.87% { transform: translate3d(-32vw, 86vh, 0) rotate(-3.4deg); opacity: 0; }
  58.26% { opacity: 0.95; }
  61.76% { opacity: 0; }
  62.42% { transform: translate3d(120vw, 70vh, 0) rotate(-3.4deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 70vh, 0) rotate(-3.4deg); opacity: 0; }
}
@keyframes ciel-filante-b {
  1.12% { transform: translate3d(-32vw, 50vh, 0) rotate(1.9deg); opacity: 0; }
  2.08% { opacity: 0.52; }
  4.49% { opacity: 0; }
  4.96% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  31.98% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  32.00% { transform: translate3d(-16vw, 50vh, 0) rotate(6.0deg); opacity: 0; }
  32.96% { opacity: 0.72; }
  35.38% { opacity: 0; }
  35.84% { transform: translate3d(155vw, 82vh, 0) rotate(6.0deg); opacity: 0; }
  59.50% { transform: translate3d(155vw, 82vh, 0) rotate(6.0deg); opacity: 0; }
  59.52% { transform: translate3d(-32vw, 62vh, 0) rotate(-3.0deg); opacity: 0; }
  60.48% { opacity: 0.46; }
  62.90% { opacity: 0; }
  63.36% { transform: translate3d(138vw, 46vh, 0) rotate(-3.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 46vh, 0) rotate(-3.0deg); opacity: 0; }
}
@keyframes ciel-filante-c {
  1.48% { transform: translate3d(-16vw, 4vh, 0) rotate(6.0deg); opacity: 0; }
  2.67% { opacity: 0.99; }
  5.65% { opacity: 0; }
  6.22% { transform: translate3d(155vw, 36vh, 0) rotate(6.0deg); opacity: 0; }
  27.99% { transform: translate3d(155vw, 36vh, 0) rotate(6.0deg); opacity: 0; }
  28.01% { transform: translate3d(-32vw, -6vh, 0) rotate(3.4deg); opacity: 0; }
  29.19% { opacity: 0.83; }
  32.18% { opacity: 0; }
  32.75% { transform: translate3d(155vw, 14vh, 0) rotate(3.4deg); opacity: 0; }
  55.47% { transform: translate3d(155vw, 14vh, 0) rotate(3.4deg); opacity: 0; }
  55.49% { transform: translate3d(-16vw, 74vh, 0) rotate(3.8deg); opacity: 0; }
  56.67% { opacity: 0.93; }
  59.66% { opacity: 0; }
  60.23% { transform: translate3d(155vw, 94vh, 0) rotate(3.8deg); opacity: 0; }
  99.98% { transform: translate3d(155vw, 94vh, 0) rotate(3.8deg); opacity: 0; }
}
@keyframes ciel-filante-d {
  2.36% { transform: translate3d(-16vw, 26vh, 0) rotate(7.5deg); opacity: 0; }
  3.41% { opacity: 0.56; }
  6.06% { opacity: 0; }
  6.56% { transform: translate3d(120vw, 58vh, 0) rotate(7.5deg); opacity: 0; }
  29.22% { transform: translate3d(120vw, 58vh, 0) rotate(7.5deg); opacity: 0; }
  29.24% { transform: translate3d(-32vw, 50vh, 0) rotate(5.5deg); opacity: 0; }
  30.29% { opacity: 0.67; }
  32.94% { opacity: 0; }
  33.44% { transform: translate3d(155vw, 82vh, 0) rotate(5.5deg); opacity: 0; }
  56.63% { transform: translate3d(155vw, 82vh, 0) rotate(5.5deg); opacity: 0; }
  56.65% { transform: translate3d(-24vw, -6vh, 0) rotate(2.0deg); opacity: 0; }
  57.70% { opacity: 0.53; }
  60.35% { opacity: 0; }
  60.85% { transform: translate3d(138vw, 4vh, 0) rotate(2.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 4vh, 0) rotate(2.0deg); opacity: 0; }
}
@keyframes ciel-filante-e {
  3.90% { transform: translate3d(-32vw, 14vh, 0) rotate(-4.8deg); opacity: 0; }
  5.34% { opacity: 0.63; }
  8.97% { opacity: 0; }
  9.66% { transform: translate3d(155vw, -14vh, 0) rotate(-4.8deg); opacity: 0; }
  31.63% { transform: translate3d(155vw, -14vh, 0) rotate(-4.8deg); opacity: 0; }
  31.65% { transform: translate3d(-16vw, 38vh, 0) rotate(3.8deg); opacity: 0; }
  33.09% { opacity: 0.63; }
  36.72% { opacity: 0; }
  37.41% { transform: translate3d(155vw, 58vh, 0) rotate(3.8deg); opacity: 0; }
  60.42% { transform: translate3d(155vw, 58vh, 0) rotate(3.8deg); opacity: 0; }
  60.44% { transform: translate3d(-24vw, 74vh, 0) rotate(4.5deg); opacity: 0; }
  61.88% { opacity: 0.43; }
  65.50% { opacity: 0; }
  66.20% { transform: translate3d(120vw, 94vh, 0) rotate(4.5deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 94vh, 0) rotate(4.5deg); opacity: 0; }
}
@keyframes ciel-filante-f {
  5.64% { transform: translate3d(-32vw, 50vh, 0) rotate(1.9deg); opacity: 0; }
  6.89% { opacity: 0.45; }
  10.05% { opacity: 0; }
  10.66% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  33.29% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  33.31% { transform: translate3d(-32vw, 50vh, 0) rotate(-4.8deg); opacity: 0; }
  34.57% { opacity: 0.51; }
  37.73% { opacity: 0; }
  38.33% { transform: translate3d(155vw, 22vh, 0) rotate(-4.8deg); opacity: 0; }
  62.86% { transform: translate3d(155vw, 22vh, 0) rotate(-4.8deg); opacity: 0; }
  62.88% { transform: translate3d(-24vw, 50vh, 0) rotate(2.0deg); opacity: 0; }
  64.13% { opacity: 0.76; }
  67.30% { opacity: 0; }
  67.90% { transform: translate3d(138vw, 60vh, 0) rotate(2.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 60vh, 0) rotate(2.0deg); opacity: 0; }
}
@keyframes ciel-filante-g {
  1.03% { transform: translate3d(-32vw, 26vh, 0) rotate(6.8deg); opacity: 0; }
  2.00% { opacity: 0.69; }
  4.44% { opacity: 0; }
  4.90% { transform: translate3d(120vw, 58vh, 0) rotate(6.8deg); opacity: 0; }
  27.49% { transform: translate3d(120vw, 58vh, 0) rotate(6.8deg); opacity: 0; }
  27.51% { transform: translate3d(-16vw, 62vh, 0) rotate(6.7deg); opacity: 0; }
  28.47% { opacity: 0.93; }
  30.91% { opacity: 0; }
  31.38% { transform: translate3d(138vw, 94vh, 0) rotate(6.7deg); opacity: 0; }
  57.46% { transform: translate3d(138vw, 94vh, 0) rotate(6.7deg); opacity: 0; }
  57.48% { transform: translate3d(-32vw, 74vh, 0) rotate(6.0deg); opacity: 0; }
  58.45% { opacity: 1.0; }
  60.88% { opacity: 0; }
  61.35% { transform: translate3d(138vw, 106vh, 0) rotate(6.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 106vh, 0) rotate(6.0deg); opacity: 0; }
}
@keyframes ciel-filante-h {
  1.07% { transform: translate3d(-16vw, 26vh, 0) rotate(4.7deg); opacity: 0; }
  2.35% { opacity: 0.67; }
  5.57% { opacity: 0; }
  6.19% { transform: translate3d(120vw, 46vh, 0) rotate(4.7deg); opacity: 0; }
  33.81% { transform: translate3d(120vw, 46vh, 0) rotate(4.7deg); opacity: 0; }
  33.83% { transform: translate3d(-16vw, 86vh, 0) rotate(-3.0deg); opacity: 0; }
  35.11% { opacity: 0.91; }
  38.34% { opacity: 0; }
  38.95% { transform: translate3d(155vw, 70vh, 0) rotate(-3.0deg); opacity: 0; }
  63.64% { transform: translate3d(155vw, 70vh, 0) rotate(-3.0deg); opacity: 0; }
  63.66% { transform: translate3d(-16vw, 62vh, 0) rotate(-5.3deg); opacity: 0; }
  64.94% { opacity: 0.67; }
  68.17% { opacity: 0; }
  68.78% { transform: translate3d(155vw, 34vh, 0) rotate(-5.3deg); opacity: 0; }
  99.98% { transform: translate3d(155vw, 34vh, 0) rotate(-5.3deg); opacity: 0; }
}
@keyframes ciel-filante-i {
  2.05% { transform: translate3d(-32vw, 38vh, 0) rotate(4.2deg); opacity: 0; }
  3.50% { opacity: 0.5; }
  7.16% { opacity: 0; }
  7.85% { transform: translate3d(120vw, 58vh, 0) rotate(4.2deg); opacity: 0; }
  29.73% { transform: translate3d(120vw, 58vh, 0) rotate(4.2deg); opacity: 0; }
  29.75% { transform: translate3d(-24vw, 86vh, 0) rotate(-3.6deg); opacity: 0; }
  31.20% { opacity: 0.91; }
  34.86% { opacity: 0; }
  35.55% { transform: translate3d(120vw, 70vh, 0) rotate(-3.6deg); opacity: 0; }
  59.58% { transform: translate3d(120vw, 70vh, 0) rotate(-3.6deg); opacity: 0; }
  59.60% { transform: translate3d(-16vw, 86vh, 0) rotate(6.7deg); opacity: 0; }
  61.05% { opacity: 0.44; }
  64.71% { opacity: 0; }
  65.40% { transform: translate3d(138vw, 118vh, 0) rotate(6.7deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 118vh, 0) rotate(6.7deg); opacity: 0; }
}
@keyframes ciel-filante-j {
  2.69% { transform: translate3d(-32vw, 74vh, 0) rotate(1.9deg); opacity: 0; }
  4.23% { opacity: 0.8; }
  8.10% { opacity: 0; }
  8.84% { transform: translate3d(138vw, 84vh, 0) rotate(1.9deg); opacity: 0; }
  28.81% { transform: translate3d(138vw, 84vh, 0) rotate(1.9deg); opacity: 0; }
  28.83% { transform: translate3d(-16vw, 26vh, 0) rotate(3.8deg); opacity: 0; }
  30.37% { opacity: 0.61; }
  34.25% { opacity: 0; }
  34.98% { transform: translate3d(155vw, 46vh, 0) rotate(3.8deg); opacity: 0; }
  60.71% { transform: translate3d(155vw, 46vh, 0) rotate(3.8deg); opacity: 0; }
  60.73% { transform: translate3d(-32vw, 26vh, 0) rotate(2.1deg); opacity: 0; }
  62.26% { opacity: 0.69; }
  66.14% { opacity: 0; }
  66.88% { transform: translate3d(120vw, 36vh, 0) rotate(2.1deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 36vh, 0) rotate(2.1deg); opacity: 0; }
}

/* --- 4. LE VOILE : la vignette, posée par-dessus tout le reste ----------- */
/*  Elle éteint les quatre coins. C'est elle qui empêche l'aurore de « baver »
    jusqu'au bord de la fenêtre et qui ramène le regard vers le centre, là où
    est le contenu. Posée EN DERNIER, donc au-dessus des étoiles : sans ça, les
    étoiles des coins resteraient à pleine clarté sur un fond assombri, et on
    verrait le truc. */
.ciel-voile {
  inset: 0;
  background:
    radial-gradient(130% 100% at 50% 40%, transparent 46%, rgba(0, 0, 0, 0.42) 100%);
}

/* ==========================================================================
 *  LES EFFETS DE FÊTE — ce qui tombe, monte ou explose
 * ==========================================================================
 *  Demandé le 29/08 : « pour Noël on peut mettre des petits flocons, pour le
 *  Nouvel An un petit feu d'artifice… fais un truc clean et propre. »
 *
 *  UN SEUL JEU DE COUCHES, HUIT ALLURES. Les deux `.ciel-fete` sont posées
 *  quel que soit l'effet ; c'est `data-effet` sur <html> qui décide de ce
 *  qu'elles peignent et de comment elles bougent. Écrire huit paires de
 *  couches aurait multiplié par huit le nombre d'éléments pour n'en montrer
 *  jamais qu'une paire à la fois.
 *
 *  DEUX PLANS ET PAS UN, toujours pour la même raison que les étoiles : des
 *  flocons qui tombent tous à la même vitesse se lisent comme un rideau qu'on
 *  descend. Deux vitesses, et c'est de la neige.
 *
 *  LA COULEUR VIENT DES DONNÉES (`--fond-teinte` / `--fond-aurore-2`, posées
 *  par head.ejs depuis le catalogue). Aucune couleur de fête n'est écrite
 *  ici : sans ça, un événement créé depuis le panneau serait forcément vert
 *  et rouge.
 * ========================================================================== */
.ciel-fete {
  inset: -240px -240px auto auto;
  width: calc(100% + 480px);
  height: calc(100% + 480px);
  top: -240px; right: -240px;
  background-size: 240px 240px;
  will-change: transform;
}
.ciel-fete-a {
  background-image:
    radial-gradient(3px 3px at 40px 30px, rgba(var(--fond-aurore-2), 0.7), transparent),
    radial-gradient(2.5px 2.5px at 170px 110px, rgba(var(--fond-teinte), 0.6), transparent),
    radial-gradient(3px 3px at 95px 190px, rgba(var(--fond-aurore-2), 0.55), transparent),
    radial-gradient(2px 2px at 215px 55px, rgba(var(--fond-teinte), 0.5), transparent);
  animation: ciel-tombe-a 18s linear infinite;
}
.ciel-fete-b {
  background-image:
    radial-gradient(2px 2px at 130px 60px, rgba(var(--fond-teinte), 0.5), transparent),
    radial-gradient(2px 2px at 20px 150px, rgba(var(--fond-aurore-2), 0.45), transparent),
    radial-gradient(1.6px 1.6px at 190px 205px, rgba(var(--fond-teinte), 0.4), transparent);
  background-size: 200px 200px;
  animation: ciel-tombe-b 27s linear infinite;
  opacity: 0.85;
}
/*  LE DÉPLACEMENT VAUT EXACTEMENT UNE TUILE, comme pour les étoiles : sinon
    la boucle fait un à-coup à chaque tour. Le décalage latéral est plus petit
    que la chute — de la neige qui tombe à 45° ressemble à de la pluie. */
@keyframes ciel-tombe-a {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-60px, 240px, 0); }
}
@keyframes ciel-tombe-b {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(50px, 200px, 0); }
}


/* ==========================================================================
 *  LES MOTIFS DE FÊTE — de vraies illustrations, pas des silhouettes
 * ==========================================================================
 *  Signalé le 29/08, deux fois : « il n'y a pas de cœurs, c'est normal ? »,
 *  puis « c'est moche comme les citrouilles, fais des trucs réalistes ».
 *
 *  LA PREMIÈRE CORRECTION AVAIT DONNÉ LES BONNES FORMES ET RATÉ LE RESTE.
 *  Elle utilisait les SVG comme MASQUES : le dessin découpait la couleur de
 *  l'événement. Ça résolvait « une citrouille doit ressembler à une
 *  citrouille », et ça rendait impossible ce qui fait qu'un objet a l'air
 *  vrai — un masque n'a qu'un canal, l'opacité. Pas d'ombre, pas de reflet,
 *  pas de côtes plus sombres que le ventre. Une silhouette monochrome, quelle
 *  que soit sa précision, reste un pictogramme.
 *
 *  DONC : DES IMAGES EN COULEURS, et ce qu'on y gagne se voit — la citrouille
 *  a un dégradé, trois côtes creusées, une queue verte et une lumière en haut
 *  à gauche ; le ballon a un reflet et une ficelle bouclée ; l'œuf est peint
 *  avec des bandes et des pois ; la feuille a une nervure et des veines.
 *
 *  CE QUE ÇA COÛTE, ET IL FAUT LE DIRE : les motifs portent désormais LEURS
 *  couleurs, plus celles de l'événement. Un événement créé depuis le panneau
 *  avec un violet à lui aura donc un ciel violet et des citrouilles orange.
 *  C'est le bon compromis dans ce sens-là : une citrouille violette n'est
 *  plus une citrouille, alors qu'un ciel violet reste un ciel. Les teintes de
 *  l'événement continuent de peindre le fond, l'aurore, les étoiles et le
 *  bandeau — tout sauf les objets qu'on doit reconnaître.
 *
 *  TOUT EST EN `data:` : aucune requête réseau, aucun fichier à déployer,
 *  aucun nœud de plus dans le DOM. La règle « le tableau de bord doit
 *  s'ouvrir sur un vieux téléphone » tient toujours.
 *
 *  L'OPACITÉ RESTE RÉGLÉE PAR `--fete-force`, et elle est mesurée : des
 *  illustrations en couleurs sont bien plus claires que des silhouettes
 *  teintées, et c'est verif-fetes.js qui dit jusqu'où on peut aller sans
 *  rendre une étiquette de formulaire pénible à lire.
 *
 *  LE FEU D'ARTIFICE N'EST PAS ICI : ce sont ses bouquets qui le dessinent,
 *  et une explosion n'est pas une tuile qu'on répète.
 * ========================================================================== */
html[data-effet="coeurs"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="coeurs"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="flocons"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="flocons"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="oeufs"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="oeufs"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="citrouilles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="citrouilles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="feuilles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="feuilles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="bulles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="bulles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="ballons"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ffe08a'/%3E%3Cstop offset='.5' stop-color='%23f2c04a'/%3E%3Cstop offset='1' stop-color='%23c08a1e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23c08a1e'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="ballons"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ffe08a'/%3E%3Cstop offset='.5' stop-color='%23f2c04a'/%3E%3Cstop offset='1' stop-color='%23c08a1e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23c08a1e'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="confettis"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23f472b6'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%236aa8ff'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23ffd166'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%2350c878'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffd166' stroke-width='1.7' fill='none' stroke-linecap='round'/%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffffff' stroke-width='.5' fill='none' stroke-linecap='round' opacity='.4' transform='translate(0,-.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="confettis"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23ffd166'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffd166' stroke-width='1.7' fill='none' stroke-linecap='round'/%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffffff' stroke-width='.5' fill='none' stroke-linecap='round' opacity='.4' transform='translate(0,-.6)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23f472b6'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}

/*  CE QUI MONTE. Cœurs et bulles ne tombent pas — de la neige rose reste de
    la neige. `reverse` suffit : le motif, lui, ne change pas. */
html[data-effet="coeurs"] .ciel-fete,
html[data-effet="ballons"] .ciel-fete,
html[data-effet="bulles"] .ciel-fete { animation-direction: reverse; }
/*  Les ballons montent plus lentement que les cœurs : c'est ce qui les rend
    lourds, et un ballon qui file vers le haut ressemble à une bulle. */
html[data-effet="ballons"] .ciel-fete-a { animation-duration: 34s; }
html[data-effet="ballons"] .ciel-fete-b { animation-duration: 46s; }

/*  CE QUI FLOTTE SUR PLACE. Un œuf de Pâques qui dégringole du ciel n'évoque
    rien ; posé et respirant, il évoque un décor. */
html[data-effet="oeufs"] .ciel-fete,
html[data-effet="citrouilles"] .ciel-fete {
  animation-name: ciel-fete-respire;
  animation-duration: 7s;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
html[data-effet="citrouilles"] .ciel-fete-b { animation-duration: 11s; }
/*  LA RESPIRATION PASSE PAR `filter`, PAS PAR `opacity` — corrigé le 29/08.
 *
 *  Écrite avec `opacity`, cette image-clé ÉCRASAIT l'opacité de la couche.
 *  Conséquence mesurée : les œufs et les citrouilles ignoraient
 *  `--fete-force`, restaient à 0,5–0,95 d'opacité, et tombaient à 4,07:1
 *  pour les étiquettes — pendant que les quatre autres fêtes obéissaient au
 *  même réglage. On a d'abord cru à un réglage sans effet : c'était une
 *  animation qui gagnait, en silence, sur deux fêtes seulement.
 *
 *  `filter: opacity()` se MULTIPLIE avec l'opacité de l'élément au lieu de la
 *  remplacer. La respiration reste, le réglage reprend la main. */
@keyframes ciel-fete-respire {
  from { transform: translate3d(0, 0, 0) scale(1);        filter: opacity(0.55); }
  to   { transform: translate3d(0, -16px, 0) scale(1.07); filter: opacity(1); }
}

/*  LES FEUILLES TOURNENT EN TOMBANT. Une feuille qui descend droit, c'est un
    confetti ; c'est la rotation qui fait la feuille. */
html[data-effet="feuilles"] .ciel-fete-a { animation: ciel-feuille-a 21s linear infinite; }
html[data-effet="feuilles"] .ciel-fete-b { animation: ciel-feuille-b 31s linear infinite; }
@keyframes ciel-feuille-a {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(-120px, 240px, 0) rotate(12deg); }
}
@keyframes ciel-feuille-b {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(110px, 200px, 0) rotate(-14deg); }
}

/*  LES CONFETTIS tombent plus vite et plus dense : c'est ce qui distingue une
    fête d'un hiver. */
html[data-effet="confettis"] .ciel-fete-a { animation-duration: 9s; }
html[data-effet="confettis"] .ciel-fete-b { animation-duration: 13s; }

/*  LA PLUIE D'ÉTOILES FILANTES n'ajoute pas de couche : elle accélère celles
    qui existent déjà. Six filantes trois fois plus fréquentes, et le ciel
    devient un ciel d'août. */
html[data-effet="etoiles"] .ciel-filante { animation-duration: 6s; }
html[data-effet="etoiles"] .ciel-filante-b { animation-duration: 8s; }
html[data-effet="etoiles"] .ciel-filante-c { animation-duration: 5s; }
html[data-effet="etoiles"] .ciel-filante-d { animation-duration: 9s; }
html[data-effet="etoiles"] .ciel-filante-e { animation-duration: 7s; }
html[data-effet="etoiles"] .ciel-filante-f { animation-duration: 4s; }

/*  LE FEU D'ARTIFICE — trois bouquets, et pas une couche qui défile.
 *
 *  UNE EXPLOSION N'EST PAS UN DÉPLACEMENT : elle part d'un point, s'ouvre, et
 *  s'éteint. C'est donc un cercle de points (un `radial-gradient` par éclat)
 *  qu'on AGRANDIT en le faisant disparaître. Le `box-shadow` aurait donné de
 *  vrais éclats séparés, mais il se recalcule à chaque image ; `transform:
 *  scale` est pris en charge par le compositeur, et sur une page de tableau
 *  de bord c'est la différence entre un décor et un ralentissement.
 *
 *  TROIS BOUQUETS, TROIS CADENCES PREMIÈRES ENTRE ELLES (7, 11, 13 s) : ils
 *  ne partent presque jamais ensemble, et le ciel n'a pas de rythme. Chacun
 *  n'est visible qu'un cinquième de son cycle — le reste du temps, il n'y a
 *  rien, et c'est ce qui permet de mettre une explosion dans un outil qu'on
 *  regarde des heures.
 */
.ciel-bouquet {
  width: 220px; height: 220px;
  border-radius: 50%;
  opacity: 0;
  will-change: transform, opacity;
  background:
    radial-gradient(4px 4px at 50% 6%,  rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 82% 20%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 94% 50%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 82% 80%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 50% 94%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 18% 80%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 6%  50%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 18% 20%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(3px 3px at 68% 32%, rgba(var(--fond-aurore-2), 0.9), transparent),
    radial-gradient(3px 3px at 32% 68%, rgba(var(--fond-teinte), 0.9), transparent),
    radial-gradient(3px 3px at 68% 68%, rgba(var(--fond-aurore-2), 0.9), transparent),
    radial-gradient(3px 3px at 32% 32%, rgba(var(--fond-teinte), 0.9), transparent);
}
.ciel-bouquet-1 { top: 0; left: 0; animation: ciel-bouquet-1 7s ease-out infinite; }
.ciel-bouquet-2 { top: 0; left: 0; animation: ciel-bouquet-2 11s ease-out infinite; animation-delay: 2.5s; }
.ciel-bouquet-3 { top: 0; left: 0; animation: ciel-bouquet-3 13s ease-out infinite; animation-delay: 6s; }

/*  LES BOUQUETS N'EXPLOSENT PLUS TOUJOURS AU MÊME ENDROIT — 29/08.
 *  Même défaut que les filantes, même remède : chacun tire DEUX fois par
 *  cycle, à deux endroits, et se replace entre les deux pendant qu'il est
 *  invisible. Un feu d'artifice dont les trois bouquets partent toujours des
 *  mêmes trois points, c'est une image, pas un feu d'artifice.
 *
 *  `scale(0.15)` au repos plutôt que `scale(0)` : à zéro, certains
 *  navigateurs cessent de composer la couche et la remontent d'un cran au
 *  réveil — on voit un clignotement au tout début de l'explosion. */
@keyframes ciel-bouquet-1 {
  0%   { transform: translate3d(14vw, 12vh, 0) scale(0.15); opacity: 0; }
  3%   { transform: translate3d(14vw, 12vh, 0) scale(0.25); opacity: 1; }
  12%  { transform: translate3d(14vw, 12vh, 0) scale(1);    opacity: 0.85; }
  18%  { transform: translate3d(14vw, 12vh, 0) scale(1.3);  opacity: 0; }
  49%  { transform: translate3d(62vw, 30vh, 0) scale(0.15); opacity: 0; }
  52%  { transform: translate3d(62vw, 30vh, 0) scale(0.25); opacity: 1; }
  61%  { transform: translate3d(62vw, 30vh, 0) scale(1);    opacity: 0.8; }
  67%  { transform: translate3d(62vw, 30vh, 0) scale(1.3);  opacity: 0; }
  100% { transform: translate3d(62vw, 30vh, 0) scale(1.3);  opacity: 0; }
}
@keyframes ciel-bouquet-2 {
  0%   { transform: translate3d(74vw, 16vh, 0) scale(0.15); opacity: 0; }
  4%   { transform: translate3d(74vw, 16vh, 0) scale(0.28); opacity: 1; }
  14%  { transform: translate3d(74vw, 16vh, 0) scale(1.05); opacity: 0.8; }
  20%  { transform: translate3d(74vw, 16vh, 0) scale(1.35); opacity: 0; }
  54%  { transform: translate3d(28vw, 34vh, 0) scale(0.15); opacity: 0; }
  58%  { transform: translate3d(28vw, 34vh, 0) scale(0.28); opacity: 0.9; }
  68%  { transform: translate3d(28vw, 34vh, 0) scale(1.05); opacity: 0.7; }
  74%  { transform: translate3d(28vw, 34vh, 0) scale(1.35); opacity: 0; }
  100% { transform: translate3d(28vw, 34vh, 0) scale(1.35); opacity: 0; }
}
@keyframes ciel-bouquet-3 {
  0%   { transform: translate3d(44vw, 8vh, 0) scale(0.15); opacity: 0; }
  3%   { transform: translate3d(44vw, 8vh, 0) scale(0.22); opacity: 0.95; }
  13%  { transform: translate3d(44vw, 8vh, 0) scale(0.95); opacity: 0.75; }
  19%  { transform: translate3d(44vw, 8vh, 0) scale(1.25); opacity: 0; }
  46%  { transform: translate3d(86vw, 42vh, 0) scale(0.15); opacity: 0; }
  49%  { transform: translate3d(86vw, 42vh, 0) scale(0.22); opacity: 0.85; }
  59%  { transform: translate3d(86vw, 42vh, 0) scale(0.95); opacity: 0.65; }
  65%  { transform: translate3d(86vw, 42vh, 0) scale(1.25); opacity: 0; }
  100% { transform: translate3d(86vw, 42vh, 0) scale(1.25); opacity: 0; }
}

/* --- LE BANDEAU DE L'ÉVÉNEMENT ------------------------------------------ */
/*  Il est DANS le flux, en haut du contenu, et pas en `fixed` : un bandeau
    flottant mange une bande d'écran sur toutes les pages, y compris celles
    où l'on remplit un formulaire de quarante champs. Celui-ci se lit une
    fois, puis on défile et il s'en va. */
.fete-bandeau {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 10px 18px;
  font-weight: 600;
  text-align: center;
  color: var(--text);
  background: linear-gradient(90deg,
    rgba(var(--fond-teinte), 0.18), rgba(var(--fond-aurore-2), 0.18));
  border-bottom: 1px solid rgba(var(--fond-aurore-2), 0.35);
}
.fete-bandeau-emoji { font-size: 1.2em; line-height: 1; }
/*  EN PRODUCTION, LE BANDEAU DIT QU'IL N'EST PAS ENCORE PUBLIC. Sans ça, le
    staff voit le décor de Noël, croit qu'il est en ligne, et ne clique jamais
    sur « public ». C'est la panne la plus probable de tout ce mécanisme :
    tout marche, et personne ne voit rien. */
.fete-bandeau-prive {
  font-weight: 500;
  font-size: 0.85em;
  padding: 2px 8px;
  border-radius: 100px;
  background: rgba(var(--prod-rgb), 0.18);
  color: var(--prod);
  border: 1px solid rgba(var(--prod-rgb), 0.4);
}

/*  MOINS DE MOUVEMENT : TOUT S'ARRÊTE, RIEN NE DISPARAÎT.
 *  Le ciel reste peint — aurore, étoiles, voile — il cesse simplement de
 *  bouger, et les filantes ne passent plus. Le réglage d'accessibilité doit
 *  rendre la page supportable, pas plus laide : quelqu'un qui demande moins
 *  d'animations n'a pas demandé un fond noir. */
@media (prefers-reduced-motion: reduce) {
  body::before,
  .ciel-aurore,
  .ciel-etoiles,
  .ciel-fete { animation: none !important; }
  /*  Les bouquets, eux, DISPARAISSENT plutôt que de se figer : une explosion
      arrêtée en plein vol laisserait une grosse fleur de points posée sur
      l'écran pour toujours. Même raison que les filantes. */
  .ciel-bouquet { display: none !important; }
  .ciel-filante { display: none !important; }
}

/*  À L'IMPRESSION, LE DÉCOR DISPARAÎT.
    Un dégradé sombre plein page viderait une cartouche pour rien, et sur la
    plupart des imprimantes il ressortirait en aplat gris sous le texte.
    `display: none` et rien d'autre : ON NE FORCE PAS LE PAPIER EN BLANC.
    Le site écrit en --text (#e6e8ef, presque blanc) ; repeindre le fond en
    blanc rendrait chaque page ILLISIBLE à l'impression au lieu de la nettoyer.
    Rendre tout le site imprimable est un autre chantier que celui-ci — ici on
    retire le décor qu'on vient d'ajouter, on ne change pas ce qui existait. */
@media print {
  body::before,
  body::after { display: none !important; }
}

/*  LE BOUTON « PUBLIC » DES RUBRIQUES — voir views/partials/bouton-public.ejs
 *
 *  Il vit À CÔTÉ d'un lien de menu ou d'un titre : il doit se voir sans
 *  attirer l'œil plus que ce qu'il commande. D'où une taille en `em`, qui
 *  suit celle du texte voisin, plutôt qu'en pixels — posé dans la barre du
 *  haut et dans un titre de page, un bouton en pixels serait énorme dans
 *  l'un et minuscule dans l'autre.
 *
 *  Les deux états ne se distinguent PAS par la seule couleur : la case
 *  cochée ou vide porte l'information, et le titre au survol la répète en
 *  toutes lettres. Une différence uniquement colorée disparaît pour qui
 *  distingue mal le rouge du vert. */
.bp-bouton {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  padding: 0.12em 0.45em;
  font-size: 0.78em;
  line-height: 1.5;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.bp-bouton:hover { background: var(--bg-card); color: var(--text); }
.bp-ouvert { border-color: color-mix(in srgb, #3fb950 55%, var(--border)); color: #6fd07f; }
.bp-ferme  { border-color: color-mix(in srgb, #d64a63 55%, var(--border)); color: #e08494; }
/*  Dans la barre du haut, la place est comptée : on garde la case, on retire
 *  le mot. L'information reste, le titre au survol aussi. */
.navbar .bp-txt { display: none; }
@media (max-width: 900px) { .bp-txt { display: none; } }

/* ===================================================
   RÈGLEMENT — les panneaux de l'éditeur
   ===================================================
 *  POURQUOI CES CLASSES EXISTENT. Ces blocs sont fabriqués en JavaScript,
 *  et le JavaScript posait leur allure EN DUR :
 *
 *      d.style.border = '1px solid rgba(255,255,255,.08)';
 *
 *  Un blanc translucide, écrit à la main, qui passait PAR-DESSUS le
 *  `border: 1px solid var(--border)` de `.form-section` — un style en ligne
 *  bat toujours une classe. Résultat : la bordure ignorait le monde (violet
 *  au public, jaune en prod) et ignorait les couleurs d'événement. Personne
 *  ne voyait d'erreur, parce qu'il n'y en avait pas : les deux moitiés
 *  marchaient, et se contredisaient.
 *
 *  ET CE N'ÉTAIT PAS UNE `.form-section`. Les panneaux prenaient la classe
 *  des grandes cartes, à l'intérieur d'une grande carte : même fond sur même
 *  fond, deux ombres au survol, et l'animation d'entrée rejouée à chaque
 *  ajout. Ils ont maintenant leur propre allure de SOUS-bloc.
 */
.reg-panneau {
  position: relative;
  /*  UN CREUX, PAS UNE CARTE DE PLUS. `--bg-softer` (#1c2030) posé sur le
      `--bg-card` (#1a1e2b) de la section parente, c'est deux teintes d'écart :
      à l'écran, le panneau ne se détachait pas du tout. On descend vers le
      fond de la page : le bloc se lit comme creusé DANS la carte, ce qui est
      exactement ce qu'il est. */
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px 20px 26px;
  margin-bottom: 14px;
  overflow: hidden;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
/*  LE LISERÉ PREND LA COULEUR DU MONDE — et celle de l'événement en cours,
 *  puisque --accent est exactement ce que head.ejs redéfinit. C'est aussi ce
 *  qui distingue un panneau d'un simple encadré, sans ajouter de trait. */
.reg-panneau::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: linear-gradient(to bottom,
              rgba(var(--accent-rgb), 0.85), rgba(var(--accent-rgb), 0.15));
}
.reg-panneau:hover { border-color: rgba(var(--accent-rgb), 0.45); }
.reg-panneau-tete {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.reg-panneau-titre {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/*  Un panneau sans titre ne doit pas ressembler à un panneau nommé « rien ». */
.reg-panneau-titre:empty::after,
.reg-panneau-titre.reg-vide { color: var(--text-dim); font-weight: 500; font-style: italic; }
/*  LE BOUTON DE SUPPRESSION DIT CE QU'IL FAIT. Il ne portait qu'une croix :
 *  au lecteur d'écran, « ✕ » ne s'annonce pas, et à la souris rien ne
 *  prévenait que le bloc partait. Le titre et l'étiquette sont posés par le
 *  JavaScript, dans la langue de la page. */
.reg-supprimer {
  flex: 0 0 auto;
  line-height: 1;
  padding: 6px 10px;
  color: var(--text-muted);
}
.reg-supprimer:hover { color: #e08494; border-color: rgba(214, 74, 99, 0.5); }

/* ===================================================
   ALERTE « INFO » — la variante qui n'existait pas
   ===================================================
 *  DEUX VUES LA DEMANDENT DEPUIS LE DÉBUT, LA FEUILLE NE L'A JAMAIS DÉFINIE.
 *
 *      <div class="alert alert-info">   (règlement, créateur)
 *
 *  `.alert-success`, `.alert-warning` et `.alert-danger` existent ; `info`,
 *  non. Ces deux encadrés ne recevaient donc que le `.alert` de base — de la
 *  marge intérieure et un `display:flex`, sans fond, sans bordure, sans
 *  couleur. Résultat à l'écran : du texte nu posé entre deux cartes, au
 *  milieu d'une page où tout le reste est encadré.
 *
 *  ET RIEN N'ÉCHOUAIT. Une classe CSS absente ne lève pas d'erreur : le
 *  navigateur l'ignore, la page s'affiche, et seul l'œil voit qu'il manque
 *  quelque chose. C'est le même défaut que partout ailleurs ce mois-ci —
 *  deux moitiés qui marchent séparément et ne se rencontrent jamais.
 *
 *  Elle prend la couleur d'accent, donc le violet au public et le jaune en
 *  prod, et les couleurs d'un événement en cours. Une info n'est ni un
 *  succès (vert) ni un avertissement (orange) : lui donner l'une des deux
 *  aurait menti sur son propos.
 */
.alert-info {
  background: rgba(var(--accent-rgb), 0.08);
  border: 1px solid rgba(var(--accent-rgb), 0.28);
  color: var(--text);
}
/*  ERREUR DE MA PART, RELEVÉE PAR LA MESURE DE CONTRASTE.
 *
 *  J'avais écrit `color: var(--accent-texte)`. Ce jeton n'est pas « la
 *  couleur d'accent pour du texte » : c'est la couleur du texte posé SUR
 *  l'accent — donc SOMBRE en production, où l'accent est jaune vif. Sur le
 *  fond sombre de l'encadré, ça donnait du #12141d sur du #22201'5 :
 *  1.12:1, c'est-à-dire un titre invisible. Il fallait mesurer pour le voir,
 *  et personne ne l'aurait vu autrement.
 *
 *  Le nom d'un jeton dit à quoi il sert. `--accent-texte` avait l'air de
 *  vouloir dire « texte accentué » ; il veut dire « texte sur l'accent ». */
.alert-info strong { color: var(--text); }
/*  LE CONTENU D'UNE ALERTE EST UN SEUL BLOC, PAS DEUX COLONNES.
 *
 *  `.alert` est un conteneur flex — prévu pour « icône + texte ». Quand le
 *  contenu est un `<strong>` suivi d'un `<br>` et d'un paragraphe, le gras
 *  devient un ÉLÉMENT FLEX à lui tout seul : il se retrouve dans une colonne
 *  étroite à gauche, coupé sur trois lignes, avec le texte à côté. On a vu
 *  exactement ça sur la page du règlement.
 *
 *  `.alert-texte` remet les deux dans le même bloc. Les cent vingt-deux
 *  autres alertes du site ne bougent pas : c'est une classe qu'on ajoute,
 *  pas une règle qu'on change. */
.alert-texte { flex: 1 1 auto; min-width: 0; }
.alert-texte strong { display: block; margin-bottom: 4px; }

/* ===================================================
   LES CLASSES QUE LES VUES DEMANDAIENT SANS RÉPONSE
   ===================================================
 *  Relevé par `audit-classes.js`, qui compare ce que les gabarits écrivent
 *  dans leurs `class="…"` à ce que les feuilles définissent réellement.
 *  Trente et une classes n'existaient nulle part. Une classe absente ne lève
 *  aucune erreur : le navigateur l'ignore et affiche la page — c'est
 *  exactement pourquoi ces défauts survivent aux tests de rendu.
 *
 *  Toutes ne méritaient pas une règle : beaucoup sont des ancres de script
 *  ou portent déjà leur style en ligne. Celles d'ici changent ce qu'on voit.
 */

/*  `.dashboard-main` — QUATRE PAGES DU PANNEAU N'AVAIENT AUCUNE MARGE.
 *
 *  Événements, tutos et les deux pages d'assistance ouvrent leur contenu par
 *  `<main class="dashboard-main">`. Les autres pages staff écrivent
 *  `class="container"`, les pages de module `class="dashboard-content"` — et
 *  ces deux-là existent. La troisième, jamais. Ces quatre pages collaient
 *  donc au menu à gauche, à la barre en haut, et s'étalaient jusqu'au bord
 *  droit de l'écran quelle que soit sa largeur.
 *
 *  Elle est plus large que `.dashboard-content` (1000 px) parce que ces
 *  pages-là portent des tableaux et des grilles de cartes, pas un formulaire
 *  en colonne. */
.dashboard-main {
  padding: 40px;
  max-width: 1180px;
  min-width: 0;
}
@media (max-width: 900px) { .dashboard-main { padding: 24px 16px; } }

/*  `.table-wrap` — SEPT TABLEAUX QUI DÉBORDAIENT DE LA PAGE.
 *
 *  Le nom dit l'intention : envelopper un tableau trop large. Sans la règle,
 *  l'enveloppe ne faisait rien et c'est la PAGE qui prenait le défilement
 *  horizontal — licences, membres, code source, propositions. Le défilement
 *  appartient au tableau, jamais au corps du document. */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table-wrap > table { min-width: 100%; }

/*  `.btn-warning` — DEUX BOUTONS SANS COULEUR.
 *
 *  `btn-primary`, `btn-danger`, `btn-ghost` existent ; `btn-warning`, non.
 *  Les deux boutons qui la portent tombaient donc sur le `.btn` nu — gris,
 *  au milieu d'une page où la couleur porte le sens de l'action. */
.btn-warning { background: var(--warning); color: #1a1200; }
.btn-warning:hover { filter: brightness(1.08); }

/*  `.etat-pill` et `.badge-status` — DES PASTILLES SANS FORME.
 *
 *  Elles reçoivent leur couleur en ligne (`style="background:…;color:…"`) ou
 *  par une classe d'état, mais rien ne leur donnait de forme : pas de
 *  marge intérieure, pas d'arrondi. La couleur s'appliquait donc à un bout
 *  de texte nu — on voyait une teinte, pas une pastille. */
.etat-pill,
.badge-status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
/*  `.badge-status` est fabriquée en JavaScript avec l'état accolé en classe
 *  (`badge-status open`, `badge-status closed`) : on couvre les mots employés
 *  et on garde un repli neutre pour tous les autres. */
.badge-status { background: var(--bg-softer); color: var(--text-muted); }
.badge-status.open, .badge-status.ouvert, .badge-status.ok {
  background: rgba(61, 220, 151, 0.14); color: var(--success);
}
.badge-status.closed, .badge-status.ferme, .badge-status.error {
  background: rgba(255, 91, 122, 0.14); color: var(--danger);
}
.badge-status.pending, .badge-status.attente {
  background: rgba(251, 146, 60, 0.14); color: var(--warning);
}

/*  UNE ALERTE QUI COMMENCE PAR DU GRAS SE LIT SUR DEUX LIGNES, PAS EN DEUX
 *  COLONNES.
 *
 *  `.alert` est un conteneur flex — prévu pour « icône + texte ». Quand le
 *  contenu commence par `<strong>…</strong><br>`, ce gras devient un ÉLÉMENT
 *  FLEX à part entière : il se tasse dans une colonne étroite à gauche,
 *  souvent coupé sur trois lignes, et le paragraphe se range à côté. Vu sur
 *  la page du règlement, puis sur celle des événements, puis sur quatre
 *  autres — six en tout.
 *
 *  On ne touche pas à `.alert` : les cent dix-sept autres alertes du site
 *  s'en servent comme prévu. On dit seulement qu'un gras EN PREMIÈRE POSITION
 *  prend toute la largeur — ce qui est le seul cas qui cassait. */
.alert { flex-wrap: wrap; }
.alert > strong:first-child { flex: 1 0 100%; margin-bottom: 2px; }

/*  LE BOUTON « PUBLIC » DANS LA BARRE DU HAUT — PLUS DISCRET AU REPOS.
 *
 *  Six de ces boutons se glissent entre les liens de navigation, en prod. Avec
 *  leur bordure permanente, la barre ressemblait à une rangée de cases à
 *  cocher où l'on cherchait les liens. L'information ne se perd pas : la case
 *  ☐ / ☑ porte l'état, la couleur le répète, le titre au survol l'écrit en
 *  toutes lettres — la bordure ne disait rien de plus. Elle revient au survol
 *  et au focus clavier, là où l'on vise. */
.navbar .bp-bouton {
  border-color: transparent;
  padding: 0.12em 0.28em;
  font-size: 0.9em;
}
.navbar .bp-bouton:hover,
.navbar .bp-bouton:focus-visible { border-color: var(--border); }
.navbar .bp-forme { margin-left: 2px !important; }

/* ==========================================================================
 *  PAGES LÉGALES — posées le 09/09/2026
 * ==========================================================================
 *  Ces pages se LISENT, elles ne se parcourent pas : la mesure de ligne est
 *  donc bornée à 66 caractères, la plus lisible en continu, au lieu de suivre
 *  la largeur du conteneur comme le reste du site. C'est le seul endroit du
 *  projet où quelqu'un lit un texte long du début à la fin.
 *
 *  Aucune carte, aucun cadre, aucune ombre : un document légal encadré
 *  ressemble à une publicité pour un document légal. Un filet et de l'espace
 *  suffisent à séparer les sections.
 */
.legal { max-width: 46rem; padding-bottom: 64px; }
.legal-tete { padding: 40px 0 24px; border-bottom: 2px solid var(--border); }
.legal-tete h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 1.2rem + 2vw, 2.4rem); line-height: 1.12; }
.legal-chapeau { margin: 0 0 14px; font-size: 1.05rem; color: var(--text-muted); max-width: 40rem; }
.legal-maj { margin: 0; font-size: 0.82rem; color: var(--text-muted); }

/*  L'avis sur la langue : visible sans crier. Une bordure d'accent à gauche
    plutôt qu'un fond coloré — c'est une précision, pas une alerte. */
.legal-avis {
  margin: 24px 0 0; padding: 12px 0 12px 16px;
  border-left: 3px solid var(--accent-dim);
  color: var(--text-muted); font-size: 0.94rem; max-width: 40rem;
}

.legal-section { margin-top: 34px; }
.legal-section h2 {
  margin: 0 0 10px; font-size: 1.22rem; line-height: 1.25;
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.legal-section p { margin: 0 0 12px; line-height: 1.68; max-width: 42rem; }
.legal-section p:last-child { margin-bottom: 0; }

/*  Terme et définition, pas une liste à puces : ces blocs sont des
    inventaires (une catégorie de données, ce qu'on en fait), et le couple
    dt/dd le dit aussi bien à l'œil qu'à un lecteur d'écran. */
.legal-liste { margin: 14px 0 0; max-width: 42rem; }
.legal-liste dt {
  font-weight: 600; color: var(--text);
  margin-top: 14px; line-height: 1.4;
}
.legal-liste dt:first-child { margin-top: 0; }
.legal-liste dd { margin: 4px 0 0; padding-left: 16px; border-left: 2px solid var(--border); color: var(--text-muted); line-height: 1.62; }

.legal-voisines {
  margin-top: 48px; padding-top: 18px; border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: 20px;
}
.legal-voisines a { font-size: 0.92rem; }

/*  Dans le pied de page, les liens légaux se distinguent des liens de
    navigation : on les cherche, on ne tombe pas dessus. */
.footer-links .footer-legal { opacity: 0.78; }
.footer-links .footer-legal:hover { opacity: 1; }

/* ==========================================================================
 *  EMPLOI DU TEMPS DE L'ACTIVITÉ — la semaine en grille, 09/09/2026
 * ==========================================================================
 *  Sept colonnes et vingt-quatre heures. Les blocs sont positionnés en
 *  absolu dans leur colonne, hauteur proportionnelle à la durée : c'est ce
 *  qui fait qu'un trou se voit sans être compté.
 */
.edt { margin-top: 8px; }
/*  LA GRILLE EST SON PROPRE CADRE DE DÉFILEMENT — corrigé le 09/09/2026.
 *
 *  CE QUI N'ALLAIT PAS. `overflow-x: auto` sur une boîte sans hauteur : elle
 *  glissait horizontalement, mais verticalement c'est LA PAGE qui défilait.
 *  Les titres de jours étaient bien en `sticky`, sauf qu'un élément collant
 *  ne colle qu'à l'intérieur de SON cadre de défilement — et le leur n'en
 *  était pas un. Descendre à 19 h faisait donc disparaître « LUN MAR MER… »
 *  tout en haut, et il restait sept colonnes de blocs sans nom. On croit à un
 *  décalage des colonnes ; en réalité on a perdu leur étiquette.
 *
 *  MAINTENANT : une hauteur bornée, un défilement dans les deux sens, et deux
 *  bandes qui restent. Les jours en haut, les heures à gauche — c'est ce que
 *  fait n'importe quel agenda, et pour cette raison-là. */
.edt-grille {
  display: grid;
  /*  minmax(9.5rem, 1fr) et plus minmax(0, 1fr) — 01/10/2026. Signalé par
      un client (« les jours se chevauchent ») : la piste pouvait rétrécir
      jusqu'à zéro alors que chaque jour exige 9,5rem (.edt-jour min-width).
      Sur un écran étroit, chaque jour débordait sur le suivant. La piste a
      maintenant le même minimum que son contenu : la grille défile sur le
      côté au lieu de se superposer. */
  grid-template-columns: 3.4rem repeat(7, minmax(9.5rem, 1fr));
  gap: 0;
  border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm);
  overflow: auto;
  max-height: min(72vh, 800px);
  /*  PAS de `overscroll-behavior: contain` : arrivé en bas de la grille, la
      molette doit continuer à faire défiler la PAGE. Sinon on se retrouve
      coincé dans un cadre au milieu d'un formulaire, sans comprendre
      pourquoi la page ne bouge plus. */
  background: var(--bg-soft);
}
/*  La gouttière des heures a EXACTEMENT la même structure qu'une colonne de
    jour : un titre de même hauteur, puis une piste de même hauteur. Aucune
    marge à accorder à la main — c'est ce qui garantit que les libellés
    tombent sur les lignes, quelle que soit la hauteur d'une heure. */
/*  La gouttière reste à gauche quand on glisse vers samedi : sans elle, on
    lit des blocs sans savoir à quelle heure ils sont. Fond opaque, sinon le
    contenu défile au travers. */
.edt-heures {
  border-right: 1px solid var(--border);
  position: sticky; left: 0; z-index: 4;
  background: var(--bg-soft);
}
/*  Le coin haut-gauche croise les deux bandes collantes : il doit passer
    devant les deux, sinon un titre de jour lui glisse dessous. */
.edt-titre-vide { background: var(--bg-softer); z-index: 6; }
.edt-piste-heures { cursor: default; }
.edt-heure {
  position: absolute; right: 6px;
  font-size: .68rem; color: var(--text-muted);
  line-height: 1; transform: translateY(-50%);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/*  Minuit se pose sous le bord plutôt qu'à cheval dessus : centré, la moitié
    du texte sortirait de la grille. */
.edt-heure.est-premiere { transform: translateY(0); }
.edt-jour { border-right: 1px solid var(--border); min-width: 9.5rem; }
/*  La colonne du jour, teintée très légèrement : on la retrouve du coin de
    l'œil sans que ça devienne un aplat de couleur. */
.edt-jour.est-aujourdhui { background: rgba(var(--accent-rgb), .05); }
.edt-jour:last-child { border-right: 0; }
.edt-jour-titre {
  height: 30px; display: flex; align-items: center; justify-content: center;
  font-size: .78rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 3;
  background: var(--bg-softer);
}
.edt-jour-titre.est-aujourdhui { color: var(--accent-lisible); }
.edt-piste { position: relative; cursor: copy; }
.edt-trait { position: absolute; left: 0; right: 0; height: 1px; background: var(--border); opacity: .55; }

.edt-bloc {
  position: absolute; left: 3px; right: 3px;
  background: rgba(var(--accent-rgb), .22);
  border: 1px solid rgba(var(--accent-rgb), .55);
  border-left: 3px solid var(--accent);
  border-radius: 3px; padding: 2px 5px; overflow: hidden;
  cursor: pointer; text-align: left;
  display: flex; flex-direction: column; gap: 1px;
}
.edt-bloc:hover, .edt-bloc:focus-visible { background: rgba(var(--accent-rgb), .34); }
/*  La seconde moitié d'un créneau qui enjambe minuit : même bloc, dessin plus
    discret, pour qu'on voie que la nuit est occupée sans croire à un second
    créneau. */
.edt-bloc.est-suite {
  border-style: dashed; background: rgba(var(--accent-rgb), .10);
  border-left-color: rgba(var(--accent-rgb), .5);
}
.edt-bloc.est-suite .edt-bloc-t { color: var(--text-muted); }
/*  Deux créneaux côte à côte : un filet plus marqué pour qu'on voie qu'ils se
    partagent la place, et pas qu'un seul bloc a rétréci. */
.edt-bloc.est-partage { border-left-width: 4px; }
/*  La pastille d'état, posée devant le texte du bloc. Sans elle, rien à
    l'écran ne dirait qu'un créneau change aussi l'état du bot — on ne le
    découvrirait qu'en regardant Discord. */
.edt-pastille-etat {
  display: inline-block; width: .5em; height: .5em; border-radius: 50%;
  margin-right: .4em; vertical-align: middle; flex: 0 0 auto;
}
.edt-pastille-etat.est-absent { background: var(--warning); }
.edt-pastille-etat.est-occupé, .edt-pastille-etat.est-occupe { background: var(--danger); }
.edt-pastille-etat.est-invisible { background: var(--text-muted); }
.edt-bloc-h {
  font-size: .64rem; color: var(--text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto;
}
/*  LE TEXTE NE DOIT PLUS ÊTRE COUPÉ EN PLEIN MOT.
 *  « Cours · Électronique (ET020) » sortait tronqué à « Cours · Electroniqu ».
 *  Deux lignes au plus, coupées proprement par des points de suspension, et
 *  les mots longs peuvent se casser plutôt que déborder de la colonne. */
.edt-bloc-t {
  font-size: .75rem; line-height: 1.22; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}

/*  Le trait « maintenant » — l'heure du SERVEUR, pas celle du navigateur :
    c'est elle qui décide de ce que le bot affiche. */
.edt-maintenant { position: absolute; left: 0; right: 0; height: 2px; background: var(--danger); z-index: 2; }
/*  L'étiquette débordait de la colonne et se posait sur la voisine. Elle est
    maintenant ancrée à GAUCHE, sur un fond opaque, et ne capte pas le clic —
    sinon elle empêcherait d'ajouter un créneau à cette heure-là. */
.edt-maintenant-mot {
  position: absolute; left: 3px; top: -.95em;
  font-size: .58rem; color: var(--danger);
  text-transform: uppercase; letter-spacing: .06em;
  background: var(--bg-soft); padding: 0 4px; border-radius: 2px;
  pointer-events: none; white-space: nowrap;
}
.edt-maintenant { pointer-events: none; }

.edt-aide { margin: 10px 0 0; }

/* ---- le panneau d'ajout / modification ---- */
.edt-form {
  margin-top: 14px; padding: 16px;
  background: var(--bg-card); border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm);
}
.edt-form.est-faux { border-color: var(--danger); }
.edt-form-titre { margin: 0 0 12px; font-size: 1rem; }
.edt-champ { margin-bottom: 12px; }
.edt-champ label { display: block; margin-bottom: 4px; font-size: .85rem; color: var(--text-muted); }
.edt-champ input, .edt-champ select {
  width: 100%; background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  padding: 8px 10px; font-family: inherit; font-size: .95rem;
}
.edt-heures-champs { display: flex; gap: 12px; }
.edt-heures-champs .edt-champ { flex: 1; }

.edt-pastilles { display: flex; flex-wrap: wrap; gap: 6px; }
.edt-pastille {
  background: var(--bg-softer); color: var(--text-muted);
  border: 1px solid var(--border-champ); border-radius: 999px;
  padding: 6px 12px; font-family: inherit; font-size: .82rem; cursor: pointer;
}
.edt-pastille.est-prise {
  background: rgba(var(--accent-rgb), .22); color: var(--text);
  border-color: var(--accent);
}
.edt-apercu {
  margin: 0 0 12px; padding: 8px 10px;
  background: var(--bg-softer); border-radius: var(--radius-sm);
  font-size: .85rem; color: var(--text-muted);
}
.edt-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.edt-actions .edt-supprimer { color: var(--danger); border-color: var(--danger); }

/* ---- le format texte, replié ---- */
.edt-brut { margin-top: 12px; }
.edt-brut summary { cursor: pointer; font-size: .88rem; color: var(--text-muted); }
.edt-brut textarea {
  width: 100%; margin-top: 8px; font-family: ui-monospace, monospace; font-size: .85rem;
}

.edt-conflit {
  margin-bottom: 14px; padding: 10px 12px;
  border-left: 3px solid var(--warning);
  background: rgba(251, 146, 60, .08); border-radius: 0 3px 3px 0;
  font-size: .9rem;
}
.edt-conflit strong { display: block; margin-bottom: 4px; }
/*  Les trous ne sont pas une faute — juste une information. Bleu plutôt
    qu'orange : on ne met pas au même niveau « décide quelque chose » et
    « au fait, il n'y a rien ici ». */
.edt-trous { border-left-color: var(--accent); background: rgba(var(--accent-rgb), .07); }

/*  La barre d'onglets n'existe que sur téléphone : sur grand écran on voit
    la semaine entière, un sélecteur de jour n'y servirait qu'à ajouter un
    clic. */
.edt-onglets { display: none; }
.edt-onglet {
  flex: 1 1 auto; min-width: 2.6rem; min-height: 44px;
  background: var(--bg-softer); color: var(--text-muted);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .82rem; font-weight: 600; cursor: pointer;
}
.edt-onglet.est-choisi {
  background: rgba(var(--accent-rgb), .22); color: var(--text); border-color: var(--accent);
}
.edt-onglet.est-aujourdhui { text-decoration: underline; text-underline-offset: 3px; }

/* ==========================================================================
 *  SUR TÉLÉPHONE : UN JOUR À LA FOIS — 09/09/2026
 * ==========================================================================
 *  Sept colonnes de 9,5 rem plus la gouttière font 1120 px. Sur un écran de
 *  390, on fait glisser la grille en permanence et on ne sait plus quelle
 *  colonne on lit : les colonnes n'étaient pas fausses, elles étaient hors de
 *  l'écran. On n'en montre donc qu'une, choisie par les onglets — c'est ce
 *  que fait n'importe quel agenda de téléphone, et pour cette raison-là.
 */
@media (max-width: 760px) {
  .edt-onglets { display: flex; gap: 4px; margin-bottom: 8px; }
  .edt-grille { grid-template-columns: 3rem minmax(0, 1fr); max-height: 60vh; }
  /*  `display: none` et pas `visibility` : un élément caché ne prend pas de
      cellule de grille, donc la colonne restante occupe toute la largeur. */
  .edt-jour:not(.est-choisi) { display: none; }
  .edt-jour { min-width: 0; }
  .edt-jour-titre { font-size: .72rem; }
  .edt-bloc-t { font-size: .78rem; -webkit-line-clamp: 3; }
  .edt-bloc-h { font-size: .66rem; }
  /*  Le formulaire passe en une colonne : deux champs côte à côte sur 390 px
      donnent deux champs illisibles. */
  .edt-heures-champs { flex-direction: column; gap: 0; }
  /*  Des cibles qu'un doigt peut viser : 44 px est le minimum admis. */
  .edt-pastille { min-height: 44px; padding: 6px 14px; }
  .edt-champ input, .edt-champ select { min-height: 44px; font-size: 16px; }
  .edt-actions .btn { flex: 1 1 auto; min-height: 44px; }
}

/*  Entre les deux : on garde les sept jours mais on serre les colonnes. */
@media (min-width: 761px) and (max-width: 1100px) {
  .edt-jour { min-width: 7.2rem; }
  .edt-bloc-t { font-size: .7rem; }
}

/* ==========================================================================
 *  LA CARTE « EN CE MOMENT » — 10/09/2026
 * ==========================================================================
 *  « Savoir dans combien de temps on passe à la prochaine activité… un truc
 *  clean, pro, et cool à regarder. »
 *
 *  C'est la seule carte de cette page, et c'est voulu : border, fond et ombre
 *  disent « objet distinct », et les dépenser partout aplatit la hiérarchie.
 *  Ici il n'y a qu'une chose à regarder en arrivant — ce que le bot affiche
 *  maintenant, et pour combien de temps encore.
 */
.edt-carte {
  margin-bottom: 16px; padding: 16px 18px;
  background: linear-gradient(180deg, rgba(var(--accent-rgb), .16), rgba(var(--accent-rgb), .06));
  border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: var(--radius-sm);
}
.edt-carte-haut { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.edt-carte-quand {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-muted);
}
/*  Le compte à rebours est LE chiffre de la carte : c'est lui qu'on vient
    chercher, il a donc le corps le plus gros et les chiffres alignés. */
.edt-carte-reste {
  font-size: 1.45rem; font-weight: 700; color: var(--accent-lisible);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.edt-carte-titre {
  font-size: 1.18rem; font-weight: 600; margin-top: 6px;
  line-height: 1.25; overflow-wrap: anywhere;
}
.edt-carte-sous { font-size: .82rem; color: var(--text-muted); margin-top: 2px; }
.edt-carte-jauge {
  height: 5px; margin-top: 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .08); overflow: hidden;
}
.edt-carte-barre {
  height: 100%; width: 0; border-radius: 999px;
  background: var(--accent);
  transition: width .6s linear;
}
.edt-carte-suite { font-size: .84rem; color: var(--text-muted); margin-top: 10px; }

@media (prefers-reduced-motion: reduce) { .edt-carte-barre { transition: none; } }
@media (max-width: 760px) {
  .edt-carte-haut { flex-direction: column; gap: 2px; }
  .edt-carte-reste { font-size: 1.7rem; }
}

/* ==========================================================================
 *  LA CLOCHE DES NOUVEAUTÉS — 10/09/2026, page d'accueil, production
 * ==========================================================================
 *  Posée en haut à droite de la zone d'accueil, discrète tant qu'il n'y a
 *  rien de neuf. La pastille est la seule couleur vive de la page : c'est
 *  elle qui doit attraper l'œil, et rien d'autre autour ne doit lui faire
 *  concurrence.
 */
/*  ELLE NE FLOTTE PLUS — corrigé le 10/09/2026, sur capture.
 *
 *  CE QUI SE PASSAIT, ET POURQUOI ÇA NE RESSEMBLAIT À RIEN. La cloche était
 *  en `float: right`. Or `.hero` porte `overflow: hidden`, ce qui en fait un
 *  CONTEXTE DE BLOC — et un contexte de bloc, par définition, ne passe pas
 *  SOUS un flottant : il se rétrécit à côté. La boîte du hero était donc
 *  rognée sur toute la hauteur de la cloche, et comme c'est elle qui peint le
 *  dégradé et les étoiles, on voyait une bande verticale plus sombre courir
 *  le long de la page. Le fond n'était pas mal choisi : il manquait.
 *
 *  ON SORT DONC DU FLUX POUR DE BON. En absolu par rapport à `.landing`, la
 *  cloche ne pousse plus rien, ne rogne plus rien, et se pose PAR-DESSUS le
 *  décor — ce qu'on voulait depuis le début.
 *
 *  La leçon vaut au-delà d'ici : un flottant à côté d'un `overflow: hidden`
 *  ne se superpose jamais, il déplace. Pour poser un élément SUR un autre,
 *  c'est `position`, jamais `float`. */
.cloche { position: absolute; top: 20px; right: 24px; z-index: 20; }
/*  LE FOND DU BOUTON EST TRANSLUCIDE — corrigé le 10/09/2026, sur capture.
 *
 *  CE QUI CLOCHAIT. Il portait `--bg-card`, un gris BLEUTÉ (#1a1e2b), posé
 *  sur le dégradé du hero — qui, en production, est chaud : le jaune de
 *  l'accent et ses halos. Un rectangle froid sur un fond chaud ne se pose
 *  pas dessus, il se colle dessus. « Ça fait bizarre », et c'était exact.
 *
 *  DEUX CORRECTIONS, ET LA PREMIÈRE COMPTE PLUS QUE LA SECONDE :
 *
 *   1. LE FOND NE VIENT PLUS D'UN JETON FIXE. Du noir translucide avec un
 *      flou d'arrière-plan : le bouton prend la teinte de CE QU'IL Y A
 *      DERRIÈRE, quel que soit le monde, le thème ou l'événement saisonnier
 *      en cours. Il ne pourra plus jurer avec un décor qu'on changera dans
 *      six mois sans penser à lui.
 *   2. LA BORDURE ET LE HALO SONT CEUX DE LA PASTILLE DU HERO, à quelques
 *      lignes d'ici : même liseré d'accent, même halo à 8 %. Il ne s'agit
 *      pas de recopier une valeur mais d'entrer dans un vocabulaire qui
 *      existe déjà sur cette page — c'est ce qui fait qu'un élément a l'air
 *      d'y appartenir plutôt que d'y avoir été ajouté.
 *
 *  Le repli sans flou (vieux navigateur) reste lisible : le noir translucide
 *  assombrit assez le décor pour que le texte tienne son contraste.
 */
.cloche-bouton {
  /*  RONDE ET SANS TEXTE — 10/09/2026 : « on va juste mettre la cloche ».
   *  Une taille fixe plutôt qu'un remplissage : le bouton doit être un
   *  disque, et un padding calculé sur la hauteur d'un emoji donne un ovale
   *  différent selon la police que le système choisit pour le dessiner. */
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; padding: 0;
  background: rgba(10, 11, 15, .55);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(var(--accent-rgb), .55);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .08);
  border-radius: 999px; cursor: pointer; list-style: none;
  font-size: .88rem; font-weight: 600; color: var(--text); user-select: none;
  transition: border-color .15s, box-shadow .15s;
}
/*  Le triangle par défaut d'un `<details>` n'a rien à faire sur un bouton
    rond — les deux navigateurs ne le dessinent pas au même endroit. */
.cloche-bouton::-webkit-details-marker { display: none; }
.cloche-bouton::marker { content: ''; }
.cloche-bouton:hover, .cloche[open] .cloche-bouton {
  border-color: var(--accent);
  box-shadow: 0 0 0 5px rgba(var(--accent-rgb), .14);
}
.cloche-icone { font-size: 1.15rem; line-height: 1; }
/*  La pastille se pose EN HAUT À DROITE du disque, comme sur n'importe quelle
    application — c'est le seul endroit où l'œil la cherche. Le liseré de la
    couleur du fond la détache du bouton sans lui ajouter d'ombre. */
.cloche-pastille {
  position: absolute; top: -3px; right: -5px;
  min-width: 22px; height: 20px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--danger); color: #fff;
  border: 2px solid var(--bg);
  border-radius: 999px; font-size: .7rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}
/*  Le panneau, lui, reste OPAQUE : on y lit du texte, et un fond qui laisse
    passer des étoiles derrière trois paragraphes se paie en lisibilité. Il
    emprunte seulement le liseré d'accent, pour qu'on voie qu'il appartient au
    bouton qui l'ouvre. */
.cloche-panneau {
  position: absolute; right: 0; top: calc(100% + 10px);
  width: min(24rem, calc(100vw - 32px));
  max-height: min(60vh, 30rem); overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid rgba(var(--accent-rgb), .35);
  border-radius: var(--radius-sm);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .55);
  padding: 4px;
}
.cloche-item { padding: 12px 14px; border-bottom: 1px solid var(--border); }
.cloche-item:last-child { border-bottom: 0; }
.cloche-item-haut { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.cloche-quand { font-size: .72rem; color: var(--text-muted); margin-left: auto; }
.cloche-item h3 { margin: 0 0 4px; font-size: .95rem; line-height: 1.3; }
.cloche-item p { margin: 0; font-size: .85rem; color: var(--text-muted); line-height: 1.5; white-space: pre-line; }
.cloche-lien { display: inline-block; margin-top: 8px; font-size: .84rem; }
.cloche-vide { margin: 0; padding: 18px 14px; text-align: center; color: var(--text-muted); font-size: .88rem; }
/*  Le lien vers l'historique, posé en pied du panneau : on le trouve après
    avoir lu, pas avant. */
.cloche-tout {
  display: block; padding: 11px 14px; text-align: center;
  font-size: .84rem; border-top: 1px solid var(--border);
}

/*  La pastille de genre, partagée avec le panneau staff : une couleur par
    nature d'annonce, posée sur 13 % d'elle-même comme les rôles du site. */
.actu-genre {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  font-weight: 700; padding: 2px 8px; border-radius: 999px;
  background: rgba(var(--accent-rgb), .18); color: var(--accent-lisible);
}
.actu-genre.est-correction { background: rgba(61, 220, 151, .16); color: var(--success); }
.actu-genre.est-maintenance { background: rgba(251, 146, 60, .16); color: var(--warning); }
.actu-genre.est-important { background: rgba(255, 91, 122, .16); color: var(--danger); }

/* ---- le panneau staff ---- */
.actus-liste { display: flex; flex-direction: column; gap: 1px; background: var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.actu-ligne { background: var(--bg-card); padding: 14px 16px; }
/*  Un brouillon se voit d'un coup d'œil : hachuré, il ne se confond pas avec
    ce qui est en ligne. */
.actu-ligne.est-brouillon { background: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-soft) 10px, var(--bg-soft) 20px); }
.actu-ligne-haut { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.actu-brouillon { font-size: .72rem; color: var(--warning); text-transform: uppercase; letter-spacing: .06em; }
.actu-texte { margin: 8px 0 0; color: var(--text-muted); font-size: .9rem; white-space: pre-line; }
.actu-pied { margin: 8px 0 0; font-size: .78rem; color: var(--text-muted); }
.actu-actions { display: flex; gap: 8px; margin-top: 10px; }
.actu-actions .actu-supprimer { color: var(--danger); border-color: var(--danger); }

@media (max-width: 760px) {
  .cloche { top: 14px; right: 14px; }
  .cloche-panneau { right: 0; width: calc(100vw - 28px); }
}

/* ==========================================================================
 *  LE FIL DES NOUVEAUTÉS — accueil et page dédiée, 10/09/2026
 * ==========================================================================
 *  Une chronologie verticale : un trait, un point par annonce. La forme dit
 *  ce que le contenu est — une suite d'événements dans le temps — là où une
 *  grille de cartes dirait « des choses côte à côte », ce qui est faux.
 *
 *  Le trait est tracé par la bordure gauche de la liste, pas par un élément :
 *  un fil dessiné en dur dépasserait sous le dernier point.
 */
.fil { max-width: 52rem; margin: 0 auto; padding: 56px 24px 8px; }
.fil-tete { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 22px; }
.fil-tete h2 { margin: 0; font-size: 1.5rem; }
.fil-tout { font-size: .88rem; white-space: nowrap; }

.fil-liste { list-style: none; margin: 0; padding: 0 0 0 22px; border-left: 1px solid var(--border); }
.fil-item { position: relative; padding: 0 0 26px 20px; }
.fil-item:last-child { padding-bottom: 0; }

/*  Le point mord sur le trait : décalé de sa moitié plus l'épaisseur du
    trait, il tombe pile dessus quelle que soit la taille du texte. */
.fil-point {
  position: absolute; left: -27px; top: 6px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
}
.fil-point.est-correction { background: var(--success); }
.fil-point.est-maintenance { background: var(--warning); }
.fil-point.est-important { background: var(--danger); }

.fil-haut { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
.fil-quand { font-size: .75rem; color: var(--text-muted); }
.fil-corps h2, .fil-corps h3 { margin: 0 0 6px; font-size: 1.02rem; line-height: 1.3; }
.fil-corps p { margin: 0; color: var(--text-muted); font-size: .92rem; line-height: 1.6; white-space: pre-line; }
.fil-lien { font-size: .88rem; }
.fil-signature { margin-top: 8px !important; font-size: .8rem; }

/* ---- la page dédiée ---- */
.fil-page { max-width: 46rem; padding-bottom: 64px; }
.fil-page-tete { padding: 40px 0 24px; border-bottom: 2px solid var(--border); margin-bottom: 28px; }
.fil-page-tete h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 1.2rem + 2vw, 2.4rem); }
.fil-page .fil-corps h2 { font-size: 1.12rem; }

@media (max-width: 760px) {
  .fil { padding: 36px 16px 4px; }
  .fil-tete { flex-direction: column; gap: 4px; }
}

/* ==========================================================================
 *  TEST — LE SUIVI DE RECETTE, 11/09/2026
 * ==========================================================================
 *  Une grille de cinquante lignes se lit à la COULEUR avant de se lire au
 *  texte. Chaque sélecteur porte donc la teinte de son état : on voit où en
 *  est la recette sans lire une seule ligne.
 *
 *  C'est le seul endroit du panneau où la couleur porte de l'information
 *  plutôt que de la décoration — et c'est pour ça qu'elle est vive ici alors
 *  qu'elle est sobre partout ailleurs.
 */
.rec-avancement { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.rec-jauge { height: 8px; border-radius: 999px; background: var(--bg-softer); overflow: hidden; }
.rec-jauge-barre { height: 100%; background: var(--success); border-radius: 999px; transition: width .4s ease; }
.rec-chiffres { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.rec-pourcent { font-size: 1.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.rec-detail { font-size: .9rem; color: var(--text-muted); }
.rec-detail b { font-variant-numeric: tabular-nums; }
.rec-detail .est-ok { color: var(--success); }
.rec-detail .est-ko { color: var(--danger); }

.rec-barre { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.rec-onglet, .rec-filtre {
  padding: 8px 14px; min-height: 40px; display: inline-flex; align-items: center;
  background: var(--bg-softer); border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm); font-size: .88rem; color: var(--text-muted);
}
.rec-onglet.est-choisi, .rec-filtre.est-choisi {
  background: rgba(var(--accent-rgb), .18); color: var(--text); border-color: var(--accent);
}
.rec-filtre { margin-left: auto; }

.rec-table-boite { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.rec-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.rec-table th, .rec-table td { padding: 7px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.rec-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg-softer); font-size: .72rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted); white-space: nowrap;
}
.rec-nom { font-weight: 600; white-space: nowrap; font-family: ui-monospace, monospace; font-size: .86rem; }
.rec-table tbody tr:hover { background: rgba(var(--accent-rgb), .04); }

/*  L'étiquette de chaque champ existe pour les lecteurs d'écran : sans elle,
    on entend « liste déroulante » deux cents fois sans savoir de quoi. Elle
    est retirée de l'affichage, pas de la page — jamais `display: none`, qui
    la retirerait aussi de l'arbre d'accessibilité. */
.rec-etiq {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.rec-etat {
  width: 100%; min-width: 7rem; padding: 5px 8px;
  background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .85rem;
}
.rec-etat.est-ok { border-color: var(--success); color: var(--success); background: rgba(61, 220, 151, .10); }
.rec-etat.est-ko { border-color: var(--danger); color: var(--danger); background: rgba(255, 91, 122, .10); }
.rec-etat.est-sans-objet { color: var(--text-muted); opacity: .65; }

.rec-note-cell { min-width: 16rem; }
.rec-note-cell textarea {
  width: 100%; min-height: 38px; padding: 6px 8px; resize: vertical;
  background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .85rem;
}

.rec-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 16px; }
.rec-vider { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rec-vider .btn { color: var(--danger); border-color: var(--danger); }

@media (max-width: 760px) {
  .rec-filtre { margin-left: 0; }
  .rec-etat { min-width: 5.5rem; font-size: .8rem; }
  .rec-note-cell { min-width: 12rem; }
}


/* ======================================================================
 *  LA BARRE DU HAUT, CÔTÉ COMPTE — 19/09/2026
 * ======================================================================
 *  LE DÉFAUT : pour un simple utilisateur connecté, la droite de la barre
 *  empilait QUATRE poids visuels sans rapport — un bouton violet plein, un
 *  avatar posé à nu sur le fond, une puce dorée « premium » et un mot gris.
 *  La puce dorée était la classe `staff-link`, réservée au staff, posée par
 *  erreur sur « Mes licences » : tout le monde portait donc un badge de
 *  staff au milieu de la barre. Quatre styles collés ne font pas un
 *  ensemble : c'est ça qui se voyait, sans qu'on sache le nommer.
 *
 *  LE PARTI PRIS : une seule zone « compte », lisible d'un coup d'œil.
 *    · l'identité devient une PASTILLE — même langage que le bouton des
 *      deux mondes juste à côté, donc rien de nouveau à apprendre ;
 *    · les deux liens qui l'accompagnent ont le MÊME poids, discret : ils
 *      accompagnent, ils ne rivalisent pas avec le bouton principal ;
 *    · un seul élément reste plein et coloré, « Dashboard », parce qu'une
 *      barre qui met tout en avant ne met rien en avant.
 */
.nav-separateur {
  width: 1px; height: 22px; flex: 0 0 auto;
  background: var(--border); margin: 0 4px;
}

/* --- L'identité : une pastille qui se tient ---------------------------- */
.navbar .user-menu {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 4px 13px 4px 4px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--bg-softer);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.navbar .user-menu:hover {
  border-color: rgba(var(--accent-rgb), 0.45);
  background: var(--bg-card);
}
/* La bordure de 2 px de l'avatar faisait doublon avec celle de la pastille :
   deux cadres concentriques à 3 px d'écart, ça se voit et ça salit. */
.navbar .user-menu .avatar {
  width: 28px; height: 28px; border: none;
  box-shadow: 0 0 0 1px var(--border);
}
.navbar .user-menu .username {
  font-size: 0.9rem; font-weight: 600; color: var(--text);
}

/* --- Les deux liens du compte : même poids, même hauteur --------------- */
.navbar .nav-licences,
.navbar .nav-deconnexion {
  display: inline-flex; align-items: center;
  padding: 7px 13px; border-radius: 999px;
  font-size: 0.88rem; font-weight: 500;
  color: var(--text-muted);
  border: 1px solid transparent;
  transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.navbar .nav-licences:hover {
  color: var(--text); background: var(--bg-softer);
  border-color: var(--border); text-decoration: none;
}
/* Se déconnecter n'est pas une action anodine : le survol le dit — en
   couleur, pas en taille. Au repos il reste aussi discret que son voisin,
   pour ne pas transformer la barre en avertissement permanent. */
.navbar .nav-deconnexion:hover {
  color: var(--danger); background: var(--bg-card);
  border-color: var(--danger); text-decoration: none;
}

/* --- Quand la place manque, on resserre, on ne casse rien -------------- */
@media (max-width: 1400px) {
  .navbar .nav-licences,
  .navbar .nav-deconnexion { padding: 6px 10px; font-size: 0.85rem; }
  .nav-separateur { margin: 0 2px; }
}
/* Sous 1620 px le pseudo est déjà masqué plus haut : la pastille se referme
   symétriquement autour du seul avatar, au lieu de garder son creux à droite. */
@media (max-width: 1620px) {
  .navbar .user-menu { padding: 4px; gap: 0; }
}
/* Menu « ☰ » (sous 1140 px) : en colonne, une pastille arrondie n'a plus de
   sens — chaque entrée devient une ligne pleine largeur, visable au doigt. */
@media (max-width: 1140px) {
  .nav-separateur { display: none; }
  .nav-burger:checked ~ .nav-actions .user-menu {
    border-radius: var(--radius-sm);
    padding: 10px; gap: 9px; justify-content: flex-start;
  }
  .navbar .nav-licences,
  .navbar .nav-deconnexion {
    border-radius: var(--radius-sm); justify-content: center;
  }
  /*  « Mes licences » est masqué sous 1220 px parce que la barre sur UNE
   *  ligne n'a plus la place. Dans le menu déplié, la place existe : le
   *  cacher là n'aurait plus aucune raison d'être, et le lien redeviendrait
   *  introuvable sur téléphone. On le remet donc ici, et seulement ici. */
  .nav-burger:checked ~ .nav-actions > .nav-licences { display: inline-flex; }
}


/* ======================================================================
 *  REFONTE DE LA PAGE D'ACCUEIL — 19/09/2026
 * ======================================================================
 *  Trois blocs nouveaux, et une règle commune : rien ne bouge sans raison,
 *  et rien n'est dessiné qui ne corresponde à une donnée réelle.
 *
 *    1. le bandeau d'actus qui défile, en remplacement de la cloche ;
 *    2. le hero en deux colonnes — le logo à gauche, ce qu'on fait à droite ;
 *    3. les cartes de chiffres et la grille d'actus illustrées.
 *
 *  LES ANIMATIONS NE TOUCHENT QUE `transform` ET `opacity` : ce sont les
 *  deux seules propriétés que le compositeur sait animer sans repeindre la
 *  page. Le réglage système « moins d'animations » les éteint toutes, plus
 *  bas — un bandeau qui défile sans qu'on puisse l'arrêter est un problème
 *  d'accessibilité, pas un effet de style.
 */

/* ---------------------------------------------------------------- 1. BANDEAU */
.bandeau-actus {
  position: relative; overflow: hidden;
  background: rgba(10, 12, 18, 0.92);
  border-bottom: 1px solid var(--border);
}
/*  LES DEUX VOILES SUR LES CÔTÉS. Sans eux, les titres apparaissent et
 *  disparaissent net au bord de l'écran, comme coupés au ciseau. */
.bandeau-actus::before,
.bandeau-actus::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 64px;
  z-index: 2; pointer-events: none;
}
.bandeau-actus::before { left: 0;  background: linear-gradient(90deg, rgba(10,12,18,.95), transparent); }
.bandeau-actus::after  { right: 0; background: linear-gradient(270deg, rgba(10,12,18,.95), transparent); }

/*  `width: max-content` : la piste fait la largeur de son contenu, pas celle
 *  de l'écran — sans ça les éléments se compriment au lieu de défiler. */
.bandeau-actus-piste {
  display: flex; align-items: center; width: max-content;
  animation: bandeau-defile 48s linear infinite;
}
/*  ON S'ARRÊTE AU SURVOL : un titre qui s'enfuit pendant qu'on le lit (ou
 *  qu'on vise son lien) est une petite cruauté. */
.bandeau-actus:hover .bandeau-actus-piste,
.bandeau-actus:focus-within .bandeau-actus-piste { animation-play-state: paused; }
/*  -50 % et pas -100 % : la liste est écrite DEUX fois, donc la moitié de la
 *  piste est exactement une liste complète. Au bout, on est revenu au même
 *  pixel qu'au départ et la boucle ne se voit pas. */
@keyframes bandeau-defile {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.bandeau-actu {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 22px; white-space: nowrap;
  border-right: 1px solid var(--border);
  color: var(--text-muted); font-size: 0.82rem;
  transition: color .15s ease, background .15s ease;
}
.bandeau-actu:hover { color: var(--text); background: rgba(255,255,255,.035); text-decoration: none; }
.bandeau-titre { font-weight: 500; }
.bandeau-date { opacity: .55; font-variant-numeric: tabular-nums; }

/* --- Les pastilles d'importance (bandeau ET cartes d'actus) ------------- */
.bandeau-pastille, .actu-pastille {
  display: inline-flex; align-items: center; padding: 2px 8px;
  border-radius: 5px; border: 1px solid transparent;
  font-size: 0.65rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; white-space: nowrap;
}
.bandeau-pastille.est-nouveaute, .actu-pastille.est-nouveaute { color:var(--accent-lisible); background:rgba(var(--accent-rgb),.16); border-color:rgba(var(--accent-rgb),.36); }
.bandeau-pastille.est-info,      .actu-pastille.est-info      { color:#7dd3fc; background:rgba(56,189,248,.14); border-color:rgba(56,189,248,.32); }
.bandeau-pastille.est-important, .actu-pastille.est-important { color:#fdba74; background:rgba(251,146,60,.15); border-color:rgba(251,146,60,.35); }
.bandeau-pastille.est-urgent,    .actu-pastille.est-urgent    { color:#fda4af; background:rgba(255,91,122,.16); border-color:rgba(255,91,122,.40); }

/* ---------------------------------------------------------------- 2. HERO */
/*  `.hero.hero-duo` et pas `.hero-duo` seul : le hero d'origine est centré,
 *  et deux classes l'emportent sur une seule sans avoir à écrire !important. */
.hero.hero-duo {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  align-items: center; gap: clamp(28px, 5vw, 72px);
  max-width: 1200px; margin: 0 auto;
  padding: clamp(38px, 6vw, 84px) 24px;
  text-align: left;
}
.hero-visuel { position: relative; display: flex; align-items: center; justify-content: center; }
.hero-logo {
  position: relative; z-index: 1;
  width: min(100%, 300px); height: auto;
  animation: hero-flotte 7s ease-in-out infinite;
}
/*  Une respiration de 16 px en sept secondes : on la remarque à peine, et
 *  c'est le but — le logo est vivant, il ne gesticule pas. */
@keyframes hero-flotte {
  0%, 100% { transform: translateY(-8px); }
  50%      { transform: translateY(8px); }
}
.hero-halo {
  /* BOÎTE CARRÉE, CENTRÉE SUR LE LOGO. Avec `inset: -6%` la boîte suivait
   * la colonne : large sur grand écran, haute sur petit. Le `border-radius:
   * 50%` découpait alors une ELLIPSE dans un dégradé rond : un bord net, que
   * le flou transformait en bande. Ici la boîte est carrée, le dégradé et le
   * masque s'éteignent à `closest-side`, c'est-à-dire pile sur le bord. */
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(130%, 420px); aspect-ratio: 1 / 1; border-radius: 50%;
  /* Paliers serrés + grain SVG très léger : sans eux, anneaux visibles sur
   * fond sombre (banding 8 bits). Pas de filter: blur, volontairement. */
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.12 0'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>"),
    radial-gradient(circle closest-side,
      rgba(var(--accent-rgb), .40) 0%, rgba(var(--accent-rgb), .36) 15%, rgba(var(--accent-rgb), .29) 30%,
      rgba(var(--accent-rgb), .21) 45%, rgba(var(--accent-rgb), .14) 60%, rgba(var(--accent-rgb), .08) 74%,
      rgba(var(--accent-rgb), .04) 85%, rgba(var(--accent-rgb), .01) 94%, transparent 100%);
  -webkit-mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
}


.hero.hero-duo .hero-texte { text-align: left; }
.hero.hero-duo .hero-texte h1 { margin: 0 0 14px; text-wrap: balance; }
.hero.hero-duo .hero-sub { margin: 0 0 26px; max-width: 54ch; }
.hero.hero-duo .hero-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: flex-start; }

/* ------------------------------------------------------- 3. LES CHIFFRES */
.stats-cartes {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
  gap: 16px; max-width: 1200px; margin: 0 auto;
  padding: 0 24px clamp(40px, 6vw, 72px);
}
.stat-carte {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 8px; min-height: 148px;
  padding: 18px 18px 16px;
  border: 1px solid var(--border); border-radius: 14px;
  background: linear-gradient(158deg, var(--teinte-faible), var(--bg-card) 64%);
}
/*  Le filet de couleur en haut : c'est lui qui distingue les cartes d'un
 *  coup d'oeil, sans avoir à teinter tout le fond et à perdre le contraste
 *  du texte par-dessus. */
.stat-carte::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--teinte);
}
.stat-nom { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); font-weight: 700; }
.stat-chiffre {
  margin: 0; display: flex; align-items: baseline; gap: 9px;
  font-size: clamp(1.9rem, 3.4vw, 2.4rem); font-weight: 800; line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.stat-delta { font-size: .78rem; font-weight: 600; color: var(--teinte); }
.stat-note { margin: 0; font-size: .8rem; color: var(--text-muted); }
/*  `margin-top:auto` colle le graphique en bas : les cartes n'ont pas toutes
 *  le même nombre de lignes, et sans ça les barres flotteraient à une hauteur
 *  différente sur chaque carte. */
.stat-barres { margin-top: auto; display: flex; align-items: flex-end; gap: 5px; height: 34px; }
.stat-barres span { flex: 1; border-radius: 3px; background: var(--teinte); opacity: .85; }
.stat-carte.est-violet { --teinte:var(--accent-lisible); --teinte-faible: rgba(var(--accent-rgb),.15); }
.stat-carte.est-bleu   { --teinte:#22d3ee; --teinte-faible: rgba(34,211,238,.13); }
.stat-carte.est-rose   { --teinte:#f472b6; --teinte-faible: rgba(244,114,182,.13); }
.stat-carte.est-vert   { --teinte:#34d399; --teinte-faible: rgba(52,211,153,.13); }
.stat-carte.est-jaune  { --teinte:#fbbf24; --teinte-faible: rgba(251,191,36,.13); }

/* ------------------------------------------------ 4. LES ACTUS ILLUSTRÉES */
.actus-section { max-width: 1200px; margin: 0 auto; padding: 0 24px clamp(48px, 7vw, 80px); }
.actus-tete { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.actus-tete h2 { margin: 0; font-size: clamp(1.3rem, 2.4vw, 1.75rem); }
.actus-tout {
  font-size: .85rem; font-weight: 600; color: var(--text-muted);
  border: 1px solid var(--border); padding: 7px 14px; border-radius: 999px;
  transition: color .15s ease, border-color .15s ease;
}
.actus-tout:hover { color: var(--text); border-color: var(--accent); text-decoration: none; }
.actus-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); gap: 18px; }
.actu-carte {
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  background: var(--bg-card);
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.actu-carte:hover {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), .5);
  box-shadow: 0 18px 38px -18px rgba(0,0,0,.75);
}
/*  TOUTE LA CARTE EST LE LIEN, pas seulement le titre : une zone cliquable
 *  de la taille d'une vignette se vise sans effort, au doigt comme à la
 *  souris. D'où les `span` à l'intérieur — un `<a>` ne peut pas contenir de
 *  blocs de texte structurés sans devenir invalide. */
.actu-lien-total { display: flex; flex-direction: column; height: 100%; color: inherit; }
.actu-lien-total:hover { text-decoration: none; }
.actu-image { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--bg-softer); }
.actu-image img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s ease; }
.actu-carte:hover .actu-image img { transform: scale(1.04); }
.actu-corps { display: flex; flex-direction: column; gap: 9px; padding: 15px 16px 16px; flex: 1; }
.actu-haut { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.actu-genre-texte { font-size: .72rem; color: var(--text-muted); }
.actu-titre { font-weight: 700; font-size: 1.02rem; line-height: 1.35; color: var(--text); text-wrap: balance; }
/*  Trois lignes maximum : au-delà, les cartes d'une même rangée n'ont plus
 *  la même hauteur et la grille se met à boiter. */
.actu-extrait {
  font-size: .87rem; color: var(--text-muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.actu-pied {
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--border);
  display: flex; justify-content: space-between; gap: 10px;
  font-size: .76rem; color: var(--text-muted);
}
.actu-quand { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- 5. LES ÉCRANS ÉTROITS */
@media (max-width: 900px) {
  .hero.hero-duo { grid-template-columns: 1fr; text-align: center; gap: 24px; }
  .hero.hero-duo .hero-texte { text-align: center; }
  .hero.hero-duo .hero-sub { margin-left: auto; margin-right: auto; }
  .hero.hero-duo .hero-cta { justify-content: center; }
  .hero-logo { width: min(58%, 210px); }
  .bandeau-actu { padding: 8px 16px; font-size: .78rem; }
}

/* ------------------------------------------------- 6. « MOINS D'ANIMATIONS »
 *  Le réglage système est un choix, pas une préférence esthétique : certaines
 *  personnes ont mal au coeur devant une page qui bouge. On arrête TOUT ce
 *  qui bouge tout seul — le bandeau se lit alors en le survolant, et le logo
 *  reste simplement posé. */
@media (prefers-reduced-motion: reduce) {
  .bandeau-actus-piste, .hero-logo { animation: none; }
  .actu-carte, .actu-image img { transition: none; }
  .actu-carte:hover { transform: none; }
  .actu-carte:hover .actu-image img { transform: none; }
}


/* ======================================================================
 *  UN CIEL QU'ON VOIT — 19/09/2026
 * ======================================================================
 *  « Les etoiles vont pas du tout, fais un truc plus comme eux. »
 *
 *  LE DIAGNOSTIC. Le decor n'etait pas casse : il etait peint a 10 %
 *  d'opacite sur un fond presque noir. Mesurable, et invisible a l'oeil —
 *  exactement le piege deja rencontre ici en aout (« 194 mesures disaient que
 *  le decor est peint ; elles repondaient a cote de la question »).
 *
 *  CE QUI MANQUAIT, ET CE QU'ON AJOUTE :
 *    · DE LA COULEUR — une nebuleuse violet/bleu/rose, floutee, qui donne au
 *      fond une profondeur coloree au lieu d'un noir plat ;
 *    · DU DESSIN — des etoiles RELIEES entre elles. Des poussieres
 *      eparpillees ne se lisent pas comme un ciel ; des constellations, si.
 *
 *  LA REGLE DE LISIBILITE NE BOUGE PAS : le point le plus clair du decor
 *  reste sous la clarte d'une carte. La nebuleuse est floutee a 40 px, ce qui
 *  etale sa couleur au lieu de la concentrer, et aucune de ses taches ne
 *  depasse 0,30 d'alpha avant floutage.
 */

/* --- La nebuleuse : la couleur du fond ---------------------------------- */
.ciel-nebuleuse {
  inset: -25%;
  background-image:
    radial-gradient(38% 30% at 18% 22%, rgba(var(--accent-rgb), 0.30), transparent 70%),
    radial-gradient(32% 26% at 82% 16%, rgba(56, 189, 248, 0.20), transparent 70%),
    radial-gradient(34% 28% at 74% 76%, rgba(244, 114, 182, 0.17), transparent 70%),
    radial-gradient(30% 24% at 26% 82%, rgba(52, 211, 153, 0.12), transparent 72%);
  filter: blur(40px);
  animation: ciel-nebuleuse 72s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes ciel-nebuleuse {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.04); }
  50%  { transform: translate3d(2%, 3%, 0)   scale(1.12); }
  100% { transform: translate3d(3%, -3%, 0)  scale(1.06); }
}

/* --- Les constellations : des etoiles reliees ---------------------------
 *  UNE IMAGE SVG EN LIGNE, PAS UN FICHIER : zero requete reseau, et le motif
 *  se redimensionne sans jamais pixeliser. La tuile fait 700 px et derive
 *  lentement — a cette vitesse, on ne voit pas la repetition, on voit un ciel
 *  qui tourne. */


/*  LE RESEAU D'ETOILES — 19/09/2026.
 *  Tout le mouvement est dans js/ciel-reseau.js : ici on ne fait que poser la
 *  toile. Elle est DANS `.ciel`, donc sous le contenu, transparente aux clics
 *  et jamais lue par un lecteur d'ecran. */
.ciel-reseau {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block; pointer-events: none;
}




/* ------------------------------------------------------------------------ *
 *  LE MONDE PROD : le même site, en jaune.
 * ------------------------------------------------------------------------ *
 *  Seuls les jetons d'accent changent. Les fonds, les bordures et les
 *  couleurs de signal restent identiques : c'est le MÊME site, pas un autre
 *  thème — on doit reconnaître ses repères, juste savoir où l'on est.
 *
 *  --accent-texte repasse en sombre : c'est obligatoire, pas esthétique.
 *  Du blanc sur ce jaune tombe à 1.53:1 et devient illisible.
 */
html[data-monde="prod"] {
  --accent: #facc15;
  --accent-rgb: 250, 204, 21;
  /* En jaune, la couleur de marque tient telle quelle sous du texte sombre
     (11.99:1) : pas besoin d'assombrir le fond des boutons. */
  --accent-plein: #facc15;
  --accent-texte: #12141d;
  --accent-hover: #fde047;
  --accent-dim: #a16207;
  /*  En production, l'accent atteint déjà 10.84:1 comme texte : le jeton
   *  lisible n'a rien à éclaircir, il EST l'accent. */
  --accent-lisible: #facc15;
  /* Le premium en prod : le jaune demandé. C'est l'or d'origine, donc rien ne
     bouge visuellement de ce côté — c'est le site public qui change. */
  --premium: #ffe9a8;
  --premium-rgb: 255, 233, 168;
  --premium-2: #facc15;
  --premium-2-rgb: 250, 204, 21;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  /* ------------------------------------------------------------------ *
   *  LA PAGE NE DÉFILE JAMAIS SUR LES CÔTÉS
   * ------------------------------------------------------------------ *
   *  Symptôme observé : une bande NOIRE à droite, sous la barre du haut.
   *  L'explication : la barre est collante, elle ne peint que la largeur de
   *  la fenêtre. Dès qu'un seul élément dépasse et rend la page défilable
   *  horizontalement, tout ce qui est au-delà se retrouve sans fond.
   *
   *  `clip` et non `hidden` : `hidden` crée un conteneur de défilement, ce
   *  qui casse `position: sticky` — la barre du haut cesserait de coller.
   *  `clip` empêche le défilement sans rien créer.
   *
   *  Ce n'est pas un tapis sous lequel on pousse la poussière : ce qui a
   *  vraiment besoin d'être large a déjà son propre défilement
   *  (`.table-defilante` pour les tableaux, `pre` ci-dessous pour le code).
   */
  overflow-x: clip;
}
/* Le code ne pousse plus la page : il défile dans son cadre. */
pre { overflow-x: auto; max-width: 100%; }

html, body {
  background: var(--bg);
  color: var(--text);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 16px;
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent-lisible); text-decoration: none; }
a:hover { color: var(--accent-hover); }
code { background: var(--bg-softer); padding: 2px 6px; border-radius: 4px; font-size: 0.9em; color: var(--premium); }

/* ======= NAVBAR ======= */
.navbar {
  position: sticky; top: 0; z-index: 100;
  background: rgba(15, 17, 21, 0.92);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.navbar-inner {
  /* 1600, et non 1280 comme le contenu des pages : mesurée dans un vrai
     navigateur, la barre complète réclame 1560 px. Les deux contraintes n'ont
     aucune raison d'être identiques — une barre porte bien plus d'éléments
     qu'un paragraphe n'a besoin de largeur. Alignées à 1280, les éléments se
     chevauchaient : le lien « Staff » passait par-dessus le bouton PROD. */
  max-width: 1600px; margin: 0 auto;
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 24px; gap: clamp(10px, 1.6vw, 24px);
}
.brand { display: flex; align-items: center; gap: 10px; color: var(--text); font-weight: 700; font-size: 1.2rem; }
/* La marque est passée d'un émoji à une image : il lui faut une taille fixe,
   sinon la barre saute pendant le chargement. `display:block` supprime le
   petit espace sous les images en ligne, qui décalait le nom vers le haut. */
.brand-logo { display: block; width: 42px; height: 42px; flex: 0 0 auto; }

/* --- Le bouton des deux mondes ------------------------------------------
 * Il doit se remarquer sans crier : on est soit sur le site normal, soit sur
 * la production, et se tromper de monde n'est pas une petite erreur. D'où la
 * pastille de couleur, lisible d'un coup d'œil même sans lire le texte. */
.monde-bascule {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-softer);
  color: var(--text-muted); font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.4px; text-transform: uppercase;
  transition: border-color 0.15s, color 0.15s;
}
.monde-bascule:hover { border-color: var(--accent); color: var(--text); }
.monde-point {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--text-dim); flex: 0 0 auto;
}
.monde-bascule.en-prod { border-color: var(--danger); color: var(--danger); }
/* « Prod ? » : le droit d'entrer existe, la configuration non. En pointillé
   pour qu'on voie tout de suite que ce n'est pas un bouton comme les autres. */
.monde-bascule.a-configurer { border-style: dashed; color: var(--text-dim); }
.monde-bascule.a-configurer .monde-point { background: var(--warning); }
.monde-bascule.en-prod .monde-point { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
.monde-lecture {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(251, 146, 60, 0.14); color: var(--warning);
  font-size: 0.75rem; font-weight: 600;
}
/* ------------------------------------------------------------------------ *
 *  LA BARRE DU HAUT NE DÉBORDE JAMAIS
 * ------------------------------------------------------------------------ *
 *  Mesuré dans un vrai navigateur : à 1400 px la barre débordait de 141 px,
 *  à 1100 px de 283 px. Comme elle est collante, elle ne peint que la largeur
 *  de la fenêtre — en défilant vers la droite on tombait sur du NOIR sous
 *  « Déconnexion ». C'était ça, la bande noire.
 *
 *  Entre 1200 et 1500 px on retire seulement du confort : le mot du bouton,
 *  puis le pseudo (l'avatar reste, on sait qui est connecté). En dessous de
 *  1200, le menu « ☰ » prend le relais et TOUT redevient atteignable —
 *  cacher « Déconnexion » n'aurait jamais été acceptable.
 */
/* Les seuils viennent de mesures, pas d'estimations. Besoin de la barre :
   1560 px complète · 1426 sans le pseudo ni le badge · 1353 sans le mot du
   bouton. Chaque seuil est posé un cran AVANT que la place manque. */
@media (max-width: 1620px) {
  .navbar .username { display: none; }
  .navbar .badge { display: none; }
  .monde-lecture { display: none; }
}
@media (max-width: 1500px) {
  .monde-nom { display: none; }
  .monde-bascule { padding: 5px 8px; }
}

/* ---- Mode compact : la barre écrite tient jusqu'au téléphone -------------
 * Plutôt que de replier la navigation dans un « ☰ » dès 1400 px, on la
 * resserre. Les liens restent lisibles et surtout VISIBLES : sur un
 * ordinateur, cacher la navigation derrière un bouton est une régression.
 * On rogne dans cet ordre : les espaces, la taille du texte, les rembourrages
 * — jamais le nombre de liens. */
@media (max-width: 1400px) {
  .navbar-inner { padding: 12px 16px; gap: 10px; }
  .nav-links { gap: 14px; }
  .nav-links a { font-size: 0.92rem; }
  .staff-link { padding: 5px 9px !important; }
  .navbar .btn { padding: 8px 14px; font-size: 0.9rem; }
  /* Sélecteur de langue en menu déroulant (drapeau + nom) — 14/09/2026 */
.lang-picker { position: relative; }
.lang-picker > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.04); font-size: .9rem; font-weight: 500; color: inherit; }
.lang-picker > summary::-webkit-details-marker, .lang-picker > summary::marker { display: none; content: ""; }
.lang-picker .lang-chevron { font-size: .7rem; opacity: .7; transition: transform .15s; }
.lang-picker[open] .lang-chevron { transform: rotate(180deg); }
.lang-menu { position: absolute; top: calc(100% + 6px); right: 0; min-width: 168px; background: #161a26; border: 1px solid rgba(255,255,255,.12); border-radius: 10px; padding: 6px; box-shadow: 0 12px 34px rgba(0,0,0,.45); z-index: 200; }
.lang-opt { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px; color: inherit; text-decoration: none; font-size: .9rem; }
.lang-opt:hover { background: rgba(var(--accent-rgb), .15); }
.lang-opt.active { color: var(--accent); font-weight: 600; }
.lang-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); margin-left: auto; }

.lang-switch { transform: scale(0.92); }
}
@media (max-width: 1220px) {
  .nav-links { gap: 10px; }
  .nav-links a { font-size: 0.86rem; }
  .navbar .btn { padding: 7px 11px; font-size: 0.85rem; }
  .navbar .avatar { width: 26px; height: 26px; }
  /* Le raccourci « Mes licences » : un confort, pas une porte. Il reste
     atteignable depuis la page Premium. */
  .navbar-inner .nav-actions > a[href*="mes-licences"] { display: none; }
}
@media (max-width: 1180px) {
  .nav-links { gap: 8px; }
  .nav-links a { font-size: 0.82rem; }
  .staff-link { padding: 4px 8px !important; }
  .navbar .btn { padding: 6px 10px; font-size: 0.82rem; }
  .lang-switch { transform: scale(0.85); }
  .brand { font-size: 1.02rem; }
  .brand-logo { width: 34px; height: 34px; }
}
@media (max-width: 1080px) {
  .nav-links { gap: 6px; }
  .nav-links a { font-size: 0.78rem; }
  .navbar-inner { padding: 10px 10px; gap: 8px; }
  .navbar .avatar { width: 24px; height: 24px; }
}
/* --- La barre du haut tient sur une ligne ---------------------------------
 * L'ajout du bouton des deux mondes a fait déborder la barre : « Mes bots »
 * se coupait en deux lignes et le bouton sortait de l'écran à gauche.
 *
 * Trois réglages, chacun pour une raison :
 *   - nowrap sur les liens : un intitulé de menu ne doit jamais se couper au
 *     milieu, c'est illisible et ça déforme la hauteur de la barre ;
 *   - un espacement qui rétrécit quand la place manque (clamp) au lieu de
 *     pousser le contenu dehors ;
 *   - min-width:0 sur les groupes, sans quoi un enfant flex refuse de
 *     rétrécir sous sa largeur naturelle et déborde en silence.
 */
.nav-links { display: flex; gap: clamp(10px, 1.6vw, 24px); }
.nav-links a { color: var(--text-muted); font-weight: 500; white-space: nowrap; }
.nav-links a:hover, .nav-links a.active { color: var(--text); }
.nav-actions { display: flex; align-items: center; gap: clamp(6px, 0.8vw, 12px); min-width: 0; flex-wrap: nowrap; }
.nav-actions > * { flex: 0 0 auto; }
.username { white-space: nowrap; }
.user-menu { display: flex; align-items: center; gap: 10px; }
.avatar { width: 32px; height: 32px; border-radius: 50%; border: 2px solid var(--border); }
.username { color: var(--text); font-weight: 500; }

/* ======= BUTTONS ======= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 18px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font-weight: 600; cursor: pointer; font-size: 0.95rem; font-family: inherit;
  transition: all 0.2s ease; text-decoration: none;
  /*  `.btn` NE DÉCLARAIT AUCUNE COULEUR DE TEXTE.
   *
   *  Les variantes en posent une — `btn-primary`, `btn-danger`, `btn-ghost` —
   *  mais un bouton nu, `class="btn btn-sm"`, retombait sur la couleur par
   *  défaut du navigateur pour un `<button>` : du NOIR. Sur le fond sombre du
   *  site, mesuré à 1.5:1 sur les filtres d'avis de la page Tickets.
   *
   *  Deux cent quatre-vingts boutons du projet n'ont pas de variante. Ils
   *  étaient tous dans ce cas, et ça ne se voyait pas : le noir sur gris très
   *  sombre se lit encore un peu, juste assez pour qu'on croie à un choix. */
  color: var(--text);
  background: transparent;
}
.btn-primary { background: var(--accent-plein); color: var(--accent-texte); }
.btn-primary:hover { background: var(--accent-hover); color: var(--accent-texte); transform: translateY(-1px); box-shadow: 0 10px 20px -5px rgba(var(--accent-rgb), 0.5); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border); }
.btn-ghost:hover { background: var(--bg-card); border-color: var(--accent); color: var(--text); }
.btn-sm { padding: 6px 12px; font-size: 0.85rem; }
.btn-lg { padding: 14px 28px; font-size: 1.05rem; }
.btn-danger { background: var(--danger); color: var(--sur-couleur); }

/* `btn-ghost` sert à deux choses différentes dans les gabarits :
   · avec `btn` — une cinquantaine d'endroits : un vrai bouton secondaire,
     encadré (« Annuler », « Se connecter », les filtres du panel staff) ;
   · seule — trois endroits, tous dans `partials/navbar.ejs` (Mes bots,
     ✨ licences, Déconnexion) : un lien discret, sans cadre.
   Une seconde définition de `.btn-ghost` retirait bordure et remplissage pour
   obtenir le second aspect. Écrite plus bas, elle gagnait sur la première :
   les cinquante vrais boutons ressemblaient donc à du texte inerte, sur
   téléphone comme sur ordinateur. Le `:not(.btn)` sépare les deux cas sans
   toucher un seul gabarit. */
.btn-ghost:not(.btn) {
  background: transparent; color: var(--text-muted);
  padding: 6px 10px; border: none; font-weight: 500;
}
.btn-ghost:not(.btn):hover { color: var(--text); text-decoration: underline; }

/* ======= LANDING / HERO ======= */
/*  `position: relative` sert de repère à la cloche, posée en absolu par-dessus
    le hero. Voir la note de `.cloche` : c'est ce qui remplace le flottant. */
.landing { overflow-x: hidden; position: relative; }
.hero { position: relative; padding: 100px 24px 80px; overflow: hidden; }
.hero-inner { max-width: 900px; margin: 0 auto; text-align: center; position: relative; z-index: 2; }
/*  LA PASTILLE DU HERO — corrigée le 29/08, sur capture.
 *
 *  Elle posait du texte en `--accent` sur un fond `rgba(--accent, .15)` :
 *  le fond était une teinte de l'encre. Tant que l'accent était le violet de
 *  la marque et le fond de page presque noir, ça passait. Pendant Halloween,
 *  la pastille s'est retrouvée en orange sur orange, posée en plus sur un
 *  halo orange du hero — et elle s'est lue comme une tache brune avec
 *  quelque chose d'écrit dedans.
 *
 *  LE CALCUL DISAIT POURTANT 6,3:1. C'est la leçon de cette correction : le
 *  ratio était bon parce qu'il compare une LUMINANCE, et il ne dit rien du
 *  fait que l'encre et le fond soient la même couleur. Deux tons du même
 *  orange peuvent passer WCAG et rester illisibles à l'œil.
 *
 *  DONC : un fond de carte, opaque et neutre, et la couleur de l'événement
 *  garde la bordure — c'est elle qui porte l'identité, pas le remplissage.
 *  La pastille est ainsi la même sur un halo clair ou sur du noir. */
.hero-badge {
  display: inline-block; padding: 6px 14px; border-radius: 100px;
  background: var(--bg-card); color: var(--text);
  font-size: 0.85rem; font-weight: 600; margin-bottom: 24px;
  border: 1px solid rgba(var(--accent-rgb), 0.55);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.08);
}
.hero h1 { font-size: clamp(2.2rem, 5vw, 4rem); font-weight: 800; line-height: 1.1; margin-bottom: 24px; }
/*  LE TITRE : du dégradé de l'accent vers la SECONDE teinte du décor, et
    non vers un rose écrit en dur. Pendant Halloween, « comme un boss »
    partait en orange et finissait en rose bonbon — deux couleurs qui n'ont
    aucun rapport, sur le mot le plus regardé de la page. */
.gradient { background: linear-gradient(135deg, var(--accent) 0%, rgb(var(--fond-aurore-2)) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero-sub { font-size: 1.2rem; color: var(--text-muted); max-width: 650px; margin: 0 auto 36px; }
.hero-cta { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; margin-bottom: 56px; }
.hero-stats { display: flex; gap: 48px; justify-content: center; flex-wrap: wrap; }
.hero-stats div { display: flex; flex-direction: column; align-items: center; }
.hero-stats strong { font-size: 2.2rem; color: var(--text); font-weight: 800; }
.hero-stats span { color: var(--text-muted); font-size: 0.9rem; }

/*  LES TROIS HALOS DU HERO — repris le 29/08.
 *
 *  DEUX D'ENTRE EUX NE SUIVAIENT RIEN. `.orb-2` était rose (#ff6bcb) et
 *  `.orb-3` verte (#3ddc97), écrites en dur bien avant que le site ait deux
 *  mondes et des événements. Résultat visible sur la capture d'Halloween :
 *  un ciel orange, un halo rose à droite et une tache VERTE en bas — trois
 *  couleurs qui ne se parlent pas. Ce n'est pas une question de goût : ces
 *  deux halos-là ignoraient aussi la production, où tout le reste est ambre.
 *
 *  LE TROISIÈME SUIVAIT L'ACCENT, ET C'ÉTAIT PIRE. Son opacité de 0,4 avait
 *  été réglée pour le violet de la marque. L'orange d'Halloween est deux fois
 *  et demie plus lumineux à opacité égale : le même réglage donnait un aplat
 *  orange sur tout le tiers gauche. C'est exactement la marche que le projet
 *  avait déjà rencontrée entre le violet et l'ambre de la production.
 *
 *  Ils prennent donc les teintes du DÉCOR (--fond-teinte / --fond-aurore-2),
 *  qui suivent déjà le monde ET l'événement, et leur force est un jeton :
 *  --orbe-force, plus faible en production comme tout le reste. */
.hero-decoration { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
/*  LE FLOU EST DESCENDU DE 80 À 55 px — signalé le 29/08 : « je trouve que
    c'est un peu flou en fond ». À 80 px sur des cercles de 300 à 400, il ne
    restait plus de forme du tout : trois taches sans bord, qui se lisent
    comme un voile sale posé sur la page plutôt que comme des lumières. À
    55 px les halos ont de nouveau un centre et une chute — c'est ce qui fait
    la différence entre « éclairé » et « embué ». */
.orb { position: absolute; border-radius: 50%; filter: blur(55px); opacity: var(--orbe-force); }
.orb-1 { width: 400px; height: 400px; background: rgb(var(--fond-teinte)); top: -100px; left: -100px; }
/*  LE HALO DE DROITE EST PLUS FAIBLE QUE LES DEUX AUTRES, et c'est mesuré :
    il porte la SECONDE teinte, qui est la plus claire des deux par
    construction (un bleu à côté du violet, un violet à côté de l'orange).
    À force égale, c'est lui qui faisait tomber l'accueil public à 4,92:1 —
    la seule page des six à échouer, parce que c'est la seule qui a des
    halos de hero. */
.orb-2 { width: 300px; height: 300px; background: rgb(var(--fond-aurore-2)); top: 100px; right: -50px;
  opacity: calc(var(--orbe-force) * 0.62); }
/*  UN FLOU COUPÉ NET N'EST PLUS UN FLOU : C'EST UN RECTANGLE.
 *
 *  `.hero` porte `overflow: hidden` — il le faut, sinon les halos débordent
 *  sur le reste de la page. Les trois orbes sont donc découpées par ses
 *  bords. Pour deux d'entre elles c'est sans conséquence : elles sortent en
 *  haut à gauche et à droite, c'est-à-dire aux BORDS DE L'ÉCRAN, où personne
 *  ne voit la coupure.
 *
 *  La troisième sortait par le BAS, avec `bottom: -50px` — donc en plein
 *  milieu de la page, là où l'œil se pose. Additionné aux 55 px de flou, ça
 *  faisait 105 px de halo tranchés d'un trait horizontal net : un rectangle
 *  clair, aux quatre coins vifs, posé derrière le bouton « Ajouter à
 *  Discord ». C'est la première chose qu'on voit en arrivant sur le site.
 *
 *  RÈGLE : une surface floutée de N pixels doit rester à N pixels au moins à
 *  l'intérieur du cadre qui la découpe. Ici : 250 px de haut, 55 px de flou,
 *  donc au moins 55 px de marge basse. On en met 60. */
.orb-3 { width: 250px; height: 250px; background: rgb(var(--fond-teinte)); bottom: 60px; left: 40%;
  opacity: calc(var(--orbe-force) * 0.6); }

/* ======= FEATURES PREVIEW ======= */
.features-preview { padding: 80px 24px; max-width: 1280px; margin: 0 auto; }
.features-preview h2 { font-size: 2.2rem; text-align: center; margin-bottom: 48px; }
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
.feature-card {
  background: var(--bg-card); padding: 28px; border-radius: var(--radius);
  border: 1px solid var(--border); transition: all 0.3s ease;
}
.feature-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); transform: translateY(-2px); }
.feature-icon { font-size: 2.4rem; margin-bottom: 12px; }
.feature-card h3 { font-size: 1.25rem; margin-bottom: 8px; }
.feature-card p { color: var(--text-muted); font-size: 0.95rem; }
/*  LA CARTE EST DEVENUE UN LIEN — elle mène à la fiche du module sur la page
 *  Fonctionnalités. Un `<a>` hérite du bleu et du soulignement du navigateur :
 *  sans ces deux lignes, les huit cartes de la page d'arrivée se seraient
 *  affichées en bleu souligné, ce qui n'aurait échappé à personne — mais
 *  autant ne pas le laisser arriver. */
.feature-card { display: block; text-decoration: none; color: inherit; }
/*  LE PALIER SE DIT, ET C'EST L'ARGUMENT LE PLUS FORT DE LA PAGE : la plupart
 *  de ces modules s'ouvrent en gratuit, et personne ne le sait avant de
 *  l'avoir lu. Discret, mais présent. */
.feature-palier {
  display: inline-block;
  margin-top: 14px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--accent-lisible);
}

/* ======= CTA BAND ======= */
/*  LE BANDEAU DE FIN — dernier rose écrit en dur, retiré le 29/08.
 *
 *  Il partait de l'accent et finissait sur `rgba(255,107,203,.1)`, le même
 *  rose que les halos du hero et le titre. Sur une capture de l'événement
 *  « Été », ça se voyait tout de suite : un ciel bleu, des cartes bleues, et
 *  le grand bandeau du bas qui vire au violet. Ce n'est pas une faute de
 *  goût isolée — c'est le même défaut que les orbes, dans un troisième
 *  endroit : des couleurs de marque figées avant que le site ait deux mondes
 *  et des événements. Chaque fois qu'on en trouve une, il faut chercher les
 *  autres.
 *
 *  Il va maintenant de l'accent à la SECONDE teinte du décor : violet→bleu
 *  d'ordinaire, bleu→ambre en été, vert→rouge à Noël. */
.cta-band {
  text-align: center; padding: 80px 24px;
  background: linear-gradient(135deg,
    rgba(var(--accent-rgb), 0.15), rgba(var(--fond-aurore-2), 0.12));
  margin-top: 60px;
}
.cta-band h2 { font-size: 2rem; margin-bottom: 12px; }
.cta-band p { color: var(--text-muted); margin-bottom: 28px; }

/* ======= CONTAINER ======= */
.container { max-width: 1280px; margin: 0 auto; padding: 40px 24px; }
.center-column { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 70vh; }
.page-head { margin-bottom: 32px; }
.page-head.center { text-align: center; max-width: 700px; margin-left: auto; margin-right: auto; }
.page-head h1 { font-size: 2rem; margin-bottom: 8px; }
.page-head p { color: var(--text-muted); }

/* ======= SERVER GRID ======= */
.server-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.server-card {
  display: flex; align-items: center; gap: 16px;
  background: var(--bg-card); padding: 20px; border-radius: var(--radius);
  border: 1px solid var(--border); color: var(--text);
  transition: all 0.2s ease;
}
.server-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); color: var(--text); transform: translateX(4px); }
.server-icon { width: 48px; height: 48px; border-radius: 50%; }
.server-icon-placeholder {
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--bg-softer); display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 1.2rem; color: var(--text-muted);
}
.server-info { flex: 1; }
.server-name { font-weight: 600; font-size: 1.05rem; }
.server-role { margin-top: 4px; }
.server-arrow { color: var(--text-dim); font-size: 1.3rem; }
.server-card-add { border-style: dashed; }

.tag {
  display: inline-block; padding: 2px 8px; border-radius: 4px;
  font-size: 0.75rem; font-weight: 600;
}
.tag-owner { background: rgba(var(--premium-rgb), 0.15); color: var(--premium); }
.tag-admin { background: rgba(var(--accent-rgb), 0.15); color: var(--accent-lisible); }

.muted { color: var(--text-muted); font-size: 0.9rem; }

/* ======= DASHBOARD LAYOUT (avec sidebar) ======= */
.dashboard-layout { display: grid; grid-template-columns: 260px 1fr; min-height: calc(100vh - 72px); }
.sidebar {
  background: var(--bg-soft); padding: 24px; border-right: 1px solid var(--border);
  position: sticky; top: 72px; height: calc(100vh - 72px); overflow-y: auto;
}
.sidebar-server { display: flex; flex-direction: column; align-items: center; gap: 10px; padding-bottom: 24px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.sidebar-server-icon, .sidebar-server-icon-placeholder { width: 64px; height: 64px; border-radius: 50%; }
.sidebar-server-icon-placeholder { background: var(--bg-softer); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.5rem; color: var(--text-muted); }
.sidebar-server-name { font-weight: 600; text-align: center; }
.sidebar-nav { display: flex; flex-direction: column; gap: 4px; }
.sidebar-nav a { padding: 10px 12px; border-radius: var(--radius-sm); color: var(--text-muted); font-weight: 500; transition: all 0.15s; }
.sidebar-nav a:hover { background: var(--bg-card); color: var(--text); }
.sidebar-nav a.active { background: var(--accent-plein); color: var(--accent-texte); }
/*  LA PASTILLE DU MENU STAFF — combien de demandes attendent.
 *
 *  Elle existe parce qu'un onglet sans signal ne se visite pas : la file des
 *  demandes de suppression d'avis est restée invisible tant qu'il fallait
 *  cliquer dessus pour savoir si elle était vide.
 *
 *  ELLE EST DIMENSIONNÉE EN `em`, PAS EN PIXELS : posée dans un lien dont la
 *  taille de police suit celle du menu, une pastille en pixels grossit ou
 *  rétrécit de travers dès que le menu change d'échelle.
 *
 *  Le lien passe en `flex` pour que le nombre aille à droite sans dépendre
 *  d'une largeur devinée ; `min-width` garde le rond ROND à un chiffre et le
 *  laisse s'allonger à deux ou trois. La couleur ne vient pas de l'accent :
 *  sur le lien actif, l'accent devient le fond, et une pastille de la même
 *  couleur y disparaîtrait — c'est pour ça qu'elle a sa propre paire. */
.sidebar-nav a:has(.sf-pastille) { display: flex; align-items: center; gap: 8px; }
.sf-pastille {
  margin-left: auto;
  min-width: 1.55em;
  padding: 0 0.42em;
  height: 1.55em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  /* #d64a63 mesurait 4,18:1 contre le blanc — sous le seuil de 4,5:1, et
     mesuré, pas supposé : verif-pastille-avis.js calcule le contraste. Un
     nombre qu'on ne lit pas ne sert à rien, et une pastille est faite pour
     être lue d'un coup d'œil. #c9304b donne 5,24:1. */
  background: #c9304b;
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* Sur le lien actif le fond est déjà l'accent : la pastille se détache par
   un contour clair plutôt que par sa couleur, qui s'y noierait. */
.sidebar-nav a.active .sf-pastille { box-shadow: 0 0 0 1.5px var(--accent-texte, #fff); }

.sidebar-section { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--text-dim); margin: 16px 8px 4px; }
.dashboard-content { padding: 40px; max-width: 1000px; }

/* ======= STATS / MODULES ======= */
.stats-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; margin-bottom: 32px; }
.stat-card { background: var(--bg-card); padding: 20px; border-radius: var(--radius); border: 1px solid var(--border); }
.stat-label { display: block; color: var(--text-muted); font-size: 0.85rem; margin-bottom: 4px; }
.stat-value { display: block; font-size: 1.8rem; font-weight: 700; }

.section-title { font-size: 1.4rem; margin: 24px 0 16px; }
.module-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; }
.module-card {
  background: var(--bg-card); padding: 20px; border-radius: var(--radius);
  border: 1px solid var(--border); text-align: center; color: var(--text);
  transition: all 0.2s ease;
}
.module-card:hover { background: var(--bg-card-hover); border-color: var(--accent-dim); color: var(--text); transform: translateY(-2px); }
.module-card.is-on { border-color: var(--success); box-shadow: 0 0 0 1px rgba(61, 220, 151, 0.2); }
.module-icon { font-size: 2.4rem; margin-bottom: 8px; }
.module-name { font-weight: 600; margin-bottom: 4px; }
.module-status { font-size: 0.85rem; color: var(--text-muted); }
.module-card.is-on .module-status { color: var(--success); }

.premium-cta {
  margin-top: 32px; padding: 24px; border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.15), rgba(var(--premium-2-rgb), 0.15));
  border: 1px solid rgba(var(--premium-rgb), 0.3);
  display: flex; justify-content: space-between; align-items: center; gap: 20px;
  flex-wrap: wrap;
}

/* ======= FORMS ======= */
.form { display: flex; flex-direction: column; gap: 24px; }
.form-section { background: var(--bg-card); padding: 28px; border-radius: var(--radius); border: 1px solid var(--border); }
.form-section h2 { font-size: 1.2rem; margin-bottom: 16px; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px; margin-bottom: 16px; }
.form-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.form-field label { color: var(--text-muted); font-size: 0.9rem; font-weight: 500; }

/* --- Étiquettes des formulaires : au-dessus du champ, pas à côté ----------
 *
 *  Une <label> est `inline` par défaut. Les formulaires écrits en
 *  `<div><label>…</label><input></div>` — il y en a beaucoup, d'antiraid à
 *  modération — affichaient donc « Nom [champ] » sur la même ligne, avec un
 *  champ à la largeur du navigateur et une colonne d'étiquettes en escalier.
 *
 *  `.switch` est exclu : ces étiquettes-là ENTOURENT une case à cocher et
 *  doivent rester en ligne. Les 11 étiquettes portant un style inline gagnent
 *  de toute façon sur cette règle.
 */
.form-section label:not(.switch):not(.switch-label) {
  display: block;
  margin-bottom: 6px;
  color: var(--text-muted); font-size: 0.9rem; font-weight: 500;
}
.form-section input:not([type="checkbox"]):not([type="radio"]),
.form-section select,
.form-section textarea { width: 100%; }

/* Un tableau à cinq colonnes de champs ne rentre pas sur un portable : on le
   laisse défiler dans son coin plutôt que de faire déborder toute la page. */
.table-defilante { overflow-x: auto; }
.table-defilante > table { min-width: 560px; }

/* Style global pour TOUS les inputs/selects/textareas du dashboard ET staff (cohérence visuelle) */
.dashboard-content input[type="text"],
.dashboard-content input[type="number"],
.dashboard-content input[type="password"],
.dashboard-content input[type="url"],
.dashboard-content input[type="email"],
.dashboard-content input[type="date"],
.dashboard-content input[type="time"],
.dashboard-content input[type="datetime-local"],
.dashboard-content input:not([type]),
.dashboard-content select,
.dashboard-content textarea,
.container input[type="text"],
.container input[type="number"],
.container input[type="password"],
.container input[type="url"],
.container input[type="email"],
.container input[type="date"],
.container input[type="time"],
.container input[type="datetime-local"],
.container input:not([type]),
.container select,
.container textarea,
.form-field input, .form-field select, .form-field textarea {
  background: var(--bg-softer); border: 1px solid var(--border-champ);
  color: var(--text); padding: 10px 12px; border-radius: var(--radius-sm);
  font-family: inherit; font-size: 0.95rem;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.dashboard-content input:focus,
.dashboard-content select:focus,
.dashboard-content textarea:focus,
.container input:focus,
.container select:focus,
.container textarea:focus,
.form-field input:focus, .form-field select:focus, .form-field textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.2);
}
.dashboard-content input::placeholder,
.dashboard-content textarea::placeholder,
.container input::placeholder,
.container textarea::placeholder {
  color: var(--text-dim);
}
.dashboard-content input[type="checkbox"],
.container input[type="checkbox"] {
  accent-color: var(--accent);
}
.select-multiple { min-height: 200px; }

.form-actions { display: flex; gap: 12px; align-items: center; justify-content: flex-end; }

/* switch */
.switch { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; cursor: pointer; }
.switch input { display: none; }
.switch-slider {
  width: 44px; height: 24px; background: var(--bg-softer); border-radius: 100px;
  position: relative; transition: all 0.2s; flex-shrink: 0; border: 1px solid var(--border);
}
.switch-slider::after {
  content: ''; position: absolute; width: 18px; height: 18px; border-radius: 50%;
  background: var(--text-muted); top: 2px; left: 2px; transition: all 0.2s;
}
.switch input:checked + .switch-slider { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch-slider::after { background: white; left: 22px; }
.switch-label { color: var(--text); font-weight: 500; }

/* ======= ROLES GRID (loup-garou) ======= */
.roles-filter { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.roles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.role-card {
  display: flex; gap: 12px; padding: 16px; border-radius: var(--radius-sm);
  background: var(--bg-softer); border: 1px solid var(--border); cursor: pointer;
  transition: all 0.15s; align-items: flex-start;
}
.role-card:hover { border-color: var(--accent-dim); }
.role-card input[type="checkbox"] { margin-top: 3px; flex-shrink: 0; accent-color: var(--accent); }
.role-card:has(input:checked) { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.1); }
.role-info { flex: 1; }
.role-name { font-weight: 600; margin-bottom: 2px; }
.role-camp { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }
.role-desc { font-size: 0.85rem; color: var(--text-muted); }
.role-loup .role-camp { color: var(--danger); }
.role-solo .role-camp { color: var(--premium); }
.role-card.locked { opacity: 0.5; cursor: not-allowed; background: var(--bg); }
.role-card.locked:hover { border-color: var(--border); }
.role-premium-badge { display: inline-block; font-size: 0.65rem; padding: 2px 6px; border-radius: 4px; background: rgba(var(--premium-rgb), 0.15); color: var(--premium); font-weight: 600; margin-left: 6px; }

.game-snapshot { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.game-snapshot > div { background: var(--bg-softer); padding: 12px; border-radius: var(--radius-sm); }

/* ======= ALERTS ======= */
.alert { padding: 16px 20px; border-radius: var(--radius); margin-bottom: 16px; display: flex; align-items: center; gap: 12px; }
.alert-success { background: rgba(61, 220, 151, 0.1); border: 1px solid rgba(61, 220, 151, 0.3); color: var(--success); }
.alert-warning { background: rgba(255, 181, 71, 0.1); border: 1px solid rgba(255, 181, 71, 0.3); color: var(--warning); }
.alert-danger { background: rgba(255, 91, 122, 0.1); border: 1px solid rgba(255, 91, 122, 0.3); color: var(--danger); }

.badge { display: inline-block; padding: 4px 10px; border-radius: 100px; font-size: 0.8rem; font-weight: 600; }
.badge-premium { background: rgba(var(--premium-rgb), 0.15); color: var(--premium); }

/* ======= PRICING ======= */
.pricing-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; max-width: 1100px; margin: 0 auto; }
.pricing-card {
  background: var(--bg-card); padding: 32px; border-radius: var(--radius);
  border: 1px solid var(--border); display: flex; flex-direction: column; gap: 20px;
}
.pricing-card.highlighted {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 20px 60px -20px rgba(var(--accent-rgb), 0.4);
  transform: scale(1.03);
}
.pricing-card h2 { font-size: 1.5rem; }
.pricing-price { font-size: 2rem; font-weight: 800; color: var(--accent-lisible); margin-top: 8px; }
.pricing-features { list-style: none; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.pricing-features li { color: var(--text-muted); }

.faq { margin-top: 60px; max-width: 800px; margin-left: auto; margin-right: auto; }
.faq h2 { margin-bottom: 20px; }
.faq details {
  background: var(--bg-card); padding: 16px 20px; border-radius: var(--radius-sm);
  margin-bottom: 8px; border: 1px solid var(--border);
}
.faq summary { cursor: pointer; font-weight: 600; }
.faq details p { color: var(--text-muted); margin-top: 10px; }

/* ======= TABLE ======= */
.table { width: 100%; border-collapse: collapse; background: var(--bg-card); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border); }
.table th, .table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--border); }
.table th { background: var(--bg-softer); color: var(--text-muted); font-weight: 600; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; }
.table tr:last-child td { border-bottom: none; }

/* ======= ERROR PAGE ======= */
.error-page { text-align: center; }
/*  Même raison que le bandeau ci-dessus : le grand numéro d'erreur finissait
    sur un rose de marque, quel que soit le monde ou la fête. */
.error-code { font-size: 6rem; font-weight: 800; background: linear-gradient(135deg, var(--accent), rgb(var(--fond-aurore-2))); -webkit-background-clip: text; background-clip: text; color: transparent; }
.error-page h1 { margin-bottom: 24px; color: var(--text-muted); font-weight: 500; }

/* --- Le detail technique d'une erreur, et l'astuce qui va avec -------------- *
 *  Ajoutes le 28/08 : la page d'authentification affichait son propre balisage
 *  en toutes lettres, parce que la vue echappe le message (a raison) et que la
 *  route y mettait du HTML. La mise en forme revient donc ici, ou elle doit
 *  etre, et le texte reste du texte. */
.error-detail-titre {
  margin: 0 0 6px; text-align: left; font-size: 0.78rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-muted); opacity: .8;
}
.error-detail {
  margin: 0 auto 18px; max-width: 640px; text-align: left;
  padding: 12px 14px; border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.10);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem; line-height: 1.5; color: var(--text-muted);
  /*  Une erreur d'API tient sur une ligne de trois cents caracteres : sans
   *  ces deux regles elle deborde de l'ecran et on n'en lit que le debut. */
  white-space: pre-wrap; word-break: break-word;
}
.error-astuce {
  margin: 0 auto 20px; max-width: 640px; text-align: left;
  padding: 12px 14px; border-radius: 8px;
  background: rgba(255, 216, 107, 0.08);
  border: 1px solid rgba(255, 216, 107, 0.25);
  font-size: 0.9rem; line-height: 1.6;
}
.error-actions { display: flex; gap: 12px; justify-content: center; }

/* ======= EMPTY STATE ======= */
.empty-state { text-align: center; padding: 80px 24px; background: var(--bg-card); border-radius: var(--radius); border: 1px dashed var(--border); }
.empty-state h2 { margin-bottom: 12px; }
.empty-state p { color: var(--text-muted); margin-bottom: 24px; }

/* ======= FOOTER ======= */
.footer { border-top: 1px solid var(--border); margin-top: 60px; background: var(--bg-soft); }
.footer-inner { max-width: 1280px; margin: 0 auto; padding: 40px 24px; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 24px; align-items: center; }
.footer-links { display: flex; gap: 24px; }
.footer-links a { color: var(--text-muted); }
.footer-copy { color: var(--text-dim); font-size: 0.9rem; }

/* ======= RESPONSIVE =======
   Tout le comportement des petits écrans est regroupé en fin de fichier
   (section « TÉLÉPHONES & TABLETTES »). Il doit venir après les règles
   d'animation ajoutées plus bas, sinon celles-ci le récrivent. */

/* ======= STAFF ======= */
.staff-link {
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.15), rgba(var(--premium-2-rgb), 0.15));
  padding: 6px 12px !important;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(var(--premium-rgb), 0.3);
  color: var(--premium) !important;
  font-weight: 600;
}
.staff-link:hover, .staff-link.active {
  background: linear-gradient(135deg, rgba(var(--premium-rgb), 0.25), rgba(var(--premium-2-rgb), 0.25));
  color: var(--text) !important;
}

/* ===========================================================
   Chip picker — sélection click-to-toggle pour rôles & salons
   =========================================================== */
.chip-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  max-height: 280px;
  overflow-y: auto;
}

.chip-picker.compact { max-height: 180px; }

.chip-picker .chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text, #ddd);
  border-radius: 999px;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
  white-space: nowrap;
}

.chip-picker .chip:hover {
  background: rgba(var(--accent-rgb), 0.15);
  border-color: rgba(var(--accent-rgb), 0.4);
}

.chip-picker .chip.selected {
  background: linear-gradient(135deg, rgba(var(--accent-rgb), 0.6), rgba(255, 91, 122, 0.6));
  border-color: rgba(255, 255, 255, 0.4);
  color: var(--accent-texte);
  box-shadow: 0 2px 8px rgba(var(--accent-rgb), 0.3);
}

.chip-picker .chip.selected::after {
  content: '✓';
  margin-left: 4px;
  font-weight: bold;
}

.chip-picker .chip-color {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}

.chip-picker .chip-empty {
  color: rgba(255, 255, 255, 0.4);
  font-style: italic;
  padding: 8px;
}

.chip-picker-toolbar {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
  align-items: center;
}

.chip-picker-toolbar input.search {
  flex: 1;
  padding: 6px 12px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  color: var(--text, #ddd);
  font-size: 13px;
}

.chip-picker-toolbar .picker-count {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.5);
  white-space: nowrap;
}

.chip-picker-toolbar button.mini {
  padding: 4px 10px;
  font-size: 12px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--text, #ddd);
  border-radius: 6px;
  cursor: pointer;
}

.chip-picker-toolbar button.mini:hover {
  background: rgba(var(--accent-rgb), 0.2);
}

/* === Language switcher (navbar) === */
.lang-switch {
  display: inline-flex;
  gap: 0.25rem;
  align-items: center;
  margin-right: 0.5rem;
  padding: 0.15rem 0.35rem;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.lang-switch .lang-flag {
  display: inline-block;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.2rem 0.35rem;
  border-radius: 6px;
  text-decoration: none;
  filter: grayscale(50%);
  opacity: 0.65;
  transition: all 0.15s ease;
}
.lang-switch .lang-flag:hover { filter: none; opacity: 1; background: rgba(var(--accent-rgb), 0.15); }
.lang-switch .lang-flag.active { filter: none; opacity: 1; background: rgba(var(--accent-rgb), 0.25); }

/* ============================================================
   ANIMATIONS & MICRO-INTERACTIONS — Polish Vaeron
   ============================================================ */

/* ============================================================
   LA TRANSITION DOUCE — SUR CE QUI RÉAGIT, PAS SUR TOUT
   ============================================================
 *  ELLE ÉTAIT POSÉE SUR `*`. Mesuré le 29/08, sur la page « Membres du
 *  staff » : 1 882 éléments, tous porteurs de la règle, soit 9 788 propriétés
 *  déclarées animables — pour une page où il y a peut-être quarante choses
 *  qui réagissent à la souris. Chaque `<td>`, chaque `<span>`, chaque
 *  `<option>` payait une transition qu'aucun œil ne verra jamais.
 *
 *  ET ELLE MENTAIT AUX OUTILS. Une couleur lue juste après avoir été posée
 *  rend l'ANCIENNE valeur pendant 180 ms — la transition est en cours. Une
 *  vérification de palette a conclu que `--danger` « ne peignait rien »,
 *  alors que le jeton était parfaitement défini : elle mesurait le début de
 *  l'animation. Un décor qui trompe les instruments finit par cacher un vrai
 *  défaut.
 *
 *  On garde exactement la même douceur, aux mêmes durée et courbe, sur ce qui
 *  répond au survol, au focus ou au clic. Le reste du document ne bouge pas,
 *  et n'a jamais eu de raison de bouger. */
a,
button,
input,
select,
textarea,
summary,
label,
.btn,
.card, .module-card, .server-card, .feature-card, .pricing-card, .form-section,
.stat-card, .badge, .etat-pill, .badge-status, .switch-slider,
.table tbody tr, .rg-table tbody tr, .reg-panneau, .alert,
.sidebar a, .navbar a, .tk-capacite, .bp-bouton {
  transition-property: background-color, border-color, color, opacity, transform, box-shadow;
  transition-duration: 0.18s;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* On respecte la preference utilisateur reduce-motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* === Page fade-in à l'entrée === */
@keyframes vaeron-page-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
main {
  animation: vaeron-page-in 0.3s ease-out;
}

/* === Cartes : float au survol === */
.feature-card,
.module-card,
.server-card,
.pricing-card,
.form-section {
  position: relative;
  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.2s ease;
}
.feature-card:hover,
.module-card:hover,
.server-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px -8px rgba(var(--accent-rgb), 0.3);
  border-color: rgba(var(--accent-rgb), 0.4);
}

/* ==========================================================================
 *  CARTES DE TARIFS — l'animation appartient a la FORMULE, pas au surlignage
 * ==========================================================================
 *  CE QUI ETAIT FAUX. Le balayage tournant etait accroche a
 *  `.pricing-card.highlighted`, c'est-a-dire a « la carte mise en avant ».
 *  Or `highlighted` porte deja trois choses : la bordure d'accent, l'ombre
 *  portee et l'agrandissement — bref, « c'est celle-ci qu'on recommande ».
 *
 *  Tant qu'une seule carte devait bouger ET etre recommandee, la confusion
 *  ne se voyait pas. Le 28/08 elle s'est vue : « enleve l'anim de Max, mets-la
 *  sur Ultra ». Deplacer `highlighted: true` de Max vers Ultra aurait bien
 *  deplace l'animation — en emportant AUSSI la recommandation, la bordure et
 *  l'agrandissement, que personne n'avait demande de deplacer. Et l'inverse
 *  etait impossible : on ne pouvait pas animer Premium sans le declarer
 *  recommande, donc sans recommander deux formules a la fois.
 *
 *  DONC : l'animation se branche desormais sur `data-formule`, pose par la
 *  vue depuis l'identifiant de la formule (voir views/premium.ejs). Une
 *  seule source — l'id du plan dans src/plans.js — et deux decisions
 *  independantes : qui est recommande (highlighted, cote route) et qui
 *  respire (data-formule, ici).
 *
 *  Le mouvement reste DERRIERE le contenu (z-index 0 contre 1) et
 *  `pointer-events: none` : une carte de tarifs doit rester cliquable, et un
 *  reflet qui intercepte le clic sur « S'abonner » coute une vente.
 *  Le reglage systeme « moins d'animations » neutralise les deux (bloc
 *  prefers-reduced-motion plus bas).
 * ========================================================================== */

/* --- ULTRA : le balayage tournant (celui qui etait sur Max) --------------- */
.pricing-card[data-formule="ultra"] {
  position: relative;
  overflow: hidden;
}
.pricing-card[data-formule="ultra"]::before {
  content: '';
  position: absolute;
  inset: -50%;
  background: conic-gradient(from 0deg, transparent 0%, rgba(var(--accent-rgb), 0.15) 25%, transparent 50%);
  animation: vaeron-shimmer 4s linear infinite;
  pointer-events: none;
  z-index: 0;
}
.pricing-card[data-formule="ultra"] > * { position: relative; z-index: 1; }

@keyframes vaeron-shimmer {
  to { transform: rotate(360deg); }
}

/* --- PREMIUM : un reflet qui traverse, dans l'or de la formule ------------ *
 *  Volontairement d'une AUTRE nature que celui d'Ultra : l'un tourne en
 *  continu, l'autre passe puis disparait. Deux cartes qui bougent de la meme
 *  façon se lisent comme un seul niveau ; ici on voit du premier coup d'oeil
 *  que ce ne sont pas les memes.
 *
 *  Il se deplace en `transform` et non en `left` : la premiere est prise en
 *  charge par le compositeur, la seconde reflowe la carte soixante fois par
 *  seconde. Sur la page des tarifs, ou six cartes coexistent, la difference
 *  se sent sur une machine modeste.
 *
 *  Deux tiers du cycle sont un temps mort (opacite 0, hors cadre) : un reflet
 *  qui repasse sans arret devient un clignotant, et on finit par regarder le
 *  reflet au lieu du prix. */
.pricing-card[data-formule="premium"] {
  position: relative;
  overflow: hidden;
}
.pricing-card[data-formule="premium"]::before {
  content: '';
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: 0;
  width: 38%;
  background: linear-gradient(100deg,
    transparent 0%,
    rgba(var(--premium-2-rgb), 0.10) 35%,
    rgba(var(--premium-rgb), 0.20) 50%,
    rgba(var(--premium-2-rgb), 0.10) 65%,
    transparent 100%);
  filter: blur(6px);
  animation: vaeron-reflet-premium 6s ease-in-out infinite;
  pointer-events: none;
  z-index: 0;
}
.pricing-card[data-formule="premium"] > * { position: relative; z-index: 1; }

@keyframes vaeron-reflet-premium {
  0%   { transform: translateX(-120%) rotate(6deg); opacity: 0; }
  8%   { opacity: 1; }
  42%  { opacity: 1; }
  50%  { transform: translateX(320%) rotate(6deg); opacity: 0; }
  100% { transform: translateX(320%) rotate(6deg); opacity: 0; }
}

/* === Boutons : glow sur primary === */
.btn-primary {
  position: relative;
  overflow: hidden;
  transition: transform 0.15s ease, box-shadow 0.2s ease;
}
.btn-primary::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.4s ease, height 0.4s ease;
  pointer-events: none;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 20px -4px rgba(var(--accent-rgb), 0.5);
}
.btn-primary:active {
  transform: translateY(0);
}
.btn-primary:active::after {
  width: 200%;
  height: 200%;
  transition: width 0s, height 0s;
}

/* === Toggle switch : smooth === */
.switch-slider, .slider {
  transition: background-color 0.25s ease, transform 0.25s ease !important;
}

/* === Inputs : ring focus === */
input[type="text"], input[type="number"], input[type="password"],
input[type="email"], input[type="url"], textarea, select {
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
input[type="text"]:focus, input[type="number"]:focus, input[type="password"]:focus,
input[type="email"]:focus, input[type="url"]:focus, textarea:focus, select:focus {
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.6) !important;
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}

/* === Liens nav : underline animée === */
.navbar .nav-links a {
  position: relative;
}
.navbar .nav-links a::after {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.navbar .nav-links a:hover::after,
.navbar .nav-links a.active::after {
  width: 100%;
}

/* === Sidebar items : indicateur slide === */
.sidebar-nav a {
  position: relative;
  transition: background-color 0.15s ease, color 0.15s ease, padding-left 0.2s ease;
}
.sidebar-nav a::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: linear-gradient(180deg, var(--accent), var(--accent-hover));
  transform: scaleY(0);
  transition: transform 0.2s ease;
  border-radius: 0 3px 3px 0;
}
.sidebar-nav a:hover {
  padding-left: 14px;
}
.sidebar-nav a.active::before,
.sidebar-nav a:hover::before {
  transform: scaleY(1);
}

/* === Badges : pulse pour STAFF === */
.badge-premium {
  animation: vaeron-pulse 2.4s ease-in-out infinite;
}
@keyframes vaeron-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.4); }
  50%      { box-shadow: 0 0 0 6px rgba(var(--accent-rgb), 0); }
}

/* === Alertes : slide-down === */
.alert {
  animation: vaeron-alert-in 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes vaeron-alert-in {
  from { opacity: 0; transform: translateY(-8px); max-height: 0; }
  to   { opacity: 1; transform: translateY(0); max-height: 200px; }
}

/* === Hero orbs : float souple === */
.orb {
  animation: vaeron-orb-float 7s ease-in-out infinite;
}
.orb-2 { animation-delay: -2s; animation-duration: 9s; }
.orb-3 { animation-delay: -4s; animation-duration: 11s; }
@keyframes vaeron-orb-float {
  0%, 100% { transform: translate(0, 0); }
  33%      { transform: translate(20px, -25px); }
  66%      { transform: translate(-15px, 15px); }
}

/* === Stats card : nombres comptent (visual cue) === */
.stat-card .stat-value,
.stats-row .stat-value {
  background: linear-gradient(135deg, #fff 0%, var(--accent-hover) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-weight: 800;
}

/* === Lang switch : drapeau flip === */
.lang-switch .lang-flag {
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.15s, opacity 0.15s, background 0.15s;
}
.lang-switch .lang-flag:hover {
  transform: scale(1.15) rotate(-5deg);
}
.lang-switch .lang-flag.active {
  transform: scale(1.1);
}

/* === Avatars : zoom subtil au hover === */
.user-menu .avatar,
.sidebar-server-icon,
.server-card img {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.user-menu:hover .avatar,
.server-card:hover img {
  transform: scale(1.08);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.3);
}

/* === Transition entre pages (link click) === */
a:not(.btn):not(.brand):not(.lang-flag):not([target="_blank"]) {
  transition: color 0.15s ease;
}

/* === Spinner moderne (à utiliser dans le checkout) === */
.vaeron-spinner {
  display: inline-block;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(var(--accent-rgb), 0.2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: vaeron-spin 0.7s linear infinite;
}
@keyframes vaeron-spin {
  to { transform: rotate(360deg); }
}

/* === Scroll smooth === */
html { scroll-behavior: smooth; }

/* === Selection : couleur Vaeron === */
::selection {
  background: rgba(var(--accent-rgb), 0.4);
  color: var(--accent-texte);
}

/* === Scrollbar customisée === */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.02); }
::-webkit-scrollbar-thumb {
  background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.5), rgba(var(--accent-rgb), 0.3));
  border-radius: 5px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(180deg, rgba(var(--accent-rgb), 0.7), rgba(var(--accent-rgb), 0.5));
  background-clip: padding-box;
}

/* === Form-section : entry stagger === */
.form-section {
  animation: vaeron-fade-up 0.4s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}
.form-section:nth-child(1) { animation-delay: 0.05s; }
.form-section:nth-child(2) { animation-delay: 0.1s; }
.form-section:nth-child(3) { animation-delay: 0.15s; }
.form-section:nth-child(4) { animation-delay: 0.2s; }
.form-section:nth-child(5) { animation-delay: 0.25s; }
@keyframes vaeron-fade-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* === Confetti pour la page success premium === */
@keyframes vaeron-confetti {
  0%   { transform: translateY(-20vh) rotate(0deg);   opacity: 0; }
  10%  { opacity: 1; }
  100% { transform: translateY(120vh) rotate(720deg); opacity: 0; }
}

/* === Tab focus visible (accessibilité) === */
*:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), 0.7);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ============================================================
 * Mission 3 — Animations stylées (discrètes, 200-400ms)
 * ============================================================ */
html { scroll-behavior: smooth; }

.module-card,
.stat-card,
.server-card,
.plugin-card {
  transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.22s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.22s;
}
.module-card:hover,
.stat-card:hover,
.server-card:hover,
.plugin-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 28px rgba(var(--accent-rgb), 0.22),
              0 2px 6px rgba(0, 0, 0, 0.18);
}

.badge-premium,
.badge-pro,
.badge-active {
  animation: vaeron-badge-pulse 3.4s ease-in-out infinite;
}
@keyframes vaeron-badge-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--premium-rgb), 0.4); }
  50%      { box-shadow: 0 0 0 6px rgba(var(--premium-rgb), 0); }
}

.dashboard-sidebar {
  animation: vaeron-slide-in-left 0.32s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes vaeron-slide-in-left {
  from { opacity: 0; transform: translateX(-18px); }
  to   { opacity: 1; transform: translateX(0); }
}

.btn,
button.btn,
.btn-primary,
.btn-secondary,
.btn-danger,
.btn-success {
  transition: transform 0.15s ease,
              box-shadow 0.2s ease,
              background-color 0.2s ease,
              border-color 0.2s ease,
              filter 0.15s ease;
}
.btn:hover,
button.btn:hover {
  transform: translateY(-1px);
  filter: brightness(1.05);
}
.btn:active,
button.btn:active {
  transform: translateY(0) scale(0.98);
  filter: brightness(0.95);
}

.switch input + .switch-slider,
.switch-slider {
  transition: background-color 0.25s ease,
              box-shadow 0.25s ease;
}
.switch-slider::before {
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
              box-shadow 0.25s ease;
}

form[aria-busy="true"] .btn[type="submit"],
form.is-submitting .btn[type="submit"] {
  position: relative;
  pointer-events: none;
  opacity: 0.85;
  color: transparent !important;
}
form[aria-busy="true"] .btn[type="submit"]::after,
form.is-submitting .btn[type="submit"]::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: vaeron-spin 0.7s linear infinite;
}

input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="url"],
select,
textarea {
  transition: border-color 0.2s ease,
              box-shadow 0.2s ease,
              background-color 0.2s ease;
}
input:focus,
select:focus,
textarea:focus {
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.18);
}

.navbar a,
.dashboard-sidebar a {
  transition: color 0.18s ease, background-color 0.18s ease;
}

table tbody tr {
  transition: background-color 0.18s ease;
}
table tbody tr:hover {
  background-color: rgba(var(--accent-rgb), 0.06);
}

.alert {
  animation: vaeron-fade-up 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Tables : surlignage de la ligne survolée */
table tbody tr {
  transition: background-color 0.18s ease;
}
table tbody tr:hover {
  background-color: rgba(var(--accent-rgb), 0.06);
}

/* Alerts : entrée descendante douce (au cas où vaeron-alert-in pas applique) */
.alert {
  animation: vaeron-fade-up 0.3s cubic-bezier(0.4, 0, 0.2, 1) backwards;
}

/* Respect prefers-reduced-motion : désactive tout */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Petit script JS optionnel : auto-ajout de .is-submitting sur form submit
 * (CSS ne peut pas le faire seul). On le branche depuis un partial JS si besoin. */

/* ============================================================================
   TÉLÉPHONES & TABLETTES
   ============================================================================
   Cette section est volontairement la DERNIÈRE du fichier. Les blocs
   d'animation au-dessus redéfinissent `transform`, `padding-left`, etc. avec la
   même spécificité ; placées plus haut, les règles mobiles se faisaient récrire
   sans qu'on comprenne pourquoi.

   Trois seuils :
     · 1024px — on resserre, la mise en page reste la même
     · 900px  — bascule : la barre latérale et le menu haut deviennent des
                menus dépliants
     · 600px  — téléphone : une colonne, textes et marges réduits

   Aucun JavaScript n'est utilisé pour les menus : une case à cocher masquée
   porte l'état ouvert/fermé, et le CSS montre ou cache le panneau. Elle se
   remet à zéro à chaque changement de page, ce qui est exactement le
   comportement voulu (on ouvre, on clique un lien, le menu se referme).
   ========================================================================== */

/* ---- Grilles : ne jamais être plus large que l'écran ----------------------
   `minmax(320px, 1fr)` impose une colonne de 320px même dans un conteneur de
   300px : la page déborde alors sur la droite et tout part de travers. Le
   `min(…, 100%)` autorise la colonne à rétrécir sous la largeur idéale quand
   l'écran est plus étroit qu'elle. C'est la cause n°1 du défilement horizontal
   parasite sur téléphone. */
/* Un élément de grille refuse par défaut de descendre sous la largeur minimale
   de son contenu (`min-width: auto`). Un seul tableau un peu large dans la
   colonne de droite élargissait donc TOUTE la colonne, et la page entière
   partait sur la droite : cartes coupées, menu qui dépasse. `min-width: 0`
   autorise la colonne à s'en tenir à la largeur de l'écran ; c'est alors le
   tableau qui défile tout seul, à l'intérieur de son cadre. */
.dashboard-layout > * { min-width: 0; }

.server-grid   { grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); }
.feature-grid  { grid-template-columns: repeat(auto-fit,  minmax(min(280px, 100%), 1fr)); }
.pricing-grid  { grid-template-columns: repeat(auto-fit,  minmax(min(280px, 100%), 1fr)); }
.roles-grid    { grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); }
.form-grid     { grid-template-columns: repeat(auto-fit,  minmax(min(240px, 100%), 1fr)); }
.module-grid   { grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); }
.stats-row     { grid-template-columns: repeat(auto-fit,  minmax(min(140px, 100%), 1fr)); }
.game-snapshot { grid-template-columns: repeat(auto-fit,  minmax(min(140px, 100%), 1fr)); }

/* ---- Boutons des menus dépliants : invisibles sur ordinateur ---- */
.nav-burger, .sidebar-toggle,
.nav-burger-label, .sidebar-toggle-label { display: none; }

/* ==========================================================================
   ≤ 1024px — on resserre
   ========================================================================== */
@media (max-width: 1024px) {
  .navbar-inner { padding: 14px 18px; gap: 16px; }
  .nav-links { gap: 16px; }
  .dashboard-layout { grid-template-columns: 220px 1fr; }
  .dashboard-content { padding: 32px 24px; }
  .hero { padding: 72px 20px 64px; }
}

/* ==========================================================================
   ≤ 900px — bascule en menus dépliants
   ========================================================================== */
/* Le menu « ☰ » est réservé aux petits écrans : téléphone et petite tablette.
   Sur un ordinateur, on veut le menu ÉCRIT — un burger sur grand écran cache
   la navigation sans raison. Au-dessus de ce seuil, la barre tient grâce au
   mode compact plus bas, pas en se repliant.
   Le seuil vient d'une mesure, pas d'une habitude : en dessous de 1140 px la
   barre écrite ne rentre plus, même compactée au maximum. Au-dessus, elle
   tient — donc pas de burger. 1140 px, c'est une petite tablette ; aucun
   ordinateur n'y tombe, sauf en zoom fort.
   Les règles de la barre LATÉRALE restent à 900 px : elle a encore la place
   à 1140, et un seul seuil pour deux problèmes différents ne marche pas. */
@media (max-width: 1140px) {


  /* ---- Barre du haut ---------------------------------------------------
     Avant, `.nav-links { display: none }` faisait simplement disparaître
     Fonctionnalités / Commandes / Premium / Support : depuis un téléphone, ces
     pages n'étaient plus atteignables du tout. Elles reviennent, dans un
     panneau qu'on ouvre avec le « ☰ ». */
  .navbar-inner { flex-wrap: wrap; padding: 10px 14px; gap: 10px; }
  .brand { flex: 1; min-width: 0; font-size: 1.1rem; }
  .brand-logo { width: 34px; height: 34px; }

  .nav-burger, .sidebar-toggle {
    display: block; position: absolute;
    width: 1px; height: 1px; margin: 0; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%);
  }

  .nav-burger-label {
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    width: 44px; height: 44px;          /* 44px : la cible tactile minimale */
    border-radius: var(--radius-sm);
    border: 1px solid var(--border); background: var(--bg-soft);
    color: var(--text); font-size: 1.3rem; line-height: 1;
    cursor: pointer; user-select: none;
  }
  .nav-burger:checked ~ .nav-burger-label { border-color: var(--accent); color: var(--accent-lisible); }
  .nav-burger:focus-visible ~ .nav-burger-label { outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 2px; }

  .nav-links, .nav-actions { display: none; }
  .nav-burger:checked ~ .nav-links,
  .nav-burger:checked ~ .nav-actions {
    display: flex; flex-direction: column; align-items: stretch;
    flex-basis: 100%; width: 100%; gap: 2px;
  }
  .nav-burger:checked ~ .nav-actions {
    gap: 10px; padding-top: 12px; margin-top: 4px;
    border-top: 1px solid var(--border);
  }
  .nav-burger:checked ~ .nav-links a {
    padding: 12px 10px; border-radius: var(--radius-sm);
  }
  .nav-burger:checked ~ .nav-links a:hover,
  .nav-burger:checked ~ .nav-links a.active { background: var(--bg-card); }
  /* Le soulignement animé du menu de bureau n'a plus de sens en colonne. */
  .navbar .nav-links a::after { display: none; }

  .nav-burger:checked ~ .nav-actions .lang-switch { align-self: flex-start; margin-right: 0; }
  .nav-burger:checked ~ .nav-actions > .btn { width: 100%; }
  /* Les liens discrets (Mes bots, ✨ licences, Déconnexion) prennent la même
     hauteur que les autres entrées du menu : 6 px de remplissage, ça ne se
     vise pas au doigt. */
  .nav-burger:checked ~ .nav-actions .btn-ghost:not(.btn) {
    padding: 12px 10px; border-radius: var(--radius-sm);
  }
  .nav-burger:checked ~ .nav-actions .user-menu {
    flex-wrap: wrap; gap: 8px; padding-top: 10px;
    border-top: 1px solid var(--border);
  }
  .nav-burger:checked ~ .nav-actions .username { overflow-wrap: anywhere; }

  /* ---- Des cibles qu'un doigt peut viser -------------------------------
     Mesuré sur iPhone 13, Pixel 5 et Galaxy S9+ : dans le menu ouvert, cinq
     éléments faisaient moins de 34 px de haut — le drapeau de langue ne
     faisait que 20 px. On vise un doigt, pas un curseur : 44 px est la taille
     recommandée, et en dessous de 40 on rate sa cible une fois sur trois. */
  .nav-burger:checked ~ .nav-actions .monde-bascule,
  .nav-burger:checked ~ .nav-links a,
  .nav-burger:checked ~ .nav-actions .btn,
  .nav-burger:checked ~ .nav-actions .btn-ghost {
    /* `.nav-links a` et non seulement `.staff-link` : mesurés à 43 px, les
       cinq liens ordinaires passaient juste sous la barre. Un pixel, mais du
       mauvais côté du seuil. */
    min-height: 44px; min-width: 44px;
    display: flex; align-items: center; justify-content: center;
  }
  /* On ANNULE ici le scale(0.85) appliqué plus haut aux barres étroites.
     Il servait à gagner de la place sur une seule ligne ; dans le menu
     déplié il n'y a plus de contrainte de largeur, et il ne faisait que
     rapetisser les cibles tactiles : 44 px × 0.85 = 37 px, soit en dessous
     du seuil où l'on rate sa cible au doigt. */
  .nav-burger:checked ~ .nav-actions .lang-switch { gap: 8px; transform: none; }
  /* `.lang-switch .lang-flag` est plus spécifique que `.lang-flag` seul :
     ma première version était ignorée pour l'un des deux drapeaux. On reprend
     la même spécificité plutôt que d'empiler un !important. */
  .nav-burger:checked ~ .nav-actions .lang-switch .lang-flag {
    min-width: 44px; min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.25rem; padding: 0;
  }
  /* Le mot du bouton revient dans le menu : la place ne manque plus, et une
     pastille seule ne dit rien quand elle n'est plus dans la barre. */
  .nav-burger:checked ~ .nav-actions .monde-nom { display: inline; }
}

@media (max-width: 900px) {
  /* ---- Barre latérale du tableau de bord ----------------------------------
     Elle passe au-dessus du contenu quand il n'y a plus deux colonnes. Avec
     une trentaine de liens, la page demandée se retrouvait à un écran et demi
     plus bas. On la replie, en gardant le nom du serveur visible. */
  .dashboard-layout { grid-template-columns: 1fr; min-height: 0; }
  .sidebar {
    position: static; height: auto; overflow: visible;
    padding: 12px 16px;
    border-right: none; border-bottom: 1px solid var(--border);
  }
  .sidebar-toggle-label {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    width: 100%; padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--bg-card); border: 1px solid var(--border);
    color: var(--text); font-weight: 600; cursor: pointer; user-select: none;
  }
  .sidebar-toggle-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sidebar-toggle-label::after { content: '▾'; flex-shrink: 0; color: var(--text-muted); }
  .sidebar-toggle:checked ~ .sidebar-toggle-label { border-color: var(--accent); }
  .sidebar-toggle:checked ~ .sidebar-toggle-label::after { transform: rotate(180deg); }
  .sidebar-toggle:focus-visible ~ .sidebar-toggle-label { outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 2px; }

  .sidebar-body { display: none; }
  .sidebar-toggle:checked ~ .sidebar-body {
    display: block; margin-top: 12px;
    max-height: 65vh; overflow-y: auto;   /* le menu ne mange jamais tout l'écran */
  }
  /* Replié, l'entête « icône + nom du serveur » ferait doublon avec le bouton. */
  .sidebar-body .sidebar-server { padding-bottom: 16px; margin-bottom: 12px; }
  .sidebar-nav a { padding: 12px; }      /* liens plus hauts = plus faciles à viser */
  .dashboard-sidebar { animation: none; }

  .dashboard-content { padding: 24px 16px 48px; max-width: 100%; }
  .container { padding: 28px 16px 48px; }
}

/* ==========================================================================
   ≤ 700px — téléphone
   ========================================================================== */
@media (max-width: 700px) {

  /* ---- Champs de saisie : 16px obligatoires -------------------------------
     Sous 16px, Safari iOS zoome automatiquement à la mise au point du champ,
     et l'utilisateur se retrouve avec une page décalée qu'il doit repincer.
     Les sélecteurs reprennent la forme `[type]` de la règle d'origine : à
     spécificité égale, c'est la dernière écrite qui gagne — pas de !important. */
  .dashboard-content input[type], .dashboard-content input:not([type]),
  .dashboard-content select, .dashboard-content textarea,
  .container input[type], .container input:not([type]),
  .container select, .container textarea,
  .form-field input, .form-field select, .form-field textarea,
  input, select, textarea { font-size: 16px; }

  /* ---- Tableaux ------------------------------------------------------------
     Un tableau de 5 colonnes ne rentre pas dans 360px : sans ça, c'est la page
     ENTIÈRE qui se met à défiler de côté, entête et menu compris. En bloc avec
     `overflow-x`, seul le tableau glisse, le reste de la page ne bouge plus. */
  .table, .dashboard-content table, .container table {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  .table th, .table td { padding: 10px 12px; }
  .table th { font-size: 0.72rem; letter-spacing: 0.5px; }
  .table td { font-size: 0.9rem; }

  /* ---- Grilles écrites en clair dans les gabarits --------------------------
     Une trentaine de pages portent un `style="grid-template-columns:1fr 1fr"`
     directement sur la balise. Deux colonnes dans 328px donnent des champs de
     150px, inutilisables. Un style en ligne ne peut se récrire qu'avec
     `!important` — c'est le seul endroit du fichier où on s'y résout. */
  .dashboard-content [style*="grid-template-columns"],
  .container [style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }

  /* ---- Formulaires ---- */
  .form-section { padding: 18px 16px; }
  .form-actions { flex-direction: column; align-items: stretch; }
  .form-actions .btn, .form-actions button { width: 100%; }
  .chip-picker-toolbar { flex-wrap: wrap; }
  .chip-picker-toolbar input.search { min-width: 140px; }
  .chip-picker { max-height: 220px; }
  .select-multiple { min-height: 160px; }

  /* ---- Rien ne doit dépasser sur la droite ---- */
  .page-head h1, .hero h1, .section-title,
  .sidebar-server-name, .server-name, .username, .role-name, code {
    overflow-wrap: anywhere;
  }
  pre { overflow-x: auto; }
}

/* ==========================================================================
   ≤ 600px — petits téléphones
   ========================================================================== */
@media (max-width: 600px) {
  .hero { padding: 56px 16px 48px; }
  .hero h1 { font-size: 2rem; }
  .hero-sub { font-size: 1.05rem; margin-bottom: 28px; }
  .hero-cta { flex-direction: column; align-items: stretch; margin-bottom: 40px; }
  .hero-cta .btn { width: 100%; }
  .hero-stats { gap: 20px 28px; }
  .hero-stats strong { font-size: 1.8rem; }
  /* Les halos flous coûtent cher à dessiner et sortent du cadre sur un petit
     écran ; on les réduit plutôt que de les supprimer. */
  .orb-1 { width: 260px; height: 260px; }
  .orb-2 { width: 200px; height: 200px; }
  .orb-3 { display: none; }

  .features-preview { padding: 48px 16px; }
  .features-preview h2 { font-size: 1.6rem; margin-bottom: 28px; }
  .feature-card { padding: 20px; }
  .cta-band { padding: 48px 16px; margin-top: 40px; }
  .cta-band h2 { font-size: 1.5rem; }

  .page-head { margin-bottom: 24px; }
  .page-head h1 { font-size: 1.55rem; }
  .section-title { font-size: 1.2rem; }

  .server-card { padding: 16px; gap: 12px; }
  .pricing-card { padding: 24px 20px; }
  /* Une carte agrandie de 3 % déborde du cadre quand il n'y a plus de marge. */
  .pricing-card.highlighted { transform: none; }
  .premium-cta { flex-direction: column; align-items: flex-start; padding: 20px; }
  .empty-state { padding: 48px 20px; }

  .error-code { font-size: 4rem; }
  .error-actions { flex-wrap: wrap; justify-content: center; }

  .alert { padding: 14px 16px; align-items: flex-start; }
  .footer-inner { padding: 28px 16px; flex-direction: column; align-items: flex-start; gap: 14px; }
  .footer-links { flex-wrap: wrap; gap: 16px; }
}

/* ==========================================================================
   Écrans tactiles
   ==========================================================================
   Sans souris, `:hover` reste collé après le doigt : la carte touchée garde son
   décalage vers le haut jusqu'au prochain appui ailleurs. On rend donc les
   effets de survol inertes quand il n'y a pas de vrai pointeur. */
@media (hover: none) {
  .feature-card:hover, .module-card:hover, .server-card:hover,
  .stat-card:hover, .plugin-card:hover, .pricing-card:hover {
    transform: none; box-shadow: var(--shadow);
  }
  .btn:hover, button.btn:hover, .btn-primary:hover { transform: none; box-shadow: none; }
  .sidebar-nav a:hover { padding-left: 12px; }
  .lang-switch .lang-flag:hover { transform: none; }
  .user-menu:hover .avatar, .server-card:hover img { transform: none; box-shadow: none; }
}

/* ==========================================================================
 *  SOCLE — cohérence des états et du mouvement
 * ==========================================================================
 *
 *  Ajouté en fin de feuille, volontairement : à spécificité égale la dernière
 *  règle gagne, donc ce socle harmonise l'existant sans qu'il faille réécrire
 *  les 1250 lignes au-dessus, ni ajouter un seul !important.
 *
 *  Ce qui manquait, constaté sur la feuille d'origine :
 *    - :disabled n'était stylé nulle part. Un bouton désactivé — celui de
 *      création de backup quand le quota est atteint, par exemple — avait
 *      exactement l'apparence d'un bouton actif. On clique, rien ne se passe,
 *      et rien à l'écran n'explique pourquoi.
 *    - :focus-visible n'apparaissait que 3 fois contre 22 :focus. Résultat :
 *      un contour au clic de souris (inesthétique) et rien de fiable au
 *      clavier (inaccessible). :focus-visible règle les deux d'un coup.
 *    - :active n'était présent que 4 fois : la plupart des boutons ne
 *      réagissaient pas à l'enfoncement, ce qui donne une impression de
 *      page morte.
 *    - 10 animations définies, utilisées une ou deux fois chacune.
 *
 *  Parti pris : discret. C'est un tableau de bord qu'on garde ouvert des
 *  heures, pas une page d'accueil qu'on visite une fois. Les durées restent
 *  courtes (120 à 320 ms) et rien ne bouge en boucle.
 * ========================================================================== */

:root {
  --duree-rapide: 120ms;
  --duree: 200ms;
  --duree-entree: 320ms;
  --courbe: cubic-bezier(0.22, 0.61, 0.36, 1);   /* démarre franc, finit doux */
  --anneau-focus: 0 0 0 3px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* --- Réactivité au survol, au clavier et à l'enfoncement ------------------ */

.btn, button, select, input, textarea, .bk-card, .bk-opt, .card {
  transition:
    background-color var(--duree) var(--courbe),
    border-color     var(--duree) var(--courbe),
    color            var(--duree) var(--courbe),
    box-shadow       var(--duree) var(--courbe),
    transform        var(--duree-rapide) var(--courbe);
}

.btn:hover:not(:disabled),
button:hover:not(:disabled) { transform: translateY(-1px); }

.btn:active:not(:disabled),
button:active:not(:disabled) {
  transform: translateY(1px) scale(0.99);
  transition-duration: 60ms;   /* l'enfoncement doit être immédiat */
}

/* Un seul anneau de focus pour tout le site, et seulement au clavier :
 * :focus-visible ne se déclenche pas sur un clic de souris. */
:where(a, button, .btn, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  box-shadow: var(--anneau-focus);
  border-radius: var(--radius-sm);
}

/* --- États désactivés : ils doivent se voir ------------------------------ */

.btn:disabled, button:disabled,
input:disabled, select:disabled, textarea:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.4);
  transform: none;
  box-shadow: none;
}

/* --- Champs de saisie ---------------------------------------------------- */

input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
}

input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
  cursor: pointer;
}

/* Une case cochée doit se distinguer sans avoir à la chercher. */
.bk-opt:has(input:checked) {
  border-color: color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-card));
}
.bk-opt:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }

/* --- Cartes -------------------------------------------------------------- */

.bk-card:hover, .card:hover {
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
  box-shadow: var(--shadow);
}

/* --- Entrée en scène ----------------------------------------------------- *
 *
 *  Les sélecteurs visent les classes réellement utilisées dans les vues :
 *  .page-head, .form-section, .stat-card, .module-card, .server-card,
 *  .feature-card, .pricing-card, .plugin-card, .role-card, .alert, .table.
 *  Cibler .card ou .content ne servait à rien, ces classes n'existent pas.
 *
 *  Distance portée à 16 px et durée à 380 ms : la version d'origine faisait
 *  8 px en 300 ms, assez discret pour passer complètement inaperçu. Le but
 *  n'est pas d'attirer l'attention mais qu'on sente la page se poser. */

main, .dashboard-content {
  animation: vaeron-page-in var(--duree-entree) var(--courbe) both;
}

.page-head {
  animation: vaeron-fade-up 420ms var(--courbe) both;
}

.form-section,
.stat-card, .module-card, .server-card, .feature-card,
.pricing-card, .plugin-card, .role-card,
.alert, .table, .bk-card, .bk-opt {
  animation: vaeron-fade-up 380ms var(--courbe) both;
}

/* Décalage entre voisins : l'œil suit l'arrivée au lieu de subir un bloc
 * entier d'un coup. Plafonné au huitième — au-delà l'attente devient
 * perceptible et l'effet se retourne contre lui. */
.form-section:nth-child(1),  .stat-card:nth-child(1),  .module-card:nth-child(1),
.server-card:nth-child(1),   .bk-card:nth-child(1),    .bk-opt:nth-child(1)   { animation-delay: 0ms; }
.form-section:nth-child(2),  .stat-card:nth-child(2),  .module-card:nth-child(2),
.server-card:nth-child(2),   .bk-card:nth-child(2),    .bk-opt:nth-child(2)   { animation-delay: 50ms; }
.form-section:nth-child(3),  .stat-card:nth-child(3),  .module-card:nth-child(3),
.server-card:nth-child(3),   .bk-card:nth-child(3),    .bk-opt:nth-child(3)   { animation-delay: 100ms; }
.form-section:nth-child(4),  .stat-card:nth-child(4),  .module-card:nth-child(4),
.server-card:nth-child(4),   .bk-card:nth-child(4),    .bk-opt:nth-child(4)   { animation-delay: 150ms; }
.form-section:nth-child(5),  .stat-card:nth-child(5),  .module-card:nth-child(5),
.server-card:nth-child(5),   .bk-card:nth-child(5),    .bk-opt:nth-child(5)   { animation-delay: 200ms; }
.form-section:nth-child(6),  .stat-card:nth-child(6),  .module-card:nth-child(6),
.server-card:nth-child(6),   .bk-card:nth-child(6),    .bk-opt:nth-child(6)   { animation-delay: 250ms; }
.form-section:nth-child(n+7), .stat-card:nth-child(n+7), .module-card:nth-child(n+7),
.server-card:nth-child(n+7), .bk-card:nth-child(n+7),  .bk-opt:nth-child(n+7) { animation-delay: 300ms; }

/* La barre latérale entre par la gauche, ses entrées se posent ensuite. */
.dashboard-sidebar, .sidebar {
  animation: vaeron-slide-in-left 380ms var(--courbe) both;
}

/* --- Survol : les éléments cliquables doivent se soulever ---------------- */

.stat-card:hover, .module-card:hover, .server-card:hover,
.feature-card:hover, .pricing-card:hover, .plugin-card:hover,
.role-card:hover, .bk-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  box-shadow: var(--shadow);
}

.sidebar-nav a, .nav-links a {
  transition: color var(--duree) var(--courbe),
              background-color var(--duree) var(--courbe),
              padding-left var(--duree) var(--courbe);
}
.sidebar-nav a:hover, .nav-links a:hover { padding-left: 4px; }

/* --- Retours visuels d'état ---------------------------------------------- */

/* Un texte de statut qui change doit attirer l'œil une fois, pas clignoter. */
#optionsStatut:not(:empty),
#copyStatus:not(:empty),
#createStatus:not(:empty) {
  animation: vaeron-fade-up var(--duree) var(--courbe) both;
}

/* --- Respect du réglage système ------------------------------------------ *
 *
 *  Dernier bloc de la feuille, et c'est délibéré : il neutralise TOUT ce qui
 *  précède, y compris les animations déclarées plus haut dans le fichier.
 *  Les trois blocs prefers-reduced-motion d'origine ne couvraient qu'une
 *  partie des règles ; celui-ci ne laisse rien passer.
 *
 *  Le mouvement n'est pas qu'une question de goût : il déclenche des nausées
 *  et des migraines chez une partie des utilisateurs. Quand le système le
 *  signale, on obéit. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover, button:hover, .btn:active, button:active { transform: none !important; }
}

/* ==========================================================================
 *  GRAPHIQUES — courbes et barres (voir public/js/graphiques.js)
 * ==========================================================================
 *  Le texte ne porte jamais la couleur d'une série : les valeurs et les
 *  libellés restent en encre neutre, une pastille colorée à côté porte
 *  l'identité. Sans ça, un daltonien lit un chiffre dont il ne peut pas
 *  déduire à quelle courbe il appartient.
 * ========================================================================== */

.gr-hote { position: relative; width: 100%; }
.gr-svg  { width: 100%; height: auto; display: block; overflow: visible; }
.gr-tick { fill: var(--text-dim); font-size: 11px; font-family: inherit; }
.gr-vide { color: var(--text-muted); font-size: 14px; padding: 24px 0; text-align: center; }

.gr-puce {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  margin-right: 6px; vertical-align: -1px;
}

.gr-legende {
  display: flex; gap: 18px; flex-wrap: wrap;
  margin-top: 10px; font-size: 13px; color: var(--text-muted);
}

.gr-infobulle {
  position: absolute; top: 0; left: 0; pointer-events: none; opacity: 0;
  background: var(--bg-softer); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 8px 10px;
  font-size: 13px; color: var(--text); box-shadow: var(--shadow);
  transition: opacity var(--duree-rapide) var(--courbe); white-space: nowrap; z-index: 5;
}
.gr-infobulle .gr-date { color: var(--text-muted); font-size: 12px; margin-bottom: 4px; }

/* --- Barres horizontales -------------------------------------------------- */

.gr-barres { display: flex; flex-direction: column; gap: 8px; }

.gr-barre {
  display: grid; grid-template-columns: minmax(90px, 30%) 1fr auto;
  gap: 12px; align-items: center;
}
.gr-barre-nom {
  font-size: 13px; color: var(--text); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.gr-barre-piste {
  height: 14px;                       /* mark fin : on ne remplit pas la ligne */
  background: var(--bg-softer); border-radius: 4px; overflow: hidden;
}
.gr-barre-fill {
  display: block; height: 100%; background: var(--accent);
  /* Bout arrondi côté valeur, carré contre la ligne de base. */
  border-radius: 0 4px 4px 0;
  transition: width 420ms var(--courbe);
}
.gr-barre:hover .gr-barre-fill { background: var(--accent-hover); }
.gr-barre-val {
  font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums;
  min-width: 3.5ch; text-align: right;
}

/* --- Tuiles de synthèse --------------------------------------------------- */

.gr-tuiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px; margin-bottom: 20px;
}
.gr-tuile {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px;
}
.gr-tuile-label { font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.gr-tuile-valeur {
  font-size: 28px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.gr-tuile-detail { font-size: 12px; color: var(--text-dim); margin-top: 2px; }

/* --- Panneau Staff : les pages utilisent .container, pas .dashboard-content -
 *
 *  Les vues du panneau ont été écrites sans barre latérale et centrent leur
 *  contenu avec .container. Maintenant qu'elles vivent dans .dashboard-layout,
 *  ce centrage les décalerait vers la droite : on le neutralise seulement dans
 *  ce contexte, sans toucher aux pages publiques qui l'utilisent aussi. */

.dashboard-layout > main.container {
  flex: 1;
  min-width: 0;              /* sans ça un tableau large déborde de la grille */
  margin: 0;
  max-width: none;
  padding: 24px 28px;
  animation: vaeron-page-in var(--duree-entree) var(--courbe) both;
}

/* ======= EN-TÊTE AVEC ACTIONS =======
 *
 * Le titre à gauche, l'état et le bouton à droite. En dessous de 640 px la
 * ligne se casse : sur un écran étroit, un titre et un bouton côte à côte se
 * disputent la largeur et le titre se retrouve haché sur trois lignes.
 */
.entete-ligne {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.entete-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}
.entete-fraicheur {
  font-size: 0.85rem;
  white-space: nowrap;
}
@media (max-width: 640px) {
  .entete-ligne { flex-direction: column; }
  .entete-actions { width: 100%; justify-content: space-between; }
}

/* ============================================================
 *  TICKETS — éditeur de catégories et réglages par formule
 * ============================================================
 *  Le principe visuel : ce qui est fermé reste LISIBLE. Un bloc hors formule
 *  est atténué et barré d'un cadenas, pas retiré de la page — sinon personne
 *  ne peut savoir ce qu'il obtiendrait en changeant de formule, et un réglage
 *  dont on a entendu parler semble simplement ne pas exister.
 *
 *  L'atténuation reste au-dessus du seuil de lisibilité (0.55, pas 0.3) :
 *  un texte qu'on doit deviner ne remplit pas cette fonction.
 */

/* Le bandeau de tête : ce que la formule du serveur ouvre, d'un coup d'œil. */
.tk-formule { border-left: 3px solid var(--accent); }

.tk-formule-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
}

.tk-capacites {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px; margin-top: 12px;
}
.tk-capacite {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: rgba(255, 255, 255, 0.02);
  font-size: 0.88rem;
}
.tk-capacite-etat { flex-shrink: 0; }
.tk-capacite-nom { flex: 1; }
.tk-capacite.tk-oui { border-color: rgba(61, 220, 151, 0.35); }
.tk-capacite.tk-oui .tk-capacite-etat { color: #3ddc97; }
.tk-capacite.tk-non { opacity: 0.62; }
.tk-capacite-formule {
  font-size: 0.75rem; padding: 2px 8px; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.18); color: var(--accent-lisible); white-space: nowrap;
}

/* ---- le cadenas ---- */
.tk-verrou {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.78rem; padding: 3px 10px; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.15); color: var(--accent-lisible);
  border: 1px solid rgba(var(--accent-rgb), 0.3); white-space: nowrap;
}
.tk-verrou-cle { font-size: 0.9em; }

/* ---- blocs de réglages ---- */
.tk-section {
  border: 1px solid var(--border); border-radius: 10px;
  padding: 14px; margin-top: 12px; background: rgba(255, 255, 255, 0.015);
}
.tk-section-tete {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.tk-section-tete h4 { margin: 0; font-size: 0.98rem; }
.tk-section-corps { margin-top: 10px; }
.tk-section.tk-ferme { border-style: dashed; }
.tk-section.tk-ferme .tk-section-corps { opacity: 0.55; }
.form-field.tk-ferme { opacity: 0.62; }

.tk-aide { color: var(--text-muted); font-size: 0.82rem; margin: 4px 0 0; }
.tk-etiquette { display: block; font-size: 0.85rem; margin-bottom: 4px; }
.tk-apercu {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem; color: var(--text-muted); margin: 6px 0 0;
}

.tk-champ { display: block; margin-top: 10px; }
.tk-champ input[type="text"], .tk-champ input[type="number"],
.tk-champ select, .tk-champ textarea { width: 100%; }
.tk-champ-case { display: flex; align-items: center; gap: 8px; margin-top: 26px; }
.tk-champ-case input { width: auto; }

.tk-ligne-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.tk-ligne-4 { display: grid; grid-template-columns: 90px 1.4fr 1fr auto; gap: 12px; align-items: end; }
@media (max-width: 760px) {
  .tk-ligne-2, .tk-ligne-4 { grid-template-columns: 1fr; }
}

/* ---- listes de rôles à cocher ----
 * Des cases plutôt qu'un <select multiple> : la sélection multiple native
 * exige un Ctrl+clic que rien n'annonce, et un clic simple efface tout.
 */
.tk-cases {
  max-height: 150px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 8px; padding: 6px;
  background: rgba(0, 0, 0, 0.18);
}
.tk-case {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: 6px; font-size: 0.88rem; cursor: pointer;
}
.tk-case:hover { background: rgba(255, 255, 255, 0.05); }
.tk-case input { width: auto; margin: 0; }
.tk-pastille {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-muted); flex-shrink: 0;
}

/* ---- une catégorie ---- */
.tk-liste { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.tk-cat {
  border: 1px solid var(--border); border-radius: 10px;
  background: rgba(255, 255, 255, 0.03); overflow: hidden;
}
.tk-cat[open] { border-color: var(--accent-dim); }
.tk-cat-tete {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 14px; cursor: pointer; list-style: none; user-select: none;
}
.tk-cat-tete::-webkit-details-marker { display: none; }
.tk-cat-tete::before {
  content: '▸'; color: var(--text-muted); transition: transform 160ms var(--courbe);
}
.tk-cat[open] > .tk-cat-tete::before { transform: rotate(90deg); }
.tk-cat-tete:hover { background: rgba(255, 255, 255, 0.04); }
.tk-cat-titre { flex: 1; font-weight: 600; }
.tk-tag-off { background: rgba(255, 255, 255, 0.1); color: var(--text-muted); }
.tk-cat-corps { padding: 0 14px 14px; }
.tk-cat-pied { margin-top: 14px; text-align: right; }
/*  #ef4444 sur une carte : 4.07:1, sous la norme. Le jeton, lui, est
 *  calibré pour ce fond. */
.tk-supprimer { color: var(--danger); }
.tk-supprimer:hover { background: rgba(239, 68, 68, 0.12); }

/* ---- questions du formulaire ---- */
.tk-questions { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.tk-question {
  display: grid; grid-template-columns: 1.6fr 1.4fr auto auto auto;
  gap: 8px; align-items: center;
}
@media (max-width: 760px) { .tk-question { grid-template-columns: 1fr 1fr; } }
.tk-mini { display: flex; align-items: center; gap: 5px; font-size: 0.8rem; white-space: nowrap; }
.tk-mini input { width: auto; margin: 0; }

/* ---- horaires ---- */
.tk-jour {
  display: flex; align-items: center; gap: 12px;
  padding: 7px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap;
}
.tk-jour:last-of-type { border-bottom: none; }
.tk-jour-nom { width: 100px; flex-shrink: 0; font-size: 0.88rem; }
.tk-jour-plages { display: flex; gap: 10px; flex-wrap: wrap; flex: 1; align-items: center; }
.tk-plage {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 6px; border: 1px solid var(--border); border-radius: 8px;
}
.tk-plage input[type="time"] { width: auto; padding: 4px 6px; }
/* Une fin avant le début : signalée tout de suite, pas écartée en silence
 * à l'enregistrement. */
.tk-plage-fausse { border-color: #ef4444; }
.tk-fleche { color: var(--text-muted); }

#categories-compteur.tk-plein { background: rgba(217, 89, 38, 0.2); color: #ffab7d; }

/* ============================================================
 *  TARIFS — la longue liste, repliée
 * ============================================================
 *  Cent quinze lignes par carte : les boutons d'achat se retrouvaient à des
 *  écrans de défilement, et on ne voyait jamais deux formules en même temps,
 *  donc on ne pouvait pas les comparer.
 *
 *  C'est un <details> natif : ça se déplie SANS JavaScript. Une page de
 *  tarifs qui cache ses prix quand un script tombe est pire que la liste
 *  longue.
 */
.pricing-plus { margin-top: 4px; }
.pricing-plus > summary {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; list-style: none;
  padding: 8px 10px; border-radius: 8px;
  font-size: 0.86rem; color: var(--text-muted);
  border: 1px dashed var(--border);
}
/* Safari pose son propre triangle : on le retire pour ne pas en avoir deux. */
.pricing-plus > summary::-webkit-details-marker { display: none; }
.pricing-plus > summary:hover { color: var(--text); border-color: var(--accent-dim); }
.pricing-plus-fleche { transition: transform 0.15s ease; }
.pricing-plus[open] > summary > .pricing-plus-fleche { transform: rotate(180deg); }
/* Le repli s'ouvre sur cent lignes : on lui donne sa propre barre plutôt que
 * d'allonger la carte — sinon on a juste déplacé le problème. */
.pricing-plus-corps { max-height: 320px; overflow-y: auto; margin-top: 8px; }
.pricing-plus-titre {
  margin: 12px 0 2px; font-size: 0.78rem; letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}
.pricing-plus-titre:first-child { margin-top: 0; }

/* ============================================================
 *  ÉDITEUR DE LIGNES — boutique, mur des messages, réputation
 * ============================================================
 *  Réutilise le socle des tickets (.tk-cat, .tk-champ, .tk-mini) : même
 *  grammaire visuelle, un seul endroit à corriger. Seule la grille des
 *  champs d'une ligne est propre à l'éditeur.
 *
 *  `auto-fit` plutôt qu'un nombre de colonnes fixe : la boutique en a cinq,
 *  la réputation deux. Écrire le nombre ici, c'est une règle par module —
 *  et une de plus à chaque module suivant.
 */
.ed-grille {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px 14px;
  align-items: start;
}
.ed-grille .tk-champ { margin-top: 0; }
.ed-grille .tk-champ label { display: block; font-size: 0.85rem; margin-bottom: 4px; }
.ed-grille .tk-champ small { display: block; margin-top: 4px; font-size: 0.75rem; }
/* Un champ fermé par la formule est GRISÉ, jamais caché : on doit pouvoir
 * voir ce qu'on obtiendrait en changeant de formule. */
.ed-grille .tk-champ [disabled] { opacity: 0.55; cursor: not-allowed; }
/* Le compteur vire à l'orange au plafond — le même signal que sur les
 * catégories de tickets, mais sans être accroché à un seul identifiant. */
.tk-mini.tk-plein {
  padding: 2px 8px; border-radius: 999px;
  background: rgba(217, 89, 38, 0.2); color: #ffab7d;
}

/* ============================================================
 *  RÔLES EN LIBRE-SERVICE — éditeur de panneaux
 * ============================================================
 *  Réutilise le socle des tickets (.tk-*) : même grammaire visuelle pour les
 *  blocs, les cadenas et les listes à cocher. Seul ce qui est propre aux
 *  panneaux est défini ici.
 */

/* Le barème des limites : il doit se lire d'un coup d'œil, c'est son seul
 * intérêt. La ligne de la formule en cours est marquée. */
.rr-bareme {
  width: 100%; border-collapse: collapse; margin: 12px 0 8px;
  font-size: 0.9rem;
}
.rr-bareme th, .rr-bareme td {
  text-align: left; padding: 7px 12px; border-bottom: 1px solid var(--border);
}
.rr-bareme th { color: var(--text-muted); font-weight: 600; font-size: 0.82rem; }
.rr-bareme tr:last-child td { border-bottom: none; }
.rr-bareme .rr-moi { background: rgba(var(--accent-rgb), 0.1); }
.rr-bareme .rr-moi td { font-weight: 600; }

/* Un panneau replié */
.rr-panneau .tk-cat-titre { font-weight: 600; }
.rr-publie { background: rgba(61, 220, 151, 0.18); color: #3ddc97; }

/* Choix de l'affichage et du mode : des cartes, pas une liste déroulante.
 * Le texte d'explication est ce qui permet de choisir sans se tromper. */
.rr-choix {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px;
}
.rr-option {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px;
  cursor: pointer; background: rgba(255, 255, 255, 0.02);
  transition: border-color 160ms var(--courbe), background 160ms var(--courbe);
}
.rr-option:hover { border-color: var(--accent-dim); }
.rr-option input { margin-top: 3px; flex-shrink: 0; }
.rr-option input:checked ~ div .rr-option-nom { color: var(--accent-lisible); }
.rr-option:has(input:checked) { border-color: var(--accent); background: rgba(var(--accent-rgb), 0.08); }
.rr-option-nom { font-weight: 600; font-size: 0.92rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rr-option-fermee { opacity: 0.62; cursor: not-allowed; }
.rr-option-fermee input { cursor: not-allowed; }

/* La grille des rôles : une ligne par rôle, colonnes alignées avec l'en-tête. */
.rr-entete, .rr-ligne {
  display: grid;
  grid-template-columns: 28px 70px minmax(140px, 1.4fr) minmax(120px, 1.2fr) minmax(120px, 1.2fr) 68px 68px 34px 34px;
  gap: 8px; align-items: center;
}
.rr-entete {
  font-size: 0.75rem; color: var(--text-muted);
  padding: 0 4px 6px; border-bottom: 1px solid var(--border); margin-bottom: 8px;
}
.rr-roles { display: flex; flex-direction: column; gap: 6px; }
.rr-ligne {
  padding: 4px; border-radius: 8px;
  transition: background 140ms var(--courbe);
}
.rr-ligne:hover { background: rgba(255, 255, 255, 0.03); }
.rr-rang { color: var(--text-muted); font-size: 0.8rem; text-align: center; }
.rr-emoji { text-align: center; font-size: 1.15em; }
.rr-ligne input, .rr-ligne select { width: 100%; min-width: 0; }
.rr-petit { text-align: center; }
/* Ces deux-là n'ont pas de style propre — elles servent de repère dans le
 * balisage et de point d'accroche pour l'adaptation aux petits écrans. */
.rr-libelle, .rr-desc { min-width: 0; }
.rr-fleche { padding: 4px 8px; }

/* Un champ obligatoire encore vide : signalé pendant la saisie, pas découvert
 * à l'enregistrement quand la ligne a déjà été écartée en silence. */
.rr-manque { border-color: #d95926 !important; }

.rr-roles-pied {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 10px; flex-wrap: wrap;
}
.rr-plein { color: #ffab7d; }

.rr-actions {
  display: flex; align-items: center; gap: 10px;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.rr-actions .tk-aide { flex: 1; margin: 0; }

@media (max-width: 900px) {
  .rr-entete { display: none; }
  .rr-ligne {
    grid-template-columns: 44px 1fr 1fr 34px;
    padding: 10px; border: 1px solid var(--border); border-radius: 8px;
  }
  .rr-ligne .rr-role { grid-column: span 3; }
}

/* L'état d'un panneau, annoncé dès son ouverture. Un panneau configuré mais
 * jamais publié n'existe pas sur Discord — ça doit se voir sans chercher. */
.rr-etat {
  padding: 10px 12px; border-radius: 8px; margin-bottom: 14px;
  font-size: 0.88rem; line-height: 1.45;
}
.rr-etat-ok {
  background: rgba(61, 220, 151, 0.12); color: #7fe9c0;
  border: 1px solid rgba(61, 220, 151, 0.3);
}
.rr-etat-attente {
  background: rgba(217, 89, 38, 0.14); color: #ffab7d;
  border: 1px solid rgba(217, 89, 38, 0.35);
}
/* Un module éteint alors que des panneaux existent : le cas où tout paraît
 * en ordre et où rien ne marche. */
.rr-alerte-forte { border-left: 4px solid var(--danger, #ef4444); }

/* Deux boutons de fin de formulaire : celui qui publie est le principal,
 * parce que c'est ce que les gens veulent faire neuf fois sur dix. */
.rr-enregistrer { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rr-enregistrer .tk-aide { flex-basis: 100%; margin: 0; }

/* ==========================================================================
 *  L'ASSISTANCE EN DIRECT — la bulle en bas à droite
 * ==========================================================================
 *  Elle n'apparaît que sur le site de production. La couleur suit le monde
 *  comme tout le reste : elle est donc jaune, puisque la prod est jaune.
 *  Rien n'est écrit en dur ici — si la bulle passait un jour sur le site
 *  public, elle deviendrait violette toute seule.
 */
.assist {
  position: fixed; right: 18px; bottom: 18px; z-index: 900;
  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
}
.assist-bouton {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 48px; padding: 0 18px;
  border: none; border-radius: 999px; cursor: pointer;
  background: var(--accent-plein); color: var(--accent-texte);
  font-size: 0.95rem; font-weight: 600; font-family: inherit;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}
.assist-bouton:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(0, 0, 0, 0.45); }
.assist-bouton:focus-visible { outline: 3px solid rgba(var(--accent-rgb), 0.85); outline-offset: 3px; }
.assist-ouvert .assist-bouton-texte { display: none; }

.assist-panneau {
  width: 340px; max-width: calc(100vw - 36px);
  max-height: min(560px, calc(100vh - 120px));
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg-card); color: var(--text);
  border: 1px solid var(--border, #262b3a); border-radius: 16px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
}
.assist-panneau[hidden] { display: none; }

.assist-tete {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border, #262b3a);
  background: rgba(var(--accent-rgb), 0.10);
}
.assist-fermer {
  background: none; border: none; cursor: pointer; font-family: inherit;
  color: var(--text-dim, #9aa3b8); font-size: 1.5rem; line-height: 1;
  /* 44 px : on vise un doigt, pas un curseur. Même règle que le menu du
     téléphone — une croix de 16 px se rate une fois sur trois. */
  min-width: 44px; min-height: 44px;
}
.assist-fermer:hover { color: var(--text); }

.assist-fil {
  flex: 1; overflow-y: auto; padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.assist-msg {
  padding: 10px 12px; border-radius: 12px;
  font-size: 0.9rem; line-height: 1.5;
  /* Une réponse peut contenir une commande longue ou un chemin : elle doit
     revenir à la ligne dans la bulle, pas l'élargir hors de l'écran. */
  overflow-wrap: anywhere;
}
.assist-msg-bot { background: var(--bg-softer); align-self: stretch; }
.assist-msg-moi {
  background: var(--accent-plein); color: var(--accent-texte);
  align-self: flex-end; max-width: 85%;
}
.assist-attente { opacity: 0.6; font-style: italic; }

.assist-lien, .assist-piste {
  display: block; margin-top: 8px; font-size: 0.85rem;
  color: var(--accent-lisible); text-decoration: none;
}
.assist-lien:hover { text-decoration: underline; }
.assist-piste { color: var(--text-dim, #9aa3b8); }
.assist-note {
  display: block; margin-top: 8px; font-size: 0.78rem;
  color: var(--warning, #fb923c);
}
.assist-action {
  display: block; margin-top: 10px; width: 100%; min-height: 40px;
  border: 1px solid var(--accent); border-radius: 10px; cursor: pointer;
  background: transparent; color: var(--accent-lisible);
  font-family: inherit; font-size: 0.85rem;
}
.assist-action:hover:not(:disabled) { background: rgba(var(--accent-rgb), 0.12); }
.assist-action:disabled { opacity: 0.5; cursor: default; }

/* Reformuler est le geste qu'on veut voir d'abord : il ne derange personne et
   il marche souvent. Il reste plus discret que le ticket — un cadre plein
   attirerait l'oeil au meme titre, alors que les deux n'ont pas le meme
   cout pour l'equipe. */
.assist-action-doux {
  border-color: var(--border, #262b3a);
  color: var(--text-dim, #9aa3b8);
}
.assist-action-doux:hover:not(:disabled) {
  border-color: var(--accent); color: var(--accent-lisible);
}

/* « Cette reponse ne me convient pas » : volontairement discret. Il doit etre
   trouvable sans concurrencer la reponse elle-meme — on ne pousse pas les
   gens a rejeter une reponse juste parce que le bouton est gros. */
.assist-avis {
  display: block; margin-top: 10px; padding: 0; width: auto;
  border: 0; background: none; cursor: pointer;
  color: var(--text-dim, #9aa3b8);
  font-family: inherit; font-size: 0.78rem; text-align: left;
  text-decoration: underline; text-underline-offset: 3px;
}
.assist-avis:hover:not(:disabled) { color: var(--accent-lisible); }
.assist-avis:disabled { opacity: 0.5; cursor: default; }

/* « Effacer » : discret, a cote de la croix. La conversation se garde d'une
   page a l'autre, il faut donc pouvoir la remettre a zero sans fermer
   l'onglet — mais ce n'est pas le geste courant, il ne doit pas sauter aux
   yeux plus que le contenu. */
.assist-effacer {
  margin-left: auto; margin-right: 6px; padding: 2px 8px;
  border: 0; border-radius: 8px; background: none; cursor: pointer;
  color: var(--text-dim, #9aa3b8);
  font-family: inherit; font-size: 0.75rem;
}
.assist-effacer:hover { color: var(--accent-lisible); background: rgba(var(--accent-rgb), 0.1); }

.assist-saisie {
  display: flex; gap: 8px; padding: 12px;
  border-top: 1px solid var(--border, #262b3a);
}
.assist-saisie input {
  flex: 1; min-width: 0; min-height: 40px; padding: 0 12px;
  border: 1px solid var(--border, #262b3a); border-radius: 10px;
  background: var(--bg, #12141d); color: var(--text);
  font-family: inherit; font-size: 0.9rem;
}
.assist-saisie input:focus-visible {
  outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 1px;
}
.assist-saisie button {
  min-width: 44px; min-height: 40px;
  border: none; border-radius: 10px; cursor: pointer;
  background: var(--accent-plein); color: var(--accent-texte);
  font-family: inherit; font-size: 1.1rem;
}

/* Sur téléphone, la bulle prend toute la largeur : un panneau de 340 px sur
   un écran de 360 laisse deux marges de dix pixels et se lit mal. */
@media (max-width: 480px) {
  .assist { right: 10px; bottom: 10px; left: 10px; align-items: stretch; }
  .assist-panneau { width: auto; max-height: calc(100vh - 100px); }
  .assist-bouton { align-self: flex-end; }
}

/* Le texte réservé aux lecteurs d'écran. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ==========================================================================
 *  LES TUTOS VIDÉO
 * ========================================================================== */
.tutos-grille {
  display: grid; gap: 18px;
  /* auto-fill plutôt qu'auto-fit : avec une seule vidéo, auto-fit l'étirerait
     sur toute la largeur et le lecteur ferait 900 px de haut. */
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.tuto {
  border: 1px solid var(--border, #262b3a); border-radius: 14px;
  overflow: hidden; background: var(--bg-softer);
  display: flex; flex-direction: column;
}
/* Le cadre 16/9 : sans lui, l'iframe prend une hauteur arbitraire et la
   grille sautille au chargement de chaque vidéo. */
.tuto-video { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.tuto-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tuto-corps { padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.tuto-corps h3 { margin: 0; font-size: 1rem; }
.tuto-corps p { margin: 0; font-size: 0.88rem; line-height: 1.5; }
.tuto-meta { font-size: 0.78rem; color: var(--text-dim, #9aa3b8); }
.tuto details summary { min-height: 32px; display: flex; align-items: center; }

/* ==========================================================================
 *  LE CODE À SIX CHIFFRES — voir public/js/champ-code.js
 * ==========================================================================
 *  LA VRAIE BOÎTE DE TEXTE EST TOUJOURS LÀ, posée par-dessus les cases, encre
 *  et curseur transparents. Les cases ne sont que du décor qui recopie ce
 *  qu'elle contient : c'est ce qui laisse marcher le collage, le remplissage
 *  automatique du code reçu, et les lecteurs d'écran.
 *
 *  Sans le script, rien de tout ça n'existe : `.cc-saisie` n'est jamais posé,
 *  le champ garde son apparence ordinaire, et la page est celle d'avant.
 * ========================================================================== */

.cc { position: relative; margin: 6px 0 2px; }

/*  LA RANGÉE S'ALIGNE SUR LE RESTE DU FORMULAIRE, elle n'est pas centrée.
 *
 *  PREMIÈRE VERSION, CENTRÉE, ET ÇA SE VOYAIT : le libellé au-dessus et le
 *  bouton en dessous sont calés à gauche ; six cases centrées au milieu
 *  donnaient une rangée qui flotte, décalée d'une quarantaine de pixels vers
 *  la droite. Ce n'est pas un choix esthétique contre un autre — dans une
 *  colonne alignée à gauche, un élément centré se lit comme une erreur de
 *  mise en page. Signalé sur une capture d'écran. */
.cc-rangee {
  display: flex; gap: 10px; justify-content: flex-start; flex-wrap: wrap;
  transition: opacity var(--duree) var(--courbe);
}

.cc-case {
  flex: 0 0 auto;
  width: 48px; height: 62px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-softer);
  /* LE PIVOT EST SOUS LA RANGÉE, ET C'EST TOUT LE GESTE. Les cartes tournent
     autour d'un point commun placé bien plus bas qu'elles : c'est ce qui fait
     une main de cartes qui s'ouvre plutôt que six carrés penchés chacun dans
     son coin. */
  transform-origin: 50% 320%;
  transition: border-color var(--duree-rapide) var(--courbe),
              background var(--duree-rapide) var(--courbe),
              box-shadow var(--duree-rapide) var(--courbe);
}

.cc-chiffre {
  font-size: 1.7rem; font-weight: 600; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.cc-case.cc-remplie { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }

/* La case courante porte le même anneau que n'importe quel champ au focus :
   le curseur est invisible, c'est elle qui dit où l'on écrit. */
.cc-case.cc-active {
  border-color: var(--accent);
  box-shadow: var(--anneau-focus);
}

/* La boîte de texte : présente, cliquable, mais transparente. On garde une
   encre transparente PLUTÔT QUE `opacity: 0` — un champ à opacité nulle est
   sauté par certains lecteurs d'écran et refuse le focus sur d'autres. */
/*  LE SÉLECTEUR EST VOLONTAIREMENT LOURD, et c'est une correction, pas un
 *  tic. `.cc-saisie` seul (0,1,0) perdait contre `.form-field input` et
 *  `.container input[type="text"]` (0,2,1) : le champ gardait le fond opaque
 *  et la bordure du site, et repeignait par-dessus les six cases. Vu sur une
 *  capture — la rangée était là, invisible, sous une grande boîte grise.
 *  On monte donc à la même arme. `!important` aurait marché aussi ; il aurait
 *  surtout caché la raison. */
.cc input.cc-saisie,
.cc-saisie {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  border: 0; padding: 0; margin: 0;
  background: transparent;
  color: transparent;
  caret-color: transparent;
  text-align: center;
  /* L'écart entre les chiffres n'a pas à coller aux cases : le texte est
     invisible. Il sert seulement à ce qu'une sélection au clavier tombe à peu
     près en face de la bonne case. */
  letter-spacing: 3rem;
  font-size: 1.7rem;
  cursor: text;
  border-radius: 0;
  box-shadow: none;
}
.cc input.cc-saisie:focus,
.cc-saisie:focus { outline: none; box-shadow: none; border: 0; background: transparent; }
/* La sélection resterait visible et baverait par-dessus le décor. */
.cc-saisie::selection { background: transparent; }

/* --- L'anneau de vérification ------------------------------------------- *
 *  Il n'apparaît qu'au moment de l'envoi, quand les cases se rassemblent.
 *  Il occupe un vrai aller-retour serveur : sans lui, on regarde un écran
 *  figé en se demandant si le clic a été pris. */
.cc-anneau {
  position: absolute; left: 50%; top: 50%;
  width: 46px; height: 46px; margin: -23px 0 0 -23px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-top-color: var(--accent);
  opacity: 0; pointer-events: none;
  transition: opacity var(--duree) var(--courbe);
}
.cc-verifie .cc-anneau { opacity: 1; animation: cc-tourne 720ms linear infinite; }
.cc-verifie .cc-saisie { cursor: default; }

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

/* Un code refusé se dit LÀ OÙ IL A ÉTÉ TAPÉ, pas seulement en haut de page :
   c'est là que les yeux sont au moment où l'on revient. */
.cc-refuse .cc-case { border-color: color-mix(in srgb, #ff5b7a 55%, var(--border)); }

/* --- Petits écrans -------------------------------------------------------- *
 *  Six cases à 48px plus les écarts dépassent la largeur d'un téléphone
 *  étroit. On rétrécit plutôt que de laisser la rangée déborder — une case
 *  hors de l'écran est une case qu'on ne peut pas remplir. */
@media (max-width: 420px) {
  .cc-case { width: 40px; height: 54px; border-radius: 10px; }
  .cc-chiffre { font-size: 1.4rem; }
  .cc-rangee { gap: 7px; }
  .cc-saisie { letter-spacing: 2.4rem; font-size: 1.4rem; }
}

/* ==========================================================================
 *  LE DÉCOR DE FOND — demandé le 27/08 : « tout noir c'est joli mais… »
 * ==========================================================================
 *  CE QU'IL FAUT SAVOIR AVANT DE TOUCHER À CE BLOC.
 *
 *  1. IL SUIT LE MONDE, ET CE N'EST PAS DÉCORATIF.
 *     Violet sur le site public, ambre sur la production. La teinte est le
 *     seul repère qui fonctionne SANS LIRE : c'est elle qui dit, avant le
 *     premier clic, qu'on manipule les vraies données. Un décor identique
 *     dans les deux mondes détruirait ce repère — il ne rendrait pas la prod
 *     moins reconnaissable, il ajouterait un grand aplat commun qui la ferait
 *     ressembler au site public. Le coût : une suppression de masse lancée
 *     dans le mauvais monde parce que l'œil a cru reconnaître le public.
 *     D'où `rgba(var(--fond-teinte), …)` : la teinte bascule avec le monde
 *     comme tout le reste du site, sur `html[data-monde="prod"]`.
 *
 *  2. IL RESTE PLUS SOMBRE QU'UNE CARTE.
 *     Ce site est un tableau de bord : des tableaux, des formulaires, des
 *     étiquettes. Le point le PLUS CLAIR du décor est réglé pour rester au
 *     niveau de --bg-card (#1a1e2b), la surface sur laquelle le site pose
 *     déjà ses libellés en --text-muted. Autrement dit : un texte posé sur le
 *     décor n'est jamais moins lisible qu'un texte posé sur une carte, et ce
 *     seuil-là est déjà celui que le site assume partout. Un fond qui rend
 *     une étiquette de formulaire difficile à lire est un fond raté, même
 *     s'il est beau. verif-fond.js mesure ce point le plus clair EN PIXELS,
 *     dans un vrai navigateur, et exige 4,5:1 pour --text-muted.
 *
 *  3. IL NE COÛTE RIEN, ET C'EST UNE CONTRAINTE, PAS UN LUXE.
 *     Un seul pseudo-élément, six couches de dégradés, zéro image, zéro nœud
 *     ajouté au DOM, zéro JavaScript. Même vocabulaire et même technique que
 *     le ciel du règlement (.rg-ciel) : la poussière est faite de deux
 *     dégradés RÉPÉTÉS, pas de trois cents éléments. Le tableau de bord doit
 *     s'ouvrir sur un vieux téléphone ; trois cents nœuds pour un décor que
 *     personne ne regarde, c'est le faire ramer pour rien.
 *
 *  4. IL BOUGE — DEPUIS LE 29/08 — ET LA RAISON DE NE PAS BOUGER RESTE VRAIE.
 *     Ce bloc disait « il ne bouge pas, volontairement », et l'argument était
 *     juste : ce n'est pas une page d'accueil qu'on traverse, c'est un outil
 *     qu'on regarde des heures, et un mouvement continu en fond d'écran est
 *     exactement ce qui déclenche nausées et migraines.
 *
 *     La demande du 29/08 — « le fond n'est pas terrible, il faudrait des
 *     animations, en restant dans la thématique » — ne rend pas cet argument
 *     faux. Elle oblige à le SATISFAIRE AUTREMENT, et c'est ce que fait la
 *     couche `body::after` plus bas :
 *
 *       · EN CSS, JAMAIS EN JAVASCRIPT. C'était le coeur de l'objection :
 *         `@media (prefers-reduced-motion)` ne peut RIEN contre un
 *         `element.animate()`. Une animation CSS, elle, s'éteint pour de bon
 *         — et la feuille porte déjà une coupure générale sur `*::after`.
 *         Qui demande moins de mouvement retrouve exactement le décor d'hier.
 *
 *       · UN CYCLE DE 96 SECONDES, quelques pour cent d'amplitude. Ce qui
 *         donne la nausée, c'est le mouvement qu'on PERÇOIT du coin de l'oeil
 *         pendant qu'on lit. À cette vitesse-là — de l'ordre du tiers de pixel
 *         par seconde — on ne voit pas bouger : on trouve juste que l'écran
 *         n'est plus le même qu'il y a deux minutes. C'est la différence entre
 *         une aurore et un économiseur d'écran.
 *
 *       · LA COUCHE DU BAS NE BOUGE PAS. Le sol, la poussière et les trois
 *         halos restent posés (`body::before`). Une étoile qui dérive, l'oeil
 *         l'attrape ; c'est le point net qui trahit le mouvement, pas la
 *         brume. Seule la brume dérive.
 *
 *       · LE PLAFOND DE CLARTÉ TIENT À CHAQUE INSTANT DU CYCLE, pas seulement
 *         à l'arrêt. C'est la seule chose qu'une animation pouvait casser en
 *         silence : un décor conforme au repos et trop clair à la seconde 40.
 *         verif-fond.js parcourt donc le cycle et mesure au pire moment.
 *
 *  5. IL NE PASSE JAMAIS DEVANT.
 *     `z-index: -1` le range sous TOUT contenu positionné ou non — donc sous
 *     les cadres modaux (z-index 1000), sous la barre du haut (100) et sous
 *     les messages. `pointer-events: none` fait le reste : un décor qui
 *     avalerait un clic transformerait un bouton en décoration.
 */

/*  Les jetons du décor. Ils sont à part de --accent : l'accent sert au texte
    et aux bordures, où il doit être FRANC ; ici on veut une brume. Les
    séparer permet de rendre le décor plus discret sans délaver les boutons. */
:root {
  /* La teinte du monde public : le violet de la marque. */
  --fond-teinte: 124, 92, 255;
  /*  TROIS FORCES DIFFÉRENTES, ET C'EST VOULU : un halo haut plus présent,
      un halo latéral en rappel, un halo bas qui empêche le bas de l'écran de
      retomber au noir plat quand on a défilé. */
/*  REMONTÉ LE 27/08, APRÈS AVOIR REGARDÉ LA PAGE.
 *
 *  Les premières valeurs (0,115 / 0,085 / 0,090 côté public) avaient été
 *  choisies pour rester SOUS la clarté d'une carte — un raisonnement juste sur
 *  le papier, et un décor invisible à l'écran : sur une page de tableau de
 *  bord, qui est pleine de cartes sombres, on ne voyait rien. La demande était
 *  « tout noir c'est joli mais... », et la page restait toute noire.
 *
 *  Cent quatre-vingt-quatorze mesures disaient « le décor est peint ». Elles
 *  avaient raison, et elles répondaient à côté de la question — « est-il
 *  peint » n'est pas « le voit-on ». C'est une capture d'écran qui a tranché.
 *
 *  Les valeurs ci-dessous restent bornées par la même règle : le point le plus
 *  clair du décor ne dépasse pas la clarté d'une carte, sinon un texte posé
 *  dessus deviendrait moins lisible qu'ailleurs. La vérification le mesure
 *  page par page, dans les deux mondes. */
/*  BAISSÉS LE 29/08, PARCE QUE L'AURORE S'AJOUTE PAR-DESSUS.
 *  Ces trois halos et l'aurore peignent au même endroit : les garder à leur
 *  valeur d'hier aurait fait passer le point le plus clair AU-DESSUS d'une
 *  carte, et c'est la seule règle que ce décor n'a pas le droit d'enfreindre
 *  — un fond plus clair qu'une carte rend les étiquettes de formulaire moins
 *  lisibles que partout ailleurs. Ils gardent leur rôle propre : c'est eux, et
 *  pas l'aurore, qui garantissent que le bas de l'écran reste teinté quel que
 *  soit le moment du cycle. */
  --fond-halo-haut: 0.058;
  --fond-halo-cote: 0.050;
  --fond-halo-bas: 0.090;
  /*  LES TROIS ARRÊTS DU CIEL. Ils sont des couleurs pleines et non des
      opacités : un dégradé de ciel se règle en regardant les couleurs, pas en
      empilant des transparences dont on ne sait plus laquelle fait quoi.
      --ciel-bas est PLUS SOMBRE que --bg : c'est le sol. */
  --ciel-haut: #16172a;
  --ciel-milieu: #111320;
  --ciel-bas: #0a0b0f;
  --fond-voile: #121420;
  /* La poussière : des points, pas des étoiles. Assez pour que le fond ait
     du grain, assez peu pour qu'on ne les compte pas. */
  --fond-poussiere: 210, 200, 255;
  --fond-poussiere-force: 0.10;
  /*  L'AURORE — la couche qui dérive (voir body::after).
   *  DEUX teintes et pas une : une brume d'une seule couleur se lit comme un
   *  filtre posé sur l'écran, deux teintes qui se croisent se lisent comme de
   *  la profondeur. Le bleu est celui de la formule Premium (--premium-2) :
   *  le site n'apprend pas une couleur de plus. */
  --fond-aurore-2: 122, 184, 255;
  --fond-aurore-force: 0.066;
  /* La trame : voir body::before. Elle suit le monde comme le reste — les
     hachures sont peintes dans la couleur de la poussière, pas en blanc. */
  --fond-trame: 0.026;
  /*  LES ÉTOILES. Leur opacité est un jeton parce qu'elle doit baisser en
      production : l'ambre est trois fois plus lumineux que le violet à
      opacité égale, et un ciel deux fois plus clair là où on lit des chiffres
      réels, c'est exactement ce qu'on ne veut pas. */
  --ciel-etoiles-loin: 0.46;
  --ciel-etoiles-moyen: 0.52;
  --ciel-etoiles-pres: 0.58;
  --ciel-filante-force: 0.9;
  /*  LA FORCE DES FORMES DE FÊTE, à part de celle des étoiles : une forme de
      dix pixels ne pèse pas comme un point de un. Ce qui décide de la
      lisibilité d'un libellé, c'est la clarté MOYENNE sous lui — donc la
      surface autant que l'opacité. Réglée fête par fête, dans les deux
      mondes, sur des fenêtres de la taille d'une étiquette. */
  --fete-force: 0.16;
  --fete-force-2: 0.12;
  /* Les halos du hero — voir .orb. */
  --orbe-force: 0.30;
}

/*  LA PRODUCTION EST AMBRE — et l'ambre est BEAUCOUP plus lumineux que le
    violet à opacité égale (#facc15 pèse 12,34:1 sur le fond, #7c5cff 4,35:1).
    Reprendre les mêmes valeurs donnerait un décor deux fois plus clair côté
    prod, et c'est précisément là qu'on lit des chiffres réels. On baisse donc
    les forces d'à peu près moitié : à l'œil les deux décors ont le même
    poids, et la mesure de contraste tient des deux côtés. */
html[data-monde="prod"] {
  --fond-teinte: 250, 204, 21;
  /*  Deux fois plus faible qu'en public, et ce n'est pas un oubli : le jaune
   *  de la production pèse 12,3:1 là où le violet pèse 4,35:1. À opacité
   *  égale, la production serait deux fois plus claire que le site public. */
  --fond-halo-haut: 0.038;
  --fond-halo-cote: 0.034;
  --fond-halo-bas: 0.048;
  --ciel-haut: #221d0c;
  --ciel-milieu: #16140b;
  --ciel-bas: #0a0a07;
  --fond-voile: #16150f;
  --fond-poussiere: 255, 238, 190;
  --fond-poussiere-force: 0.09;
  /*  Même raison qu'au-dessus : l'ambre pèse trois fois le violet à opacité
   *  égale. La seconde teinte reste chaude — un bleu dans une aurore ambre
   *  ferait ressembler la production au site public, et c'est précisément le
   *  repère qu'on ne veut pas brouiller. */
  --fond-aurore-2: 255, 176, 74;
  --fond-aurore-force: 0.040;
  --fond-trame: 0.017;
  --ciel-etoiles-loin: 0.42;
  --ciel-etoiles-moyen: 0.40;
  --ciel-etoiles-pres: 0.55;
  --ciel-filante-force: 0.75;
  /*  Deux tiers de la valeur du public, pour la raison habituelle : l'ambre
      de la production pèse trois fois le violet à opacité égale. Sans ça, la
      Saint-Valentin en production tombait à 4,97:1 — mesuré, pas estimé. */
  --fete-force: 0.11;
  --fete-force-2: 0.085;
  --orbe-force: 0.20;
}

/*  LE CORPS DE PAGE DEVIENT TRANSPARENT.
 *  Sans cette ligne, rien de ce qui suit ne se voit : `html, body` peint
 *  --bg en opaque, et le fond de `body` est peint APRÈS la couche z-index
 *  négative — il recouvrirait donc intégralement le décor. Le sol sombre
 *  reste posé par `html`, qui le propage au canevas : le rebond élastique en
 *  haut et en bas de page reste noir, il ne devient pas blanc. */
body { background: transparent; }

body::before {
  content: '';
  /*  FIXE, PAS ABSOLU. Le décor doit couvrir l'écran, pas la page : sur une
      page de module qui fait six écrans de haut, un décor `absolute` serait
      étiré sur toute la hauteur et l'on ne verrait qu'une tranche de dégradé
      — arrivé en bas, le fond serait redevenu noir plat. */
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--bg);
  /* ======================================================================
   *  LE DÉGRADÉ DE CIEL — refait le 29/08
   * ======================================================================
   *  « Fais un dégradé de ciel ou autre, un truc clean, ils n'aiment pas ce
   *   fond d'écran. »
   *
   *  CE QUI FAISAIT SALE, ET CE N'ÉTAIT PAS LA COULEUR. Deux choses :
   *
   *   1. UNE TRAME DE HACHURES. Elle avait été posée pour donner du grain à
   *      la bande morte du milieu. Vu de près c'est du grain ; vu de loin,
   *      sur un grand écran, c'est un quadrillage régulier posé sur toute la
   *      page — et un motif régulier sur un fond, ça ne se lit pas comme de
   *      la matière, ça se lit comme un écran sale. Elle est retirée.
   *
   *   2. TROIS HALOS SANS DIRECTION. Un en haut à gauche, un en haut à
   *      droite, un en bas au milieu : trois taches qui se contredisent. Un
   *      ciel a un HAUT et un BAS. C'est cette verticale-là qui manquait, et
   *      c'est elle qui fait « propre » — pas la quantité de lumière.
   *
   *  CE QUI REMPLACE : un vrai dégradé vertical, de l'indigo profond en haut
   *  vers le noir en bas, avec une lueur d'horizon basse. La bande la plus
   *  claire est TOUT EN HAUT, là où il n'y a que la barre de navigation ;
   *  elle s'éteint avant le premier tiers, c'est-à-dire avant le premier
   *  tableau. C'est ce qui permet d'avoir un ciel visible sans toucher au
   *  plafond de lisibilité : on met la lumière là où personne ne lit.
   * ====================================================================== */
  background-image:
    /*  LA POUSSIÈRE FIXE. Les étoiles qui BOUGENT sont dans .ciel ; celles-ci
        ne bougent pas, et c'est exprès : un fond entièrement mobile ne donne
        à l'oeil aucun repère immobile pour mesurer le mouvement des autres.
        Deux trames premières entre elles (173 et 251 px) pour que le motif
        combiné ne se répète qu'au bout de plusieurs écrans. */
    radial-gradient(1px 1px at 34px 51px,
      rgba(var(--fond-poussiere), var(--fond-poussiere-force)), transparent),
    radial-gradient(1.4px 1.4px at 148px 97px,
      rgba(var(--fond-poussiere), calc(var(--fond-poussiere-force) * 0.7)), transparent),
    /*  LA LUEUR D'HORIZON. Très large, très basse, très faible : c'est elle
        qui empêche le bas de l'écran de retomber au noir plat quand on a
        défilé. Un ciel dont le bas est noir n'est pas un ciel, c'est un mur. */
    radial-gradient(150% 42% at 50% 118%,
      rgba(var(--fond-teinte), var(--fond-halo-bas)), transparent 70%),
    /*  DEUX LUEURS HAUTES, DÉCENTRÉES. Elles donnent au ciel son épaisseur —
        un dégradé purement vertical est propre mais plat. Décentrées exprès :
        une lueur centrée se lit comme une tache, une lueur décentrée se lit
        comme une lumière qui vient de quelque part. */
    radial-gradient(1100px 620px at 12% -18%,
      rgba(var(--fond-teinte), var(--fond-halo-haut)), transparent 62%),
    radial-gradient(900px 520px at 92% -10%,
      rgba(var(--fond-aurore-2), var(--fond-halo-cote)), transparent 58%),
    /*  LE CIEL LUI-MÊME. Quatre arrêts, et chacun a une raison :
          0%    l'indigo le plus profond, derrière la barre du haut ;
          22%   il s'éteint déjà : c'est là que commence le contenu ;
          55%   la couleur de fond du site, nette — la bande des tableaux ;
          100%  un cran PLUS SOMBRE que --bg, pour que le bas ait un sol.
        Descendre sous --bg en bas plutôt que remonter en haut, c'est ce qui
        donne de la profondeur sans coûter un point de contraste. */
    linear-gradient(180deg,
      var(--ciel-haut) 0%,
      var(--ciel-milieu) 22%,
      var(--bg) 55%,
      var(--ciel-bas) 100%);
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat, no-repeat;
  background-size: 173px 173px, 251px 251px, auto, auto, auto, auto;
}

/* ==========================================================================
 *  LE CIEL — le décor animé (voir views/partials/ciel.ejs)
 * ==========================================================================
 *  DEMANDÉ LE 29/08, DEUX FOIS. La première version dérivait déjà, mais si
 *  lentement et de si peu que la réponse a été « je ne vois rien qui a
 *  changé ». C'est un verdict sur le résultat, pas sur l'intention : un décor
 *  qu'on doit prouver avec un chronomètre n'est pas un décor animé.
 *
 *  CE QUI CLOCHAIT VRAIMENT, ET CE N'ÉTAIT PAS LA VITESSE. Tout tenait sur
 *  deux pseudo-éléments, donc sur deux `transform` — et un élément n'en porte
 *  qu'un. Tout ce qui bougeait bougeait donc EN BLOC. Or un décor qui glisse
 *  d'un seul tenant ne se lit pas comme de la profondeur, il se lit comme une
 *  image qu'on fait coulisser : l'oeil ne trouve aucun repère fixe pour
 *  mesurer le mouvement, et conclut qu'il n'y en a pas. C'est la PARALLAXE qui
 *  se voit — deux plans qui avancent à des vitesses différentes — pas la
 *  vitesse absolue.
 *
 *  D'OÙ QUATRE PLANS, ET C'EST TOUTE LA DIFFÉRENCE :
 *    · l'aurore, trois nappes de couleur qui se déforment (46 s) ;
 *    · les étoiles LOINTAINES, petites et pâles, qui dérivent lentement (150 s) ;
 *    · les étoiles PROCHES, plus grosses, deux fois plus vite (78 s), qui
 *      scintillent ;
 *    · les filantes, deux traits qui traversent toutes les 17 et 23 secondes.
 *
 *  LES FILANTES SONT LÀ POUR UNE RAISON PRÉCISE. Un mouvement continu, on
 *  cesse de le voir au bout d'une minute — c'est ainsi que l'oeil fonctionne.
 *  Un ÉVÉNEMENT, non : il se remarque à chaque fois. Deux traits qui passent
 *  de temps en temps donnent au décor la seule chose qu'aucune dérive ne peut
 *  donner — un instant où il se passe quelque chose. Et parce que c'est bref
 *  et rare, ça ne fatigue pas : on ne peut pas s'habituer à ce qui n'est pas
 *  là la plupart du temps.
 *
 *  CE QUI N'A PAS CHANGÉ, ET NE CHANGERA PAS :
 *    · LA TEINTE SUIT LE MONDE. Violet et bleu en public, ambre en prod. Ce
 *      n'est pas décoratif : c'est le seul repère qui dit « tu manipules les
 *      vraies données » sans qu'on ait à lire.
 *    · LE PLAFOND DE LISIBILITÉ. Le point le plus clair du décor reste sous
 *      la barre qui garantit 5:1 aux étiquettes de formulaire — et il est
 *      mesuré À TOUS LES MOMENTS du cycle, pas au repos.
 *    · ZÉRO IMAGE, ZÉRO JAVASCRIPT, et rien qui ne s'anime autrement qu'en
 *      `transform` ou `opacity`. Le tableau de bord doit s'ouvrir sur un vieux
 *      téléphone.
 *    · LE RÉGLAGE SYSTÈME « MOINS D'ANIMATIONS » arrête tout. C'est la
 *      condition qui a permis d'animer : en CSS, il suffit ; en JavaScript, il
 *      ne peut rien.
 * ========================================================================== */

.ciel {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /*  Il découpe ce qui déborde : les couches intérieures sont volontairement
      plus grandes que l'écran pour que rotations et translations ne montrent
      jamais un bord. Sans ce découpage, elles créeraient une barre de
      défilement horizontale sur toutes les pages du site. */
  overflow: hidden;
  /*  `contain` dit au navigateur que rien de ce qui se passe là-dedans ne peut
      affecter la mise en page du reste. Sur une page qui contient un tableau
      de six cents lignes, ça évite de recalculer quoi que ce soit à chaque
      image de l'animation. */
  contain: strict;
}
.ciel > * { position: absolute; }

/* --- 1. L'AURORE : trois nappes qui se déforment ------------------------- */
/*  Les trois nappes sont à des distances différentes du centre de rotation :
    une rotation de six degrés les déplace donc de trois quantités différentes,
    et elles passent l'une devant l'autre au lieu de glisser ensemble. C'est ce
    croisement qui donne l'impression de volume. */
.ciel-aurore {
  inset: -35%;
  background-image:
    radial-gradient(32% 26% at 22% 26%,
      rgba(var(--fond-teinte), var(--fond-aurore-force)),
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.35)) 45%, transparent 66%),
    radial-gradient(28% 22% at 76% 18%,
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.9)),
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.3)) 45%, transparent 66%),
    radial-gradient(40% 26% at 58% 84%,
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.8)),
      rgba(var(--fond-teinte), calc(var(--fond-aurore-force) * 0.28)) 45%, transparent 68%),
    radial-gradient(58% 20% at 42% 52%,
      rgba(var(--fond-aurore-2), calc(var(--fond-aurore-force) * 0.45)), transparent 72%);
  animation: ciel-aurore 46s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes ciel-aurore {
  0%   { transform: translate3d(-4%, -2%, 0) scale(1.02) rotate(-3deg); }
  35%  { transform: translate3d(3%, 2.5%, 0) scale(1.14) rotate(2deg); }
  70%  { transform: translate3d(-2%, 4%, 0)  scale(1.06) rotate(4deg); }
  100% { transform: translate3d(4%, -3%, 0)  scale(1.10) rotate(-1.5deg); }
}

/* --- 2. LES ÉTOILES : deux plans, deux vitesses -------------------------- */
/*  LE BOUCLAGE SANS SAUT. Le motif se répète toutes les T pixels ; la couche
    est agrandie de T dans chaque direction et translatée d'EXACTEMENT T. À la
    fin du cycle, l'image est donc pixel pour pixel celle du début, et la
    boucle ne se voit pas. Une valeur approchée ferait un à-coup toutes les
    deux minutes — le genre de défaut qu'on remarque sans savoir le nommer. */
.ciel-etoiles {
  /*  LA MARGE COUVRE LA PLUS GRANDE DES TROIS TUILES (420 px) : sous-estimer
      ne se verrait qu'au bout d'un cycle, sur un bord, quelques secondes. */
  inset: -440px -440px auto auto;
  width: calc(100% + 880px);
  height: calc(100% + 880px);
  top: -440px; right: -440px;
  will-change: transform;
}
/*  TROIS PAS DE TUILE DIFFÉRENTS — 300, 340 et 420 px — ET C'EST LE POINT.
    Avec la même tuile pour les trois plans, les trois grilles se
    superposaient exactement : on voyait des colonnes d'étoiles régulières,
    et un ciel dont on devine la grille n'est plus un ciel. À trois pas
    différents, le motif combiné ne se répète qu'au bout de plusieurs mètres
    d'écran — c'est-à-dire jamais.

    LES POSITIONS SONT TIRÉES UNE FOIS ET ÉCRITES EN CLAIR. Un semis
    recalculé à chaque chargement ferait sauter les étoiles d'une page à
    l'autre, ce qui se remarque beaucoup plus qu'on ne croit. */
.ciel-etoiles-loin {
  background-image:
    radial-gradient(1.2px 1.2px at 171px 83px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 280px 54px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 265px 115px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 220px 41px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 36px 69px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 209px 31px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 154px 220px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 163px 292px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 196px 55px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1.2px 1.2px at 111px 260px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 238px 191px, rgba(var(--fond-poussiere), 0.35), transparent),
    radial-gradient(1px 1px at 130px 47px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1px 1px at 181px 235px, rgba(var(--fond-poussiere), 0.28), transparent),
    radial-gradient(1.2px 1.2px at 66px 268px, rgba(var(--fond-poussiere), 0.35), transparent),
    radial-gradient(1.2px 1.2px at 83px 256px, rgba(var(--fond-poussiere), 0.55), transparent),
    radial-gradient(1px 1px at 291px 166px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 239px 41px, rgba(var(--fond-poussiere), 0.45), transparent),
    radial-gradient(1px 1px at 39px 37px, rgba(var(--fond-poussiere), 0.28), transparent);
  background-size: 300px 300px;
  opacity: var(--ciel-etoiles-loin);
  animation: ciel-derive-loin 150s linear infinite;
}

/*  LE PLAN DU MILIEU — demandé le 29/08 : « des étoiles par-ci par-là ».
    Deux plans donnaient deux vitesses ; trois donnent une profondeur
    qu'on n'obtient pas en ajoutant des points aux deux autres. */
.ciel-etoiles-moyen {
  background-image:
    radial-gradient(1.3px 1.3px at 201px 176px, rgba(var(--fond-aurore-2), 0.5), transparent),
    radial-gradient(1.1px 1.1px at 301px 151px, rgba(var(--fond-aurore-2), 0.6), transparent),
    radial-gradient(1.3px 1.3px at 301px 10px, rgba(var(--fond-aurore-2), 0.7), transparent),
    radial-gradient(1.1px 1.1px at 85px 82px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 137px 323px, rgba(var(--fond-poussiere), 0.7), transparent),
    radial-gradient(1.6px 1.6px at 306px 22px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 43px 190px, rgba(var(--fond-poussiere), 0.38), transparent),
    radial-gradient(1.3px 1.3px at 290px 15px, rgba(var(--fond-poussiere), 0.7), transparent),
    radial-gradient(1.6px 1.6px at 97px 33px, rgba(var(--fond-poussiere), 0.6), transparent),
    radial-gradient(1.1px 1.1px at 323px 19px, rgba(var(--fond-aurore-2), 0.5), transparent),
    radial-gradient(1.6px 1.6px at 35px 160px, rgba(var(--fond-poussiere), 0.5), transparent),
    radial-gradient(1.3px 1.3px at 53px 214px, rgba(var(--fond-aurore-2), 0.38), transparent),
    radial-gradient(1.3px 1.3px at 123px 17px, rgba(var(--fond-poussiere), 0.5), transparent);
  background-size: 340px 340px;
  opacity: var(--ciel-etoiles-moyen);
  animation: ciel-derive-moyen 108s linear infinite,
             ciel-scintille-lent 13s ease-in-out infinite;
}

.ciel-etoiles-pres {
  background-image:
    radial-gradient(1.8px 1.8px at 405px 154px, rgba(var(--fond-teinte), 0.9), transparent),
    radial-gradient(2.6px 2.6px at 163px 222px, rgba(var(--fond-poussiere), 0.8), transparent),
    radial-gradient(1.5px 1.5px at 377px 104px, rgba(var(--fond-poussiere), 1), transparent),
    radial-gradient(2.6px 2.6px at 119px 323px, rgba(var(--fond-poussiere), 0.9), transparent),
    radial-gradient(2.6px 2.6px at 49px 369px, rgba(var(--fond-poussiere), 1), transparent),
    radial-gradient(2.6px 2.6px at 268px 327px, rgba(var(--fond-poussiere), 0.8), transparent),
    radial-gradient(1.8px 1.8px at 111px 406px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(2.2px 2.2px at 94px 323px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(1.5px 1.5px at 395px 194px, rgba(var(--fond-teinte), 0.8), transparent),
    radial-gradient(2.2px 2.2px at 49px 248px, rgba(var(--fond-teinte), 1), transparent);
  background-size: 420px 420px;
  opacity: var(--ciel-etoiles-pres);
  animation: ciel-derive-pres 78s linear infinite,
             ciel-scintille 9s ease-in-out infinite;
}

/*  LE DÉPLACEMENT VAUT EXACTEMENT UNE TUILE — 300 px, comme
    `background-size`. À la fin du cycle l'image est pixel pour pixel celle du
    début, et la boucle ne se voit pas. Une valeur approchée ferait un à-coup
    toutes les deux minutes : le genre de défaut qu'on remarque sans savoir le
    nommer. Si la tuile change, ces trois nombres changent avec elle. */
@keyframes ciel-derive-loin {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-300px, 300px, 0); }
}
@keyframes ciel-derive-moyen {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(340px, 340px, 0); }
}
@keyframes ciel-derive-pres {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-420px, 420px, 0); }
}
@keyframes ciel-scintille-lent {
  0%, 100% { opacity: calc(var(--ciel-etoiles-moyen) * 0.7); }
  50%      { opacity: var(--ciel-etoiles-moyen); }
}
@keyframes ciel-scintille {
  0%, 100% { opacity: calc(var(--ciel-etoiles-pres) * 0.75); }
  50%      { opacity: var(--ciel-etoiles-pres); }
}

/* --- 3. LES FILANTES ----------------------------------------------------- */
/*  DEMANDÉ LE 29/08 : « les étoiles filantes c'est stylé mais il n'y en a pas
 *  assez, il faut aussi qu'il y en ait partout ».
 *
 *  ELLES ÉTAIENT DEUX, ET CANTONNÉES AU HAUT DE L'ÉCRAN. Le raisonnement de
 *  la veille était : la bande basse est celle des tableaux et des
 *  formulaires, un trait lumineux qui traverse une colonne de chiffres
 *  distrait pendant qu'on lit. Le raisonnement tient toujours — mais il
 *  s'appliquait à un trait BRILLANT. On change donc l'objet plutôt que la
 *  règle : six filantes, sur toute la hauteur, et celles qui passent BAS sont
 *  plus fines et plus discrètes que celles du haut. Un ciel n'a pas de raison
 *  d'avoir des météores seulement dans son tiers supérieur.
 *
 *  SIX, ET PAS VINGT. Chacune est visible environ cinq pour cent de son
 *  cycle ; avec des périodes premières entre elles (11, 13, 17, 19, 23, 29
 *  secondes) elles ne repassent presque jamais ensemble, et il s'en passe une
 *  toutes les trois à quatre secondes en moyenne. Vingt filantes ne feraient
 *  pas « plus de ciel », elles feraient une pluie de météores — c'est-à-dire
 *  un décor qu'on regarde au lieu de regarder la page.
 *
 *  LES PÉRIODES SONT PREMIÈRES ENTRE ELLES pour une raison précise : avec des
 *  durées rondes (10, 20, 30) elles se synchroniseraient toutes les trente
 *  secondes et partiraient en salve. Des nombres premiers ne retombent jamais
 *  ensemble, et le ciel n'a pas de rythme perceptible.
 */
/*  LA BASE COMMUNE. `top` et `left` ne servent plus qu'à poser l'origine :
    c'est la keyframe qui décide où chaque passage a lieu. */
.ciel-filante {
  top: 0;
  left: 0;
  width: 200px;
  height: 2px;
  border-radius: 2px;
  /*  La traîne est plus longue que la tête : c'est ce qui donne le sens du
      mouvement. Une barre symétrique se lit comme un trait, pas comme un
      objet qui file. */
  background: linear-gradient(90deg,
    transparent, rgba(var(--fond-poussiere), calc(var(--ciel-filante-force) * 0.25)) 45%,
    rgba(var(--fond-poussiere), var(--ciel-filante-force)) 88%, transparent);
  opacity: 0;
  will-change: transform, opacity;
  animation: ciel-filante 17s linear infinite;
}
.ciel-filante-b { width: 150px; height: 1px; animation: ciel-filante-b 13s linear infinite; animation-delay: 0.1s; }
.ciel-filante-c { width: 130px; height: 2px; animation: ciel-filante-c 17s linear infinite; animation-delay: 10.3s; }
.ciel-filante-d { width: 150px; height: 1.5px; animation: ciel-filante-d 19s linear infinite; animation-delay: 4.0s; }
.ciel-filante-e { width: 170px; height: 2px; animation: ciel-filante-e 23s linear infinite; animation-delay: 4.8s; }
.ciel-filante-f { width: 150px; height: 1.5px; animation: ciel-filante-f 29s linear infinite; animation-delay: 1.5s; }
.ciel-filante-g { width: 150px; height: 1px; animation: ciel-filante-g 31s linear infinite; animation-delay: 8.5s; }
.ciel-filante-h { width: 90px; height: 2px; animation: ciel-filante-h 37s linear infinite; animation-delay: 2.3s; }
.ciel-filante-i { width: 90px; height: 1.5px; animation: ciel-filante-i 41s linear infinite; animation-delay: 8.5s; }
.ciel-filante-j { width: 150px; height: 1px; animation: ciel-filante-j 43s linear infinite; animation-delay: 8.2s; }

/* ==========================================================================
 *  CHAQUE FILANTE PASSE À TROIS ENDROITS DIFFÉRENTS — corrigé le 29/08
 * ==========================================================================
 *  « Les animations sont toujours au même endroit, il faudrait que ça change
 *   un peu. » C'était vrai, et c'était un défaut de conception, pas un
 *  réglage : chaque filante avait UNE hauteur, UN angle, UNE trajectoire,
 *  répétés à l'identique toutes les dix-sept secondes. Au bout de deux
 *  minutes l'oeil a appris le motif — et un mouvement qu'on prévoit ne se
 *  remarque plus. C'est le même piège que la première version du décor, sous
 *  une autre forme : ce n'est pas la quantité de mouvement qui compte, c'est
 *  qu'il reste imprévisible.
 *
 *  LE CSS NE SAIT PAS TIRER AU HASARD, et c'est tout le problème. On ne peut
 *  pas changer la trajectoire à chaque passage — mais on peut en mettre
 *  PLUSIEURS dans un même cycle. Chaque filante fait donc trois passages, à
 *  trois hauteurs, trois angles et trois vitesses, et se replace entre les
 *  deux pendant qu'elle est invisible.
 *
 *  LE PALIER ENTRE DEUX PASSAGES N'EST PAS DÉCORATIF. Sans une image-clé qui
 *  TIENT la position jusqu'au passage suivant, le navigateur interpolerait
 *  tout le temps mort : la couche dériverait doucement en travers de l'écran
 *  pendant vingt-cinq secondes, invisible, et le passage suivant partirait
 *  d'ailleurs que prévu. On ne l'aurait pas vu — on aurait seulement trouvé
 *  que « ça saute ».
 *
 *  DIX FILANTES × TROIS PASSAGES = TRENTE TRAJECTOIRES, sur des cycles
 *  premiers entre eux (11 à 43 s). Le motif complet ne se répète qu'au bout
 *  de leur plus petit commun multiple — des milliers d'heures. Autant dire
 *  jamais.
 *
 *  ELLES RESTENT DISCRÈTES EN BAS. La bande basse est celle des tableaux et
 *  des formulaires : les passages qui y descendent sont plus fins et moins
 *  lumineux. C'est la seule contrainte qui n'a pas bougé — un trait brillant
 *  en travers d'une colonne de chiffres distrait pendant qu'on lit.
 */
/* ==========================================================================
 *  L'ANGLE D'UNE FILANTE SE DÉDUIT DE SA TRAJECTOIRE — corrigé le 01/09/2026
 * ==========================================================================
 *  « Corrige le sens des étoiles filantes, ça fait bizarre. » C'était vrai, et
 *  mesurable : sur les 30 passages, 23 étaient à plus de 5° de leur propre
 *  trajectoire et 13 penchaient carrément à l'ENVERS — le trait montait
 *  pendant que l'étoile descendait. Une traîne qui ne suit pas le chemin ne se
 *  lit pas comme de la vitesse : elle se lit comme un trait posé de travers.
 *
 *  LE PIÈGE, ET IL EST DISCRET : `vw` et `vh` ne valent PAS la même chose.
 *  Un passage qui va de `-32vw, 50vh` à `120vw, 22vh` semble monter beaucoup —
 *  28 unités de haut pour 152 de large — et l'angle avait été écrit là-dessus
 *  (-23,7°). En pixels sur un écran 16/9, c'est 302 px de haut pour 2918 px de
 *  large, donc **-5,9°**. Quatre fois moins. Toutes les valeurs étaient
 *  fausses de la même façon, et celles dont le signe l'était aussi donnaient
 *  la comète qui vole en marche arrière.
 *
 *  CHAQUE ANGLE EST MAINTENANT `atan2(dy, dx)` DE SON PROPRE PASSAGE, calculé
 *  en pixels sur une référence 16/9. Il reste juste sur tout écran de forme
 *  voisine, et approché sur un téléphone tenu debout — le décor y est de toute
 *  façon presque invisible.
 *
 *  LE SENS DE LA TÊTE, LUI, ÉTAIT BON : tous les passages vont de la gauche
 *  vers la droite, et le dégradé met le point brillant à droite (88 %). La
 *  tête ouvre la voie, la traîne suit. On n'y a pas touché.
 * ========================================================================== */
@keyframes ciel-filante {
  4.62% { transform: translate3d(-32vw, 50vh, 0) rotate(-5.9deg); opacity: 0; }
  6.01% { opacity: 0.89; }
  9.50% { opacity: 0; }
  10.17% { transform: translate3d(120vw, 22vh, 0) rotate(-5.9deg); opacity: 0; }
  29.82% { transform: translate3d(120vw, 22vh, 0) rotate(-5.9deg); opacity: 0; }
  29.84% { transform: translate3d(-16vw, 4vh, 0) rotate(6.7deg); opacity: 0; }
  31.23% { opacity: 0.5; }
  34.72% { opacity: 0; }
  35.39% { transform: translate3d(138vw, 36vh, 0) rotate(6.7deg); opacity: 0; }
  56.85% { transform: translate3d(138vw, 36vh, 0) rotate(6.7deg); opacity: 0; }
  56.87% { transform: translate3d(-32vw, 86vh, 0) rotate(-3.4deg); opacity: 0; }
  58.26% { opacity: 0.95; }
  61.76% { opacity: 0; }
  62.42% { transform: translate3d(120vw, 70vh, 0) rotate(-3.4deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 70vh, 0) rotate(-3.4deg); opacity: 0; }
}
@keyframes ciel-filante-b {
  1.12% { transform: translate3d(-32vw, 50vh, 0) rotate(1.9deg); opacity: 0; }
  2.08% { opacity: 0.52; }
  4.49% { opacity: 0; }
  4.96% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  31.98% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  32.00% { transform: translate3d(-16vw, 50vh, 0) rotate(6.0deg); opacity: 0; }
  32.96% { opacity: 0.72; }
  35.38% { opacity: 0; }
  35.84% { transform: translate3d(155vw, 82vh, 0) rotate(6.0deg); opacity: 0; }
  59.50% { transform: translate3d(155vw, 82vh, 0) rotate(6.0deg); opacity: 0; }
  59.52% { transform: translate3d(-32vw, 62vh, 0) rotate(-3.0deg); opacity: 0; }
  60.48% { opacity: 0.46; }
  62.90% { opacity: 0; }
  63.36% { transform: translate3d(138vw, 46vh, 0) rotate(-3.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 46vh, 0) rotate(-3.0deg); opacity: 0; }
}
@keyframes ciel-filante-c {
  1.48% { transform: translate3d(-16vw, 4vh, 0) rotate(6.0deg); opacity: 0; }
  2.67% { opacity: 0.99; }
  5.65% { opacity: 0; }
  6.22% { transform: translate3d(155vw, 36vh, 0) rotate(6.0deg); opacity: 0; }
  27.99% { transform: translate3d(155vw, 36vh, 0) rotate(6.0deg); opacity: 0; }
  28.01% { transform: translate3d(-32vw, -6vh, 0) rotate(3.4deg); opacity: 0; }
  29.19% { opacity: 0.83; }
  32.18% { opacity: 0; }
  32.75% { transform: translate3d(155vw, 14vh, 0) rotate(3.4deg); opacity: 0; }
  55.47% { transform: translate3d(155vw, 14vh, 0) rotate(3.4deg); opacity: 0; }
  55.49% { transform: translate3d(-16vw, 74vh, 0) rotate(3.8deg); opacity: 0; }
  56.67% { opacity: 0.93; }
  59.66% { opacity: 0; }
  60.23% { transform: translate3d(155vw, 94vh, 0) rotate(3.8deg); opacity: 0; }
  99.98% { transform: translate3d(155vw, 94vh, 0) rotate(3.8deg); opacity: 0; }
}
@keyframes ciel-filante-d {
  2.36% { transform: translate3d(-16vw, 26vh, 0) rotate(7.5deg); opacity: 0; }
  3.41% { opacity: 0.56; }
  6.06% { opacity: 0; }
  6.56% { transform: translate3d(120vw, 58vh, 0) rotate(7.5deg); opacity: 0; }
  29.22% { transform: translate3d(120vw, 58vh, 0) rotate(7.5deg); opacity: 0; }
  29.24% { transform: translate3d(-32vw, 50vh, 0) rotate(5.5deg); opacity: 0; }
  30.29% { opacity: 0.67; }
  32.94% { opacity: 0; }
  33.44% { transform: translate3d(155vw, 82vh, 0) rotate(5.5deg); opacity: 0; }
  56.63% { transform: translate3d(155vw, 82vh, 0) rotate(5.5deg); opacity: 0; }
  56.65% { transform: translate3d(-24vw, -6vh, 0) rotate(2.0deg); opacity: 0; }
  57.70% { opacity: 0.53; }
  60.35% { opacity: 0; }
  60.85% { transform: translate3d(138vw, 4vh, 0) rotate(2.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 4vh, 0) rotate(2.0deg); opacity: 0; }
}
@keyframes ciel-filante-e {
  3.90% { transform: translate3d(-32vw, 14vh, 0) rotate(-4.8deg); opacity: 0; }
  5.34% { opacity: 0.63; }
  8.97% { opacity: 0; }
  9.66% { transform: translate3d(155vw, -14vh, 0) rotate(-4.8deg); opacity: 0; }
  31.63% { transform: translate3d(155vw, -14vh, 0) rotate(-4.8deg); opacity: 0; }
  31.65% { transform: translate3d(-16vw, 38vh, 0) rotate(3.8deg); opacity: 0; }
  33.09% { opacity: 0.63; }
  36.72% { opacity: 0; }
  37.41% { transform: translate3d(155vw, 58vh, 0) rotate(3.8deg); opacity: 0; }
  60.42% { transform: translate3d(155vw, 58vh, 0) rotate(3.8deg); opacity: 0; }
  60.44% { transform: translate3d(-24vw, 74vh, 0) rotate(4.5deg); opacity: 0; }
  61.88% { opacity: 0.43; }
  65.50% { opacity: 0; }
  66.20% { transform: translate3d(120vw, 94vh, 0) rotate(4.5deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 94vh, 0) rotate(4.5deg); opacity: 0; }
}
@keyframes ciel-filante-f {
  5.64% { transform: translate3d(-32vw, 50vh, 0) rotate(1.9deg); opacity: 0; }
  6.89% { opacity: 0.45; }
  10.05% { opacity: 0; }
  10.66% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  33.29% { transform: translate3d(138vw, 60vh, 0) rotate(1.9deg); opacity: 0; }
  33.31% { transform: translate3d(-32vw, 50vh, 0) rotate(-4.8deg); opacity: 0; }
  34.57% { opacity: 0.51; }
  37.73% { opacity: 0; }
  38.33% { transform: translate3d(155vw, 22vh, 0) rotate(-4.8deg); opacity: 0; }
  62.86% { transform: translate3d(155vw, 22vh, 0) rotate(-4.8deg); opacity: 0; }
  62.88% { transform: translate3d(-24vw, 50vh, 0) rotate(2.0deg); opacity: 0; }
  64.13% { opacity: 0.76; }
  67.30% { opacity: 0; }
  67.90% { transform: translate3d(138vw, 60vh, 0) rotate(2.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 60vh, 0) rotate(2.0deg); opacity: 0; }
}
@keyframes ciel-filante-g {
  1.03% { transform: translate3d(-32vw, 26vh, 0) rotate(6.8deg); opacity: 0; }
  2.00% { opacity: 0.69; }
  4.44% { opacity: 0; }
  4.90% { transform: translate3d(120vw, 58vh, 0) rotate(6.8deg); opacity: 0; }
  27.49% { transform: translate3d(120vw, 58vh, 0) rotate(6.8deg); opacity: 0; }
  27.51% { transform: translate3d(-16vw, 62vh, 0) rotate(6.7deg); opacity: 0; }
  28.47% { opacity: 0.93; }
  30.91% { opacity: 0; }
  31.38% { transform: translate3d(138vw, 94vh, 0) rotate(6.7deg); opacity: 0; }
  57.46% { transform: translate3d(138vw, 94vh, 0) rotate(6.7deg); opacity: 0; }
  57.48% { transform: translate3d(-32vw, 74vh, 0) rotate(6.0deg); opacity: 0; }
  58.45% { opacity: 1.0; }
  60.88% { opacity: 0; }
  61.35% { transform: translate3d(138vw, 106vh, 0) rotate(6.0deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 106vh, 0) rotate(6.0deg); opacity: 0; }
}
@keyframes ciel-filante-h {
  1.07% { transform: translate3d(-16vw, 26vh, 0) rotate(4.7deg); opacity: 0; }
  2.35% { opacity: 0.67; }
  5.57% { opacity: 0; }
  6.19% { transform: translate3d(120vw, 46vh, 0) rotate(4.7deg); opacity: 0; }
  33.81% { transform: translate3d(120vw, 46vh, 0) rotate(4.7deg); opacity: 0; }
  33.83% { transform: translate3d(-16vw, 86vh, 0) rotate(-3.0deg); opacity: 0; }
  35.11% { opacity: 0.91; }
  38.34% { opacity: 0; }
  38.95% { transform: translate3d(155vw, 70vh, 0) rotate(-3.0deg); opacity: 0; }
  63.64% { transform: translate3d(155vw, 70vh, 0) rotate(-3.0deg); opacity: 0; }
  63.66% { transform: translate3d(-16vw, 62vh, 0) rotate(-5.3deg); opacity: 0; }
  64.94% { opacity: 0.67; }
  68.17% { opacity: 0; }
  68.78% { transform: translate3d(155vw, 34vh, 0) rotate(-5.3deg); opacity: 0; }
  99.98% { transform: translate3d(155vw, 34vh, 0) rotate(-5.3deg); opacity: 0; }
}
@keyframes ciel-filante-i {
  2.05% { transform: translate3d(-32vw, 38vh, 0) rotate(4.2deg); opacity: 0; }
  3.50% { opacity: 0.5; }
  7.16% { opacity: 0; }
  7.85% { transform: translate3d(120vw, 58vh, 0) rotate(4.2deg); opacity: 0; }
  29.73% { transform: translate3d(120vw, 58vh, 0) rotate(4.2deg); opacity: 0; }
  29.75% { transform: translate3d(-24vw, 86vh, 0) rotate(-3.6deg); opacity: 0; }
  31.20% { opacity: 0.91; }
  34.86% { opacity: 0; }
  35.55% { transform: translate3d(120vw, 70vh, 0) rotate(-3.6deg); opacity: 0; }
  59.58% { transform: translate3d(120vw, 70vh, 0) rotate(-3.6deg); opacity: 0; }
  59.60% { transform: translate3d(-16vw, 86vh, 0) rotate(6.7deg); opacity: 0; }
  61.05% { opacity: 0.44; }
  64.71% { opacity: 0; }
  65.40% { transform: translate3d(138vw, 118vh, 0) rotate(6.7deg); opacity: 0; }
  99.98% { transform: translate3d(138vw, 118vh, 0) rotate(6.7deg); opacity: 0; }
}
@keyframes ciel-filante-j {
  2.69% { transform: translate3d(-32vw, 74vh, 0) rotate(1.9deg); opacity: 0; }
  4.23% { opacity: 0.8; }
  8.10% { opacity: 0; }
  8.84% { transform: translate3d(138vw, 84vh, 0) rotate(1.9deg); opacity: 0; }
  28.81% { transform: translate3d(138vw, 84vh, 0) rotate(1.9deg); opacity: 0; }
  28.83% { transform: translate3d(-16vw, 26vh, 0) rotate(3.8deg); opacity: 0; }
  30.37% { opacity: 0.61; }
  34.25% { opacity: 0; }
  34.98% { transform: translate3d(155vw, 46vh, 0) rotate(3.8deg); opacity: 0; }
  60.71% { transform: translate3d(155vw, 46vh, 0) rotate(3.8deg); opacity: 0; }
  60.73% { transform: translate3d(-32vw, 26vh, 0) rotate(2.1deg); opacity: 0; }
  62.26% { opacity: 0.69; }
  66.14% { opacity: 0; }
  66.88% { transform: translate3d(120vw, 36vh, 0) rotate(2.1deg); opacity: 0; }
  99.98% { transform: translate3d(120vw, 36vh, 0) rotate(2.1deg); opacity: 0; }
}

/* --- 4. LE VOILE : la vignette, posée par-dessus tout le reste ----------- */
/*  Elle éteint les quatre coins. C'est elle qui empêche l'aurore de « baver »
    jusqu'au bord de la fenêtre et qui ramène le regard vers le centre, là où
    est le contenu. Posée EN DERNIER, donc au-dessus des étoiles : sans ça, les
    étoiles des coins resteraient à pleine clarté sur un fond assombri, et on
    verrait le truc. */
.ciel-voile {
  inset: 0;
  background:
    radial-gradient(130% 100% at 50% 40%, transparent 46%, rgba(0, 0, 0, 0.42) 100%);
}

/* ==========================================================================
 *  LES EFFETS DE FÊTE — ce qui tombe, monte ou explose
 * ==========================================================================
 *  Demandé le 29/08 : « pour Noël on peut mettre des petits flocons, pour le
 *  Nouvel An un petit feu d'artifice… fais un truc clean et propre. »
 *
 *  UN SEUL JEU DE COUCHES, HUIT ALLURES. Les deux `.ciel-fete` sont posées
 *  quel que soit l'effet ; c'est `data-effet` sur <html> qui décide de ce
 *  qu'elles peignent et de comment elles bougent. Écrire huit paires de
 *  couches aurait multiplié par huit le nombre d'éléments pour n'en montrer
 *  jamais qu'une paire à la fois.
 *
 *  DEUX PLANS ET PAS UN, toujours pour la même raison que les étoiles : des
 *  flocons qui tombent tous à la même vitesse se lisent comme un rideau qu'on
 *  descend. Deux vitesses, et c'est de la neige.
 *
 *  LA COULEUR VIENT DES DONNÉES (`--fond-teinte` / `--fond-aurore-2`, posées
 *  par head.ejs depuis le catalogue). Aucune couleur de fête n'est écrite
 *  ici : sans ça, un événement créé depuis le panneau serait forcément vert
 *  et rouge.
 * ========================================================================== */
.ciel-fete {
  inset: -240px -240px auto auto;
  width: calc(100% + 480px);
  height: calc(100% + 480px);
  top: -240px; right: -240px;
  background-size: 240px 240px;
  will-change: transform;
}
.ciel-fete-a {
  background-image:
    radial-gradient(3px 3px at 40px 30px, rgba(var(--fond-aurore-2), 0.7), transparent),
    radial-gradient(2.5px 2.5px at 170px 110px, rgba(var(--fond-teinte), 0.6), transparent),
    radial-gradient(3px 3px at 95px 190px, rgba(var(--fond-aurore-2), 0.55), transparent),
    radial-gradient(2px 2px at 215px 55px, rgba(var(--fond-teinte), 0.5), transparent);
  animation: ciel-tombe-a 18s linear infinite;
}
.ciel-fete-b {
  background-image:
    radial-gradient(2px 2px at 130px 60px, rgba(var(--fond-teinte), 0.5), transparent),
    radial-gradient(2px 2px at 20px 150px, rgba(var(--fond-aurore-2), 0.45), transparent),
    radial-gradient(1.6px 1.6px at 190px 205px, rgba(var(--fond-teinte), 0.4), transparent);
  background-size: 200px 200px;
  animation: ciel-tombe-b 27s linear infinite;
  opacity: 0.85;
}
/*  LE DÉPLACEMENT VAUT EXACTEMENT UNE TUILE, comme pour les étoiles : sinon
    la boucle fait un à-coup à chaque tour. Le décalage latéral est plus petit
    que la chute — de la neige qui tombe à 45° ressemble à de la pluie. */
@keyframes ciel-tombe-a {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-60px, 240px, 0); }
}
@keyframes ciel-tombe-b {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(50px, 200px, 0); }
}


/* ==========================================================================
 *  LES MOTIFS DE FÊTE — de vraies illustrations, pas des silhouettes
 * ==========================================================================
 *  Signalé le 29/08, deux fois : « il n'y a pas de cœurs, c'est normal ? »,
 *  puis « c'est moche comme les citrouilles, fais des trucs réalistes ».
 *
 *  LA PREMIÈRE CORRECTION AVAIT DONNÉ LES BONNES FORMES ET RATÉ LE RESTE.
 *  Elle utilisait les SVG comme MASQUES : le dessin découpait la couleur de
 *  l'événement. Ça résolvait « une citrouille doit ressembler à une
 *  citrouille », et ça rendait impossible ce qui fait qu'un objet a l'air
 *  vrai — un masque n'a qu'un canal, l'opacité. Pas d'ombre, pas de reflet,
 *  pas de côtes plus sombres que le ventre. Une silhouette monochrome, quelle
 *  que soit sa précision, reste un pictogramme.
 *
 *  DONC : DES IMAGES EN COULEURS, et ce qu'on y gagne se voit — la citrouille
 *  a un dégradé, trois côtes creusées, une queue verte et une lumière en haut
 *  à gauche ; le ballon a un reflet et une ficelle bouclée ; l'œuf est peint
 *  avec des bandes et des pois ; la feuille a une nervure et des veines.
 *
 *  CE QUE ÇA COÛTE, ET IL FAUT LE DIRE : les motifs portent désormais LEURS
 *  couleurs, plus celles de l'événement. Un événement créé depuis le panneau
 *  avec un violet à lui aura donc un ciel violet et des citrouilles orange.
 *  C'est le bon compromis dans ce sens-là : une citrouille violette n'est
 *  plus une citrouille, alors qu'un ciel violet reste un ciel. Les teintes de
 *  l'événement continuent de peindre le fond, l'aurore, les étoiles et le
 *  bandeau — tout sauf les objets qu'on doit reconnaître.
 *
 *  TOUT EST EN `data:` : aucune requête réseau, aucun fichier à déployer,
 *  aucun nœud de plus dans le DOM. La règle « le tableau de bord doit
 *  s'ouvrir sur un vieux téléphone » tient toujours.
 *
 *  L'OPACITÉ RESTE RÉGLÉE PAR `--fete-force`, et elle est mesurée : des
 *  illustrations en couleurs sont bien plus claires que des silhouettes
 *  teintées, et c'est verif-fetes.js qui dit jusqu'où on peut aller sans
 *  rendre une étiquette de formulaire pénible à lire.
 *
 *  LE FEU D'ARTIFICE N'EST PAS ICI : ce sont ses bouquets qui le dessinent,
 *  et une explosion n'est pas une tuile qu'on répète.
 * ========================================================================== */
html[data-effet="coeurs"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="coeurs"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='h1' cx='.32' cy='.28' r='.85'%3E%3Cstop offset='0' stop-color='%23ff8fb1'/%3E%3Cstop offset='.5' stop-color='%23f0426e'/%3E%3Cstop offset='1' stop-color='%23b81e46'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 21.3 10.5 19.9C5.2 15.2 1.9 12.1 1.9 8.3 1.9 5.2 4.4 2.7 7.5 2.7c1.8 0 3.5.85 4.5 2.2 1-1.35 2.7-2.2 4.5-2.2 3.1 0 5.6 2.5 5.6 5.6 0 3.8-3.3 6.9-8.6 11.6L12 21.3Z' fill='url(%23h1)'/%3E%3Cpath d='M7.4 5.1c-1.4 0-2.5 1.1-2.5 2.5 0 .5.1.9.3 1.3' stroke='%23ffd0dd' stroke-width='1.1' fill='none' stroke-linecap='round' opacity='.75'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="flocons"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="flocons"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cg stroke='%23dbeafe' stroke-width='1.15' stroke-linecap='round' fill='none'%3E%3Cpath d='M12 2.2V21.8M3.5 7.1 20.5 16.9M3.5 16.9 20.5 7.1'/%3E%3Cpath d='M12 5.6 9.4 3.2M12 5.6l2.6-2.4M12 18.4l-2.6 2.4M12 18.4l2.6 2.4'/%3E%3Cpath d='M7 9.2 3.6 8.5M7 9.2l.7-3.4M17 14.8l3.4.7M17 14.8l-.7 3.4'/%3E%3Cpath d='M7 14.8 3.6 15.5M7 14.8l.7 3.4M17 9.2l3.4-.7M17 9.2l-.7-3.4'/%3E%3C/g%3E%3Ccircle cx='12' cy='12' r='1.9' fill='%23ffffff' opacity='.9'/%3E%3Cg stroke='%23ffffff' stroke-width='.5' fill='none' opacity='.6'%3E%3Cpath d='M12 8.4 10.6 7M12 8.4l1.4-1.4M12 15.6l-1.4 1.4M12 15.6l1.4 1.4'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="oeufs"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="oeufs"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='e1' cx='.34' cy='.3' r='.85'%3E%3Cstop offset='0' stop-color='%23fff6e0'/%3E%3Cstop offset='.6' stop-color='%23f2dcae'/%3E%3Cstop offset='1' stop-color='%23d4b97e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2c3.5 0 6.2 4.9 6.2 9.1 0 4.8-2.8 7.7-6.2 7.7s-6.2-2.9-6.2-7.7C5.8 6.9 8.5 2 12 2Z' fill='url(%23e1)'/%3E%3Cpath d='M6.5 12.4c1.6.9 3.4 1.35 5.5 1.35s3.9-.45 5.5-1.35' stroke='%2374c7a8' stroke-width='1.2' fill='none'/%3E%3Cpath d='M6.6 9.3c1.5-.85 3.3-1.3 5.4-1.3s3.9.45 5.4 1.3' stroke='%23e08fb0' stroke-width='1' fill='none'/%3E%3Cg fill='%23f0b64d'%3E%3Ccircle cx='9.2' cy='16' r='.75'/%3E%3Ccircle cx='12' cy='16.9' r='.75'/%3E%3Ccircle cx='14.8' cy='16' r='.75'/%3E%3C/g%3E%3Cellipse cx='9.6' cy='6.6' rx='1.2' ry='1.9' fill='%23ffffff' opacity='.5' transform='rotate(-20 9.6 6.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="citrouilles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="citrouilles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E%3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E%3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3Cpath d='M12 5.4v14.6'/%3E%3C/g%3E%3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E%3Cellipse cx='9' cy='9' rx='2.1' ry='3' fill='%23ffd08a' opacity='.35' transform='rotate(-22 9 9)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="feuilles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="feuilles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3ClinearGradient id='f1' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23f5a742'/%3E%3Cstop offset='.5' stop-color='%23e2622c'/%3E%3Cstop offset='1' stop-color='%23a83a1c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cpath d='M20.6 3C9.6 3 4.2 8.6 4.2 15.4c0 1.6.35 2.9.95 3.9 1.5-4.2 4.6-7.6 8.9-9.4-3.5 2.4-6 5.9-7 10 1.2.7 2.6 1.05 4.1 1.05 6.9 0 11.3-5.3 9.45-17.95Z' fill='url(%23f1)'/%3E%3Cg stroke='%23fbd9a0' stroke-width='.55' fill='none' opacity='.65'%3E%3Cpath d='M19.4 4.5C15 9.2 10.5 14.3 6.9 20'/%3E%3Cpath d='M17.6 7.1 13.2 6.6M16 10.2l-4.3-.2M14.2 13.2l-4 .3M12.2 16l-3.6.8'/%3E%3C/g%3E%3Cpath d='M6.9 20c-.7 1.1-1.3 2.2-1.8 3.3' stroke='%238a5a2a' stroke-width='.8' fill='none' stroke-linecap='round'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="bulles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="bulles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='u1' cx='.34' cy='.3' r='.72'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='.35' stop-color='%23bfe4ff'/%3E%3Cstop offset='.78' stop-color='%237fc4f0'/%3E%3Cstop offset='1' stop-color='%23ffffff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='12' cy='12' r='8.2' fill='url(%23u1)' opacity='.32'/%3E%3Ccircle cx='12' cy='12' r='8.2' fill='none' stroke='%23cfeaff' stroke-width='.9' opacity='.85'/%3E%3Cellipse cx='9' cy='8.6' rx='2.1' ry='1.5' fill='%23ffffff' opacity='.8' transform='rotate(-30 9 8.6)'/%3E%3Ccircle cx='15.2' cy='15.4' r='.9' fill='%23ffffff' opacity='.5'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="ballons"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ffe08a'/%3E%3Cstop offset='.5' stop-color='%23f2c04a'/%3E%3Cstop offset='1' stop-color='%23c08a1e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23c08a1e'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="ballons"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%239ecbff'/%3E%3Cstop offset='.5' stop-color='%234a8fe0'/%3E%3Cstop offset='1' stop-color='%232a5ba8'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%232a5ba8'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ffe08a'/%3E%3Cstop offset='.5' stop-color='%23f2c04a'/%3E%3Cstop offset='1' stop-color='%23c08a1e'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23c08a1e'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Cdefs%3E%3CradialGradient id='b1' cx='.34' cy='.28' r='1'%3E%3Cstop offset='0' stop-color='%23ff9aa8'/%3E%3Cstop offset='.5' stop-color='%23e8455f'/%3E%3Cstop offset='1' stop-color='%23b02440'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath d='M12 2.2c3.6 0 6.4 3 6.4 7 0 3.9-2.6 7.4-5.2 8.6l-.5 1.1h-1.4l-.5-1.1C8.2 16.6 5.6 13.1 5.6 9.2c0-4 2.8-7 6.4-7Z' fill='url(%23b1)'/%3E%3Cpath d='M11.1 18.9h1.8l.7 1.3h-3.2z' fill='%23b02440'/%3E%3Cpath d='M12 20.2c.9 1 .1 1.8-.7 2.3-.8.5-1 1.2-.3 1.6' stroke='%23e8e8f0' stroke-width='.6' fill='none' stroke-linecap='round' opacity='.75'/%3E%3Cellipse cx='9.6' cy='6.8' rx='1.5' ry='2.3' fill='%23ffffff' opacity='.42' transform='rotate(-25 9.6 6.8)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}
html[data-effet="confettis"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' width='240' height='240'%3E%3Cg transform='translate(32,28) rotate(-8) scale(0.625) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23f472b6'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(152,94) rotate(14) scale(0.5) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%236aa8ff'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(96,172) rotate(-20) scale(0.5833) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23ffd166'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(202,204) rotate(9) scale(0.4167) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%2350c878'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(72,120) rotate(30) scale(0.4583) translate(-12,-12)'%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffd166' stroke-width='1.7' fill='none' stroke-linecap='round'/%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffffff' stroke-width='.5' fill='none' stroke-linecap='round' opacity='.4' transform='translate(0,-.6)'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 240px 240px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force);
}
html[data-effet="confettis"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200' width='200' height='200'%3E%3Cg transform='translate(120,44) rotate(22) scale(0.5) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23ffd166'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3Cg transform='translate(24,146) rotate(-14) scale(0.5833) translate(-12,-12)'%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffd166' stroke-width='1.7' fill='none' stroke-linecap='round'/%3E%3Cpath d='M3 13c2.2-3.4 4.4 3.4 6.6 0s4.4 3.4 6.6 0 4.4 3.4 6.6 0' stroke='%23ffffff' stroke-width='.5' fill='none' stroke-linecap='round' opacity='.4' transform='translate(0,-.6)'/%3E%3C/g%3E%3Cg transform='translate(182,152) rotate(36) scale(0.4167) translate(-12,-12)'%3E%3Crect x='7' y='10' width='10' height='4.6' rx='1.4' fill='%23f472b6'/%3E%3Crect x='7' y='10' width='10' height='1.6' rx='.8' fill='%23ffffff' opacity='.35'/%3E%3C/g%3E%3C/svg%3E");
  background-color: transparent;
  background-size: 200px 200px;
  -webkit-mask-image: none;
  mask-image: none;
  opacity: var(--fete-force-2);
}

/*  CE QUI MONTE. Cœurs et bulles ne tombent pas — de la neige rose reste de
    la neige. `reverse` suffit : le motif, lui, ne change pas. */
html[data-effet="coeurs"] .ciel-fete,
html[data-effet="ballons"] .ciel-fete,
html[data-effet="bulles"] .ciel-fete { animation-direction: reverse; }
/*  Les ballons montent plus lentement que les cœurs : c'est ce qui les rend
    lourds, et un ballon qui file vers le haut ressemble à une bulle. */
html[data-effet="ballons"] .ciel-fete-a { animation-duration: 34s; }
html[data-effet="ballons"] .ciel-fete-b { animation-duration: 46s; }

/*  CE QUI FLOTTE SUR PLACE. Un œuf de Pâques qui dégringole du ciel n'évoque
    rien ; posé et respirant, il évoque un décor. */
html[data-effet="oeufs"] .ciel-fete,
html[data-effet="citrouilles"] .ciel-fete {
  animation-name: ciel-fete-respire;
  animation-duration: 7s;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;
}
html[data-effet="citrouilles"] .ciel-fete-b { animation-duration: 11s; }
/*  LA RESPIRATION PASSE PAR `filter`, PAS PAR `opacity` — corrigé le 29/08.
 *
 *  Écrite avec `opacity`, cette image-clé ÉCRASAIT l'opacité de la couche.
 *  Conséquence mesurée : les œufs et les citrouilles ignoraient
 *  `--fete-force`, restaient à 0,5–0,95 d'opacité, et tombaient à 4,07:1
 *  pour les étiquettes — pendant que les quatre autres fêtes obéissaient au
 *  même réglage. On a d'abord cru à un réglage sans effet : c'était une
 *  animation qui gagnait, en silence, sur deux fêtes seulement.
 *
 *  `filter: opacity()` se MULTIPLIE avec l'opacité de l'élément au lieu de la
 *  remplacer. La respiration reste, le réglage reprend la main. */
@keyframes ciel-fete-respire {
  from { transform: translate3d(0, 0, 0) scale(1);        filter: opacity(0.55); }
  to   { transform: translate3d(0, -16px, 0) scale(1.07); filter: opacity(1); }
}

/*  LES FEUILLES TOURNENT EN TOMBANT. Une feuille qui descend droit, c'est un
    confetti ; c'est la rotation qui fait la feuille. */
html[data-effet="feuilles"] .ciel-fete-a { animation: ciel-feuille-a 21s linear infinite; }
html[data-effet="feuilles"] .ciel-fete-b { animation: ciel-feuille-b 31s linear infinite; }
@keyframes ciel-feuille-a {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(-120px, 240px, 0) rotate(12deg); }
}
@keyframes ciel-feuille-b {
  from { transform: translate3d(0, 0, 0) rotate(0deg); }
  to   { transform: translate3d(110px, 200px, 0) rotate(-14deg); }
}

/*  LES CONFETTIS tombent plus vite et plus dense : c'est ce qui distingue une
    fête d'un hiver. */
html[data-effet="confettis"] .ciel-fete-a { animation-duration: 9s; }
html[data-effet="confettis"] .ciel-fete-b { animation-duration: 13s; }

/*  LA PLUIE D'ÉTOILES FILANTES n'ajoute pas de couche : elle accélère celles
    qui existent déjà. Six filantes trois fois plus fréquentes, et le ciel
    devient un ciel d'août. */
html[data-effet="etoiles"] .ciel-filante { animation-duration: 6s; }
html[data-effet="etoiles"] .ciel-filante-b { animation-duration: 8s; }
html[data-effet="etoiles"] .ciel-filante-c { animation-duration: 5s; }
html[data-effet="etoiles"] .ciel-filante-d { animation-duration: 9s; }
html[data-effet="etoiles"] .ciel-filante-e { animation-duration: 7s; }
html[data-effet="etoiles"] .ciel-filante-f { animation-duration: 4s; }

/*  LE FEU D'ARTIFICE — trois bouquets, et pas une couche qui défile.
 *
 *  UNE EXPLOSION N'EST PAS UN DÉPLACEMENT : elle part d'un point, s'ouvre, et
 *  s'éteint. C'est donc un cercle de points (un `radial-gradient` par éclat)
 *  qu'on AGRANDIT en le faisant disparaître. Le `box-shadow` aurait donné de
 *  vrais éclats séparés, mais il se recalcule à chaque image ; `transform:
 *  scale` est pris en charge par le compositeur, et sur une page de tableau
 *  de bord c'est la différence entre un décor et un ralentissement.
 *
 *  TROIS BOUQUETS, TROIS CADENCES PREMIÈRES ENTRE ELLES (7, 11, 13 s) : ils
 *  ne partent presque jamais ensemble, et le ciel n'a pas de rythme. Chacun
 *  n'est visible qu'un cinquième de son cycle — le reste du temps, il n'y a
 *  rien, et c'est ce qui permet de mettre une explosion dans un outil qu'on
 *  regarde des heures.
 */
.ciel-bouquet {
  width: 220px; height: 220px;
  border-radius: 50%;
  opacity: 0;
  will-change: transform, opacity;
  background:
    radial-gradient(4px 4px at 50% 6%,  rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 82% 20%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 94% 50%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 82% 80%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 50% 94%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 18% 80%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(4px 4px at 6%  50%, rgba(var(--fond-aurore-2), 1), transparent),
    radial-gradient(4px 4px at 18% 20%, rgba(var(--fond-teinte), 1), transparent),
    radial-gradient(3px 3px at 68% 32%, rgba(var(--fond-aurore-2), 0.9), transparent),
    radial-gradient(3px 3px at 32% 68%, rgba(var(--fond-teinte), 0.9), transparent),
    radial-gradient(3px 3px at 68% 68%, rgba(var(--fond-aurore-2), 0.9), transparent),
    radial-gradient(3px 3px at 32% 32%, rgba(var(--fond-teinte), 0.9), transparent);
}
.ciel-bouquet-1 { top: 0; left: 0; animation: ciel-bouquet-1 7s ease-out infinite; }
.ciel-bouquet-2 { top: 0; left: 0; animation: ciel-bouquet-2 11s ease-out infinite; animation-delay: 2.5s; }
.ciel-bouquet-3 { top: 0; left: 0; animation: ciel-bouquet-3 13s ease-out infinite; animation-delay: 6s; }

/*  LES BOUQUETS N'EXPLOSENT PLUS TOUJOURS AU MÊME ENDROIT — 29/08.
 *  Même défaut que les filantes, même remède : chacun tire DEUX fois par
 *  cycle, à deux endroits, et se replace entre les deux pendant qu'il est
 *  invisible. Un feu d'artifice dont les trois bouquets partent toujours des
 *  mêmes trois points, c'est une image, pas un feu d'artifice.
 *
 *  `scale(0.15)` au repos plutôt que `scale(0)` : à zéro, certains
 *  navigateurs cessent de composer la couche et la remontent d'un cran au
 *  réveil — on voit un clignotement au tout début de l'explosion. */
@keyframes ciel-bouquet-1 {
  0%   { transform: translate3d(14vw, 12vh, 0) scale(0.15); opacity: 0; }
  3%   { transform: translate3d(14vw, 12vh, 0) scale(0.25); opacity: 1; }
  12%  { transform: translate3d(14vw, 12vh, 0) scale(1);    opacity: 0.85; }
  18%  { transform: translate3d(14vw, 12vh, 0) scale(1.3);  opacity: 0; }
  49%  { transform: translate3d(62vw, 30vh, 0) scale(0.15); opacity: 0; }
  52%  { transform: translate3d(62vw, 30vh, 0) scale(0.25); opacity: 1; }
  61%  { transform: translate3d(62vw, 30vh, 0) scale(1);    opacity: 0.8; }
  67%  { transform: translate3d(62vw, 30vh, 0) scale(1.3);  opacity: 0; }
  100% { transform: translate3d(62vw, 30vh, 0) scale(1.3);  opacity: 0; }
}
@keyframes ciel-bouquet-2 {
  0%   { transform: translate3d(74vw, 16vh, 0) scale(0.15); opacity: 0; }
  4%   { transform: translate3d(74vw, 16vh, 0) scale(0.28); opacity: 1; }
  14%  { transform: translate3d(74vw, 16vh, 0) scale(1.05); opacity: 0.8; }
  20%  { transform: translate3d(74vw, 16vh, 0) scale(1.35); opacity: 0; }
  54%  { transform: translate3d(28vw, 34vh, 0) scale(0.15); opacity: 0; }
  58%  { transform: translate3d(28vw, 34vh, 0) scale(0.28); opacity: 0.9; }
  68%  { transform: translate3d(28vw, 34vh, 0) scale(1.05); opacity: 0.7; }
  74%  { transform: translate3d(28vw, 34vh, 0) scale(1.35); opacity: 0; }
  100% { transform: translate3d(28vw, 34vh, 0) scale(1.35); opacity: 0; }
}
@keyframes ciel-bouquet-3 {
  0%   { transform: translate3d(44vw, 8vh, 0) scale(0.15); opacity: 0; }
  3%   { transform: translate3d(44vw, 8vh, 0) scale(0.22); opacity: 0.95; }
  13%  { transform: translate3d(44vw, 8vh, 0) scale(0.95); opacity: 0.75; }
  19%  { transform: translate3d(44vw, 8vh, 0) scale(1.25); opacity: 0; }
  46%  { transform: translate3d(86vw, 42vh, 0) scale(0.15); opacity: 0; }
  49%  { transform: translate3d(86vw, 42vh, 0) scale(0.22); opacity: 0.85; }
  59%  { transform: translate3d(86vw, 42vh, 0) scale(0.95); opacity: 0.65; }
  65%  { transform: translate3d(86vw, 42vh, 0) scale(1.25); opacity: 0; }
  100% { transform: translate3d(86vw, 42vh, 0) scale(1.25); opacity: 0; }
}

/* --- LE BANDEAU DE L'ÉVÉNEMENT ------------------------------------------ */
/*  Il est DANS le flux, en haut du contenu, et pas en `fixed` : un bandeau
    flottant mange une bande d'écran sur toutes les pages, y compris celles
    où l'on remplit un formulaire de quarante champs. Celui-ci se lit une
    fois, puis on défile et il s'en va. */
.fete-bandeau {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 10px 18px;
  font-weight: 600;
  text-align: center;
  color: var(--text);
  background: linear-gradient(90deg,
    rgba(var(--fond-teinte), 0.18), rgba(var(--fond-aurore-2), 0.18));
  border-bottom: 1px solid rgba(var(--fond-aurore-2), 0.35);
}
.fete-bandeau-emoji { font-size: 1.2em; line-height: 1; }
/*  EN PRODUCTION, LE BANDEAU DIT QU'IL N'EST PAS ENCORE PUBLIC. Sans ça, le
    staff voit le décor de Noël, croit qu'il est en ligne, et ne clique jamais
    sur « public ». C'est la panne la plus probable de tout ce mécanisme :
    tout marche, et personne ne voit rien. */
.fete-bandeau-prive {
  font-weight: 500;
  font-size: 0.85em;
  padding: 2px 8px;
  border-radius: 100px;
  background: rgba(var(--prod-rgb), 0.18);
  color: var(--prod);
  border: 1px solid rgba(var(--prod-rgb), 0.4);
}

/*  MOINS DE MOUVEMENT : TOUT S'ARRÊTE, RIEN NE DISPARAÎT.
 *  Le ciel reste peint — aurore, étoiles, voile — il cesse simplement de
 *  bouger, et les filantes ne passent plus. Le réglage d'accessibilité doit
 *  rendre la page supportable, pas plus laide : quelqu'un qui demande moins
 *  d'animations n'a pas demandé un fond noir. */
@media (prefers-reduced-motion: reduce) {
  body::before,
  .ciel-aurore,
  .ciel-etoiles,
  .ciel-fete { animation: none !important; }
  /*  Les bouquets, eux, DISPARAISSENT plutôt que de se figer : une explosion
      arrêtée en plein vol laisserait une grosse fleur de points posée sur
      l'écran pour toujours. Même raison que les filantes. */
  .ciel-bouquet { display: none !important; }
  .ciel-filante { display: none !important; }
}

/*  À L'IMPRESSION, LE DÉCOR DISPARAÎT.
    Un dégradé sombre plein page viderait une cartouche pour rien, et sur la
    plupart des imprimantes il ressortirait en aplat gris sous le texte.
    `display: none` et rien d'autre : ON NE FORCE PAS LE PAPIER EN BLANC.
    Le site écrit en --text (#e6e8ef, presque blanc) ; repeindre le fond en
    blanc rendrait chaque page ILLISIBLE à l'impression au lieu de la nettoyer.
    Rendre tout le site imprimable est un autre chantier que celui-ci — ici on
    retire le décor qu'on vient d'ajouter, on ne change pas ce qui existait. */
@media print {
  body::before,
  body::after { display: none !important; }
}

/*  LE BOUTON « PUBLIC » DES RUBRIQUES — voir views/partials/bouton-public.ejs
 *
 *  Il vit À CÔTÉ d'un lien de menu ou d'un titre : il doit se voir sans
 *  attirer l'œil plus que ce qu'il commande. D'où une taille en `em`, qui
 *  suit celle du texte voisin, plutôt qu'en pixels — posé dans la barre du
 *  haut et dans un titre de page, un bouton en pixels serait énorme dans
 *  l'un et minuscule dans l'autre.
 *
 *  Les deux états ne se distinguent PAS par la seule couleur : la case
 *  cochée ou vide porte l'information, et le titre au survol la répète en
 *  toutes lettres. Une différence uniquement colorée disparaît pour qui
 *  distingue mal le rouge du vert. */
.bp-bouton {
  display: inline-flex;
  align-items: center;
  gap: 0.28em;
  padding: 0.12em 0.45em;
  font-size: 0.78em;
  line-height: 1.5;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.bp-bouton:hover { background: var(--bg-card); color: var(--text); }
.bp-ouvert { border-color: color-mix(in srgb, #3fb950 55%, var(--border)); color: #6fd07f; }
.bp-ferme  { border-color: color-mix(in srgb, #d64a63 55%, var(--border)); color: #e08494; }
/*  Dans la barre du haut, la place est comptée : on garde la case, on retire
 *  le mot. L'information reste, le titre au survol aussi. */
.navbar .bp-txt { display: none; }
@media (max-width: 900px) { .bp-txt { display: none; } }

/* ===================================================
   RÈGLEMENT — les panneaux de l'éditeur
   ===================================================
 *  POURQUOI CES CLASSES EXISTENT. Ces blocs sont fabriqués en JavaScript,
 *  et le JavaScript posait leur allure EN DUR :
 *
 *      d.style.border = '1px solid rgba(255,255,255,.08)';
 *
 *  Un blanc translucide, écrit à la main, qui passait PAR-DESSUS le
 *  `border: 1px solid var(--border)` de `.form-section` — un style en ligne
 *  bat toujours une classe. Résultat : la bordure ignorait le monde (violet
 *  au public, jaune en prod) et ignorait les couleurs d'événement. Personne
 *  ne voyait d'erreur, parce qu'il n'y en avait pas : les deux moitiés
 *  marchaient, et se contredisaient.
 *
 *  ET CE N'ÉTAIT PAS UNE `.form-section`. Les panneaux prenaient la classe
 *  des grandes cartes, à l'intérieur d'une grande carte : même fond sur même
 *  fond, deux ombres au survol, et l'animation d'entrée rejouée à chaque
 *  ajout. Ils ont maintenant leur propre allure de SOUS-bloc.
 */
.reg-panneau {
  position: relative;
  /*  UN CREUX, PAS UNE CARTE DE PLUS. `--bg-softer` (#1c2030) posé sur le
      `--bg-card` (#1a1e2b) de la section parente, c'est deux teintes d'écart :
      à l'écran, le panneau ne se détachait pas du tout. On descend vers le
      fond de la page : le bloc se lit comme creusé DANS la carte, ce qui est
      exactement ce qu'il est. */
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px 20px 26px;
  margin-bottom: 14px;
  overflow: hidden;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}
/*  LE LISERÉ PREND LA COULEUR DU MONDE — et celle de l'événement en cours,
 *  puisque --accent est exactement ce que head.ejs redéfinit. C'est aussi ce
 *  qui distingue un panneau d'un simple encadré, sans ajouter de trait. */
.reg-panneau::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: linear-gradient(to bottom,
              rgba(var(--accent-rgb), 0.85), rgba(var(--accent-rgb), 0.15));
}
.reg-panneau:hover { border-color: rgba(var(--accent-rgb), 0.45); }
.reg-panneau-tete {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.reg-panneau-titre {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/*  Un panneau sans titre ne doit pas ressembler à un panneau nommé « rien ». */
.reg-panneau-titre:empty::after,
.reg-panneau-titre.reg-vide { color: var(--text-dim); font-weight: 500; font-style: italic; }
/*  LE BOUTON DE SUPPRESSION DIT CE QU'IL FAIT. Il ne portait qu'une croix :
 *  au lecteur d'écran, « ✕ » ne s'annonce pas, et à la souris rien ne
 *  prévenait que le bloc partait. Le titre et l'étiquette sont posés par le
 *  JavaScript, dans la langue de la page. */
.reg-supprimer {
  flex: 0 0 auto;
  line-height: 1;
  padding: 6px 10px;
  color: var(--text-muted);
}
.reg-supprimer:hover { color: #e08494; border-color: rgba(214, 74, 99, 0.5); }

/* ===================================================
   ALERTE « INFO » — la variante qui n'existait pas
   ===================================================
 *  DEUX VUES LA DEMANDENT DEPUIS LE DÉBUT, LA FEUILLE NE L'A JAMAIS DÉFINIE.
 *
 *      <div class="alert alert-info">   (règlement, créateur)
 *
 *  `.alert-success`, `.alert-warning` et `.alert-danger` existent ; `info`,
 *  non. Ces deux encadrés ne recevaient donc que le `.alert` de base — de la
 *  marge intérieure et un `display:flex`, sans fond, sans bordure, sans
 *  couleur. Résultat à l'écran : du texte nu posé entre deux cartes, au
 *  milieu d'une page où tout le reste est encadré.
 *
 *  ET RIEN N'ÉCHOUAIT. Une classe CSS absente ne lève pas d'erreur : le
 *  navigateur l'ignore, la page s'affiche, et seul l'œil voit qu'il manque
 *  quelque chose. C'est le même défaut que partout ailleurs ce mois-ci —
 *  deux moitiés qui marchent séparément et ne se rencontrent jamais.
 *
 *  Elle prend la couleur d'accent, donc le violet au public et le jaune en
 *  prod, et les couleurs d'un événement en cours. Une info n'est ni un
 *  succès (vert) ni un avertissement (orange) : lui donner l'une des deux
 *  aurait menti sur son propos.
 */
.alert-info {
  background: rgba(var(--accent-rgb), 0.08);
  border: 1px solid rgba(var(--accent-rgb), 0.28);
  color: var(--text);
}
/*  ERREUR DE MA PART, RELEVÉE PAR LA MESURE DE CONTRASTE.
 *
 *  J'avais écrit `color: var(--accent-texte)`. Ce jeton n'est pas « la
 *  couleur d'accent pour du texte » : c'est la couleur du texte posé SUR
 *  l'accent — donc SOMBRE en production, où l'accent est jaune vif. Sur le
 *  fond sombre de l'encadré, ça donnait du #12141d sur du #22201'5 :
 *  1.12:1, c'est-à-dire un titre invisible. Il fallait mesurer pour le voir,
 *  et personne ne l'aurait vu autrement.
 *
 *  Le nom d'un jeton dit à quoi il sert. `--accent-texte` avait l'air de
 *  vouloir dire « texte accentué » ; il veut dire « texte sur l'accent ». */
.alert-info strong { color: var(--text); }
/*  LE CONTENU D'UNE ALERTE EST UN SEUL BLOC, PAS DEUX COLONNES.
 *
 *  `.alert` est un conteneur flex — prévu pour « icône + texte ». Quand le
 *  contenu est un `<strong>` suivi d'un `<br>` et d'un paragraphe, le gras
 *  devient un ÉLÉMENT FLEX à lui tout seul : il se retrouve dans une colonne
 *  étroite à gauche, coupé sur trois lignes, avec le texte à côté. On a vu
 *  exactement ça sur la page du règlement.
 *
 *  `.alert-texte` remet les deux dans le même bloc. Les cent vingt-deux
 *  autres alertes du site ne bougent pas : c'est une classe qu'on ajoute,
 *  pas une règle qu'on change. */
.alert-texte { flex: 1 1 auto; min-width: 0; }
.alert-texte strong { display: block; margin-bottom: 4px; }

/* ===================================================
   LES CLASSES QUE LES VUES DEMANDAIENT SANS RÉPONSE
   ===================================================
 *  Relevé par `audit-classes.js`, qui compare ce que les gabarits écrivent
 *  dans leurs `class="…"` à ce que les feuilles définissent réellement.
 *  Trente et une classes n'existaient nulle part. Une classe absente ne lève
 *  aucune erreur : le navigateur l'ignore et affiche la page — c'est
 *  exactement pourquoi ces défauts survivent aux tests de rendu.
 *
 *  Toutes ne méritaient pas une règle : beaucoup sont des ancres de script
 *  ou portent déjà leur style en ligne. Celles d'ici changent ce qu'on voit.
 */

/*  `.dashboard-main` — QUATRE PAGES DU PANNEAU N'AVAIENT AUCUNE MARGE.
 *
 *  Événements, tutos et les deux pages d'assistance ouvrent leur contenu par
 *  `<main class="dashboard-main">`. Les autres pages staff écrivent
 *  `class="container"`, les pages de module `class="dashboard-content"` — et
 *  ces deux-là existent. La troisième, jamais. Ces quatre pages collaient
 *  donc au menu à gauche, à la barre en haut, et s'étalaient jusqu'au bord
 *  droit de l'écran quelle que soit sa largeur.
 *
 *  Elle est plus large que `.dashboard-content` (1000 px) parce que ces
 *  pages-là portent des tableaux et des grilles de cartes, pas un formulaire
 *  en colonne. */
.dashboard-main {
  padding: 40px;
  max-width: 1180px;
  min-width: 0;
}
@media (max-width: 900px) { .dashboard-main { padding: 24px 16px; } }

/*  `.table-wrap` — SEPT TABLEAUX QUI DÉBORDAIENT DE LA PAGE.
 *
 *  Le nom dit l'intention : envelopper un tableau trop large. Sans la règle,
 *  l'enveloppe ne faisait rien et c'est la PAGE qui prenait le défilement
 *  horizontal — licences, membres, code source, propositions. Le défilement
 *  appartient au tableau, jamais au corps du document. */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.table-wrap > table { min-width: 100%; }

/*  `.btn-warning` — DEUX BOUTONS SANS COULEUR.
 *
 *  `btn-primary`, `btn-danger`, `btn-ghost` existent ; `btn-warning`, non.
 *  Les deux boutons qui la portent tombaient donc sur le `.btn` nu — gris,
 *  au milieu d'une page où la couleur porte le sens de l'action. */
.btn-warning { background: var(--warning); color: #1a1200; }
.btn-warning:hover { filter: brightness(1.08); }

/*  `.etat-pill` et `.badge-status` — DES PASTILLES SANS FORME.
 *
 *  Elles reçoivent leur couleur en ligne (`style="background:…;color:…"`) ou
 *  par une classe d'état, mais rien ne leur donnait de forme : pas de
 *  marge intérieure, pas d'arrondi. La couleur s'appliquait donc à un bout
 *  de texte nu — on voyait une teinte, pas une pastille. */
.etat-pill,
.badge-status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}
/*  `.badge-status` est fabriquée en JavaScript avec l'état accolé en classe
 *  (`badge-status open`, `badge-status closed`) : on couvre les mots employés
 *  et on garde un repli neutre pour tous les autres. */
.badge-status { background: var(--bg-softer); color: var(--text-muted); }
.badge-status.open, .badge-status.ouvert, .badge-status.ok {
  background: rgba(61, 220, 151, 0.14); color: var(--success);
}
.badge-status.closed, .badge-status.ferme, .badge-status.error {
  background: rgba(255, 91, 122, 0.14); color: var(--danger);
}
.badge-status.pending, .badge-status.attente {
  background: rgba(251, 146, 60, 0.14); color: var(--warning);
}

/*  UNE ALERTE QUI COMMENCE PAR DU GRAS SE LIT SUR DEUX LIGNES, PAS EN DEUX
 *  COLONNES.
 *
 *  `.alert` est un conteneur flex — prévu pour « icône + texte ». Quand le
 *  contenu commence par `<strong>…</strong><br>`, ce gras devient un ÉLÉMENT
 *  FLEX à part entière : il se tasse dans une colonne étroite à gauche,
 *  souvent coupé sur trois lignes, et le paragraphe se range à côté. Vu sur
 *  la page du règlement, puis sur celle des événements, puis sur quatre
 *  autres — six en tout.
 *
 *  On ne touche pas à `.alert` : les cent dix-sept autres alertes du site
 *  s'en servent comme prévu. On dit seulement qu'un gras EN PREMIÈRE POSITION
 *  prend toute la largeur — ce qui est le seul cas qui cassait. */
.alert { flex-wrap: wrap; }
.alert > strong:first-child { flex: 1 0 100%; margin-bottom: 2px; }

/*  LE BOUTON « PUBLIC » DANS LA BARRE DU HAUT — PLUS DISCRET AU REPOS.
 *
 *  Six de ces boutons se glissent entre les liens de navigation, en prod. Avec
 *  leur bordure permanente, la barre ressemblait à une rangée de cases à
 *  cocher où l'on cherchait les liens. L'information ne se perd pas : la case
 *  ☐ / ☑ porte l'état, la couleur le répète, le titre au survol l'écrit en
 *  toutes lettres — la bordure ne disait rien de plus. Elle revient au survol
 *  et au focus clavier, là où l'on vise. */
.navbar .bp-bouton {
  border-color: transparent;
  padding: 0.12em 0.28em;
  font-size: 0.9em;
}
.navbar .bp-bouton:hover,
.navbar .bp-bouton:focus-visible { border-color: var(--border); }
.navbar .bp-forme { margin-left: 2px !important; }

/* ==========================================================================
 *  PAGES LÉGALES — posées le 09/09/2026
 * ==========================================================================
 *  Ces pages se LISENT, elles ne se parcourent pas : la mesure de ligne est
 *  donc bornée à 66 caractères, la plus lisible en continu, au lieu de suivre
 *  la largeur du conteneur comme le reste du site. C'est le seul endroit du
 *  projet où quelqu'un lit un texte long du début à la fin.
 *
 *  Aucune carte, aucun cadre, aucune ombre : un document légal encadré
 *  ressemble à une publicité pour un document légal. Un filet et de l'espace
 *  suffisent à séparer les sections.
 */
.legal { max-width: 46rem; padding-bottom: 64px; }
.legal-tete { padding: 40px 0 24px; border-bottom: 2px solid var(--border); }
.legal-tete h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 1.2rem + 2vw, 2.4rem); line-height: 1.12; }
.legal-chapeau { margin: 0 0 14px; font-size: 1.05rem; color: var(--text-muted); max-width: 40rem; }
.legal-maj { margin: 0; font-size: 0.82rem; color: var(--text-muted); }

/*  L'avis sur la langue : visible sans crier. Une bordure d'accent à gauche
    plutôt qu'un fond coloré — c'est une précision, pas une alerte. */
.legal-avis {
  margin: 24px 0 0; padding: 12px 0 12px 16px;
  border-left: 3px solid var(--accent-dim);
  color: var(--text-muted); font-size: 0.94rem; max-width: 40rem;
}

.legal-section { margin-top: 34px; }
.legal-section h2 {
  margin: 0 0 10px; font-size: 1.22rem; line-height: 1.25;
  padding-bottom: 6px; border-bottom: 1px solid var(--border);
}
.legal-section p { margin: 0 0 12px; line-height: 1.68; max-width: 42rem; }
.legal-section p:last-child { margin-bottom: 0; }

/*  Terme et définition, pas une liste à puces : ces blocs sont des
    inventaires (une catégorie de données, ce qu'on en fait), et le couple
    dt/dd le dit aussi bien à l'œil qu'à un lecteur d'écran. */
.legal-liste { margin: 14px 0 0; max-width: 42rem; }
.legal-liste dt {
  font-weight: 600; color: var(--text);
  margin-top: 14px; line-height: 1.4;
}
.legal-liste dt:first-child { margin-top: 0; }
.legal-liste dd { margin: 4px 0 0; padding-left: 16px; border-left: 2px solid var(--border); color: var(--text-muted); line-height: 1.62; }

.legal-voisines {
  margin-top: 48px; padding-top: 18px; border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: 20px;
}
.legal-voisines a { font-size: 0.92rem; }

/*  Dans le pied de page, les liens légaux se distinguent des liens de
    navigation : on les cherche, on ne tombe pas dessus. */
.footer-links .footer-legal { opacity: 0.78; }
.footer-links .footer-legal:hover { opacity: 1; }

/* ==========================================================================
 *  EMPLOI DU TEMPS DE L'ACTIVITÉ — la semaine en grille, 09/09/2026
 * ==========================================================================
 *  Sept colonnes et vingt-quatre heures. Les blocs sont positionnés en
 *  absolu dans leur colonne, hauteur proportionnelle à la durée : c'est ce
 *  qui fait qu'un trou se voit sans être compté.
 */
.edt { margin-top: 8px; }
/*  LA GRILLE EST SON PROPRE CADRE DE DÉFILEMENT — corrigé le 09/09/2026.
 *
 *  CE QUI N'ALLAIT PAS. `overflow-x: auto` sur une boîte sans hauteur : elle
 *  glissait horizontalement, mais verticalement c'est LA PAGE qui défilait.
 *  Les titres de jours étaient bien en `sticky`, sauf qu'un élément collant
 *  ne colle qu'à l'intérieur de SON cadre de défilement — et le leur n'en
 *  était pas un. Descendre à 19 h faisait donc disparaître « LUN MAR MER… »
 *  tout en haut, et il restait sept colonnes de blocs sans nom. On croit à un
 *  décalage des colonnes ; en réalité on a perdu leur étiquette.
 *
 *  MAINTENANT : une hauteur bornée, un défilement dans les deux sens, et deux
 *  bandes qui restent. Les jours en haut, les heures à gauche — c'est ce que
 *  fait n'importe quel agenda, et pour cette raison-là. */
.edt-grille {
  display: grid;
  /*  minmax(9.5rem, 1fr) et plus minmax(0, 1fr) — 01/10/2026. Signalé par
      un client (« les jours se chevauchent ») : la piste pouvait rétrécir
      jusqu'à zéro alors que chaque jour exige 9,5rem (.edt-jour min-width).
      Sur un écran étroit, chaque jour débordait sur le suivant. La piste a
      maintenant le même minimum que son contenu : la grille défile sur le
      côté au lieu de se superposer. */
  grid-template-columns: 3.4rem repeat(7, minmax(9.5rem, 1fr));
  gap: 0;
  border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm);
  overflow: auto;
  max-height: min(72vh, 800px);
  /*  PAS de `overscroll-behavior: contain` : arrivé en bas de la grille, la
      molette doit continuer à faire défiler la PAGE. Sinon on se retrouve
      coincé dans un cadre au milieu d'un formulaire, sans comprendre
      pourquoi la page ne bouge plus. */
  background: var(--bg-soft);
}
/*  La gouttière des heures a EXACTEMENT la même structure qu'une colonne de
    jour : un titre de même hauteur, puis une piste de même hauteur. Aucune
    marge à accorder à la main — c'est ce qui garantit que les libellés
    tombent sur les lignes, quelle que soit la hauteur d'une heure. */
/*  La gouttière reste à gauche quand on glisse vers samedi : sans elle, on
    lit des blocs sans savoir à quelle heure ils sont. Fond opaque, sinon le
    contenu défile au travers. */
.edt-heures {
  border-right: 1px solid var(--border);
  position: sticky; left: 0; z-index: 4;
  background: var(--bg-soft);
}
/*  Le coin haut-gauche croise les deux bandes collantes : il doit passer
    devant les deux, sinon un titre de jour lui glisse dessous. */
.edt-titre-vide { background: var(--bg-softer); z-index: 6; }
.edt-piste-heures { cursor: default; }
.edt-heure {
  position: absolute; right: 6px;
  font-size: .68rem; color: var(--text-muted);
  line-height: 1; transform: translateY(-50%);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/*  Minuit se pose sous le bord plutôt qu'à cheval dessus : centré, la moitié
    du texte sortirait de la grille. */
.edt-heure.est-premiere { transform: translateY(0); }
.edt-jour { border-right: 1px solid var(--border); min-width: 9.5rem; }
/*  La colonne du jour, teintée très légèrement : on la retrouve du coin de
    l'œil sans que ça devienne un aplat de couleur. */
.edt-jour.est-aujourdhui { background: rgba(var(--accent-rgb), .05); }
.edt-jour:last-child { border-right: 0; }
.edt-jour-titre {
  height: 30px; display: flex; align-items: center; justify-content: center;
  font-size: .78rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 3;
  background: var(--bg-softer);
}
.edt-jour-titre.est-aujourdhui { color: var(--accent-lisible); }
.edt-piste { position: relative; cursor: copy; }
.edt-trait { position: absolute; left: 0; right: 0; height: 1px; background: var(--border); opacity: .55; }

.edt-bloc {
  position: absolute; left: 3px; right: 3px;
  background: rgba(var(--accent-rgb), .22);
  border: 1px solid rgba(var(--accent-rgb), .55);
  border-left: 3px solid var(--accent);
  border-radius: 3px; padding: 2px 5px; overflow: hidden;
  cursor: pointer; text-align: left;
  display: flex; flex-direction: column; gap: 1px;
}
.edt-bloc:hover, .edt-bloc:focus-visible { background: rgba(var(--accent-rgb), .34); }
/*  La seconde moitié d'un créneau qui enjambe minuit : même bloc, dessin plus
    discret, pour qu'on voie que la nuit est occupée sans croire à un second
    créneau. */
.edt-bloc.est-suite {
  border-style: dashed; background: rgba(var(--accent-rgb), .10);
  border-left-color: rgba(var(--accent-rgb), .5);
}
.edt-bloc.est-suite .edt-bloc-t { color: var(--text-muted); }
/*  Deux créneaux côte à côte : un filet plus marqué pour qu'on voie qu'ils se
    partagent la place, et pas qu'un seul bloc a rétréci. */
.edt-bloc.est-partage { border-left-width: 4px; }
/*  La pastille d'état, posée devant le texte du bloc. Sans elle, rien à
    l'écran ne dirait qu'un créneau change aussi l'état du bot — on ne le
    découvrirait qu'en regardant Discord. */
.edt-pastille-etat {
  display: inline-block; width: .5em; height: .5em; border-radius: 50%;
  margin-right: .4em; vertical-align: middle; flex: 0 0 auto;
}
.edt-pastille-etat.est-absent { background: var(--warning); }
.edt-pastille-etat.est-occupé, .edt-pastille-etat.est-occupe { background: var(--danger); }
.edt-pastille-etat.est-invisible { background: var(--text-muted); }
.edt-bloc-h {
  font-size: .64rem; color: var(--text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; flex: 0 0 auto;
}
/*  LE TEXTE NE DOIT PLUS ÊTRE COUPÉ EN PLEIN MOT.
 *  « Cours · Électronique (ET020) » sortait tronqué à « Cours · Electroniqu ».
 *  Deux lignes au plus, coupées proprement par des points de suspension, et
 *  les mots longs peuvent se casser plutôt que déborder de la colonne. */
.edt-bloc-t {
  font-size: .75rem; line-height: 1.22; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}

/*  Le trait « maintenant » — l'heure du SERVEUR, pas celle du navigateur :
    c'est elle qui décide de ce que le bot affiche. */
.edt-maintenant { position: absolute; left: 0; right: 0; height: 2px; background: var(--danger); z-index: 2; }
/*  L'étiquette débordait de la colonne et se posait sur la voisine. Elle est
    maintenant ancrée à GAUCHE, sur un fond opaque, et ne capte pas le clic —
    sinon elle empêcherait d'ajouter un créneau à cette heure-là. */
.edt-maintenant-mot {
  position: absolute; left: 3px; top: -.95em;
  font-size: .58rem; color: var(--danger);
  text-transform: uppercase; letter-spacing: .06em;
  background: var(--bg-soft); padding: 0 4px; border-radius: 2px;
  pointer-events: none; white-space: nowrap;
}
.edt-maintenant { pointer-events: none; }

.edt-aide { margin: 10px 0 0; }

/* ---- le panneau d'ajout / modification ---- */
.edt-form {
  margin-top: 14px; padding: 16px;
  background: var(--bg-card); border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm);
}
.edt-form.est-faux { border-color: var(--danger); }
.edt-form-titre { margin: 0 0 12px; font-size: 1rem; }
.edt-champ { margin-bottom: 12px; }
.edt-champ label { display: block; margin-bottom: 4px; font-size: .85rem; color: var(--text-muted); }
.edt-champ input, .edt-champ select {
  width: 100%; background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  padding: 8px 10px; font-family: inherit; font-size: .95rem;
}
.edt-heures-champs { display: flex; gap: 12px; }
.edt-heures-champs .edt-champ { flex: 1; }

.edt-pastilles { display: flex; flex-wrap: wrap; gap: 6px; }
.edt-pastille {
  background: var(--bg-softer); color: var(--text-muted);
  border: 1px solid var(--border-champ); border-radius: 999px;
  padding: 6px 12px; font-family: inherit; font-size: .82rem; cursor: pointer;
}
.edt-pastille.est-prise {
  background: rgba(var(--accent-rgb), .22); color: var(--text);
  border-color: var(--accent);
}
.edt-apercu {
  margin: 0 0 12px; padding: 8px 10px;
  background: var(--bg-softer); border-radius: var(--radius-sm);
  font-size: .85rem; color: var(--text-muted);
}
.edt-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.edt-actions .edt-supprimer { color: var(--danger); border-color: var(--danger); }

/* ---- le format texte, replié ---- */
.edt-brut { margin-top: 12px; }
.edt-brut summary { cursor: pointer; font-size: .88rem; color: var(--text-muted); }
.edt-brut textarea {
  width: 100%; margin-top: 8px; font-family: ui-monospace, monospace; font-size: .85rem;
}

.edt-conflit {
  margin-bottom: 14px; padding: 10px 12px;
  border-left: 3px solid var(--warning);
  background: rgba(251, 146, 60, .08); border-radius: 0 3px 3px 0;
  font-size: .9rem;
}
.edt-conflit strong { display: block; margin-bottom: 4px; }
/*  Les trous ne sont pas une faute — juste une information. Bleu plutôt
    qu'orange : on ne met pas au même niveau « décide quelque chose » et
    « au fait, il n'y a rien ici ». */
.edt-trous { border-left-color: var(--accent); background: rgba(var(--accent-rgb), .07); }

/*  La barre d'onglets n'existe que sur téléphone : sur grand écran on voit
    la semaine entière, un sélecteur de jour n'y servirait qu'à ajouter un
    clic. */
.edt-onglets { display: none; }
.edt-onglet {
  flex: 1 1 auto; min-width: 2.6rem; min-height: 44px;
  background: var(--bg-softer); color: var(--text-muted);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .82rem; font-weight: 600; cursor: pointer;
}
.edt-onglet.est-choisi {
  background: rgba(var(--accent-rgb), .22); color: var(--text); border-color: var(--accent);
}
.edt-onglet.est-aujourdhui { text-decoration: underline; text-underline-offset: 3px; }

/* ==========================================================================
 *  SUR TÉLÉPHONE : UN JOUR À LA FOIS — 09/09/2026
 * ==========================================================================
 *  Sept colonnes de 9,5 rem plus la gouttière font 1120 px. Sur un écran de
 *  390, on fait glisser la grille en permanence et on ne sait plus quelle
 *  colonne on lit : les colonnes n'étaient pas fausses, elles étaient hors de
 *  l'écran. On n'en montre donc qu'une, choisie par les onglets — c'est ce
 *  que fait n'importe quel agenda de téléphone, et pour cette raison-là.
 */
@media (max-width: 760px) {
  .edt-onglets { display: flex; gap: 4px; margin-bottom: 8px; }
  .edt-grille { grid-template-columns: 3rem minmax(0, 1fr); max-height: 60vh; }
  /*  `display: none` et pas `visibility` : un élément caché ne prend pas de
      cellule de grille, donc la colonne restante occupe toute la largeur. */
  .edt-jour:not(.est-choisi) { display: none; }
  .edt-jour { min-width: 0; }
  .edt-jour-titre { font-size: .72rem; }
  .edt-bloc-t { font-size: .78rem; -webkit-line-clamp: 3; }
  .edt-bloc-h { font-size: .66rem; }
  /*  Le formulaire passe en une colonne : deux champs côte à côte sur 390 px
      donnent deux champs illisibles. */
  .edt-heures-champs { flex-direction: column; gap: 0; }
  /*  Des cibles qu'un doigt peut viser : 44 px est le minimum admis. */
  .edt-pastille { min-height: 44px; padding: 6px 14px; }
  .edt-champ input, .edt-champ select { min-height: 44px; font-size: 16px; }
  .edt-actions .btn { flex: 1 1 auto; min-height: 44px; }
}

/*  Entre les deux : on garde les sept jours mais on serre les colonnes. */
@media (min-width: 761px) and (max-width: 1100px) {
  .edt-jour { min-width: 7.2rem; }
  .edt-bloc-t { font-size: .7rem; }
}

/* ==========================================================================
 *  LA CARTE « EN CE MOMENT » — 10/09/2026
 * ==========================================================================
 *  « Savoir dans combien de temps on passe à la prochaine activité… un truc
 *  clean, pro, et cool à regarder. »
 *
 *  C'est la seule carte de cette page, et c'est voulu : border, fond et ombre
 *  disent « objet distinct », et les dépenser partout aplatit la hiérarchie.
 *  Ici il n'y a qu'une chose à regarder en arrivant — ce que le bot affiche
 *  maintenant, et pour combien de temps encore.
 */
.edt-carte {
  margin-bottom: 16px; padding: 16px 18px;
  background: linear-gradient(180deg, rgba(var(--accent-rgb), .16), rgba(var(--accent-rgb), .06));
  border: 1px solid rgba(var(--accent-rgb), .4);
  border-radius: var(--radius-sm);
}
.edt-carte-haut { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.edt-carte-quand {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-muted);
}
/*  Le compte à rebours est LE chiffre de la carte : c'est lui qu'on vient
    chercher, il a donc le corps le plus gros et les chiffres alignés. */
.edt-carte-reste {
  font-size: 1.45rem; font-weight: 700; color: var(--accent-lisible);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.edt-carte-titre {
  font-size: 1.18rem; font-weight: 600; margin-top: 6px;
  line-height: 1.25; overflow-wrap: anywhere;
}
.edt-carte-sous { font-size: .82rem; color: var(--text-muted); margin-top: 2px; }
.edt-carte-jauge {
  height: 5px; margin-top: 12px; border-radius: 999px;
  background: rgba(255, 255, 255, .08); overflow: hidden;
}
.edt-carte-barre {
  height: 100%; width: 0; border-radius: 999px;
  background: var(--accent);
  transition: width .6s linear;
}
.edt-carte-suite { font-size: .84rem; color: var(--text-muted); margin-top: 10px; }

@media (prefers-reduced-motion: reduce) { .edt-carte-barre { transition: none; } }
@media (max-width: 760px) {
  .edt-carte-haut { flex-direction: column; gap: 2px; }
  .edt-carte-reste { font-size: 1.7rem; }
}

/* ==========================================================================
 *  LA CLOCHE DES NOUVEAUTÉS — 10/09/2026, page d'accueil, production
 * ==========================================================================
 *  Posée en haut à droite de la zone d'accueil, discrète tant qu'il n'y a
 *  rien de neuf. La pastille est la seule couleur vive de la page : c'est
 *  elle qui doit attraper l'œil, et rien d'autre autour ne doit lui faire
 *  concurrence.
 */
/*  ELLE NE FLOTTE PLUS — corrigé le 10/09/2026, sur capture.
 *
 *  CE QUI SE PASSAIT, ET POURQUOI ÇA NE RESSEMBLAIT À RIEN. La cloche était
 *  en `float: right`. Or `.hero` porte `overflow: hidden`, ce qui en fait un
 *  CONTEXTE DE BLOC — et un contexte de bloc, par définition, ne passe pas
 *  SOUS un flottant : il se rétrécit à côté. La boîte du hero était donc
 *  rognée sur toute la hauteur de la cloche, et comme c'est elle qui peint le
 *  dégradé et les étoiles, on voyait une bande verticale plus sombre courir
 *  le long de la page. Le fond n'était pas mal choisi : il manquait.
 *
 *  ON SORT DONC DU FLUX POUR DE BON. En absolu par rapport à `.landing`, la
 *  cloche ne pousse plus rien, ne rogne plus rien, et se pose PAR-DESSUS le
 *  décor — ce qu'on voulait depuis le début.
 *
 *  La leçon vaut au-delà d'ici : un flottant à côté d'un `overflow: hidden`
 *  ne se superpose jamais, il déplace. Pour poser un élément SUR un autre,
 *  c'est `position`, jamais `float`. */
.cloche { position: absolute; top: 20px; right: 24px; z-index: 20; }
/*  LE FOND DU BOUTON EST TRANSLUCIDE — corrigé le 10/09/2026, sur capture.
 *
 *  CE QUI CLOCHAIT. Il portait `--bg-card`, un gris BLEUTÉ (#1a1e2b), posé
 *  sur le dégradé du hero — qui, en production, est chaud : le jaune de
 *  l'accent et ses halos. Un rectangle froid sur un fond chaud ne se pose
 *  pas dessus, il se colle dessus. « Ça fait bizarre », et c'était exact.
 *
 *  DEUX CORRECTIONS, ET LA PREMIÈRE COMPTE PLUS QUE LA SECONDE :
 *
 *   1. LE FOND NE VIENT PLUS D'UN JETON FIXE. Du noir translucide avec un
 *      flou d'arrière-plan : le bouton prend la teinte de CE QU'IL Y A
 *      DERRIÈRE, quel que soit le monde, le thème ou l'événement saisonnier
 *      en cours. Il ne pourra plus jurer avec un décor qu'on changera dans
 *      six mois sans penser à lui.
 *   2. LA BORDURE ET LE HALO SONT CEUX DE LA PASTILLE DU HERO, à quelques
 *      lignes d'ici : même liseré d'accent, même halo à 8 %. Il ne s'agit
 *      pas de recopier une valeur mais d'entrer dans un vocabulaire qui
 *      existe déjà sur cette page — c'est ce qui fait qu'un élément a l'air
 *      d'y appartenir plutôt que d'y avoir été ajouté.
 *
 *  Le repli sans flou (vieux navigateur) reste lisible : le noir translucide
 *  assombrit assez le décor pour que le texte tienne son contraste.
 */
.cloche-bouton {
  /*  RONDE ET SANS TEXTE — 10/09/2026 : « on va juste mettre la cloche ».
   *  Une taille fixe plutôt qu'un remplissage : le bouton doit être un
   *  disque, et un padding calculé sur la hauteur d'un emoji donne un ovale
   *  différent selon la police que le système choisit pour le dessiner. */
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; padding: 0;
  background: rgba(10, 11, 15, .55);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(var(--accent-rgb), .55);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .08);
  border-radius: 999px; cursor: pointer; list-style: none;
  font-size: .88rem; font-weight: 600; color: var(--text); user-select: none;
  transition: border-color .15s, box-shadow .15s;
}
/*  Le triangle par défaut d'un `<details>` n'a rien à faire sur un bouton
    rond — les deux navigateurs ne le dessinent pas au même endroit. */
.cloche-bouton::-webkit-details-marker { display: none; }
.cloche-bouton::marker { content: ''; }
.cloche-bouton:hover, .cloche[open] .cloche-bouton {
  border-color: var(--accent);
  box-shadow: 0 0 0 5px rgba(var(--accent-rgb), .14);
}
.cloche-icone { font-size: 1.15rem; line-height: 1; }
/*  La pastille se pose EN HAUT À DROITE du disque, comme sur n'importe quelle
    application — c'est le seul endroit où l'œil la cherche. Le liseré de la
    couleur du fond la détache du bouton sans lui ajouter d'ombre. */
.cloche-pastille {
  position: absolute; top: -3px; right: -5px;
  min-width: 22px; height: 20px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--danger); color: #fff;
  border: 2px solid var(--bg);
  border-radius: 999px; font-size: .7rem; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}
/*  Le panneau, lui, reste OPAQUE : on y lit du texte, et un fond qui laisse
    passer des étoiles derrière trois paragraphes se paie en lisibilité. Il
    emprunte seulement le liseré d'accent, pour qu'on voie qu'il appartient au
    bouton qui l'ouvre. */
.cloche-panneau {
  position: absolute; right: 0; top: calc(100% + 10px);
  width: min(24rem, calc(100vw - 32px));
  max-height: min(60vh, 30rem); overflow-y: auto;
  background: var(--bg-card);
  border: 1px solid rgba(var(--accent-rgb), .35);
  border-radius: var(--radius-sm);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .55);
  padding: 4px;
}
.cloche-item { padding: 12px 14px; border-bottom: 1px solid var(--border); }
.cloche-item:last-child { border-bottom: 0; }
.cloche-item-haut { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.cloche-quand { font-size: .72rem; color: var(--text-muted); margin-left: auto; }
.cloche-item h3 { margin: 0 0 4px; font-size: .95rem; line-height: 1.3; }
.cloche-item p { margin: 0; font-size: .85rem; color: var(--text-muted); line-height: 1.5; white-space: pre-line; }
.cloche-lien { display: inline-block; margin-top: 8px; font-size: .84rem; }
.cloche-vide { margin: 0; padding: 18px 14px; text-align: center; color: var(--text-muted); font-size: .88rem; }
/*  Le lien vers l'historique, posé en pied du panneau : on le trouve après
    avoir lu, pas avant. */
.cloche-tout {
  display: block; padding: 11px 14px; text-align: center;
  font-size: .84rem; border-top: 1px solid var(--border);
}

/*  La pastille de genre, partagée avec le panneau staff : une couleur par
    nature d'annonce, posée sur 13 % d'elle-même comme les rôles du site. */
.actu-genre {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  font-weight: 700; padding: 2px 8px; border-radius: 999px;
  background: rgba(var(--accent-rgb), .18); color: var(--accent-lisible);
}
.actu-genre.est-correction { background: rgba(61, 220, 151, .16); color: var(--success); }
.actu-genre.est-maintenance { background: rgba(251, 146, 60, .16); color: var(--warning); }
.actu-genre.est-important { background: rgba(255, 91, 122, .16); color: var(--danger); }

/* ---- le panneau staff ---- */
.actus-liste { display: flex; flex-direction: column; gap: 1px; background: var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.actu-ligne { background: var(--bg-card); padding: 14px 16px; }
/*  Un brouillon se voit d'un coup d'œil : hachuré, il ne se confond pas avec
    ce qui est en ligne. */
.actu-ligne.est-brouillon { background: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-soft) 10px, var(--bg-soft) 20px); }
.actu-ligne-haut { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.actu-brouillon { font-size: .72rem; color: var(--warning); text-transform: uppercase; letter-spacing: .06em; }
.actu-texte { margin: 8px 0 0; color: var(--text-muted); font-size: .9rem; white-space: pre-line; }
.actu-pied { margin: 8px 0 0; font-size: .78rem; color: var(--text-muted); }
.actu-actions { display: flex; gap: 8px; margin-top: 10px; }
.actu-actions .actu-supprimer { color: var(--danger); border-color: var(--danger); }

@media (max-width: 760px) {
  .cloche { top: 14px; right: 14px; }
  .cloche-panneau { right: 0; width: calc(100vw - 28px); }
}

/* ==========================================================================
 *  LE FIL DES NOUVEAUTÉS — accueil et page dédiée, 10/09/2026
 * ==========================================================================
 *  Une chronologie verticale : un trait, un point par annonce. La forme dit
 *  ce que le contenu est — une suite d'événements dans le temps — là où une
 *  grille de cartes dirait « des choses côte à côte », ce qui est faux.
 *
 *  Le trait est tracé par la bordure gauche de la liste, pas par un élément :
 *  un fil dessiné en dur dépasserait sous le dernier point.
 */
.fil { max-width: 52rem; margin: 0 auto; padding: 56px 24px 8px; }
.fil-tete { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 22px; }
.fil-tete h2 { margin: 0; font-size: 1.5rem; }
.fil-tout { font-size: .88rem; white-space: nowrap; }

.fil-liste { list-style: none; margin: 0; padding: 0 0 0 22px; border-left: 1px solid var(--border); }
.fil-item { position: relative; padding: 0 0 26px 20px; }
.fil-item:last-child { padding-bottom: 0; }

/*  Le point mord sur le trait : décalé de sa moitié plus l'épaisseur du
    trait, il tombe pile dessus quelle que soit la taille du texte. */
.fil-point {
  position: absolute; left: -27px; top: 6px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--bg);
}
.fil-point.est-correction { background: var(--success); }
.fil-point.est-maintenance { background: var(--warning); }
.fil-point.est-important { background: var(--danger); }

.fil-haut { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
.fil-quand { font-size: .75rem; color: var(--text-muted); }
.fil-corps h2, .fil-corps h3 { margin: 0 0 6px; font-size: 1.02rem; line-height: 1.3; }
.fil-corps p { margin: 0; color: var(--text-muted); font-size: .92rem; line-height: 1.6; white-space: pre-line; }
.fil-lien { font-size: .88rem; }
.fil-signature { margin-top: 8px !important; font-size: .8rem; }

/* ---- la page dédiée ---- */
.fil-page { max-width: 46rem; padding-bottom: 64px; }
.fil-page-tete { padding: 40px 0 24px; border-bottom: 2px solid var(--border); margin-bottom: 28px; }
.fil-page-tete h1 { margin: 0 0 8px; font-size: clamp(1.7rem, 1.2rem + 2vw, 2.4rem); }
.fil-page .fil-corps h2 { font-size: 1.12rem; }

@media (max-width: 760px) {
  .fil { padding: 36px 16px 4px; }
  .fil-tete { flex-direction: column; gap: 4px; }
}

/* ==========================================================================
 *  TEST — LE SUIVI DE RECETTE, 11/09/2026
 * ==========================================================================
 *  Une grille de cinquante lignes se lit à la COULEUR avant de se lire au
 *  texte. Chaque sélecteur porte donc la teinte de son état : on voit où en
 *  est la recette sans lire une seule ligne.
 *
 *  C'est le seul endroit du panneau où la couleur porte de l'information
 *  plutôt que de la décoration — et c'est pour ça qu'elle est vive ici alors
 *  qu'elle est sobre partout ailleurs.
 */
.rec-avancement { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
.rec-jauge { height: 8px; border-radius: 999px; background: var(--bg-softer); overflow: hidden; }
.rec-jauge-barre { height: 100%; background: var(--success); border-radius: 999px; transition: width .4s ease; }
.rec-chiffres { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.rec-pourcent { font-size: 1.6rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.rec-detail { font-size: .9rem; color: var(--text-muted); }
.rec-detail b { font-variant-numeric: tabular-nums; }
.rec-detail .est-ok { color: var(--success); }
.rec-detail .est-ko { color: var(--danger); }

.rec-barre { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.rec-onglet, .rec-filtre {
  padding: 8px 14px; min-height: 40px; display: inline-flex; align-items: center;
  background: var(--bg-softer); border: 1px solid var(--border-champ);
  border-radius: var(--radius-sm); font-size: .88rem; color: var(--text-muted);
}
.rec-onglet.est-choisi, .rec-filtre.est-choisi {
  background: rgba(var(--accent-rgb), .18); color: var(--text); border-color: var(--accent);
}
.rec-filtre { margin-left: auto; }

.rec-table-boite { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.rec-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.rec-table th, .rec-table td { padding: 7px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.rec-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg-softer); font-size: .72rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-muted); white-space: nowrap;
}
.rec-nom { font-weight: 600; white-space: nowrap; font-family: ui-monospace, monospace; font-size: .86rem; }
.rec-table tbody tr:hover { background: rgba(var(--accent-rgb), .04); }

/*  L'étiquette de chaque champ existe pour les lecteurs d'écran : sans elle,
    on entend « liste déroulante » deux cents fois sans savoir de quoi. Elle
    est retirée de l'affichage, pas de la page — jamais `display: none`, qui
    la retirerait aussi de l'arbre d'accessibilité. */
.rec-etiq {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.rec-etat {
  width: 100%; min-width: 7rem; padding: 5px 8px;
  background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .85rem;
}
.rec-etat.est-ok { border-color: var(--success); color: var(--success); background: rgba(61, 220, 151, .10); }
.rec-etat.est-ko { border-color: var(--danger); color: var(--danger); background: rgba(255, 91, 122, .10); }
.rec-etat.est-sans-objet { color: var(--text-muted); opacity: .65; }

.rec-note-cell { min-width: 16rem; }
.rec-note-cell textarea {
  width: 100%; min-height: 38px; padding: 6px 8px; resize: vertical;
  background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  font-family: inherit; font-size: .85rem;
}

.rec-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 16px; }
.rec-vider { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rec-vider .btn { color: var(--danger); border-color: var(--danger); }

@media (max-width: 760px) {
  .rec-filtre { margin-left: 0; }
  .rec-etat { min-width: 5.5rem; font-size: .8rem; }
  .rec-note-cell { min-width: 12rem; }
}


/* ======================================================================
 *  LA BARRE DU HAUT, CÔTÉ COMPTE — 19/09/2026
 * ======================================================================
 *  LE DÉFAUT : pour un simple utilisateur connecté, la droite de la barre
 *  empilait QUATRE poids visuels sans rapport — un bouton violet plein, un
 *  avatar posé à nu sur le fond, une puce dorée « premium » et un mot gris.
 *  La puce dorée était la classe `staff-link`, réservée au staff, posée par
 *  erreur sur « Mes licences » : tout le monde portait donc un badge de
 *  staff au milieu de la barre. Quatre styles collés ne font pas un
 *  ensemble : c'est ça qui se voyait, sans qu'on sache le nommer.
 *
 *  LE PARTI PRIS : une seule zone « compte », lisible d'un coup d'œil.
 *    · l'identité devient une PASTILLE — même langage que le bouton des
 *      deux mondes juste à côté, donc rien de nouveau à apprendre ;
 *    · les deux liens qui l'accompagnent ont le MÊME poids, discret : ils
 *      accompagnent, ils ne rivalisent pas avec le bouton principal ;
 *    · un seul élément reste plein et coloré, « Dashboard », parce qu'une
 *      barre qui met tout en avant ne met rien en avant.
 */
.nav-separateur {
  width: 1px; height: 22px; flex: 0 0 auto;
  background: var(--border); margin: 0 4px;
}

/* --- L'identité : une pastille qui se tient ---------------------------- */
.navbar .user-menu {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 4px 13px 4px 4px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--bg-softer);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.navbar .user-menu:hover {
  border-color: rgba(var(--accent-rgb), 0.45);
  background: var(--bg-card);
}
/* La bordure de 2 px de l'avatar faisait doublon avec celle de la pastille :
   deux cadres concentriques à 3 px d'écart, ça se voit et ça salit. */
.navbar .user-menu .avatar {
  width: 28px; height: 28px; border: none;
  box-shadow: 0 0 0 1px var(--border);
}
.navbar .user-menu .username {
  font-size: 0.9rem; font-weight: 600; color: var(--text);
}

/* --- Les deux liens du compte : même poids, même hauteur --------------- */
.navbar .nav-licences,
.navbar .nav-deconnexion {
  display: inline-flex; align-items: center;
  padding: 7px 13px; border-radius: 999px;
  font-size: 0.88rem; font-weight: 500;
  color: var(--text-muted);
  border: 1px solid transparent;
  transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.navbar .nav-licences:hover {
  color: var(--text); background: var(--bg-softer);
  border-color: var(--border); text-decoration: none;
}
/* Se déconnecter n'est pas une action anodine : le survol le dit — en
   couleur, pas en taille. Au repos il reste aussi discret que son voisin,
   pour ne pas transformer la barre en avertissement permanent. */
.navbar .nav-deconnexion:hover {
  color: var(--danger); background: var(--bg-card);
  border-color: var(--danger); text-decoration: none;
}

/* --- Quand la place manque, on resserre, on ne casse rien -------------- */
@media (max-width: 1400px) {
  .navbar .nav-licences,
  .navbar .nav-deconnexion { padding: 6px 10px; font-size: 0.85rem; }
  .nav-separateur { margin: 0 2px; }
}
/* Sous 1620 px le pseudo est déjà masqué plus haut : la pastille se referme
   symétriquement autour du seul avatar, au lieu de garder son creux à droite. */
@media (max-width: 1620px) {
  .navbar .user-menu { padding: 4px; gap: 0; }
}
/* Menu « ☰ » (sous 1140 px) : en colonne, une pastille arrondie n'a plus de
   sens — chaque entrée devient une ligne pleine largeur, visable au doigt. */
@media (max-width: 1140px) {
  .nav-separateur { display: none; }
  .nav-burger:checked ~ .nav-actions .user-menu {
    border-radius: var(--radius-sm);
    padding: 10px; gap: 9px; justify-content: flex-start;
  }
  .navbar .nav-licences,
  .navbar .nav-deconnexion {
    border-radius: var(--radius-sm); justify-content: center;
  }
  /*  « Mes licences » est masqué sous 1220 px parce que la barre sur UNE
   *  ligne n'a plus la place. Dans le menu déplié, la place existe : le
   *  cacher là n'aurait plus aucune raison d'être, et le lien redeviendrait
   *  introuvable sur téléphone. On le remet donc ici, et seulement ici. */
  .nav-burger:checked ~ .nav-actions > .nav-licences { display: inline-flex; }
}


/* ======================================================================
 *  REFONTE DE LA PAGE D'ACCUEIL — 19/09/2026
 * ======================================================================
 *  Trois blocs nouveaux, et une règle commune : rien ne bouge sans raison,
 *  et rien n'est dessiné qui ne corresponde à une donnée réelle.
 *
 *    1. le bandeau d'actus qui défile, en remplacement de la cloche ;
 *    2. le hero en deux colonnes — le logo à gauche, ce qu'on fait à droite ;
 *    3. les cartes de chiffres et la grille d'actus illustrées.
 *
 *  LES ANIMATIONS NE TOUCHENT QUE `transform` ET `opacity` : ce sont les
 *  deux seules propriétés que le compositeur sait animer sans repeindre la
 *  page. Le réglage système « moins d'animations » les éteint toutes, plus
 *  bas — un bandeau qui défile sans qu'on puisse l'arrêter est un problème
 *  d'accessibilité, pas un effet de style.
 */

/* ---------------------------------------------------------------- 1. BANDEAU */
.bandeau-actus {
  position: relative; overflow: hidden;
  background: rgba(10, 12, 18, 0.92);
  border-bottom: 1px solid var(--border);
}
/*  LES DEUX VOILES SUR LES CÔTÉS. Sans eux, les titres apparaissent et
 *  disparaissent net au bord de l'écran, comme coupés au ciseau. */
.bandeau-actus::before,
.bandeau-actus::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 64px;
  z-index: 2; pointer-events: none;
}
.bandeau-actus::before { left: 0;  background: linear-gradient(90deg, rgba(10,12,18,.95), transparent); }
.bandeau-actus::after  { right: 0; background: linear-gradient(270deg, rgba(10,12,18,.95), transparent); }

/*  `width: max-content` : la piste fait la largeur de son contenu, pas celle
 *  de l'écran — sans ça les éléments se compriment au lieu de défiler. */
.bandeau-actus-piste {
  display: flex; align-items: center; width: max-content;
  animation: bandeau-defile 48s linear infinite;
}
/*  ON S'ARRÊTE AU SURVOL : un titre qui s'enfuit pendant qu'on le lit (ou
 *  qu'on vise son lien) est une petite cruauté. */
.bandeau-actus:hover .bandeau-actus-piste,
.bandeau-actus:focus-within .bandeau-actus-piste { animation-play-state: paused; }
/*  -50 % et pas -100 % : la liste est écrite DEUX fois, donc la moitié de la
 *  piste est exactement une liste complète. Au bout, on est revenu au même
 *  pixel qu'au départ et la boucle ne se voit pas. */
@keyframes bandeau-defile {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.bandeau-actu {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 22px; white-space: nowrap;
  border-right: 1px solid var(--border);
  color: var(--text-muted); font-size: 0.82rem;
  transition: color .15s ease, background .15s ease;
}
.bandeau-actu:hover { color: var(--text); background: rgba(255,255,255,.035); text-decoration: none; }
.bandeau-titre { font-weight: 500; }
.bandeau-date { opacity: .55; font-variant-numeric: tabular-nums; }

/* --- Les pastilles d'importance (bandeau ET cartes d'actus) ------------- */
.bandeau-pastille, .actu-pastille {
  display: inline-flex; align-items: center; padding: 2px 8px;
  border-radius: 5px; border: 1px solid transparent;
  font-size: 0.65rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; white-space: nowrap;
}
.bandeau-pastille.est-nouveaute, .actu-pastille.est-nouveaute { color:var(--accent-lisible); background:rgba(var(--accent-rgb),.16); border-color:rgba(var(--accent-rgb),.36); }
.bandeau-pastille.est-info,      .actu-pastille.est-info      { color:#7dd3fc; background:rgba(56,189,248,.14); border-color:rgba(56,189,248,.32); }
.bandeau-pastille.est-important, .actu-pastille.est-important { color:#fdba74; background:rgba(251,146,60,.15); border-color:rgba(251,146,60,.35); }
.bandeau-pastille.est-urgent,    .actu-pastille.est-urgent    { color:#fda4af; background:rgba(255,91,122,.16); border-color:rgba(255,91,122,.40); }

/* ---------------------------------------------------------------- 2. HERO */
/*  `.hero.hero-duo` et pas `.hero-duo` seul : le hero d'origine est centré,
 *  et deux classes l'emportent sur une seule sans avoir à écrire !important. */
.hero.hero-duo {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  align-items: center; gap: clamp(28px, 5vw, 72px);
  max-width: 1200px; margin: 0 auto;
  padding: clamp(38px, 6vw, 84px) 24px;
  text-align: left;
}
.hero-visuel { position: relative; display: flex; align-items: center; justify-content: center; }
.hero-logo {
  position: relative; z-index: 1;
  width: min(100%, 300px); height: auto;
  animation: hero-flotte 7s ease-in-out infinite;
}
/*  Une respiration de 16 px en sept secondes : on la remarque à peine, et
 *  c'est le but — le logo est vivant, il ne gesticule pas. */
@keyframes hero-flotte {
  0%, 100% { transform: translateY(-8px); }
  50%      { transform: translateY(8px); }
}
.hero-halo {
  /* BOÎTE CARRÉE, CENTRÉE SUR LE LOGO. Avec `inset: -6%` la boîte suivait
   * la colonne : large sur grand écran, haute sur petit. Le `border-radius:
   * 50%` découpait alors une ELLIPSE dans un dégradé rond : un bord net, que
   * le flou transformait en bande. Ici la boîte est carrée, le dégradé et le
   * masque s'éteignent à `closest-side`, c'est-à-dire pile sur le bord. */
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(130%, 420px); aspect-ratio: 1 / 1; border-radius: 50%;
  /* Paliers serrés + grain SVG très léger : sans eux, anneaux visibles sur
   * fond sombre (banding 8 bits). Pas de filter: blur, volontairement. */
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.12 0'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>"),
    radial-gradient(circle closest-side,
      rgba(var(--accent-rgb), .40) 0%, rgba(var(--accent-rgb), .36) 15%, rgba(var(--accent-rgb), .29) 30%,
      rgba(var(--accent-rgb), .21) 45%, rgba(var(--accent-rgb), .14) 60%, rgba(var(--accent-rgb), .08) 74%,
      rgba(var(--accent-rgb), .04) 85%, rgba(var(--accent-rgb), .01) 94%, transparent 100%);
  -webkit-mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
}


.hero.hero-duo .hero-texte { text-align: left; }
.hero.hero-duo .hero-texte h1 { margin: 0 0 14px; text-wrap: balance; }
.hero.hero-duo .hero-sub { margin: 0 0 26px; max-width: 54ch; }
.hero.hero-duo .hero-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: flex-start; }

/* ------------------------------------------------------- 3. LES CHIFFRES */
.stats-cartes {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(212px, 1fr));
  gap: 16px; max-width: 1200px; margin: 0 auto;
  padding: 0 24px clamp(40px, 6vw, 72px);
}
.stat-carte {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 8px; min-height: 148px;
  padding: 18px 18px 16px;
  border: 1px solid var(--border); border-radius: 14px;
  background: linear-gradient(158deg, var(--teinte-faible), var(--bg-card) 64%);
}
/*  Le filet de couleur en haut : c'est lui qui distingue les cartes d'un
 *  coup d'oeil, sans avoir à teinter tout le fond et à perdre le contraste
 *  du texte par-dessus. */
.stat-carte::after {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--teinte);
}
.stat-nom { font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); font-weight: 700; }
.stat-chiffre {
  margin: 0; display: flex; align-items: baseline; gap: 9px;
  font-size: clamp(1.9rem, 3.4vw, 2.4rem); font-weight: 800; line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.stat-delta { font-size: .78rem; font-weight: 600; color: var(--teinte); }
.stat-note { margin: 0; font-size: .8rem; color: var(--text-muted); }
/*  `margin-top:auto` colle le graphique en bas : les cartes n'ont pas toutes
 *  le même nombre de lignes, et sans ça les barres flotteraient à une hauteur
 *  différente sur chaque carte. */
.stat-barres { margin-top: auto; display: flex; align-items: flex-end; gap: 5px; height: 34px; }
.stat-barres span { flex: 1; border-radius: 3px; background: var(--teinte); opacity: .85; }
.stat-carte.est-violet { --teinte:var(--accent-lisible); --teinte-faible: rgba(var(--accent-rgb),.15); }
.stat-carte.est-bleu   { --teinte:#22d3ee; --teinte-faible: rgba(34,211,238,.13); }
.stat-carte.est-rose   { --teinte:#f472b6; --teinte-faible: rgba(244,114,182,.13); }
.stat-carte.est-vert   { --teinte:#34d399; --teinte-faible: rgba(52,211,153,.13); }
.stat-carte.est-jaune  { --teinte:#fbbf24; --teinte-faible: rgba(251,191,36,.13); }

/* ------------------------------------------------ 4. LES ACTUS ILLUSTRÉES */
.actus-section { max-width: 1200px; margin: 0 auto; padding: 0 24px clamp(48px, 7vw, 80px); }
.actus-tete { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.actus-tete h2 { margin: 0; font-size: clamp(1.3rem, 2.4vw, 1.75rem); }
.actus-tout {
  font-size: .85rem; font-weight: 600; color: var(--text-muted);
  border: 1px solid var(--border); padding: 7px 14px; border-radius: 999px;
  transition: color .15s ease, border-color .15s ease;
}
.actus-tout:hover { color: var(--text); border-color: var(--accent); text-decoration: none; }
.actus-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr)); gap: 18px; }
.actu-carte {
  border: 1px solid var(--border); border-radius: 14px; overflow: hidden;
  background: var(--bg-card);
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.actu-carte:hover {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), .5);
  box-shadow: 0 18px 38px -18px rgba(0,0,0,.75);
}
/*  TOUTE LA CARTE EST LE LIEN, pas seulement le titre : une zone cliquable
 *  de la taille d'une vignette se vise sans effort, au doigt comme à la
 *  souris. D'où les `span` à l'intérieur — un `<a>` ne peut pas contenir de
 *  blocs de texte structurés sans devenir invalide. */
.actu-lien-total { display: flex; flex-direction: column; height: 100%; color: inherit; }
.actu-lien-total:hover { text-decoration: none; }
.actu-image { display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--bg-softer); }
.actu-image img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s ease; }
.actu-carte:hover .actu-image img { transform: scale(1.04); }
.actu-corps { display: flex; flex-direction: column; gap: 9px; padding: 15px 16px 16px; flex: 1; }
.actu-haut { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.actu-genre-texte { font-size: .72rem; color: var(--text-muted); }
.actu-titre { font-weight: 700; font-size: 1.02rem; line-height: 1.35; color: var(--text); text-wrap: balance; }
/*  Trois lignes maximum : au-delà, les cartes d'une même rangée n'ont plus
 *  la même hauteur et la grille se met à boiter. */
.actu-extrait {
  font-size: .87rem; color: var(--text-muted); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.actu-pied {
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--border);
  display: flex; justify-content: space-between; gap: 10px;
  font-size: .76rem; color: var(--text-muted);
}
.actu-quand { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- 5. LES ÉCRANS ÉTROITS */
@media (max-width: 900px) {
  .hero.hero-duo { grid-template-columns: 1fr; text-align: center; gap: 24px; }
  .hero.hero-duo .hero-texte { text-align: center; }
  .hero.hero-duo .hero-sub { margin-left: auto; margin-right: auto; }
  .hero.hero-duo .hero-cta { justify-content: center; }
  .hero-logo { width: min(58%, 210px); }
  .bandeau-actu { padding: 8px 16px; font-size: .78rem; }
}

/* ------------------------------------------------- 6. « MOINS D'ANIMATIONS »
 *  Le réglage système est un choix, pas une préférence esthétique : certaines
 *  personnes ont mal au coeur devant une page qui bouge. On arrête TOUT ce
 *  qui bouge tout seul — le bandeau se lit alors en le survolant, et le logo
 *  reste simplement posé. */
@media (prefers-reduced-motion: reduce) {
  .bandeau-actus-piste, .hero-logo { animation: none; }
  .actu-carte, .actu-image img { transition: none; }
  .actu-carte:hover { transform: none; }
  .actu-carte:hover .actu-image img { transform: none; }
}


/* ======================================================================
 *  UN CIEL QU'ON VOIT — 19/09/2026
 * ======================================================================
 *  « Les etoiles vont pas du tout, fais un truc plus comme eux. »
 *
 *  LE DIAGNOSTIC. Le decor n'etait pas casse : il etait peint a 10 %
 *  d'opacite sur un fond presque noir. Mesurable, et invisible a l'oeil —
 *  exactement le piege deja rencontre ici en aout (« 194 mesures disaient que
 *  le decor est peint ; elles repondaient a cote de la question »).
 *
 *  CE QUI MANQUAIT, ET CE QU'ON AJOUTE :
 *    · DE LA COULEUR — une nebuleuse violet/bleu/rose, floutee, qui donne au
 *      fond une profondeur coloree au lieu d'un noir plat ;
 *    · DU DESSIN — des etoiles RELIEES entre elles. Des poussieres
 *      eparpillees ne se lisent pas comme un ciel ; des constellations, si.
 *
 *  LA REGLE DE LISIBILITE NE BOUGE PAS : le point le plus clair du decor
 *  reste sous la clarte d'une carte. La nebuleuse est floutee a 40 px, ce qui
 *  etale sa couleur au lieu de la concentrer, et aucune de ses taches ne
 *  depasse 0,30 d'alpha avant floutage.
 */

/* --- La nebuleuse : la couleur du fond ---------------------------------- */
.ciel-nebuleuse {
  inset: -25%;
  background-image:
    radial-gradient(38% 30% at 18% 22%, rgba(var(--accent-rgb), 0.30), transparent 70%),
    radial-gradient(32% 26% at 82% 16%, rgba(56, 189, 248, 0.20), transparent 70%),
    radial-gradient(34% 28% at 74% 76%, rgba(244, 114, 182, 0.17), transparent 70%),
    radial-gradient(30% 24% at 26% 82%, rgba(52, 211, 153, 0.12), transparent 72%);
  filter: blur(40px);
  animation: ciel-nebuleuse 72s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes ciel-nebuleuse {
  0%   { transform: translate3d(-3%, -2%, 0) scale(1.04); }
  50%  { transform: translate3d(2%, 3%, 0)   scale(1.12); }
  100% { transform: translate3d(3%, -3%, 0)  scale(1.06); }
}

/* --- Les constellations : des etoiles reliees ---------------------------
 *  UNE IMAGE SVG EN LIGNE, PAS UN FICHIER : zero requete reseau, et le motif
 *  se redimensionne sans jamais pixeliser. La tuile fait 700 px et derive
 *  lentement — a cette vitesse, on ne voit pas la repetition, on voit un ciel
 *  qui tourne. */




/*  Les anciennes poussieres restent, elles font la profondeur entre les
 *  constellations — mais a 10 % elles ne servaient a rien. */
.ciel-etoiles-loin  { opacity: 0.85; }
.ciel-etoiles-moyen { opacity: 0.95; }
.ciel-etoiles-pres  { opacity: 1; }

/*  LES FILANTES PENDANT UN EVENEMENT : « ajoute les etoiles filantes pour les
 *  evenements ». Elles existaient deja toute l'annee ; pendant un evenement
 *  elles deviennent plus longues et plus lumineuses, pour que le ciel dise
 *  lui aussi qu'il se passe quelque chose. */
html[data-evenement] .ciel-filante { opacity: 1; filter: brightness(1.6); }

/* ======================================================================
 *  LE LOGO DU HERO — 19/09/2026 : « le logo ça va pas, c'est moche »
 * ======================================================================
 *  Un SVG plat pose sur du noir, ce n'est pas un visuel : c'est un fichier.
 *  On lui donne ce qu'il n'avait pas — un objet qui existe dans l'espace :
 *  une pastille en verre teinte, un anneau de couleur qui tourne lentement
 *  derriere, et une ombre portee coloree qui le decolle du fond.
 */
.hero-visuel { isolation: isolate; }
/*  PAS DE PASTILLE, PAS D'ARC-EN-CIEL — repris le 19/09/2026.
 *
 *  La version d'avant enfermait le logo dans un gros carre arrondi, avec un
 *  degrade conique multicolore qui tournait derriere. Resultat a l'ecran :
 *  un V minuscule perdu au milieu d'une boite vide, entoure d'un halo vert /
 *  rose / bleu qui n'a rien a voir avec la marque. « Logo bizarre, ca va
 *  pas » — et c'etait exact.
 *
 *  ON REVIENT AU SIMPLE, EN PLUS GRAND. Le logo lui-meme, large, pose sur un
 *  seul halo violet — la couleur de la marque, et une seule. C'est le halo
 *  (.hero-halo, deja dans le gabarit) qui le decolle du fond ; il n'a besoin
 *  de rien d'autre. */
.hero.hero-duo .hero-logo {
  width: min(88%, 310px); height: auto; padding: 0;
  background: none; border: none; border-radius: 0;
  filter: drop-shadow(0 26px 60px rgba(var(--accent-rgb), .58));
  animation: hero-flotte 7s ease-in-out infinite;
}
.hero-halo {
  /* BOÎTE CARRÉE, CENTRÉE SUR LE LOGO. Avec `inset: -6%` la boîte suivait
   * la colonne : large sur grand écran, haute sur petit. Le `border-radius:
   * 50%` découpait alors une ELLIPSE dans un dégradé rond : un bord net, que
   * le flou transformait en bande. Ici la boîte est carrée, le dégradé et le
   * masque s'éteignent à `closest-side`, c'est-à-dire pile sur le bord. */
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(130%, 420px); aspect-ratio: 1 / 1; border-radius: 50%;
  /* Paliers serrés + grain SVG très léger : sans eux, anneaux visibles sur
   * fond sombre (banding 8 bits). Pas de filter: blur, volontairement. */
  background:
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.12 0'/></filter><rect width='100%25' height='100%25' filter='url(%23g)'/></svg>"),
    radial-gradient(circle closest-side,
      rgba(var(--accent-rgb), .40) 0%, rgba(var(--accent-rgb), .36) 15%, rgba(var(--accent-rgb), .29) 30%,
      rgba(var(--accent-rgb), .21) 45%, rgba(var(--accent-rgb), .14) 60%, rgba(var(--accent-rgb), .08) 74%,
      rgba(var(--accent-rgb), .04) 85%, rgba(var(--accent-rgb), .01) 94%, transparent 100%);
  -webkit-mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 0%, #000 45%, transparent 100%);
}



/*  LA PASTILLE A ETE SUPPRIMEE ICI — 19/09/2026.
 *
 *  Elle etait ecrite DEUX FOIS dans ce fichier : la version corrigee plus
 *  haut (sans boite) et celle-ci, plus bas, qui la remettait. A specificite
 *  egale c'est la derniere qui gagne — donc la correction ne se voyait pas,
 *  et on pouvait la refaire dix fois sans effet. Le vrai correctif, c'etait
 *  d'effacer ce doublon. Voir .hero.hero-duo .hero-logo, plus haut. */

/* ======================================================================
 *  LE BANDEAU D'EVENEMENT — plus clair, et sans le double emoji
 * ======================================================================
 *  « Le bandeau plus clair. » Il etait a 18 % de teinte sur un fond sombre :
 *  on lisait le texte, on ne voyait pas le bandeau. Le double emoji, lui,
 *  venait du texte de l'annonce QUI COMMENCAIT DEJA par une fusee, alors que
 *  le gabarit en pose une a cote : corrige dans les donnees, pas ici.
 */
.fete-bandeau {
  /*  Le troisieme arret etait un rose ecrit en dur : sur un evenement vert
   *  ou orange, il jurait. Il reprend la teinte principale. */
  background: linear-gradient(90deg,
    rgba(var(--fond-teinte), 0.38),
    rgba(var(--fond-aurore-2), 0.34) 55%,
    rgba(var(--fond-teinte), 0.30));
  border-bottom: 1px solid rgba(var(--fond-aurore-2), 0.55);
  color: #fff;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}

@media (prefers-reduced-motion: reduce) {
  .ciel-nebuleuse, .ciel-constellation { animation: none; }
  
}


/* ======================================================================
 *  LES CARTES DE CHIFFRES : L'ICONE, ET DE LA COULEUR PARTOUT — 19/09/2026
 * ======================================================================
 *  « Il manque dans la liste membre/serveur des petits logos comme sur le
 *  leur. » Ils manquaient en effet : sans eux, quatre cartes ne different que
 *  par un liseré, et l'oeil doit LIRE pour savoir laquelle est laquelle.
 *  Une icone se reconnait avant d'etre lue — c'est tout l'interet.
 */
.stat-carte > header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.stat-ico {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 9px;
  background: var(--teinte-faible);
  border: 1px solid color-mix(in srgb, var(--teinte) 34%, transparent);
  color: var(--teinte);
}
.stat-ico svg {
  width: 17px; height: 17px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
/*  LE REPLI SANS `color-mix` : quelques navigateurs un peu anciens ne le
 *  connaissent pas, et une bordure `transparent` sur fond sombre disparait
 *  purement et simplement. On en pose une neutre avant. */
@supports not (border-color: color-mix(in srgb, red 30%, transparent)) {
  .stat-ico { border-color: var(--border); }
}
.stat-barres span { min-height: 4px; }

/* ======================================================================
 *  « TOUT CE DONT TU AS BESOIN » — DE LA COULEUR
 * ======================================================================
 *  Huit cartes grises alignees, c'est un tableau, pas une vitrine. Chacune
 *  recoit sa teinte, prise dans la meme palette que les cartes de chiffres :
 *  le liseré du haut se colore, et le survol allume la carte entiere.
 *
 *  LA COULEUR TOURNE AVEC `nth-child` et non avec une classe par module :
 *  la liste des huit vedettes est calculee depuis ce que les serveurs
 *  ouvrent le plus, elle change donc toute seule. Une couleur fixee par
 *  module aurait laisse des trous le jour ou l'ordre bouge.
 */
.feature-grid .feature-card {
  position: relative; overflow: hidden;
  border: 1px solid var(--border); border-radius: 14px;
  background: linear-gradient(158deg, var(--teinte-faible), var(--bg-card) 66%);
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.feature-grid .feature-card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  background: var(--teinte);
}
.feature-grid .feature-card:hover {
  transform: translateY(-3px);
  border-color: color-mix(in srgb, var(--teinte) 55%, transparent);
  box-shadow: 0 18px 38px -20px color-mix(in srgb, var(--teinte) 70%, transparent);
}
.feature-grid .feature-card h3 { color: var(--text); }
.feature-grid .feature-card:nth-child(8n+1) { --teinte:var(--accent-lisible); --teinte-faible: rgba(var(--accent-rgb),.15); }
.feature-grid .feature-card:nth-child(8n+2) { --teinte:#22d3ee; --teinte-faible: rgba(34,211,238,.13); }
.feature-grid .feature-card:nth-child(8n+3) { --teinte:#f472b6; --teinte-faible: rgba(244,114,182,.13); }
.feature-grid .feature-card:nth-child(8n+4) { --teinte:#34d399; --teinte-faible: rgba(52,211,153,.13); }
.feature-grid .feature-card:nth-child(8n+5) { --teinte:#fbbf24; --teinte-faible: rgba(251,191,36,.13); }
.feature-grid .feature-card:nth-child(8n+6) { --teinte:#fb7185; --teinte-faible: rgba(251,113,133,.13); }
.feature-grid .feature-card:nth-child(8n+7) { --teinte:#60a5fa; --teinte-faible: rgba(96,165,250,.13); }
.feature-grid .feature-card:nth-child(8n+8) { --teinte:#c084fc; --teinte-faible: rgba(192,132,252,.13); }
/*  Le palier (Pro, Max...) prend lui aussi la teinte de sa carte. */
.feature-grid .feature-card .feature-palier {
  background: var(--teinte-faible); color: var(--teinte);
  border: 1px solid color-mix(in srgb, var(--teinte) 34%, transparent);
}
@media (prefers-reduced-motion: reduce) {
  .feature-grid .feature-card { transition: none; }
  .feature-grid .feature-card:hover { transform: none; }
}


/*  LES BARRES ÉTAIENT DES CARRÉS — corrigé le 19/09/2026 après capture.
 *
 *  34 px de haut pour sept barres réparties sur toute la largeur d'une carte,
 *  ça donne sept blocs presque carrés : on ne lit plus un graphique, on lit
 *  une rangée de pavés. Chez la référence, la bande fait une quinzaine de
 *  pixels — assez pour voir les écarts, assez peu pour rester un DÉTAIL sous
 *  le chiffre, qui reste l'objet de la carte. */
.stat-barres { height: 15px; gap: 4px; }
.stat-barres span { border-radius: 2px; min-height: 3px; }


/*  LES DEUX CARTES RESERVEES AU STAFF — 19/09/2026.
 *  Le jaune pour la note du support, l'or pour l'annuaire. Elles portent une
 *  petite mention « staff uniquement » : sur une page vue par des visiteurs,
 *  une carte qui n'apparait que pour certains doit DIRE qu'elle est a part,
 *  sinon on finit par croire qu'elle est publique et on s'autocensure dessus. */
.stat-carte.est-or { --teinte:#f59e0b; --teinte-faible: rgba(245,158,11,.14); }
.stat-carte.est-staff { position: relative; }
.stat-staff-marque {
  position: absolute; right: 12px; bottom: 10px;
  font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--teinte); opacity: .75;
}
.stat-carte.est-staff .stat-barres { padding-right: 96px; }


/* ======================================================================
 *  LA PAGE PREND TOUTE LA LARGEUR — ET LE TELEPHONE AUSSI — 19/09/2026
 * ======================================================================
 *  « On peut utiliser les cotes, faut un truc pour les tel aussi. »
 *
 *  LE DEFAUT : le contenu etait bride a 1200 px alors que la barre du haut,
 *  elle, va jusqu'a 1600. Sur un grand ecran, six cartes ne tenaient donc pas
 *  sur une ligne : cinq passaient, la sixieme tombait toute seule en dessous,
 *  avec quatre cents pixels de vide a sa droite. On elargit a 1500 px : les
 *  six tiennent, et les quatre cartes publiques respirent au lieu d'etre
 *  serrees au milieu.
 */
.hero.hero-duo, .stats-cartes, .actus-section { max-width: 1500px; }

/*  La mention « staff » reprend sa place dans le flux. */
.stat-carte.est-staff { position: relative; }
.stat-staff-marque {
  position: static; display: block; margin: -2px 0 2px;
  font-size: .62rem; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--teinte); opacity: .8;
}
.stat-carte.est-staff .stat-barres { padding-right: 0; }

/* ---------------------------------------------------------- LE TELEPHONE
 *  Deux colonnes, pas une : sur un ecran de telephone, une carte par ligne
 *  donne six ecrans a faire defiler avant d'arriver au contenu. Tout est
 *  reduit dans les memes proportions pour que le chiffre reste l'objet de la
 *  carte — c'est lui qu'on lit, le reste l'accompagne. */
@media (max-width: 680px) {
  .stats-cartes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding-left: 14px; padding-right: 14px; }
  .stat-carte { min-height: 118px; padding: 13px 13px 12px; border-radius: 12px; }
  .stat-nom { font-size: .62rem; letter-spacing: .1em; }
  .stat-chiffre { font-size: 1.5rem; gap: 6px; }
  .stat-delta { font-size: .66rem; }
  .stat-note { font-size: .72rem; }
  .stat-ico { width: 26px; height: 26px; border-radius: 8px; }
  .stat-ico svg { width: 15px; height: 15px; }
  .stat-barres { height: 12px; gap: 3px; }
  .stat-staff-marque { font-size: .55rem; }
  .actus-grille { grid-template-columns: 1fr; }
  .actus-section, .hero.hero-duo { padding-left: 14px; padding-right: 14px; }
}
/*  Les tres petits ecrans (moins de 380 px) : une seule colonne, sinon le
 *  chiffre et son « +2 cette sem. » se coupent en plein milieu. */
@media (max-width: 380px) {
  .stats-cartes { grid-template-columns: 1fr; }
}


/*  LA CARTE « NOTRE SERVEUR » — 19/09/2026.
 *  Turquoise : il fallait une teinte qui ne se confonde ni avec le vert des
 *  commandes, ni avec l'or de l'annuaire, ni avec le jaune des tickets — les
 *  trois cartes qui l'entourent. */
.stat-carte.est-turquoise { --teinte:#2dd4bf; --teinte-faible: rgba(45,212,191,.13); }

/*  LE TITRE DES ACTUS AU MILIEU — « au milieu stp ».
 *  Depuis l'elargissement a 1500 px, « Dernieres actus » se retrouvait collee
 *  tout a gauche d'une bande tres large, avec le lien a l'autre bout : deux
 *  elements minuscules aux deux extremites d'un vide. Le titre revient au
 *  centre ; le lien reste a droite, mais en position absolue, donc SANS
 *  decaler le titre — un titre centre « a peu pres » se voit tout de suite. */
.actus-tete { position: relative; justify-content: center; }
.actus-tete h2 { text-align: center; }
.actus-tout { position: absolute; right: 0; top: 50%; transform: translateY(-50%); }
@media (max-width: 680px) {
  /*  Sur telephone il n'y a plus la place : on empile, et tout est centre. */
  .actus-tete { flex-direction: column; gap: 10px; }
  .actus-tout { position: static; transform: none; }
}


/* ======================================================================
 *  UN CIEL PLUS VIOLET, ET PLUS PROFOND — 19/09/2026
 * ======================================================================
 *  « Pour le fond, mets-en plus, + violet foncé. »
 *
 *  Les trois arrets du ciel etaient des gris bleutes tres neutres (#16172a,
 *  #111320, #0a0b0f) : sur un ecran, ca se lit comme du noir. La marque, elle,
 *  est violette. On rapproche donc le fond de la marque au lieu de le laisser
 *  neutre — un fond teinte donne aussi plus de relief aux etoiles claires
 *  posees dessus, parce qu'il y a enfin un ecart de teinte, et pas seulement
 *  un ecart de clarte.
 *
 *  ON RESTE SOMBRE. Le plus clair des trois arrets est toujours plus sombre
 *  qu'une carte : c'est la regle que ce decor n'a pas le droit d'enfreindre,
 *  sinon un texte pose dessus devient moins lisible qu'ailleurs sur le site.
 *
 *  LA PRODUCTION N'EST PAS CONCERNEE : son monde est jaune, un ciel violet
 *  jurerait avec. D'ou le `:not([data-monde="prod"])` — le monde prod garde
 *  exactement ce qu'il avait.
 */
html:not([data-monde="prod"]) {
  --ciel-haut: #1c1439;
  --ciel-milieu: #150f29;
  --ciel-bas: #0b0715;
}

/*  La nebuleuse prend elle aussi plus de violet : c'est elle qui colore les
 *  grandes zones, les etoiles ne font que ponctuer. */
html:not([data-monde="prod"]) .ciel-nebuleuse {
  background-image:
    radial-gradient(38% 30% at 18% 22%, rgba(var(--accent-rgb), 0.42), transparent 70%),
    radial-gradient(32% 26% at 82% 16%, rgba(99, 102, 241, 0.30), transparent 70%),
    radial-gradient(34% 28% at 74% 76%, rgba(168, 85, 247, 0.26), transparent 70%),
    radial-gradient(30% 24% at 26% 82%, rgba(76, 29, 149, 0.30), transparent 72%);
}


/* ======================================================================
 *  LE CIEL : NOIR VIOLET, AVEC DES ZONES A PEINE PLUS CLAIRES — 19/09/2026
 * ======================================================================
 *  « Pas noir noir : noir violet = zone sombre et endroit tres tres
 *  legerement plus clair. »
 *
 *  LE NOIR PUR ETAIT UNE FAUSSE BONNE IDEE. Un fond parfaitement uniforme n'a
 *  pas de profondeur : les points du reseau flottent dessus comme sur du
 *  papier. Ce qui donne l'impression d'un ciel, c'est que le fond n'est PAS
 *  egal partout — des zones a peine plus claires, si diffuses qu'on ne
 *  saurait pas dire ou elles commencent.
 *
 *  D'OU DEUX REGLAGES, ET PAS UN :
 *    · la base est un NOIR VIOLET PROFOND (#100628 -> #050211), 3e essai.
 *      Deux choses a la fois, et il fallait les deux : plus SOMBRE que
 *      l'entre-deux precedent (#130c28), et surtout plus SATURE. Baisser
 *      seulement la clarte donne un gris de cendre — la teinte s'efface
 *      avant la lumiere. En remontant le bleu par rapport au rouge et au
 *      vert, le fond reste franchement violet tout en etant plus noir.
 *      Trois valeurs tres sombres, mais teintees : c'est la teinte qui evite
 *      le gris de cendre qu'on obtient en delavant du noir ;
 *    · la nebuleuse est a un tiers de sa force d'origine (0,095 a 0,16 au
 *      lieu de 0,26 a 0,42), toujours floutee a 40 px : les zones claires
 *      se remarquent enfin, sans jamais devenir un halo franc.
 *
 *  Restent masquees, comme demande : les etoiles filantes et les anciennes
 *  couches d'etoiles CSS. Seul le reseau anime ponctue le fond.
 *
 *  La production (monde jaune) n'est pas concernee.
 */
html:not([data-monde="prod"]) {
  --ciel-haut: #100628;
  --ciel-milieu: #0b0420;
  --ciel-bas: #050211;
}
html:not([data-monde="prod"]) .ciel-etoiles,
html:not([data-monde="prod"]) .ciel-filante {
  display: none;
}
/*  L'aurore reste eteinte : elle bougeait beaucoup et se voyait trop. C'est
 *  la nebuleuse, immobile ou presque, qui porte les zones plus claires. */
html:not([data-monde="prod"]) .ciel-aurore { display: none; }

/*  LES ZONES CLAIRES SUIVENT L'EVENEMENT — corrige le 19/09/2026.
 *
 *  J'avais ecrit ces quatre couleurs EN DUR (des violets). Consequence : un
 *  evenement pouvait bien poser ses teintes — Noel en vert et rouge,
 *  Halloween en orange — le fond restait violet et les ignorait. Tout le
 *  mecanisme saisonnier etait branche sur `--fond-teinte`, et je venais de
 *  couper le fil.
 *
 *  On repasse donc par les variables. Sans evenement elles valent le violet
 *  de la marque, donc l'aspect ne change pas d'un pixel ; des qu'un evenement
 *  s'allume, le fond prend ses couleurs tout seul, sans une ligne de plus. */
html:not([data-monde="prod"]) .ciel-nebuleuse {
  display: block;
  background-image:
    radial-gradient(40% 32% at 18% 20%, rgba(var(--fond-teinte), 0.16), transparent 72%),
    radial-gradient(34% 28% at 82% 14%, rgba(var(--fond-aurore-2), 0.12), transparent 72%),
    radial-gradient(36% 30% at 74% 78%, rgba(var(--fond-teinte), 0.105), transparent 72%),
    radial-gradient(32% 26% at 26% 84%, rgba(var(--fond-aurore-2), 0.095), transparent 74%);
  filter: blur(40px);
}

/*  LES FILANTES REVIENNENT, MAIS SEULEMENT PENDANT UN EVENEMENT.
 *  Elles avaient ete retirees du decor courant (« on retire les etoiles
 *  filantes ») — et demandees, plus tot, POUR les evenements. Les deux tiennent
 *  ensemble : ciel calme au quotidien, ciel qui se passe quelque chose les
 *  jours de fete. C'est meme mieux ainsi, l'effet ne s'use pas. */
html:not([data-monde="prod"])[data-evenement] .ciel-filante {
  display: block; opacity: 1; filter: brightness(1.6);
}
/*  Le voile revient aussi : il assombrit tres legerement les bords, ce qui
 *  concentre le regard au centre. Sans lui, un fond teinte semble « a plat »
 *  jusqu'au bord de l'ecran. */
html:not([data-monde="prod"]) .ciel-voile { display: block; }
html:not([data-monde="prod"]) .ciel-reseau { display: block; }


/*  LES ONGLETS DU PANNEAU DES DEMANDES — 19/09/2026.
 *  Trois files derriere un seul ecran : l'onglet actif doit se reconnaitre
 *  SANS lire, sinon on clique deux fois sur le meme. D'ou le trait de couleur
 *  sous l'actif, et non un simple changement de nuance. */
.onglets-demandes {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin: 0 0 18px; padding-bottom: 2px;
  border-bottom: 1px solid var(--border);
}
.onglet-demande {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; border-radius: 10px 10px 0 0;
  color: var(--text-muted); font-weight: 600; font-size: .92rem;
  border: 1px solid transparent; border-bottom: none;
  position: relative; top: 1px;
  transition: color .15s ease, background .15s ease;
}
.onglet-demande:hover { color: var(--text); background: var(--bg-softer); text-decoration: none; }
.onglet-demande.actif {
  color: var(--text); background: var(--bg-card);
  border-color: var(--border); border-bottom: 1px solid var(--bg-card);
}
.onglet-demande.actif::after {
  content: ""; position: absolute; left: 12px; right: 12px; top: 0; height: 2px;
  background: var(--accent); border-radius: 2px;
}
.onglet-compte {
  min-width: 20px; padding: 1px 7px; border-radius: 999px;
  background: rgba(var(--accent-rgb), .18); color: var(--accent-lisible, var(--accent));
  font-size: .72rem; font-weight: 700; text-align: center;
}
@media (max-width: 680px) {
  .onglet-demande { padding: 8px 11px; font-size: .84rem; }
}


/*  LE BADGE DE LICENCE QUI EXPIRE — 19/09/2026.
 *  Il reste un badge, pas une alarme : la meme forme que « Payant », avec le
 *  compte a rebours en plus. Sous huit jours il vire a l'orange — c'est le
 *  moment ou l'information devient une ACTION a faire, pas un detail. */
.badge-licence-fin { white-space: nowrap; }
.badge-licence-bientot {
  background: rgba(251, 146, 60, .16);
  color: #fdba74;
  border: 1px solid rgba(251, 146, 60, .38);
}


/* ======================================================================
 *  LES PASTILLES DE FORMULE — LA COULEUR DIT LAQUELLE, LA CLARTE DIT L'ACCES
 * ======================================================================
 *  « Pro en orange, Max en jaune, juste le cercle. Quand elle n'est pas
 *  obtenue tu la fonces, si elle est obtenue tu l'eclaircies. »
 *
 *  DEUX INFORMATIONS DANS UN SEUL OBJET, et c'est ce qui le rend lisible :
 *  la TEINTE repond a « quelle formule ? », la CLARTE repond a « est-ce que je
 *  l'ai ? ». Avant, toutes les pastilles etaient violettes : il fallait LIRE
 *  chacune pour savoir de quel palier on parlait.
 *
 *  Les classes sont posees par js/verrous-formules.js, qui lit le libelle
 *  affiche. Sans JavaScript, la pastille garde son aspect d'origine : elle
 *  reste lisible, elle perd seulement sa couleur.
 */
/*  LA PASTILLE NE S'ETIRE JAMAIS — 19/09/2026.
 *  Valerian : « je comprends pas pourquoi le tour orange du Pro est aussi
 *  large ». Ce n'etait pas un contour : c'etait LA PASTILLE ELLE-MEME, etiree
 *  sur toute la largeur du champ.
 *
 *  POURQUOI. `.form-field` est un conteneur flex en colonne (ligne 726). Dans
 *  un flex en colonne, `align-items` vaut `stretch` par defaut : tout enfant
 *  direct est etire a la largeur du conteneur — y compris un `<span>` pourtant
 *  declare `inline-flex`, car `display` ne protege pas un element de
 *  l'etirement que lui impose son PARENT. La pastille etait donc deja etiree
 *  avant aujourd'hui ; elle etait simplement violet pale, donc personne ne la
 *  voyait. Lui donner une couleur franche a rendu le defaut evident.
 *
 *  Les trois proprietes sont necessaires ensemble, chacune contre un cas :
 *    · `align-self: flex-start` annule l'etirement dans un flex EN COLONNE ;
 *    · `flex: 0 0 auto` empeche la compression dans un flex EN LIGNE ;
 *    · `width: fit-content` couvre le cas d'un parent en grid, ou `stretch`
 *      s'applique aussi mais ou `align-self` ne suffit pas toujours.
 */
.tk-verrou {
  transition: background .15s ease, border-color .15s ease, color .15s ease;
  align-self: flex-start;
  flex: 0 0 auto;
  width: fit-content;
  max-width: 100%;
}

/*  FERMEE = FONCEE. Le fond descend, la bordure se retient, le texte reste
 *  lisible : c'est une information, pas une punition. */
.tk-verrou--ferme.tk-verrou--pro      { background: rgba(249, 115, 22, .13); border-color: rgba(249, 115, 22, .34); color: #c2703a; }
.tk-verrou--ferme.tk-verrou--max      { background: rgba(234, 179, 8, .12);  border-color: rgba(234, 179, 8, .32);  color: #b99512; }
.tk-verrou--ferme.tk-verrou--maxplus  { background: rgba(245, 158, 11, .12); border-color: rgba(245, 158, 11, .32); color: #bd8420; }
.tk-verrou--ferme.tk-verrou--ultra    { background: rgba(56, 189, 248, .12);  border-color: rgba(56, 189, 248, .30);  color: #4a93b8; }
.tk-verrou--ferme.tk-verrou--premium  { background: rgba(var(--accent-rgb), .13); border-color: rgba(var(--accent-rgb), .32); color: #8b7cc4; }
.tk-verrou--ferme.tk-verrou--vie      { background: rgba(52, 211, 153, .12);  border-color: rgba(52, 211, 153, .30);  color: #4a9c7d; }
.tk-verrou--ferme.tk-verrou--standard { background: rgba(148, 163, 184, .12); border-color: rgba(148, 163, 184, .30); color: #8b95a5; }

/*  OBTENUE = ECLAIRCIE. Meme teinte, montee en clarte : on reconnait la
 *  formule du premier coup d'oeil, et on voit qu'on l'a. */
.tk-verrou--ouverte.tk-verrou--pro      { background: rgba(249, 115, 22, .22); border-color: rgba(249, 115, 22, .55); color: #fdba74; }
.tk-verrou--ouverte.tk-verrou--max      { background: rgba(234, 179, 8, .22);  border-color: rgba(234, 179, 8, .55);  color: #fde047; }
.tk-verrou--ouverte.tk-verrou--maxplus  { background: rgba(245, 158, 11, .22); border-color: rgba(245, 158, 11, .55); color: #fcd34d; }
.tk-verrou--ouverte.tk-verrou--ultra    { background: rgba(56, 189, 248, .22);  border-color: rgba(56, 189, 248, .52);  color: #7dd3fc; }
.tk-verrou--ouverte.tk-verrou--premium  { background: rgba(var(--accent-rgb), .22); border-color: rgba(var(--accent-rgb), .52); color: var(--accent-lisible); }
.tk-verrou--ouverte.tk-verrou--vie      { background: rgba(52, 211, 153, .22);  border-color: rgba(52, 211, 153, .52);  color: #6ee7b7; }
.tk-verrou--ouverte.tk-verrou--standard { background: rgba(148, 163, 184, .20); border-color: rgba(148, 163, 184, .48); color: #cbd5e1; }

/*  ON NE PEUT PAS Y TOUCHER. `disabled` est pose par le gabarit et complete
 *  par le script ; ceci n'est que la traduction visuelle — le curseur dit
 *  « non » avant meme le clic. Le vrai refus, lui, est cote serveur. */
.tk-ferme input:disabled,
.tk-ferme select:disabled,
.tk-ferme textarea:disabled,
.tk-ferme button:disabled { cursor: not-allowed; opacity: .55; }
.tk-ferme .switch { cursor: not-allowed; }


/* ======================================================================
 *  LES ZONES VERROUILLEES — CONTOUR = FORMULE, INTERIEUR = GRIS — 19/09/2026
 * ======================================================================
 *  « Entoure toute la zone ou les gens ne peuvent pas utiliser parce qu'ils
 *  n'ont pas cette licence. Quand elle n'est pas obtenue tu fonces
 *  l'interieur, mais pas en couleur : l'interieur en gris. »
 *
 *  DEUX MESSAGES, DEUX MOYENS, ET C'EST TOUT L'INTERET :
 *    · le CONTOUR porte la couleur de la formule — il repond a « il me
 *      faudrait quoi ? » (Pro orange, Max jaune, Ultra cyan…) ;
 *    · l'INTERIEUR est assombri en GRIS, jamais teinte — il repond a « est-ce
 *      que je peux m'en servir ? ».
 *    Teinter l'interieur avec la couleur de la formule, comme je l'avais fait
 *    d'abord, melangeait les deux questions et n'en repondait plus aucune.
 *
 *  `!important` EST ICI NECESSAIRE, ce n'est pas de la paresse : plusieurs
 *  gabarits ecrivent `style="border-left:4px solid #555"` DANS la balise, et
 *  un style en ligne bat toute regle de feuille — sauf `!important`. La teinte
 *  arrive par la variable `--palier`, posee par js/verrous-formules.js.
 *
 *  SANS JAVASCRIPT, rien de tout ceci ne s'applique et les zones gardent leur
 *  aspect d'origine : elles restent lisibles et toujours desactivees, puisque
 *  c'est le GABARIT qui pose `disabled`, et le SERVEUR qui refuse vraiment.
 */
.est-verrouillee {
  border: 1px solid var(--palier, var(--border)) !important;
  border-radius: 10px;
  /*  Le gris de l'interieur : une nappe noire tres legere. On assombrit le
   *  fond EXISTANT au lieu d'imposer une couleur, pour que les cartes claires
   *  comme les cartes sombres reculent de la meme facon. */
  background-color: rgba(0, 0, 0, .28) !important;
  transition: border-color .15s ease, background-color .15s ease;
}
/*  Le texte recule aussi, sans devenir illisible : on doit pouvoir LIRE ce
 *  qu'on n'a pas — c'est meme tout l'interet de le montrer. */
.est-verrouillee .rr-option-nom,
.est-verrouillee h4,
.est-verrouillee label { opacity: .72; }
.est-verrouillee p,
.est-verrouillee .tk-aide,
.est-verrouillee small { opacity: .62; }

/*  La pastille, elle, garde sa couleur pleine : c'est le seul element de la
 *  zone qui doit rester franc, puisque c'est lui qui nomme la formule. */
.est-verrouillee .tk-verrou { opacity: 1; }

/*  LA GRILLE DES CAPACITES suit exactement la meme regle : contour teinte,
 *  interieur gris. Les classes `est-*` viennent du partiel partage
 *  module-entete.ejs, donc de TOUS les modules a la fois. */
.tk-capacite { transition: border-color .15s ease, background-color .15s ease; }
.tk-capacite.tk-non.est-pro      { border-color: rgba(249, 115, 22, .60); }
.tk-capacite.tk-non.est-max      { border-color: rgba(234, 179, 8, .60); }
.tk-capacite.tk-non.est-maxplus  { border-color: rgba(245, 158, 11, .60); }
.tk-capacite.tk-non.est-ultra    { border-color: rgba(34, 211, 238, .60); }
.tk-capacite.tk-non.est-premium  { border-color: rgba(var(--accent-rgb), .60); }
.tk-capacite.tk-non.est-vie      { border-color: rgba(52, 211, 153, .60); }
.tk-capacite.tk-non { background-color: rgba(0, 0, 0, .28); }

/*  OBTENUE : le contour prend la teinte franche, et l'interieur ne recule
 *  plus — rien a assombrir, on y a acces. */
.tk-capacite.tk-oui.est-pro      { border-color: rgba(249, 115, 22, .85); }
.tk-capacite.tk-oui.est-max      { border-color: rgba(234, 179, 8, .85); }
.tk-capacite.tk-oui.est-maxplus  { border-color: rgba(245, 158, 11, .85); }
.tk-capacite.tk-oui.est-ultra    { border-color: rgba(34, 211, 238, .80); }
.tk-capacite.tk-oui.est-premium  { border-color: rgba(var(--accent-rgb), .80); }
.tk-capacite.tk-oui.est-vie      { border-color: rgba(52, 211, 153, .80); }

/*  L'etiquette de droite suit la teinte de sa ligne. */
.tk-capacite.est-pro     .tk-capacite-formule { color: #fdba74; }
.tk-capacite.est-max     .tk-capacite-formule { color: #fde047; }
.tk-capacite.est-maxplus .tk-capacite-formule { color: #fcd34d; }
.tk-capacite.est-ultra   .tk-capacite-formule { color: #67e8f9; }
.tk-capacite.est-premium .tk-capacite-formule { color: var(--accent-lisible); }
.tk-capacite.est-vie     .tk-capacite-formule { color: #6ee7b7; }


/*  UN CHAMP QU'ON NE PEUT PAS REMPLIR DOIT LE DIRE AVANT LE CLIC — 19/09/2026.
 *  `readonly` bloque la frappe mais ne change RIEN a l'apparence : le champ
 *  garde son fond de champ actif et son curseur en I, donc il invite a ecrire
 *  avant de refuser. On lui donne donc l'aspect d'un champ desactive, qu'il
 *  soit ferme par `readonly` (zones de saisie) ou par `disabled` (le reste).
 *
 *  Les fleches des champs numeriques sont retirees : elles restent cliquables
 *  sur un champ `readonly` dans plusieurs navigateurs — le champ refuserait la
 *  frappe tout en acceptant la fleche. */
.est-verrouillee input[readonly], .est-verrouillee textarea[readonly],
.tk-ferme input[readonly], .tk-ferme textarea[readonly],
.rr-option-fermee input[readonly], .rr-option-fermee textarea[readonly] {
  cursor: not-allowed;
  opacity: .55;
  background: rgba(0, 0, 0, .22);
}
.est-verrouillee input[type="number"][readonly],
.tk-ferme input[type="number"][readonly] { -moz-appearance: textfield; appearance: textfield; }
.est-verrouillee input[type="number"][readonly]::-webkit-inner-spin-button,
.est-verrouillee input[type="number"][readonly]::-webkit-outer-spin-button,
.tk-ferme input[type="number"][readonly]::-webkit-inner-spin-button,
.tk-ferme input[type="number"][readonly]::-webkit-outer-spin-button {
  -webkit-appearance: none; appearance: none; margin: 0;
}
.est-verrouillee input:disabled, .est-verrouillee select:disabled,
.est-verrouillee textarea:disabled, .est-verrouillee button:disabled {
  cursor: not-allowed; opacity: .55;
}


/*  PROPOSITION AUTOMATIQUE — 20/09/2026.
 *  Volontairement discrete et grise : ce n'est pas une alerte, c'est une
 *  provenance. Elle doit se remarquer en relisant la ligne, pas sauter aux
 *  yeux avant le titre — le titre reste l'information principale. */
.actu-auto {
  display: inline-block; font-size: .72rem; padding: 2px 8px; border-radius: 999px;
  background: rgba(148, 163, 184, .14); color: #a9b2c3;
  border: 1px solid rgba(148, 163, 184, .30); white-space: nowrap;
  letter-spacing: .02em;
}


/* ======================================================================
 *  LA CARTE DES MAINTENANCES — 20/09/2026
 * ======================================================================
 *  Une carte comme les autres, à sa place dans la grille. Premier essai :
 *  un bandeau pleine largeur au-dessus — il écrasait toute la section pour
 *  une information qui n'est pas plus importante que les autres. Ici seule
 *  la TEINTE distingue : l'orange dit « service interrompu », et le rouge
 *  reste réservé à l'urgent, que porte déjà la pastille d'importance.
 */
.actu-carte.est-maintenance {
  border-color: rgba(251, 146, 60, .34);
  background: rgba(251, 146, 60, .05);
}
.actu-carte.est-maintenance:hover { border-color: rgba(251, 146, 60, .55); }
.actu-maint-compte {
  font-size: .78rem; font-weight: 600; color: #fdba74; letter-spacing: .01em;
}
.actu-maint-voir { color: #fdba74; font-weight: 600; white-space: nowrap; }

/*  La page dédiée. */
.maint-resume { margin: 0 0 18px; color: var(--text-muted); }
.maint-rien {
  padding: 18px; border-radius: 12px;
  border: 1px solid rgba(52, 211, 153, .32);
  background: rgba(52, 211, 153, .07); color: #6ee7b7;
}
.maint-retour { margin-top: 24px; }


/*  MODULE RÉTABLI — 20/09/2026.
 *  Le vert répond à la question qu'on se pose en arrivant : « est-ce que ça
 *  remarche ? ». Même carte, même place, seule la teinte change d'état. */
.actu-carte.est-retabli {
  border-color: rgba(52, 211, 153, .32);
  background: rgba(52, 211, 153, .05);
}
.actu-carte.est-retabli:hover { border-color: rgba(52, 211, 153, .55); }
.actu-carte.est-retabli .actu-maint-compte,
.actu-carte.est-retabli .actu-maint-voir { color: #6ee7b7; }
.maint-soustitre { margin: 26px 0 12px; font-size: 1.05rem; }


/* ======================================================================
 *  TABLEAU DE BORD DES TICKETS — 20/09/2026
 * ======================================================================
 *  Des tableaux denses, lisibles en diagonale. Les chiffres sont alignés en
 *  colonnes (`tabular-nums`) : sans ça, un 1 et un 7 ne prennent pas la même
 *  largeur et la colonne ondule, ce qui empêche justement de comparer d'un
 *  coup d'oeil — la seule chose qu'on demande à ce tableau.
 */
.tkb-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.tkb-carte { margin: 0; }
.tkb-large { grid-column: 1 / -1; }
.tkb-scroll { overflow-x: auto; }
.tkb-table { width: 100%; border-collapse: collapse; font-size: .92rem; }
.tkb-table th, .tkb-table td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--border); }
.tkb-table thead th { color: var(--text-muted); font-weight: 600; font-size: .78rem;
  text-transform: uppercase; letter-spacing: .04em; }
.tkb-table tbody th { font-weight: 600; }
.tkb-table td { font-variant-numeric: tabular-nums; }
.tkb-nombre {
  display: inline-block; min-width: 2.2em; padding: 2px 8px; border-radius: 6px;
  font-weight: 700; text-decoration: none;
  background: rgba(var(--accent-rgb), .16); color: var(--accent-lisible);
  border: 1px solid rgba(var(--accent-rgb), .30);
}
.tkb-nombre:hover { background: rgba(var(--accent-rgb), .28); border-color: rgba(var(--accent-rgb), .55); }
/*  Le zéro n'est pas un lien : il n'y a rien à ouvrir. Il reste visible mais
 *  recule, pour que l'oeil aille droit aux cases qui portent quelque chose. */
.tkb-zero { display: inline-block; min-width: 2.2em; padding: 2px 8px; color: var(--text-muted); opacity: .55; }
.tkb-detail { border-color: rgba(var(--accent-rgb), .45); }
.tkb-detail-tete { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tkb-type { display: inline-block; padding: 1px 7px; border-radius: 5px; font-size: .74rem; font-weight: 700; }
.tkb-type.est-inc { background: rgba(239, 68, 68, .16); color: #fca5a5; border: 1px solid rgba(239, 68, 68, .35); }
.tkb-type.est-ritm { background: rgba(56, 189, 248, .16); color: #7dd3fc; border: 1px solid rgba(56, 189, 248, .35); }

.tkb-vers-liste { margin: 16px 0 4px; }

/*  Les actions du panneau : trois formulaires sur une ligne, compacts.
 *  `flex-wrap` parce qu'à trois colonnes de sélecteurs dans un tableau déjà
 *  large, la ligne doit pouvoir se replier plutôt que pousser un défilement
 *  horizontal sur toute la page. */
.tkb-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tkb-actions form { display: flex; align-items: center; gap: 3px; margin: 0; }
.tkb-actions select { padding: 3px 6px; font-size: .82rem; max-width: 130px; }
.tkb-actions .btn-sm { padding: 3px 8px; font-size: .78rem; }

/*  Plugins sur mesure : le code se lit, donc il lui faut une chasse fixe et
 *  de la place. `white-space: pre` plutôt que `pre-wrap` : une ligne de code
 *  repliée au milieu se relit mal, mieux vaut défiler. */
.tkb-code, .tkb-code-lecture {
  width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .82rem; line-height: 1.5; tab-size: 2;
  background: var(--bg-softer); color: var(--text);
  border: 1px solid var(--border-champ); border-radius: var(--radius-sm);
  padding: 12px; white-space: pre; overflow-x: auto;
}
.tkb-code { min-height: 420px; resize: vertical; }
.tkb-spec { white-space: pre-wrap; font-size: .88rem; color: var(--text-muted); margin: 0; }
.tkb-gros { font-size: 2rem; font-weight: 700; margin: 4px 0 0; font-variant-numeric: tabular-nums; }
.tkb-etat { display: inline-block; padding: 1px 7px; border-radius: 5px; font-size: .74rem; font-weight: 700; }
.tkb-etat.est-ok { background: rgba(52,211,153,.16); color: #6ee7b7; border: 1px solid rgba(52,211,153,.35); }
.tkb-etat.est-muet { background: rgba(239,68,68,.16); color: #fca5a5; border: 1px solid rgba(239,68,68,.35); }
.tkb-etat.est-squelette { background: rgba(251,146,60,.16); color: #fdba74; border: 1px solid rgba(251,146,60,.35); }
.tkb-etat.est-vide { background: rgba(148,163,184,.14); color: #a9b2c3; border: 1px solid rgba(148,163,184,.30); }

/*  Les demandes d'ajout, côté client : une liste sobre. Chaque entrée porte
 *  son état en premier — c'est la seule chose qu'on vient vérifier. */
.dem-liste { list-style: none; padding: 0; margin: 0 0 18px; display: flex; flex-direction: column; gap: 10px; }
.dem-liste li { padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; }
.dem-liste li > div { margin-top: 6px; color: var(--text-muted); font-size: .92rem; white-space: pre-wrap; }

/*  Une demande d'ajout : le texte est l'essentiel, les actions viennent après.
 *  `align-items: center` sur la tête pour que la pastille d'état, le nom du
 *  plugin et la date tiennent sur une ligne lisible. */
.dem-carte { padding: 14px; border: 1px solid var(--border); border-radius: 12px; margin-bottom: 14px; }
.dem-tete { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.dem-texte { width: 100%; font-family: inherit; font-size: .92rem; line-height: 1.5; margin-bottom: 8px; }
.dem-texte-lecture { white-space: pre-wrap; color: var(--text-muted); margin: 0 0 8px; }
.dem-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.dem-actions form { margin: 0; }
.dem-notes { margin: 8px 0; }
.dem-notes summary { cursor: pointer; color: var(--text-muted); font-size: .85rem; }
.dem-notes pre { white-space: pre-wrap; font-size: .82rem; color: var(--text-muted); margin: 6px 0 0; }

/*  Les identifiants Discord : lisibles chiffre à chiffre, donc en chasse fixe
 *  et un peu plus petits — ils se copient, ils ne se lisent pas. */
.tkb-id { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .76rem; }

/* ======================================================================
 *  LE BANDEAU DE L'OFFRE DE LANCEMENT — 21/09/2026
 * ======================================================================
 *  Vert, et pas violet : le violet est la couleur du site, il se fond dans
 *  la page. On veut ici qu'un œil qui descend s'arrête une seconde. Le vert
 *  dit « gratuit » sans avoir à crier, là où l'orange dirait « attention »
 *  et le rouge « problème ».
 *
 *  Pleine largeur, contrairement aux cartes d'actualité : ce n'est pas une
 *  information parmi d'autres, c'est la réponse à la question qu'on se pose
 *  juste après avoir lu ce que fait le bot — combien ça coûte.
 */
.offre-bandeau {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  max-width: 1200px; margin: 0 auto 26px; padding: 16px 22px;
  border: 1px solid rgba(52, 211, 153, .38);
  border-radius: 14px;
  background: linear-gradient(100deg, rgba(52, 211, 153, .12), rgba(52, 211, 153, .04));
  color: inherit; text-decoration: none;
  transition: border-color .15s ease, background .15s ease;
}
.offre-bandeau:hover { border-color: rgba(52, 211, 153, .65); background: linear-gradient(100deg, rgba(52,211,153,.18), rgba(52,211,153,.07)); }
.offre-pastille {
  flex: 0 0 auto; padding: 5px 12px; border-radius: 999px;
  background: rgba(52, 211, 153, .22); color: #6ee7b7;
  border: 1px solid rgba(52, 211, 153, .5);
  font-size: .78rem; font-weight: 800; letter-spacing: .06em;
}
.offre-corps { flex: 1 1 320px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.offre-titre { font-size: 1.06rem; font-weight: 700; }
.offre-texte { color: var(--text-muted); font-size: .92rem; line-height: 1.45; }
.offre-fin { flex: 0 0 auto; color: #6ee7b7; font-weight: 700; white-space: nowrap; }
.offre-fin strong { font-size: 1.3rem; font-variant-numeric: tabular-nums; }

/*  Les deux boutons de l'offre : l'action et sa conséquence côte à côte, pour
 *  qu'on lise ce qui va se passer avant de cliquer, pas après. */
.offre-boutons { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.offre-boutons form { margin: 0; }


/* ======================================================================
 *  LE CALENDRIER DE L'ÉQUIPE — 24/09/2026
 * ======================================================================
 *  Une semaine en sept colonnes, chacune listant ce qui s'y passe. Pas de
 *  grille horaire au pixel : une absence dure quatre jours, une réunion une
 *  heure, une échéance une minute — les placer à l'échelle ferait des traits
 *  invisibles à côté de blocs énormes. La liste chronologique se lit mieux et
 *  ne ment pas sur les durées.
 *
 *  `--c` porte la couleur de l'entrée : celle du TYPE en vue personnelle (on
 *  sait déjà de qui il s'agit), celle de la PERSONNE en vue globale (c'est
 *  justement ce qu'on cherche).
 */
.cal-barre { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px 0 12px; }
.cal-legende { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.cal-pastille { font-size: .76rem; color: var(--text-muted); border-left: 3px solid var(--c); padding-left: 6px; }

.cal-semaine { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
@media (max-width: 1100px) { .cal-semaine { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .cal-semaine { grid-template-columns: 1fr; } }

/*  DES COLONNES QUI RESPIRENT — 25/09/2026.
 *  À 130 px, la semaine tenait dans une bande de la hauteur d'un bouton, en
 *  haut d'une page vide : on ne voyait pas un calendrier, on voyait une frise.
 *  `min-height` en `vh` pour occuper une vraie part d'un grand écran sans
 *  devenir absurde sur un portable ; `max-height` + défilement interne pour
 *  qu'une journée chargée s'étende DANS sa colonne au lieu d'étirer les six
 *  autres. */
.cal-jour {
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px; min-height: min(58vh, 520px);
  max-height: 72vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 8px;
}
@media (max-width: 1100px) { .cal-jour { min-height: 220px; max-height: none; } }
.cal-jour.est-aujourdhui { border-color: rgba(var(--accent-rgb), .55); background: rgba(var(--accent-rgb), .06); }
.cal-jour > header { display: flex; justify-content: space-between; align-items: baseline; }
.cal-jour-nom { font-weight: 700; font-size: .84rem; }
.cal-jour-date { color: var(--text-muted); font-size: .76rem; font-variant-numeric: tabular-nums; }
.cal-vide { color: var(--text-muted); font-size: .76rem; margin: 0; opacity: .5; }

.cal-entree {
  display: flex; flex-direction: column; gap: 2px;
  border-left: 3px solid var(--c); border-radius: 6px;
  background: color-mix(in srgb, var(--c) 12%, transparent);
  padding: 5px 7px; font-size: .78rem;
}
/*  L'ÉTIQUETTE ET L'HEURE SE MARCHAIENT DESSUS — 25/09/2026.
 *  Rendu observé : « RÉUNION20h30 », collés, l'heure sortant de la carte.
 *  `space-between` ne réserve aucun espace et n'empêche rien de dépasser :
 *  sans `gap`, sans `min-width: 0` et sans passage à la ligne, un texte long
 *  pousse le reste dehors.
 *  L'HEURE NE SE COUPE JAMAIS (`flex: 0 0 auto`) : c'est l'information la plus
 *  utile de la ligne. C'est l'étiquette qui cède et s'abrège. */
.cal-entree-haut {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; flex-wrap: wrap; min-width: 0;
}
.cal-entree-haut > .cal-entree-type { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cal-heure {
  color: var(--text-muted); font-variant-numeric: tabular-nums;
  font-size: .72rem; flex: 0 0 auto; margin-left: auto;
}
.cal-entree-titre { line-height: 1.3; word-break: break-word; }
.cal-entree-qui { color: var(--text-muted); font-size: .72rem; }
.cal-garde select { width: 100%; font-size: .74rem; padding: 2px 4px; }
.cal-puce { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--c); margin-right: 8px; }

.cal-taches { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-direction: column; gap: 8px; }
.cal-taches li { padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; }
.cal-taches li.est-faite .cal-tache-titre { text-decoration: line-through; opacity: .55; }
.cal-coche { background: none; border: 0; cursor: pointer; font-size: 1rem; padding: 0 6px 0 0; }
.cal-coche:disabled { cursor: not-allowed; opacity: .4; }
.cal-tache-detail { font-size: .84rem; margin-top: 4px; }

.cal-notes { list-style: none; padding: 0; margin: 14px 0 0; display: flex; flex-direction: column; gap: 10px; }
.cal-notes li { padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; }
.cal-note-tete { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.cal-note-texte { white-space: pre-wrap; font-size: .9rem; }

.cal-dispo-ligne { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.cal-dispo-ligne .switch { min-width: 150px; }
.cal-dispo-ligne input[type="time"] { padding: 4px 8px; }


/*  LA GARDE DU JOUR — 24/09/2026.
 *  Un bandeau, pas une carte : il annonce qui prend les demandes sans
 *  traitant, il ne rivalise pas avec les chiffres du tableau. Quand personne
 *  n'est de garde, il vire a l'orange — c'est une information qui appelle une
 *  action, pas un etat normal. */
.tk-garde {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin: 0 0 16px; padding: 10px 14px; border-radius: 10px;
  background: rgba(61, 220, 151, .08);
  border: 1px solid rgba(61, 220, 151, .28);
}
.tk-garde--vide {
  background: rgba(251, 146, 60, .08);
  border-color: rgba(251, 146, 60, .30);
}
.tk-garde-pastille { font-weight: 600; white-space: nowrap; }
.tk-garde-lien { margin-left: auto; white-space: nowrap; }


/*  LES RENDEZ-VOUS DU CLIENT — 24/09/2026.
 *  Une ligne par rendez-vous, l'etat a droite. Celui qui attend une reponse
 *  du client porte un liseré orange : c'est le seul qui lui demande une
 *  action, et il doit se distinguer sans crier. */
.rdv-ligne {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  padding: 10px 0; border-top: 1px solid var(--border);
}
.rdv-ligne:first-of-type { border-top: 0; }
.rdv-ligne--attente {
  border-left: 3px solid rgba(251, 146, 60, .55);
  padding-left: 10px; margin-left: -10px;
}
.rdv-etat { white-space: nowrap; }


/*  AVEC QUI, LE TICKET, ET LE RÔLE — 25/09/2026.
 *  Trois informations que le calendrier avait mais n'affichait pas : on lisait
 *  « Appel — ticket » et il fallait rouvrir Discord pour savoir de qui il
 *  s'agissait. */
.cal-entree-avec { font-size: .74rem; color: var(--text); opacity: .9; word-break: break-word; }
.cal-entree-lien {
  font-size: .72rem; align-self: flex-start;
  padding: 1px 6px; border-radius: 999px; text-decoration: none;
  background: rgba(var(--accent-rgb), .14);
  border: 1px solid rgba(var(--accent-rgb), .3);
}
.cal-entree-lien:hover { background: rgba(var(--accent-rgb), .24); }
/*  Le rôle sous le nom, pas à côté : la ligne reste lisible quand le nom est
 *  long, et la hiérarchie visuelle dit lequel des deux compte. */
.cal-role {
  display: block; font-size: .7rem; color: var(--text-muted);
  font-weight: 400; margin-left: 18px; text-transform: uppercase; letter-spacing: .04em;
}

/*  L'ÉTIQUETTE DE TYPE — 25/09/2026.
 *  « Réunion », « Appel », « Tâche » : le mot, pas seulement l'emoji. Une
 *  réunion s'affichait « 20h30 · bot musique » sans que rien ne dise que c'en
 *  était une, et l'emoji seul demande de connaître la légende du haut de page. */
.cal-entree-type {
  font-size: .66rem; letter-spacing: .05em; text-transform: uppercase;
  font-weight: 700; opacity: .85; white-space: nowrap;
}

/*  CHOISIR PLUSIEURS RÔLES SANS CTRL+CLIC — 25/09/2026.
 *  Une `<select multiple>` cache son mode d'emploi : un clic simple REMPLACE
 *  la sélection au lieu de l'étendre, donc on efface les rôles déjà choisis en
 *  croyant en ajouter un. Des cases disent ce qu'elles font.
 *  La liste défile au-delà de six rôles : certains serveurs en ont trente. */
.cases-roles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 6px 14px; max-height: 240px; overflow-y: auto;
  padding: 10px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-soft);
}
.case-role {
  display: flex; align-items: center; gap: 8px;
  font-size: .86rem; cursor: pointer; padding: 3px 4px; border-radius: 6px;
}
.case-role:hover { background: rgba(var(--accent-rgb), .08); }
.case-role input { margin: 0; cursor: pointer; flex: 0 0 auto; }
.case-role span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }


/*  LE CHOIX D'ADRESSE — 25/09/2026.
 *  Deux cartes plutot qu'une liste : le choix se juge sur l'EXEMPLE, pas sur
 *  l'etiquette. « Tutoiement » ne dit rien a qui hesite ; « Tu peux fermer ton
 *  ticket » le dit tout de suite. */
.pol-choix { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.pol-carte {
  display: flex; flex-direction: column; gap: 6px;
  padding: 14px 16px; border-radius: 12px; cursor: pointer;
  border: 1px solid var(--border); background: var(--bg-soft);
  transition: border-color .15s ease, background .15s ease;
}
.pol-carte:hover { border-color: rgba(var(--accent-rgb), .45); }
.pol-carte.est-choisie {
  border-color: rgba(var(--accent-rgb), .75);
  background: rgba(var(--accent-rgb), .10);
}
.pol-carte input { margin: 0 0 4px; }
.pol-titre { font-weight: 700; }
.pol-exemple { color: var(--text-muted); font-size: .88rem; font-style: italic; }

/* ======================================================================
 *  PÉRIODE ET POPUP D'UNE ANNONCE — 26/09/2026
 * ======================================================================
 *  « De telle date à telle date », « pendant x temps », « fin à x h », et
 *  une popup plein écran qui reste tant que l'annonce est en cours.
 */
.actu-periode, .fil-periode, .popup-actu-periode { font-size: .78rem; color: var(--text-muted); white-space: nowrap; }
.fil-periode { margin: 4px 0 8px; }
.fil-image { display: block; max-width: 520px; margin: 8px 0 10px; border-radius: 10px; overflow: hidden; border: 1px solid var(--border); }
.fil-image img { display: block; width: 100%; height: auto; }

/*  Le panneau : les champs d'un mode sur une ligne, et la vignette. */
.periode-bloc { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.periode-bloc label { margin: 0; }
.periode-bloc input[type="number"] { width: 90px; }
.periode-bloc input, .periode-bloc select { flex: 0 1 auto; }
.actu-etiquette {
  font-size: .68rem; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px; border: 1px solid var(--border); color: var(--text-muted);
}
.actu-etiquette.est-popup { color: #fdba74; border-color: rgba(251,146,60,.35); background: rgba(251,146,60,.12); }
.actu-vignette { display: block; max-width: 260px; max-height: 150px; border-radius: 8px; margin: 6px 0; border: 1px solid var(--border); }

/*  LA POPUP. Fixe, au-dessus de tout (l'assistance est à 900), le fond
 *  assombri ferme au clic. La boîte se limite à la hauteur de l'écran et
 *  défile à l'intérieur : une longue annonce sur un téléphone ne doit pas
 *  cacher son bouton de fermeture sous le bord. */
.popup-actu { position: fixed; inset: 0; z-index: 1200; display: flex; align-items: center; justify-content: center; padding: 16px; }
.popup-actu[hidden] { display: none; }
.popup-actu-fond { position: absolute; inset: 0; background: rgba(6, 8, 20, .78); backdrop-filter: blur(3px); }
.popup-actu-boite {
  position: relative; width: min(680px, 100%); max-height: calc(100vh - 32px); overflow: auto;
  background: var(--bg-card); color: var(--text);
  border: 1px solid var(--border); border-radius: 16px; box-shadow: 0 30px 80px rgba(0,0,0,.55);
  animation: popup-actu-entree .28s ease-out;
}
@keyframes popup-actu-entree { from { opacity: 0; transform: translateY(14px) scale(.98); } to { opacity: 1; transform: none; } }
.popup-actu-fermer {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--border);
  background: rgba(6, 8, 20, .6); color: #fff; font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.popup-actu-fermer:hover, .popup-actu-fermer:focus-visible { background: rgba(255,255,255,.14); outline: 2px solid var(--accent); }
.popup-actu-image { max-height: 55vh; overflow: hidden; background: var(--bg-softer); }
.popup-actu-image img { display: block; width: 100%; height: auto; max-height: 55vh; object-fit: contain; }
.popup-actu-corps { padding: 22px 24px 24px; display: flex; flex-direction: column; gap: 12px; }
.popup-actu-haut { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.popup-actu-titre { margin: 0; font-size: 1.5rem; line-height: 1.25; text-wrap: balance; }
.popup-actu-texte { margin: 0; color: var(--text-muted); line-height: 1.55; white-space: pre-line; }
.popup-actu-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
html.popup-actu-ouverte { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .popup-actu-boite { animation: none; } }
@media (max-width: 520px) {
  .popup-actu { padding: 10px; }
  .popup-actu-corps { padding: 18px 16px 18px; }
  .popup-actu-titre { font-size: 1.25rem; }
}

/* ======================================================================
 *  LES CARTES D'ACTUS S'ANIMENT, ELLES NE S'OUVRENT PLUS — 26/09/2026
 * ======================================================================
 *  Le survol soulève la carte, l'éclaire et fait respirer l'image ; à
 *  l'arrivée, les cartes montent l'une après l'autre. Rien ne se clique,
 *  sauf le bouton « En savoir plus » quand le staff a donné un lien.
 */
.actu-carte { cursor: default; animation: actu-carte-arrivee .55s ease-out both; }
.actu-carte:nth-child(2) { animation-delay: .07s; }
.actu-carte:nth-child(3) { animation-delay: .14s; }
.actu-carte:nth-child(4) { animation-delay: .21s; }
.actu-carte:nth-child(5) { animation-delay: .28s; }
.actu-carte:nth-child(6) { animation-delay: .35s; }
@keyframes actu-carte-arrivee { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
.actu-carte:hover {
  transform: translateY(-6px) scale(1.012);
  border-color: rgba(var(--accent-rgb), .7);
  box-shadow: 0 22px 44px -16px rgba(0,0,0,.8), 0 0 0 1px rgba(var(--accent-rgb), .25), 0 0 32px -6px rgba(var(--accent-rgb), .45);
}
.actu-carte:hover .actu-image img { transform: scale(1.07); }
.actu-carte .actu-titre { transition: color .18s ease; }
.actu-carte:hover .actu-titre { color: var(--accent-lisible, var(--accent)); }
.actu-lire {
  margin-left: auto; font-size: .78rem; font-weight: 600; color: var(--accent-lisible, var(--accent));
  padding: 3px 9px; border-radius: 999px; border: 1px solid rgba(var(--accent-rgb), .35);
  transition: background .15s ease, transform .15s ease;
}
.actu-lire:hover { background: rgba(var(--accent-rgb), .14); transform: translateX(2px); text-decoration: none; }
span.bandeau-actu { cursor: default; }
@media (prefers-reduced-motion: reduce) {
  .actu-carte { animation: none; }
  .actu-carte:hover { transform: none; }
  .actu-carte:hover .actu-image img { transform: none; }
}

/* ======================================================================
 *  CALENDRIER : FILTRE ET ENTRÉES PRIVÉES — 26/09/2026
 * ====================================================================== */
.cal-filtre { display: inline-flex; align-items: center; gap: 6px; margin-left: 8px; }
.cal-filtre select { max-width: 260px; padding: 4px 8px; font-size: .82rem; }
.cal-prive { font-size: .8em; opacity: .85; }
.cal-entree.est-privee { border-style: dashed; }

/* ======================================================================
 *  LA BARRE LATÉRALE PASSE SOUS LE SÉLECTEUR DE LANGUE — 27/09/2026
 * ======================================================================
 *  Le choix de langue est fixé en bas à gauche de l'écran (`.lp` dans la
 *  barre du haut). Sur le tableau de bord, il recouvrait les dernières
 *  entrées du menu de gauche : « quand on descend tout en bas, ça bloque, on
 *  ne voit pas ce qui est marqué ». On réserve la place en bas du menu, pour
 *  que la dernière entrée puisse toujours remonter au-dessus du sélecteur.
 */
.sidebar { padding-bottom: 96px; scroll-padding-bottom: 96px; }
.sidebar > *:last-child { margin-bottom: 8px; }

/* ======================================================================
 *  CALENDRIER : RÉSUMÉ, GARDE REPLIÉE, COLONNES MOINS VIDES — 27/09/2026
 * ======================================================================
 *  « Qu'est-ce qu'on peut faire pour l'améliorer ? » Trois choses vues à
 *  l'écran : sept menus déroulants de garde alignés (du bruit), le mot « De
 *  garde » écrit deux fois par carte, et des colonnes très hautes pour peu de
 *  contenu qui repoussent la charge de la semaine hors de l'écran. Et une
 *  chose qui manquait : la réponse immédiate à « qui est de garde
 *  aujourd'hui, qui manque cette semaine ».
 */
.cal-resume {
  display: flex; flex-wrap: wrap; gap: 6px 22px; align-items: center;
  padding: 10px 14px; margin: 0 0 12px; border-radius: 12px;
  background: rgba(var(--accent-rgb), .07); border: 1px solid rgba(var(--accent-rgb), .22);
  font-size: .86rem;
}
.cal-resume-bloc strong { font-weight: 700; margin-right: 4px; }
.cal-resume-alerte { color: #fdba74; font-weight: 600; }
.cal-garde-details { margin: 6px 0 8px; }
.cal-garde-details > summary {
  list-style: none; cursor: pointer; font-size: .78rem; font-weight: 600;
  padding: 4px 8px; border-radius: 8px; background: rgba(255,255,255,.05); border: 1px solid var(--border);
  display: flex; align-items: center; gap: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-garde-details > summary::-webkit-details-marker, .cal-garde-details > summary::marker { display: none; content: ''; }
.cal-garde-details.est-vide > summary { color: var(--text-muted); font-weight: 500; }
.cal-garde-details > summary:hover { border-color: rgba(var(--accent-rgb), .5); }
.cal-garde-crayon { margin-left: auto; opacity: .55; font-size: .8rem; }
.cal-garde-details[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.cal-garde-details > .cal-garde { display: flex; gap: 4px; align-items: center; padding: 6px; border: 1px solid var(--border); border-top: 0; border-radius: 0 0 8px 8px; background: rgba(0,0,0,.15); }
.cal-garde-details .cal-garde select { flex: 1 1 auto; min-width: 0; }
/*  Des colonnes moins hautes : 58 vh laissait un grand vide sous deux cartes
 *  et repoussait « Charge de la semaine » sous l'écran. Une journée chargée
 *  s'étend jusqu'à 72 vh puis défile, comme avant. */
.cal-jour { min-height: min(38vh, 340px); }

/* ---- Bandeaux de session (plafond 8 h) — 10/10/2026 ---- */
.session-bandeau {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 16px; font-size: 14px; text-align: center;
  border-bottom: 1px solid var(--border);
}
.session-bandeau-bientot { background: rgba(255, 181, 71, 0.14); color: var(--warning); }
.session-bandeau-expiree { background: rgba(255, 91, 122, 0.12); color: var(--danger); }

/* --- LE HALO N'EST PLUS COUPÉ — 11/10/2026 ----------------------------------
   « Le cercle autour du logo est coupé. » `.hero` porte `overflow: hidden`
   (hérité du hero d'origine et de ses orbes) et `.hero.hero-duo` est limité à
   1200 px, centré. Dès que l'écran est plus large (ou dézoomé), le bord
   gauche du hero passe à ~75 px du logo, et le halo — plus large que sa
   colonne — y est tranché net : un trait vertical, visible à l'œil. Le hero
   duo n'a rien à découper (plus d'orbes, un logo qui respire de 16 px) :
   on le laisse déborder, et on dimensionne le halo sur sa colonne. */
.hero.hero-duo { overflow: visible; }
.hero.hero-duo .hero-halo { width: 100%; max-width: 420px; }

/* ---------------------------------------------------------------------
   Tableau de bord — quel bot est sur chaque serveur (12/10/2026).
   Violet = Vaeron (monde normal), doré = Vaeron Prod, vert = bot perso,
   gris = aucun. La présence vient de la table guild_presence que chaque
   bot tient lui-même, dans les deux mondes (voir bots-des-serveurs.js). */
.server-bots { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; }
.server-bots .tag { font-weight: 500; }
.tag-bot-normal { background: rgba(var(--accent-rgb), 0.15); color: var(--accent-lisible); }
.tag-bot-prod { background: rgba(var(--premium-rgb), 0.18); color: var(--premium); }
.tag-bot-perso { background: rgba(72, 187, 120, 0.16); color: #5fd38d; }
.tag-bot-aucun { background: rgba(255, 255, 255, 0.06); color: var(--text-muted); }

/* ==========================================================================
   MENU LATÉRAL SUR UNE LIGNE — 22/10/2026.
   « Mets-moi ça sur une ligne » : « 🎮 Mini-jeux & casino » se cassait en deux
   (et « ⭐ Mur des meilleurs messages » en trois). Mesuré au navigateur : avec
   260 px de colonne, 24 px de marge de chaque côté, 12 px dans le lien et les
   27 px de la bulle « ? » d’Auron, il restait 166 px pour le texte ; le plus
   long libellé en fait 214 à 15 px (228 à 16 px).
   Trois gestes, prod et public (même code) :
     1. colonne 260 → 300 px (250 px sous 1024 px), marges latérales 24 → 16 px ;
     2. police des liens 16 → 15,2 px (0.95rem) ;
     3. filet de sécurité : le texte ne se replie plus jamais, il se coupe
        avec « … » si une police plus large que prévu (DejaVu) dépasse encore.
   Ce bloc est EN FIN de fichier exprès : style.css contient deux fois le bloc
   « DASHBOARD LAYOUT » (l. 637 et l. 5897), et c’est le dernier qui gagne.
   ========================================================================== */
.dashboard-layout { grid-template-columns: 300px 1fr; }
.sidebar { padding-left: 16px; padding-right: 16px; }
.sidebar-nav a { font-size: 0.95rem; }
.sidebar-nav a > span:first-child {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
@media (max-width: 1024px) {
  .dashboard-layout { grid-template-columns: 250px 1fr; }
}
@media (max-width: 900px) {
  .dashboard-layout { grid-template-columns: 1fr; }
}

/* ==========================================================================
   CITROUILLES PLUS VISIBLES — 23/10/2026 (prod, puis « public aussi » dix minutes après).
   « Peux-tu faire en sorte qu'on voie mieux les citrouilles, que en prod. »
   En prod, `--fete-force` vaut 0,11 (et 0,085 pour la seconde couche) parce
   que l'ambre pèse trois fois le violet ; l'animation « respire » y ajoute un
   `filter: opacity(0.55 → 1)`. Résultat mesuré sur sa capture : des citrouilles
   de 10 à 15 px à 6-11 % d'opacité, invisibles sur le fond sombre.
   Trois gestes, dès que l'effet citrouilles est allumé, dans les DEUX mondes (les
   autres fêtes gardent leurs réglages mesurés ; en public la base était 0,16) :
     1. opacité des deux couches ×6 (0,70 / 0,55 — la respiration la fait
        osciller entre 0,39 et 0,70) ;
     2. tuiles 240 → 400 px et 200 → 330 px : les citrouilles font 1,65 fois
        leur taille (15 → 25 px pour la plus grosse) ;
     3. rien d'autre : même dessin, même mouvement, toujours sous le contenu.
   En FIN de fichier exprès (deux blocs « DASHBOARD LAYOUT », le dernier gagne).
   ========================================================================== */
html[data-effet="citrouilles"] {
  --fete-force: 0.70;
  --fete-force-2: 0.55;
}
html[data-effet="citrouilles"] .ciel-fete-a { background-size: 400px 400px; }
html[data-effet="citrouilles"] .ciel-fete-b { background-size: 330px 330px; }

/* ==========================================================================
   HALLOWEEN « STYLÉ » — 23/10/2026, dix minutes après les citrouilles visibles.
   « Pour Halloween on peut mettre des fantômes, sorcière et tout ? fais un truc
   stylé. » L'effet garde sa clé `citrouilles` (rien à changer dans le panneau
   ni dans les événements enregistrés) ; seul le dessin des deux couches change,
   et deux pseudo-éléments s'y ajoutent — ils n'existent donc que pendant
   l'événement, comme les couches elles-mêmes :
     • couche A (tuile 400 px, respire sur place) : citrouilles SCULPTÉES (yeux et
       bouche qui luisent) + chauves-souris violettes ;
     • couche B (tuile 330 px) : fantômes qui MONTENT lentement en ondulant
       (la tuile fait 330 px, la montée fait 330 px : la boucle est invisible) ;
     • A::before : la lune, croissant dans un halo, en haut à droite ;
     • A::after : la sorcière sur son balai qui TRAVERSE l'écran de gauche à
       droite en 33 s puis attend 13 s hors champ (position animée en `left`,
       un seul élément : ça ne coûte rien) ; elle tangue en même temps.
   Tout reste dans .ciel (sous le contenu, transparent aux clics), et le réglage
   « moins d'animations » du système fige tout (règle *::before/*::after plus haut).
   Généré par ~/vaeron-citrouilles/gen-halloween.js — modifier LÀ, pas ici.
   ========================================================================== */
html[data-effet="citrouilles"] .ciel-fete-a {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400' width='400' height='400'%3E%3Cdefs%3E %3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E %3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E %3CradialGradient id='gf' cx='.4' cy='.3' r='.8'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='1' stop-color='%23c9d2ff'/%3E%3C/radialGradient%3E %3CradialGradient id='gh' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='0' stop-color='%23dfe6ff' stop-opacity='.45'/%3E%3Cstop offset='1' stop-color='%23dfe6ff' stop-opacity='0'/%3E%3C/radialGradient%3E %3CradialGradient id='lu' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='.45' stop-color='%23ffe9a8' stop-opacity='.5'/%3E%3Cstop offset='1' stop-color='%23ffe9a8' stop-opacity='0'/%3E%3C/radialGradient%3E %3C/defs%3E%3Cg transform='translate(55,48) rotate(-8) scale(1.35) translate(-12,-12)'%3E %3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E %3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3C/g%3E %3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E %3Cg fill='%23ffe08a'%3E%3Cpath d='M7.4 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M13.2 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M7.2 14.2h9.6l-1.2 2.2-1.1-1.1-1.1 1.6-1.4-1.3-1.4 1.3-1.1-1.6-1.1 1.1z'/%3E%3C/g%3E %3C/g%3E%3Cg transform='translate(262,150) rotate(14) scale(1.05) translate(-12,-12)'%3E %3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E %3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3C/g%3E %3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E %3Cg fill='%23ffe08a'%3E%3Cpath d='M7.4 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M13.2 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M7.2 14.2h9.6l-1.2 2.2-1.1-1.1-1.1 1.6-1.4-1.3-1.4 1.3-1.1-1.6-1.1 1.1z'/%3E%3C/g%3E %3C/g%3E%3Cg transform='translate(160,292) rotate(-20) scale(1.25) translate(-12,-12)'%3E %3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E %3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3C/g%3E %3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E %3Cg fill='%23ffe08a'%3E%3Cpath d='M7.4 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M13.2 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M7.2 14.2h9.6l-1.2 2.2-1.1-1.1-1.1 1.6-1.4-1.3-1.4 1.3-1.1-1.6-1.1 1.1z'/%3E%3C/g%3E %3C/g%3E%3Cg transform='translate(338,342) rotate(9) scale(0.9) translate(-12,-12)'%3E %3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E %3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3C/g%3E %3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E %3Cg fill='%23ffe08a'%3E%3Cpath d='M7.4 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M13.2 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M7.2 14.2h9.6l-1.2 2.2-1.1-1.1-1.1 1.6-1.4-1.3-1.4 1.3-1.1-1.6-1.1 1.1z'/%3E%3C/g%3E %3C/g%3E%3Cg transform='translate(118,196) rotate(30) scale(0.95) translate(-12,-12)'%3E %3Cpath d='M12 4.9c1.1 0 1.9.45 2.45 1.15.95-.65 2.15-.85 3.3-.4 2.35.95 3.75 3.5 3.75 6.85s-1.6 6.4-3.95 7.25c-1.15.4-2.25.1-3.1-.55-.75.55-1.55.85-2.45.85s-1.7-.3-2.45-.85c-.85.65-1.95.95-3.1.55C4.15 18.9 2.5 16.35 2.5 12.5S3.9 6.6 6.25 5.65c1.15-.45 2.35-.25 3.3.4C10.1 5.35 10.9 4.9 12 4.9Z' fill='url(%23cc)'/%3E %3Cg stroke='%23c2621a' stroke-width='.75' fill='none' opacity='.85'%3E%3Cpath d='M8.6 6.3c-1.1 1.7-1.6 3.7-1.6 6.2s.5 4.6 1.6 6.4'/%3E%3Cpath d='M15.4 6.3c1.1 1.7 1.6 3.7 1.6 6.2s-.5 4.6-1.6 6.4'/%3E%3C/g%3E %3Cpath d='M11.3 1.9c1-1.3 3-1 3.4.35.25.8-.3 1.5-1.1 1.9-.7.35-1.4.45-2 .45v1.3h-1.5v-1.4c-.9-.15-1.6-.6-1.9-1.15' fill='url(%23ct)'/%3E %3Cg fill='%23ffe08a'%3E%3Cpath d='M7.4 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M13.2 11.6l1.7-2.8 1.7 2.8z'/%3E%3Cpath d='M7.2 14.2h9.6l-1.2 2.2-1.1-1.1-1.1 1.6-1.4-1.3-1.4 1.3-1.1-1.6-1.1 1.1z'/%3E%3C/g%3E %3C/g%3E%3Cg transform='translate(300,62) rotate(-12) scale(1.2) translate(-12,-12)'%3E %3Cpath d='M12 8.5c-.6-2.2-2.3-3.4-4.8-3.3.9 1 1 2.1.4 3.1C5 8.5 3 9.6 1.2 11.8c2-.9 3.8-.7 5.2.6.3-1.1 1.2-1.8 2.5-1.9.5 1 1.6 1.7 3.1 1.7s2.6-.7 3.1-1.7c1.3.1 2.2.8 2.5 1.9 1.4-1.3 3.2-1.5 5.2-.6-1.8-2.2-3.8-3.3-6.4-3.5-.6-1-.5-2.1.4-3.1-2.5-.1-4.2 1.1-4.8 3.3z' fill='%235a44a3' stroke='%23c3b5ff' stroke-width='.7' stroke-linejoin='round'/%3E %3Cpath d='M10.2 7.2l.6-1.8.9 1.4zM13.8 7.2l-.6-1.8-.9 1.4z' fill='%235a44a3'/%3E %3Ccircle cx='10.9' cy='9.6' r='.55' fill='%23ff6a3c'/%3E%3Ccircle cx='13.1' cy='9.6' r='.55' fill='%23ff6a3c'/%3E %3C/g%3E%3Cg transform='translate(42,332) rotate(10) scale(1) translate(-12,-12)'%3E %3Cpath d='M12 8.5c-.6-2.2-2.3-3.4-4.8-3.3.9 1 1 2.1.4 3.1C5 8.5 3 9.6 1.2 11.8c2-.9 3.8-.7 5.2.6.3-1.1 1.2-1.8 2.5-1.9.5 1 1.6 1.7 3.1 1.7s2.6-.7 3.1-1.7c1.3.1 2.2.8 2.5 1.9 1.4-1.3 3.2-1.5 5.2-.6-1.8-2.2-3.8-3.3-6.4-3.5-.6-1-.5-2.1.4-3.1-2.5-.1-4.2 1.1-4.8 3.3z' fill='%235a44a3' stroke='%23c3b5ff' stroke-width='.7' stroke-linejoin='round'/%3E %3Cpath d='M10.2 7.2l.6-1.8.9 1.4zM13.8 7.2l-.6-1.8-.9 1.4z' fill='%235a44a3'/%3E %3Ccircle cx='10.9' cy='9.6' r='.55' fill='%23ff6a3c'/%3E%3Ccircle cx='13.1' cy='9.6' r='.55' fill='%23ff6a3c'/%3E %3C/g%3E%3Cg transform='translate(372,236) rotate(18) scale(1.05) translate(-12,-12)'%3E %3Cpath d='M12 8.5c-.6-2.2-2.3-3.4-4.8-3.3.9 1 1 2.1.4 3.1C5 8.5 3 9.6 1.2 11.8c2-.9 3.8-.7 5.2.6.3-1.1 1.2-1.8 2.5-1.9.5 1 1.6 1.7 3.1 1.7s2.6-.7 3.1-1.7c1.3.1 2.2.8 2.5 1.9 1.4-1.3 3.2-1.5 5.2-.6-1.8-2.2-3.8-3.3-6.4-3.5-.6-1-.5-2.1.4-3.1-2.5-.1-4.2 1.1-4.8 3.3z' fill='%235a44a3' stroke='%23c3b5ff' stroke-width='.7' stroke-linejoin='round'/%3E %3Cpath d='M10.2 7.2l.6-1.8.9 1.4zM13.8 7.2l-.6-1.8-.9 1.4z' fill='%235a44a3'/%3E %3Ccircle cx='10.9' cy='9.6' r='.55' fill='%23ff6a3c'/%3E%3Ccircle cx='13.1' cy='9.6' r='.55' fill='%23ff6a3c'/%3E %3C/g%3E%3Cg transform='translate(222,20) rotate(-6) scale(0.85) translate(-12,-12)'%3E %3Cpath d='M12 8.5c-.6-2.2-2.3-3.4-4.8-3.3.9 1 1 2.1.4 3.1C5 8.5 3 9.6 1.2 11.8c2-.9 3.8-.7 5.2.6.3-1.1 1.2-1.8 2.5-1.9.5 1 1.6 1.7 3.1 1.7s2.6-.7 3.1-1.7c1.3.1 2.2.8 2.5 1.9 1.4-1.3 3.2-1.5 5.2-.6-1.8-2.2-3.8-3.3-6.4-3.5-.6-1-.5-2.1.4-3.1-2.5-.1-4.2 1.1-4.8 3.3z' fill='%235a44a3' stroke='%23c3b5ff' stroke-width='.7' stroke-linejoin='round'/%3E %3Cpath d='M10.2 7.2l.6-1.8.9 1.4zM13.8 7.2l-.6-1.8-.9 1.4z' fill='%235a44a3'/%3E %3Ccircle cx='10.9' cy='9.6' r='.55' fill='%23ff6a3c'/%3E%3Ccircle cx='13.1' cy='9.6' r='.55' fill='%23ff6a3c'/%3E %3C/g%3E%3C/svg%3E");
  background-size: 400px 400px;
}
html[data-effet="citrouilles"] .ciel-fete-b {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 330 330' width='330' height='330'%3E%3Cdefs%3E %3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E %3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E %3CradialGradient id='gf' cx='.4' cy='.3' r='.8'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='1' stop-color='%23c9d2ff'/%3E%3C/radialGradient%3E %3CradialGradient id='gh' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='0' stop-color='%23dfe6ff' stop-opacity='.45'/%3E%3Cstop offset='1' stop-color='%23dfe6ff' stop-opacity='0'/%3E%3C/radialGradient%3E %3CradialGradient id='lu' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='.45' stop-color='%23ffe9a8' stop-opacity='.5'/%3E%3Cstop offset='1' stop-color='%23ffe9a8' stop-opacity='0'/%3E%3C/radialGradient%3E %3C/defs%3E%3Cg transform='translate(62,70) rotate(-6) scale(1.7) translate(-12,-12)'%3E %3Cellipse cx='12' cy='12' rx='13' ry='14' fill='url(%23gh)'/%3E %3Cpath d='M12 1.6c-4.8 0-8 3.9-8 9.2v10.6l2.6-2.1 2.6 2.1 2.8-2.1 2.8 2.1 2.6-2.1 2.6 2.1V10.8c0-5.3-3.2-9.2-8-9.2z' fill='url(%23gf)' opacity='.95'/%3E %3Cellipse cx='9.2' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E%3Cellipse cx='14.8' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E %3Cellipse cx='12' cy='14.8' rx='1.1' ry='1.6' fill='%232a1f4a'/%3E %3Cellipse cx='8.3' cy='6.2' rx='1.6' ry='2.4' fill='%23fff' opacity='.5' transform='rotate(-25 8.3 6.2)'/%3E %3C/g%3E%3Cg transform='translate(236,188) rotate(8) scale(1.5) translate(-12,-12)'%3E %3Cellipse cx='12' cy='12' rx='13' ry='14' fill='url(%23gh)'/%3E %3Cpath d='M12 1.6c-4.8 0-8 3.9-8 9.2v10.6l2.6-2.1 2.6 2.1 2.8-2.1 2.8 2.1 2.6-2.1 2.6 2.1V10.8c0-5.3-3.2-9.2-8-9.2z' fill='url(%23gf)' opacity='.95'/%3E %3Cellipse cx='9.2' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E%3Cellipse cx='14.8' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E %3Cellipse cx='12' cy='14.8' rx='1.1' ry='1.6' fill='%232a1f4a'/%3E %3Cellipse cx='8.3' cy='6.2' rx='1.6' ry='2.4' fill='%23fff' opacity='.5' transform='rotate(-25 8.3 6.2)'/%3E %3C/g%3E%3Cg transform='translate(122,282) rotate(-3) scale(1.3) translate(-12,-12)'%3E %3Cellipse cx='12' cy='12' rx='13' ry='14' fill='url(%23gh)'/%3E %3Cpath d='M12 1.6c-4.8 0-8 3.9-8 9.2v10.6l2.6-2.1 2.6 2.1 2.8-2.1 2.8 2.1 2.6-2.1 2.6 2.1V10.8c0-5.3-3.2-9.2-8-9.2z' fill='url(%23gf)' opacity='.95'/%3E %3Cellipse cx='9.2' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E%3Cellipse cx='14.8' cy='10' rx='1.3' ry='1.9' fill='%232a1f4a'/%3E %3Cellipse cx='12' cy='14.8' rx='1.1' ry='1.6' fill='%232a1f4a'/%3E %3Cellipse cx='8.3' cy='6.2' rx='1.6' ry='2.4' fill='%23fff' opacity='.5' transform='rotate(-25 8.3 6.2)'/%3E %3C/g%3E%3C/svg%3E");
  background-size: 330px 330px;
  animation: ciel-fantome 36s linear infinite;
}
@keyframes ciel-fantome {
  0%   { transform: translate3d(0, 0, 0); }
  25%  { transform: translate3d(14px, -82.5px, 0); }
  50%  { transform: translate3d(0, -165px, 0); }
  75%  { transform: translate3d(-14px, -247.5px, 0); }
  100% { transform: translate3d(0, -330px, 0); }
}
html[data-effet="citrouilles"] .ciel-fete-a::before,
html[data-effet="citrouilles"] .ciel-fete-a::after {
  content: '';
  position: absolute;
  background-repeat: no-repeat;
  background-size: contain;
  pointer-events: none;
}
/* La lune : la couche commence 240 px au-dessus et à droite de l'écran. */
html[data-effet="citrouilles"] .ciel-fete-a::before {
  top: 320px; right: calc(240px + 5vw);
  width: 120px; height: 120px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 120' width='120' height='120'%3E%3Cdefs%3E %3CradialGradient id='cc' cx='.35' cy='.3' r='.75'%3E%3Cstop offset='0' stop-color='%23ffb454'/%3E%3Cstop offset='.55' stop-color='%23f08a2a'/%3E%3Cstop offset='1' stop-color='%23c2621a'/%3E%3C/radialGradient%3E %3ClinearGradient id='ct' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%236aa84f'/%3E%3Cstop offset='1' stop-color='%233d6b2c'/%3E%3C/linearGradient%3E %3CradialGradient id='gf' cx='.4' cy='.3' r='.8'%3E%3Cstop offset='0' stop-color='%23ffffff'/%3E%3Cstop offset='1' stop-color='%23c9d2ff'/%3E%3C/radialGradient%3E %3CradialGradient id='gh' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='0' stop-color='%23dfe6ff' stop-opacity='.45'/%3E%3Cstop offset='1' stop-color='%23dfe6ff' stop-opacity='0'/%3E%3C/radialGradient%3E %3CradialGradient id='lu' cx='.5' cy='.5' r='.5'%3E%3Cstop offset='.45' stop-color='%23ffe9a8' stop-opacity='.5'/%3E%3Cstop offset='1' stop-color='%23ffe9a8' stop-opacity='0'/%3E%3C/radialGradient%3E %3C/defs%3E %3Ccircle cx='60' cy='60' r='60' fill='url(%23lu)'/%3E %3Cpath d='M99.35 36.19A46 46 0 1 0 60.57 105.99A40 40 0 1 1 99.35 36.19Z' fill='%23fff1c2'/%3E %3Ccircle cx='34' cy='58' r='4' fill='%23efe0a8' opacity='.8'/%3E%3Ccircle cx='46' cy='88' r='2.6' fill='%23efe0a8' opacity='.8'/%3E%3Ccircle cx='30' cy='78' r='1.8' fill='%23efe0a8' opacity='.7'/%3E %3C/svg%3E");
}
/* La sorcière : part hors champ à gauche, traverse, attend hors champ à droite. */
html[data-effet="citrouilles"] .ciel-fete-a::after {
  top: 430px; left: -220px;
  width: 140px; height: 100px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 140 100' width='140' height='100'%3E %3Cg fill='%23ffd86b' opacity='.75'%3E%3Ccircle cx='6' cy='64' r='1.6'/%3E%3Ccircle cx='14' cy='70' r='1.1'/%3E%3Ccircle cx='4' cy='74' r='1'/%3E%3Ccircle cx='18' cy='60' r='1.3'/%3E%3C/g%3E %3Cpolygon points='10,58 32,52 36,70 14,78' fill='%23d6a84a' stroke='%239a7429' stroke-width='1.5' stroke-linejoin='round'/%3E %3Cpath d='M30 54l4 14' stroke='%236b4a1c' stroke-width='2.5' stroke-linecap='round'/%3E %3Cpath d='M34 60L130 40' stroke='%238a5a2b' stroke-width='4' stroke-linecap='round'/%3E %3Cpath d='M62 48L36 62 56 64Z' fill='%233a1f6b'/%3E %3Cpath d='M74 30L54 64h44Z' fill='%234b2a86'/%3E %3Cpath d='M68 64l-7 11M84 64l6 10' stroke='%231d1233' stroke-width='3.5' stroke-linecap='round'/%3E %3Cpath d='M72 42l20 6' stroke='%239bd36a' stroke-width='3.5' stroke-linecap='round'/%3E %3Cpath d='M66 22c-4 6-6 12-5 18l12-6z' fill='%236e4bb8'/%3E %3Ccircle cx='74' cy='27' r='7.5' fill='%239bd36a'/%3E %3Cpath d='M80 26l4 2-4 1.5z' fill='%237fb552'/%3E%3Ccircle cx='77' cy='25.5' r='1' fill='%231d1233'/%3E %3Cpath d='M56 22h36L80 2Z' fill='%231d1233' stroke='%238b6cff' stroke-width='.8' stroke-linejoin='round'/%3E %3Cpath d='M63 18h22l-1.8-4.5H64.5Z' fill='%23f08a2a'/%3E %3Cellipse cx='74' cy='22' rx='19' ry='3.2' fill='%231d1233' stroke='%238b6cff' stroke-width='.8'/%3E %3C/svg%3E");
  animation: ciel-sorciere-x 46s linear infinite, ciel-sorciere-y 3.4s ease-in-out infinite alternate;
}
@keyframes ciel-sorciere-x {
  0%   { left: -220px; }
  72%  { left: calc(100% + 40px); }
  100% { left: calc(100% + 40px); }
}
@keyframes ciel-sorciere-y {
  from { transform: translate3d(0, -14px, 0) rotate(-3deg); }
  to   { transform: translate3d(0, 14px, 0) rotate(3deg); }
}
@media (max-width: 700px) {
  html[data-effet="citrouilles"] .ciel-fete-a::before { width: 84px; height: 84px; top: 300px; }
  html[data-effet="citrouilles"] .ciel-fete-a::after { width: 100px; height: 72px; }
}

/* ==========================================================================
   THÈME « ROUGE » PAR COMPTE — 26/10/2026 : « le site rouge pour mon compte et seulement mon compte ».
   « Ça fait tout violet… 3 couleurs suffisent largement » : noir profond,
   noir profond #000000, bleu électrique #0070ff, rouge pur #ff1a1a — « bleu, blanc, rouge sur fond noir » (maquette palette 5 validée).
   Le violet n'est pas remplacé ligne à ligne : tout ce qui était violet lit
   déjà --accent / --accent-rgb / --accent-lisible (les derniers hex en dur
   ont été ramenés à ces variables au passage), et le ciel lit --fond-teinte /
   --fond-aurore-2 / --ciel-*. On redéfinit donc les variables, pour le monde
   compte dont l'identifiant est dans data/theme-comptes.json : src/theme-comptes.js
   pose res.locals.themeCompte, head.ejs écrit data-theme="rouge" sur <html> (site
   public seulement). Tous les autres comptes, et les visiteurs, gardent le site tel quel.
   --accent-texte passe en sombre : du blanc sur #ff6a00 fait 2,9:1, du
   sombre 6,9:1. --accent-lisible = orange clair pour le texte sur fond noir.
   Les étoiles du canvas (js/ciel-reseau.js) lisent --etoiles et
   --etoiles-cable, définis ici pour les deux mondes. Les images de marque
   (logo, favicons, Auron) ont été recolorées pixel par pixel (+120° de teinte),
   les variantes -prod sont intactes. En FIN de fichier exprès.
   ========================================================================== */
html[data-theme="rouge"] {
  --accent: #ff1a1a;
  --accent-rgb: 255, 26, 26;
  --accent-plein: #e60000;
  --accent-texte: #ffffff;
  --accent-hover: #ff4040;
  --accent-dim: #8a0000;
  --accent-lisible: #ff5252;
  --fond-teinte: 255, 26, 26;
  --fond-aurore-2: 0, 112, 255;
  --fond-poussiere: 255, 180, 180;
  --ciel-haut: #0a0202;
  --ciel-milieu: #040202;
  --ciel-bas: #000000;
  --fond-voile: #050303;
  --etoiles: #ff5252 #66a9ff #0070ff #ff1a1a #ffbdbd #ffffff #ffffff;
  --etoiles-cable: 255, 26, 26;
}
html[data-monde="prod"] {
  --etoiles: #fde68a #93c5fd #ffffff #ffffff #fef3c7 #c4b5fd;
  --etoiles-cable: 253, 230, 138;
}
/*  TROIS COULEURS, PAS HUIT : les cartes de fonctionnalités et les cartes de
    statistiques alternaient cyan, rose, vert, jaune, lilas… On les ramène à la
    palette : orange néon / bleu acier en alternance. Public seulement. */
html[data-theme="rouge"] .feature-grid .feature-card:nth-child(odd),
html[data-theme="rouge"] .stat-carte.est-violet,
html[data-theme="rouge"] .stat-carte.est-rose,
html[data-theme="rouge"] .stat-carte.est-jaune {
  --teinte: var(--accent-lisible); --teinte-faible: rgba(var(--accent-rgb), .15);
}
html[data-theme="rouge"] .feature-grid .feature-card:nth-child(even),
html[data-theme="rouge"] .stat-carte.est-bleu,
html[data-theme="rouge"] .stat-carte.est-vert {
  --teinte: #3d8bff; --teinte-faible: rgba(0, 112, 255, .15);
}
html[data-theme="rouge"] .stat-carte.est-turquoise {
  --teinte: #3d8bff; --teinte-faible: rgba(0, 112, 255, .15);
}
/*  LES LICENCES AUSSI : la page des tarifs pose une teinte par formule en ligne
    (--lic-teinte / --lic-rgb, depuis les données). En public, on les ramène à la
    palette : Starter bleu acier, Medium rouge, Premium rouge clair, Custom bleu. */
html[data-theme="rouge"] .pricing-card[data-formule="pro"],
html[data-theme="rouge"] .pricing-card[data-formule="selfhost"] { --lic-teinte: #3d8bff; --lic-rgb: 0, 112, 255; }
html[data-theme="rouge"] .pricing-card[data-formule="medium"] { --lic-teinte: #ff1a1a; --lic-rgb: 255, 26, 26; }
html[data-theme="rouge"] .pricing-card[data-formule="ultra"] { --lic-teinte: #ff5252; --lic-rgb: 255, 82, 82; }
html[data-theme="rouge"] .pricing-card[data-formule="free"] { --lic-teinte: #c9ccd6; --lic-rgb: 201, 204, 214; }

/*  NOIR PROFOND : le fond des pages et des cartes tirait sur le bleu nuit ; avec la
    palette de la vidéo, c'est du noir. */
html[data-theme="rouge"] { --bg: #000000; --bg-soft: #0a0a0d; --bg-softer: #111115; --bg-card: #0e0e13; --bg-card-hover: #16161d; --border: #22222b; }

/*  LES IMAGES DE MARQUE, SANS FICHIERS SUPPLÉMENTAIRES : logo, Auron, avatar sont
    recolorés par filtre (même teinte +96° que les images préparées pour la refonte
    complète). L'icône d'onglet, elle, ne peut pas être filtrée : head.ejs sert
    favicon-rouge-*.png quand le thème est actif. */
html[data-theme="rouge"] img[src*="/img/vaeron-"],
html[data-theme="rouge"] img[src*="/img/auron-tete"],
html[data-theme="rouge"] img[src*="/img/custom-logo"],
html[data-theme="rouge"] img[src*="/img/favicon-"] { filter: hue-rotate(96deg) saturate(1.5); }
/*  LE GROS LOGO DE L'ACCUEIL ET CELUI DE LA BARRE : vus encore violets sur sa capture
    du 26/10 au soir malgré la règle générique ci-dessus — on les nomme, en forçant. */
html[data-theme="rouge"] .hero-logo,
html[data-theme="rouge"] .brand-logo,
html[data-theme="rouge"] .navbar img,
html[data-theme="rouge"] .auron-encart img,
html[data-theme="rouge"] .auron-bulle img { filter: hue-rotate(96deg) saturate(1.5) !important; }
/*  LA NOUVELLE MASCOTTE DU THÈME — 26/10/2026, « ho moi je veux ça » : le petit
    dragon noir aux ailes rouges et aux yeux bleus remplace le portrait d'Auron
    PARTOUT où l'image auron-tete est affichée (bulle, encart, réponses de
    l'assistant, guides, cartes des fonctionnalités), pour les comptes au thème
    rouge seulement. `content: url()` sur une <img> remplace l'image sans toucher
    aux gabarits ; le fond des réponses de l'assistant est une image CSS, on la
    redéfinit aussi. Le filtre de recoloration ne s'applique plus : le dragon est
    déjà dans la palette. */
html[data-theme="rouge"] img[src*="/img/auron-tete"] { content: url("/img/auron-dragon.webp?v=2"); filter: none !important; }
html[data-theme="rouge"] .assist-msg-bot { background-image: url("/img/auron-dragon.webp?v=2"); }
html[data-theme="rouge"] .assist-saisie button { background: linear-gradient(135deg, var(--accent), var(--accent-dim)); }
/*  LES FLAMMES BOUGENT — 26/10/2026, « on peut les animer ? ». Trois dessins du
    même dragon avec des flammes différentes, côte à côte dans une planche
    (auron-dragon-anim.webp = 3 × 192 px). L'<img> est un cadre carré : on lui
    fait montrer un tiers de la planche (object-fit: cover) et on déplace la
    fenêtre par sauts (steps) — 0 %, 50 %, 100 % = image 1, 2, 3 — quatre
    images par seconde, ça flambe sans clignoter. Même principe pour le fond
    des réponses de l'assistant, en background-position. L'ancienne image
    fixe reste en repli si la planche ne charge pas (ligne `content` plus haut). */
html[data-theme="rouge"] img[src*="/img/auron-tete"] {
  content: url("/img/auron-dragon-anim.webp?v=1");
  object-fit: cover !important; object-position: 0% 50%;
  animation: dragon-flammes 0.36s steps(3, end) infinite;
}
@keyframes dragon-flammes { from { object-position: 0% 50%; } to { object-position: 150% 50%; } }
/*  Dans les réponses de l'assistant, le portrait est un FOND : la planche entière
    s'y peignait (trois dragons côte à côte, vu le 26/10 à 21:26). On retire ce fond
    et on pose une fenêtre de 26 px (::before) qui fait défiler la planche dedans. */
html[data-theme="rouge"] .assist-msg-bot { background-image: none; }
html[data-theme="rouge"] .assist-msg-bot::before {
  content: ''; position: absolute; left: 9px; top: 9px; width: 26px; height: 26px;
  background: url("/img/auron-dragon-anim.webp?v=1") 0 0 / 78px 26px no-repeat;
  animation: dragon-flammes-fond 0.36s steps(3, end) infinite;
  pointer-events: none;
}
@keyframes dragon-flammes-fond { from { background-position: 0 0; } to { background-position: -78px 0; } }
@media (prefers-reduced-motion: reduce) {
  html[data-theme="rouge"] img[src*="/img/auron-tete"], html[data-theme="rouge"] .assist-msg-bot::before { animation: none; }
}

/* ======================================================================
 *  MISE EN PAGE DES CARTES D'ACTUS — 01/11/2026, sur capture
 * ======================================================================
 *  « On peut faire en sorte que la mise en page soit mieux. » Ce qu'on
 *  voyait : une carte seule, étroite, perdue au milieu de la page ; dans le
 *  pied, « el_vaerun à / l'instant » coupé en deux parce que la période
 *  (« 📅 Du 1 nov. … ») réclamait sa place sur la même ligne ; et le corps
 *  qui répétait le titre avant d'être coupé par des points de suspension,
 *  sans moyen de lire la suite.
 *
 *  Écrit EN FIN DE FICHIER, comme tout ce qui touche style.css : le fichier
 *  est en double (l. 637 ↔ 5897) et une règle posée au milieu se fait
 *  écraser par sa jumelle.
 */

/*  La grille s'adapte au nombre de cartes. Une seule : une carte large et
 *  centrée (au lieu d'une vignette de 300 px seule à gauche). Deux : deux
 *  cartes centrées. Trois et plus : la grille habituelle. */
.actus-grille:has(> .actu-carte:only-child) {
  grid-template-columns: minmax(0, 760px);
  justify-content: center;
}
.actus-grille:has(> .actu-carte:nth-child(2):last-child) {
  grid-template-columns: repeat(2, minmax(0, 520px));
  justify-content: center;
}
/*  Une carte seule AVEC image : l'image à gauche, le texte à droite, au
 *  lieu d'une grande bannière suivie de trois lignes. */
@media (min-width: 720px) {
  .actus-grille > .actu-carte:only-child .actu-lien-total { flex-direction: row; }
  .actus-grille > .actu-carte:only-child .actu-image {
    flex: 0 0 42%; aspect-ratio: auto; position: relative; min-height: 220px;
  }
  .actus-grille > .actu-carte:only-child .actu-image img { position: absolute; inset: 0; }
  .actus-grille > .actu-carte:only-child .actu-corps { padding: 20px 22px 20px; gap: 11px; }
  .actus-grille > .actu-carte:only-child .actu-titre { font-size: 1.18rem; }
  .actus-grille > .actu-carte:only-child .actu-extrait { -webkit-line-clamp: 5; font-size: .92rem; }
}
@media (max-width: 900px) {
  .actus-grille:has(> .actu-carte:nth-child(2):last-child) { grid-template-columns: 1fr; }
}

/*  Le genre (« Nouveauté », « Correction ») en petites capitales espacées :
 *  à côté de la pastille d'importance, un mot en minuscules grises avait
 *  l'air d'un reste. */
.actu-genre-texte { text-transform: uppercase; letter-spacing: .06em; font-weight: 600; font-size: .68rem; }

/*  Un titre en capitales et sans espace ne doit jamais déborder de la carte. */
.actu-titre { overflow-wrap: anywhere; }

/*  Le corps garde ses retours à la ligne (comme la popup) ; quatre lignes
 *  visibles, le reste derrière « Lire la suite ». Le dégradé en bas dit
 *  « ça continue » plus honnêtement que trois points. */
.actu-extrait { white-space: pre-line; -webkit-line-clamp: 4; position: relative; }
.actu-carte.est-ouverte .actu-extrait { -webkit-line-clamp: unset; display: block; overflow: visible; }
.actu-plus {
  align-self: flex-start; margin-top: -2px;
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: .8rem; font-weight: 600; color: var(--accent-lisible, var(--accent));
}
.actu-plus:hover, .actu-plus:focus-visible { text-decoration: underline; outline: none; }
.actu-plus::after { content: ' ▾'; font-size: .7em; }
.actu-carte.est-ouverte .actu-plus::after { content: ' ▴'; }

/*  La période d'affichage : une étiquette sur sa propre ligne, qui peut se
 *  replier sur un écran étroit au lieu de pousser le reste. */
.actu-carte .actu-periode {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px; white-space: normal;
  border: 1px solid var(--border); background: rgba(255,255,255,.035);
  font-size: .74rem; color: var(--text-muted); line-height: 1.3;
}

/*  Le pied : la signature d'un côté, le bouton de l'autre. La signature est
 *  UN bloc (initiale, nom, date) qui descend entier à la ligne si la place
 *  manque — plus jamais « à » sur une ligne et « l'instant » sur la suivante. */
.actu-pied { flex-wrap: wrap; align-items: center; row-gap: 8px; }
.actu-signature { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.actu-auteur, .actu-quand { white-space: nowrap; }
.actu-auteur { font-weight: 600; color: var(--text); }
.actu-sep { opacity: .6; }
.actu-avatar {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .7rem; font-weight: 700; line-height: 1;
  color: var(--accent-lisible, var(--accent));
  background: rgba(var(--accent-rgb), .16); border: 1px solid rgba(var(--accent-rgb), .36);
}
