/* DRaGoNCHaT — feuille de style
   ---------------------------------------------------------------------------
   La palette est celle du projet d'origine, relevee dans Teleporteurs.inc.html
   et src_b.htm : le bleu #003366 / #006699 / #0099CC, et l'oxblood #990000 de
   la frame de contrôle. Elle est étendue vers le bas (un abysse plus profond
   que ne le permettait la palette web-safe de 2004) et vers le haut (une gemme
   cyan qui sert de lumière). L'or n'apparaît que sur le rang.

   Les plaques biseautees viennent des classes .DCCadre / .DCTitre / .DCFormBouton
   d'origine : bord clair en haut a gauche, bord sombre en bas a droite. C'est
   la signature visuelle du système, on la garde.

   Verdana porte l'interface — personne ne s'en sert plus, et c'est justement
   ce qui donne a ces plaques leur air d'instrument venu d'ailleurs. Le flux de
   parole est en monospace : les logs de chat du carnet sont des listings.
*/

/* ─── Les fontes ────────────────────────────────────────────────────────────
   Poppins porte les titres, Inter le reste. Elles sont servies depuis
   /marque/fontes/ et non depuis un CDN : voir le LISEZMOI qui les accompagne.
   `font-display: swap` — le texte paraît tout de suite dans la fonte de
   secours et se recompose quand la vraie arrive ; l'inverse (`block`) laisse
   une page blanche, ce qui est pire que la mauvaise fonte. */
@font-face {
  font-family: "Inter";
  src: url("/marque/fontes/inter-var.woff2") format("woff2");
  font-weight: 100 900;      /* une seule fonte variable pour toutes les graisses */
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/marque/fontes/poppins-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/marque/fontes/poppins-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Poppins";
  src: url("/marque/fontes/poppins-800.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

:root {
  --abysse: #070a14;
  --nuit: #0b0f1a;
  --profond: #10233d;
  --eau: #1d6fb8;
  --brume: #2f8fe0;
  --gemme: #4fd8ff;
  /* Le bleu d'action et le violet du titre. Ils n'existaient pas : la maquette
     du 2 septembre 2026 les demande, et ils ne remplacent pas `--brume` — ils
     servent aux boutons et au degrade du titre, nulle part ailleurs. */
  --azur: #3b9dff;
  --violet: #8b7ff5;
  --sang: #990000;
  --braise: #d94a2b;
  --or: #e0b65c;
  --os: #f2f6fb;
  --cendre: #a9b8ce;
  --sourd: #78899f;

  /* La fonte de l'interface. Verdana portait les plaques depuis 2004 et lui
     donnait son air d'instrument venu d'ailleurs ; demande le 2 septembre
     2026, le produit passe a la grotesque systeme, qui est ce que les
     interfaces de 2026 portent reellement. Rien n'est charge depuis le
     reseau : pas de fonte distante, donc pas de dependance et pas de saut de
     rendu. Pour revenir en arriere, il suffit de remettre Verdana en tete. */
  --chrome: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
            Roboto, "Helvetica Neue", "DejaVu Sans", Arial, sans-serif;
  /* La fonte des titres. Geometrique et fermee la ou Inter est neutre : c'est
     ce contraste qui porte la personnalite de la page, pas la couleur. */
  --titre: "Poppins", "Inter", ui-sans-serif, system-ui, sans-serif;
  --parole: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "DejaVu Sans Mono", monospace;

  --gouttiere: clamp(1rem, 3vw, 2rem);
  --plaque-clair: #0099cc;
  --plaque-sombre: #05070d;

  /* ─── Les formes ────────────────────────────────────────────────────────
     Un rayon fin, jamais rond : le produit reste un instrument, pas une
     application de telephone. Quatre crans suffisent, et ils sont lies —
     une plaque, son titre et ses champs doivent parler du meme rayon. */
  --r-xs: 5px;    /* jetons, code en ligne, cases */
  --r-sm: 9px;    /* boutons, champs, outils */
  --r-md: 14px;   /* plaques, cartouches */
  --r-lg: 22px;   /* grands panneaux, la demo posee */
  --r-pilule: 999px;

  /* ─── Le verre ──────────────────────────────────────────────────────────
     Le biseau de 2004 disait « la lumiere vient d'en haut a gauche » avec
     deux bords pleins. On garde la phrase et on change l'accent : une seule
     arete speculaire d'un pixel en haut, sur une surface qui laisse passer
     le ciel. Le flou est ce qui rend la translucidite lisible — sans lui,
     une surface a faible opacite n'est pas du verre, c'est un voile sale. */
  --verre-flou: blur(18px) saturate(1.5);
  --verre-flou-leger: blur(10px) saturate(1.3);
  --filet: rgba(255, 255, 255, .10);        /* l'arete eclairee, en haut */
  --filet-bas: rgba(0, 0, 0, .30);          /* l'ombre portee, en bas */
  --cadre: rgba(var(--accent-rgb), .26);    /* le trait qui ferme la surface */

  /* ─── L'elevation ───────────────────────────────────────────────────────
     Larges, basses, tres peu opaques. Une ombre qui se voit est une ombre
     ratee ; ce qu'on doit voir, c'est que l'objet flotte. */
  --ombre-1: 0 1px 2px rgba(0, 0, 0, .28);
  --ombre-2: 0 10px 30px -12px rgba(0, 0, 0, .60);
  --ombre-3: 0 24px 60px -20px rgba(0, 0, 0, .72);
  /* La lueur : c'est la seule chose qui dit « magie » dans le chrome. Elle ne
     paraît qu'au survol et au foyer, jamais au repos. */
  --lueur: 0 0 28px -8px rgba(var(--accent-rgb), .85);

  --vite: .16s cubic-bezier(.2, .7, .3, 1);
  --lent: .42s cubic-bezier(.2, .7, .3, 1);

  /* Les surfaces du chrome. Elles portaient leurs couleurs en dur, et c'est ce
     qui a fait le premier défaut du 2 septembre 2026 : la salle prenait la
     lumière de son thème et **tout le chrome restait bleu**. Nommées ici avec
     exactement les valeurs qu'elles avaient, elles ne changent rien par
     défaut ; lib/themes.js les redéfinit par salon. */
  /* L'accent en composantes. Trente-cinq règles le posaient en dur à faible
     opacité -- liserés, fonds de code, séparateurs -- et aucune n'aurait suivi
     le thème sans cela. C'est la même couleur que --brume, dite autrement,
     parce que rgba() ne sait pas prendre un hexadécimal. */
  --accent-rgb: 59, 157, 255;
  /* Le halo ambiant du fond, un cran plus sourd que l'accent. Même raison. */
  --halo-rgb: 29, 111, 184;
  /* Le fond des champs de saisie : la nuit, presque noire. C'est la surface
     sur laquelle on écrit, et elle doit suivre la salle comme le reste. */
  --champ-rgb: 5, 8, 15;

  /* Les surfaces sont passees au verre le 2 septembre 2026. Elles gardent
     leurs noms et leur role ; ce qui change, c'est l'opacite — assez basse
     pour que le ciel se lise a travers, assez haute pour que la parole reste
     posee sur quelque chose. `lib/themes.js` les redefinit par salon. */
  --plaque-a: rgba(var(--halo-rgb), .30);
  --plaque-b: rgba(0, 51, 102, .16);
  --plaque-tete: rgba(var(--accent-rgb), .10);
  --entete: rgba(var(--halo-rgb), .22);
  --panneau: rgba(1, 30, 51, .58);
  --barre: rgba(0, 26, 46, .62);

  /* Le voile posé sur l'image de fond. Sans thème il n'y a pas d'image, donc
     il ne sert pas -- mais une variable non définie casserait la règle. */
  --voile: rgba(7, 10, 20, .78);
}

*, *::before, *::after { box-sizing: border-box; }

/* L'attribut `hidden` ne vaut, dans la feuille du navigateur, qu'un
   `display: none` de toute dernière priorite : la moindre règle de classe qui
   pose un `display` le recouvre. Or la porte et le chat sont tous les deux des
   grilles, et c'est `hidden` qui bascule de l'un a l'autre. Sans cette ligne,
   la porte masquee garde sa hauteur d'écran entier et repousse le chat sous la
   ligne de flottaison. */
[hidden] { display: none !important; }

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

/* ─── Le ciel ───────────────────────────────────────────────────────────────
   Le semis de 2008 etait une tuile unique et immobile. Le ciel se lit
   maintenant sur trois plans qui derivent a des vitesses differentes : c'est
   la parallaxe qui donne la profondeur, pas le nombre d'etoiles.

   Trois choses commandent ce bloc.

   1. **Les tailles de tuile sont premieres entre elles** (617, 853, 1187).
      Avec des tailles multiples, les trois motifs se recalent au meme moment
      et l'oeil voit la repetition d'un coup.

   2. **Chaque plan parcourt un nombre entier de tuiles** sur la duree de
      l'animation. C'est la seule facon d'avoir une boucle sans saut : a la
      derniere image, le motif est exactement revenu sur lui-meme.

   3. **900 secondes.** Le plan le plus proche met un quart d'heure a traverser
      trois tuiles. C'est volontairement sous le seuil de ce qu'on remarque :
      on ne voit pas le ciel bouger, on voit qu'il n'est pas le meme qu'avant.

   Les deux degrades restent dessous : si les SVG ne chargent pas, le fond
   garde sa profondeur au lieu de tomber a plat. */
body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--abysse);
  background-image:
    url("/marque/etoiles-pres.svg"),
    url("/marque/etoiles-milieu.svg"),
    url("/marque/etoiles-loin.svg"),
    radial-gradient(90% 55% at 78% -6%, rgba(139, 127, 245, .20), transparent 64%),
    radial-gradient(110% 70% at 32% -12%, rgba(29, 111, 184, .26), transparent 62%);
  background-repeat: repeat, repeat, repeat, no-repeat, no-repeat;
  background-size: 1187px 1187px, 853px 853px, 617px 617px, auto, auto;
  background-position: 0 0, 0 0, 0 0, 78% -6%, 32% -12%;
  background-attachment: fixed;
  color: var(--os);
  font-family: var(--chrome);
  font-size: 14px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* La derive. Elle ne tourne que si le visiteur n'a pas demande moins de
   mouvement — la regle globale de `prefers-reduced-motion` plus haut coupe
   deja toute animation, celle-ci comprise, mais on ne la pose meme pas. */
@media (prefers-reduced-motion: no-preference) {
  body { animation: derive-ciel 900s linear infinite; }
}
@keyframes derive-ciel {
  from { background-position: 0 0, 0 0, 0 0, 78% -6%, 32% -12%; }
  to   { background-position: -3561px 1187px, -1706px 853px, -617px 0, 78% -6%, 32% -12%; }
}

/* ─── Le scintillement ──────────────────────────────────────────────────────
   Deux couches de la meme tuile, decalees et **dephasees** : l'une respire en
   6,5 s, l'autre en 9,1 s avec trois secondes d'avance. Une couche unique
   ferait clignoter toutes les etoiles ensemble, ce qui se lit tout de suite
   comme un effet ; deux couches qui ne se recalent jamais se lisent comme un
   ciel. Le fond de `body` se propage au canevas, donc ces deux calques a
   `z-index: -1` se posent **au-dessus des etoiles et sous le contenu**. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: url("/marque/etoiles-scintille.svg");
  background-repeat: repeat;
  background-size: 761px 761px;
  opacity: .5;
}
body::before { background-position: 0 0; }
body::after  { background-position: 380px 190px; }

@media (prefers-reduced-motion: no-preference) {
  body::before { animation: scintille 6.5s ease-in-out infinite; }
  body::after  { animation: scintille 9.1s ease-in-out -3s infinite; }
}
@keyframes scintille {
  0%, 100% { opacity: .22; }
  50%      { opacity: .85; }
}

/* Sous 700 px, une seule tuile de 1187 px deborde deja tout l'ecran : on n'en
   voit qu'un coin, et les rares etoiles qui y tombent -- les grandes croix de
   diffraction du plan proche surtout -- paraissent demesurees sur un fond
   presque vide, comme un ciel casse plutot que lointain. On reduit les quatre
   tuiles de moitie.

   La derive doit boucler sans saut a cette taille aussi : `derive-ciel-petit`
   n'est que `derive-ciel` divise par deux de bout en bout -- meme nombre de
   tuiles parcourues (3, 2 et 1, comme plus haut), tuiles deux fois plus
   petites, donc une derive de moitie moins de pixels. Un keyframes distinct et
   non une variable : `background-position` ne s'anime pas proprement a partir
   d'une custom property sur tous les moteurs, et deux jeux de nombres surs
   valent mieux qu'un calc() fragile. Meme raisonnement pour les deux calques
   de scintillement : la tuile de 761 px descend a 380.5, et le decalage de la
   seconde couche (moitie de la largeur, quart de la hauteur -- voir plus haut)
   descend avec elle. */
@media (max-width: 700px) {
  body { background-size: 593.5px 593.5px, 426.5px 426.5px, 308.5px 308.5px, auto, auto; }
  body::before, body::after { background-size: 380.5px 380.5px; }
  body::after { background-position: 190px 95px; }
}
@media (max-width: 700px) and (prefers-reduced-motion: no-preference) {
  body { animation-name: derive-ciel-petit; }
}
@keyframes derive-ciel-petit {
  from { background-position: 0 0, 0 0, 0 0, 78% -6%, 32% -12%; }
  to   { background-position: -1780.5px 593.5px, -853px 426.5px, -308.5px 0, 78% -6%, 32% -12%; }
}

a { color: var(--gemme); text-decoration-color: rgba(127, 227, 255, .4); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }
:focus-visible { outline: 2px solid var(--gemme); outline-offset: 2px; }

h1, h2, h3 { margin: 0; font-family: var(--titre); font-weight: 700; line-height: 1.15; letter-spacing: -.015em; }

/* --- Typographie ---------------------------------------------------------- */

/* Le titre du portail. Le resserrement (`-.03em`) est ce qui fait la difference
   entre un titre de 2004 et un titre de 2026 : a cette taille, l'espacement par
   defaut d'une grotesque est calcule pour du texte courant, pas pour une
   affiche. La seconde ligne prend un degrade decoupe dans le texte — c'est le
   seul endroit du produit ou la couleur se deplace a l'interieur d'une lettre,
   et c'est pour cela qu'on le remarque. */
.titre-geant {
  font-size: clamp(2.5rem, 7.5vw, 5rem);
  letter-spacing: -.03em;
  line-height: 1.02;
  text-transform: uppercase;
  text-wrap: balance;
}
.titre-geant em {
  display: block;
  font-style: normal;
  background: linear-gradient(180deg, var(--os) 4%, var(--gemme) 52%, var(--eau) 116%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* Le halo se pose derriere la lettre, pas dessus : `drop-shadow` suit la
     forme du glyphe, la ou `text-shadow` serait masque par le decoupage du
     degrade et ne se verrait pas du tout. */
  filter: drop-shadow(0 0 34px rgba(var(--accent-rgb), .45));
}

.eyebrow {
  margin: 0 0 .9rem;
  font-size: 10px;
  letter-spacing: .38em;
  text-transform: uppercase;
  color: var(--sourd);
}

.titre-section {
  font-size: clamp(1.125rem, 2.4vw, 1.5rem);
  letter-spacing: .04em;
  text-transform: uppercase;
}

.prose { max-width: 60ch; color: var(--cendre); }
.prose strong { color: var(--os); font-weight: 700; }

.menu { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--sourd); }

/* --- Plaques -------------------------------------------------------------- */

/* La plaque de 2004 disait « la lumiere vient d'en haut a gauche » avec quatre
   bords pleins : clair en haut et a gauche, sombre en bas et a droite. On garde
   la phrase, on change l'accent. Le biseau devient **une seule arete speculaire
   d'un pixel** posee en haut (`inset 0 1px 0 var(--filet)`), et la surface
   laisse passer le ciel au lieu de le masquer.

   `overflow: hidden` n'est pas cosmetique : sans lui le fond du titre deborde
   du rayon et rend les deux coins hauts carres.

   `isolation: isolate` non plus : la lueur de survol est un pseudo-element en
   `mix-blend-mode`, et sans contexte d'empilement propre elle irait teinter ce
   qui se trouve derriere la plaque. */
.plaque {
  position: relative;
  isolation: isolate;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--plaque-a), var(--plaque-b));
  border: 1px solid var(--cadre);
  box-shadow:
    inset 0 1px 0 var(--filet),
    inset 0 -1px 0 var(--filet-bas),
    var(--ombre-2);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  overflow: hidden;
}

/* Le titre ne se detache plus par un aplat : il se detache par une teinte et
   un filet. Une barre pleine sur une surface translucide annule justement la
   translucidite a l'endroit qu'on regarde en premier. */
.plaque-titre {
  padding: .55rem .875rem;
  background: var(--plaque-tete);
  border-bottom: 1px solid var(--cadre);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--os);
}

.plaque-corps { padding: 1rem .875rem; }

/* --- Boutons et champs ---------------------------------------------------- */

/* Le bouton porte la meme lumiere que la plaque : une arete claire en haut,
   rien sur les cotes. La lueur ne paraît qu'au survol — au repos, un bouton
   qui brille est un bouton qui crie. */
.bouton {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .55rem 1rem;
  font: inherit; font-size: 12px; font-weight: 650;
  letter-spacing: .02em;
  color: var(--plaque-sombre);
  background: linear-gradient(180deg, var(--gemme), var(--brume));
  border: 0;
  border-radius: var(--r-sm);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45), var(--ombre-1);
  cursor: pointer;
  text-decoration: none;
  transition: box-shadow var(--vite), transform .08s ease, filter var(--vite);
}
.bouton:hover { filter: brightness(1.08); box-shadow: inset 0 1px 0 rgba(255,255,255,.45), var(--ombre-1), var(--lueur); }
.bouton:active { transform: translateY(1px); }
.bouton[disabled] { filter: grayscale(.8); cursor: not-allowed; box-shadow: var(--ombre-1); }

.bouton-fantome {
  background: rgba(var(--accent-rgb), .06);
  color: var(--gemme);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .40), inset 0 1px 0 var(--filet);
  backdrop-filter: var(--verre-flou-leger);
  -webkit-backdrop-filter: var(--verre-flou-leger);
}
.bouton-fantome:hover {
  background: rgba(var(--accent-rgb), .16); filter: none;
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .70), inset 0 1px 0 var(--filet), var(--lueur);
}

.bouton-sang {
  background: linear-gradient(180deg, #d8433f, var(--sang));
  color: #fff0f0;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .28), var(--ombre-1);
}
.bouton-sang:hover {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.28), var(--ombre-1),
              0 0 28px -8px rgba(217, 74, 43, .9);
}

/* Le champ est un creux, pas une boîte posée dessus. Le biseau d'origine
   disait déjà cela -- il prend la plaque a l'envers, sombre en haut a gauche
   et clair en bas a droite, quand .DCCadre fait le contraire. On garde ce
   sens et on adoucit le trait : plus de cadre a quatre traits, un rayon
   discret, et un liseré interne qui ne ferme pas le champ mais dessine la
   lèvre du creux. Le fond y descend d'un ton au lieu de s'en détacher, si
   bien que la saisie parait creusee dans la plaque et non collee dessus.

   L'outline de :focus-visible (§ plus haut) n'est pas touchee : c'est elle qui
   rend le champ visible au clavier, et une bordure douce ne la remplace pas. */
.champ {
  width: 100%;
  padding: .625rem .75rem;
  font: inherit; font-size: 13px;
  color: var(--os);
  border: 0;
  border-radius: var(--r-sm);
  background: rgba(var(--champ-rgb), .55);
  backdrop-filter: var(--verre-flou-leger);
  -webkit-backdrop-filter: var(--verre-flou-leger);
  box-shadow:
    inset 0 2px 6px rgba(0, 0, 0, .45),               /* le creux lui-meme */
    inset 0 0 0 1px rgba(var(--accent-rgb), .18),     /* la levre, un lisere et pas un cadre */
    0 1px 0 var(--filet);                             /* ce que la plaque renvoie en dessous */
  transition: background-color var(--vite), box-shadow var(--vite);
}
.champ:hover { background: rgba(var(--champ-rgb), .68); }
/* Au foyer, le creux s'allume : la levre passe a la gemme et une lueur sort du
   champ. `--gemme` et non un litteral cyan — sans cela le foyer serait la
   seule chose de l'interface a ne pas suivre le theme du salon. */
.champ:focus {
  background: rgba(var(--champ-rgb), .78);
  box-shadow:
    inset 0 2px 6px rgba(0, 0, 0, .45),
    inset 0 0 0 1px rgba(var(--accent-rgb), .55),
    var(--lueur);
}
/* L'outline generale se pose a 2px du bord : sur un champ arrondi elle fait
   une seconde boite autour de la premiere, et c'est precisement ce qu'un creux
   ne doit pas avoir. On la ramene contre le bord, ou elle epouse le rayon et
   se lit comme la lèvre qui s'allume. Elle reste pleine et opaque -- c'est
   l'indicateur de foyer au clavier, il ne se dilue pas. */
.champ:focus-visible {
  outline: 1px solid var(--gemme);
  outline-offset: 0;
}
.champ::placeholder { color: var(--sourd); }

.etiquette {
  display: block; margin-bottom: .3rem;
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--gemme);
}

/* --- Rang et état --------------------------------------------------------- */

.rang {
  font-size: 9px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--or); border: 1px solid rgba(224, 182, 92, .35);
  border-radius: var(--r-pilule);
  padding: .1rem .45rem; white-space: nowrap;
}
.rang-sage { background: rgba(224, 182, 92, .12); }

.pastille {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--gemme); vertical-align: middle;
}
.pastille-vide { background: transparent; border: 1px solid var(--sourd); }

/* --- Portail -------------------------------------------------------------- */

.enveloppe { max-width: 1180px; margin: 0 auto; padding: 0 var(--gouttiere); }

.barre {
  position: sticky; top: 0; z-index: 20;
  /* `flex-wrap` n'est pas un détail : sans lui le menu poussait la barre a
     488 px de large sur un telephone de 360, et c'est **toute la page** qui
     partait en defilement lateral -- le titre coupe, la prose coupée. Le menu
     descend donc sur sa propre ligne plutôt que de forcer la barre. */
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: .5rem 1rem;
  padding: .75rem var(--gouttiere);
  background: var(--barre);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  border-bottom: 1px solid var(--cadre);
  box-shadow: inset 0 -1px 0 var(--filet-bas), 0 1px 0 var(--filet);
}
/* La marque : le monogramme de 2008 (l'étoile et les deux croissants, D et C)
   suivi du mot. Le SVG est en ligne dans la page, donc il herite d'ici — et
   l'étoile s'allume au survol comme une lampe qu'on approche. */
.marque {
  display: inline-flex; align-items: center; gap: .6rem;
  font-size: 13px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--os); text-decoration: none;
}
.marque-astre { width: 34px; height: 22.5px; flex: none; overflow: visible; }
.astre-etoile { fill: var(--gemme); }
.astre-lune { fill: var(--cendre); opacity: .75; }
.marque:hover .astre-etoile { fill: #fff; }
.marque:hover .astre-lune { opacity: 1; }
.marque-astre * { transition: fill .25s ease, opacity .25s ease; }
.marque-mot span { color: var(--gemme); }

/* L'accroche tournante, sous le titre du portail. Elle est en italique et en
   retrait : c'est un aparte, pas une promesse commerciale. */
.accroche {
  margin: .85rem 0 0;
  font-style: italic; color: var(--cendre); opacity: .85;
  font-size: clamp(.9rem, 1.5vw, 1rem);
  min-height: 1.5em; /* pas de sursaut quand le texte arrive */
}
/* L'espace des guillemets est **insecable** : c'est la règle francaise, et
   c'est aussi ce qui empêche le « » de partir seul sur la ligne suivante
   quand l'accroche remplit toute la largeur. */
.accroche::before { content: "«\00a0"; }
.accroche:empty::before { content: none; }
.accroche::after { content: "\00a0»"; }
.accroche:empty::after { content: none; }
.barre nav { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }
.barre nav a { text-decoration: none; }
.barre nav a:hover { color: #fff; }

.hero {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
  align-items: center;
  /* `padding-block`, et surtout pas le raccourci `padding` : le héros porte
     aussi `.enveloppe`, et un raccourci avec un `0` horizontal **effacait sa
     gouttiere**. Au-delà de 1180px la marge automatique le cachait ; en
     dessous, le titre venait toucher le bord de l'écran. */
  padding-block: clamp(3rem, 9vw, 6rem) clamp(2rem, 6vw, 4rem);
}
@media (min-width: 900px) { .hero { grid-template-columns: 1.05fr .95fr; } }
/* Le titre de la maquette tient en deux lignes : « Bienvenue dans », puis le
   nom du produit. A colonnes egales il en prenait trois, et « dans » se
   retrouvait seul sur une ligne — ce qui casse la phrase en deux endroits au
   lieu d'un. La colonne de gauche prend donc la part du lion. */
@media (min-width: 900px) { .hero-portail { grid-template-columns: 1.22fr .78fr; gap: clamp(1rem, 2vw, 2rem); } }

.hero p { font-size: clamp(.95rem, 1.6vw, 1.0625rem); }
.hero-actions { display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1.75rem; }

/* La page hôte factice : le décor de TestLancement.htm, en 2004 c'était
   « Ma Page Perso qu'elle est bien ». On la garde comme démonstration. */
.hote {
  background: #d8d8d4;
  color: #333;
  border: 1px solid rgba(0, 0, 0, .55);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--ombre-3);
  font-size: 12px;
  /* Le demi-degré de travers : la démo est une page posée sur la table, pas un
     panneau de l'interface. La rotation fait deborder la boîte englobante de
     quelques pixels -- d'où la petite marge laterale, qui les lui rend. Sans
     elle, la page defile lateralement de 1 px, ce qui suffit a la casser. */
  transform: rotate(-.5deg);
  margin-inline: 6px;
}
.hote-chrome {
  display: flex; align-items: center; gap: .4rem;
  padding: .4rem .6rem; background: #b9b9b4; border-bottom: 1px solid #8a8a86;
}
.hote-chrome i { width: 9px; height: 9px; border-radius: 50%; background: #8a8a86; }
.hote-url {
  flex: 1; margin-left: .4rem; padding: .1rem .4rem; background: #efefec;
  border: 1px inset #8a8a86; font-family: var(--parole); font-size: 10px; color: #555;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hote-bandeau { padding: .6rem .75rem; background: #036; color: #fff; font-size: 15px; font-weight: 700; }
.hote-corps { display: grid; grid-template-columns: 1fr auto; gap: .75rem; padding: .75rem; align-items: start; }
.hote-texte p { margin: 0 0 .5rem; line-height: 1.45; }
.hote-legende { margin-top: .5rem; font-size: 10px; color: #666; font-style: italic; }
@media (max-width: 560px) {
  .hote-corps { grid-template-columns: 1fr; }
  /* Sur un telephone la place manque pour la coquetterie : la démo se redresse
     et rend ses marges. Elle reste une page posée, elle n'est plus de travers. */
  .hote { transform: none; margin-inline: 0; }
}

/* Le filet qui separe deux sections s'eteint sur les bords : un trait plein
   d'un bord a l'autre coupe la page en tranches, un trait qui s'efface la
   laisse respirer. */
.section { padding: clamp(3rem, 8vw, 5.5rem) 0; border-top: 0; position: relative; }
.section::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .40) 22%,
                              rgba(var(--accent-rgb), .40) 78%, transparent);
}
.section-tete { margin-bottom: 1.75rem; }

.grille-domaines { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); }

.monde-titre {
  display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
  margin: 2rem 0 .875rem;
  font-size: 12px; letter-spacing: .2em; text-transform: uppercase; color: var(--gemme);
}
.monde-titre small { color: var(--sourd); letter-spacing: .12em; }
.monde-titre::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, rgba(var(--accent-rgb),.5), transparent); }

.domaine { display: flex; flex-direction: column; text-decoration: none; color: inherit; transition: transform .18s ease, box-shadow .18s ease; }
.domaine:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(0, 20, 40, .5); }
.domaine .plaque-corps { flex: 1; display: flex; flex-direction: column; gap: .625rem; }
.domaine-note { margin: 0; font-size: 12px; font-style: italic; color: var(--cendre); }
.domaine-pied { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-top: auto; padding-top: .5rem; border-top: 1px solid rgba(var(--accent-rgb), .2); font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--sourd); }
.domaine-ferme { opacity: .62; }

.cartouches { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.cartouche h3 { font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: var(--gemme); margin-bottom: .4rem; }
.cartouche p { margin: 0; font-size: 12.5px; color: var(--cendre); }

.bloc-code {
  display: block; overflow-x: auto;
  padding: .875rem 1rem;
  background: rgba(var(--champ-rgb), .55);
  backdrop-filter: var(--verre-flou-leger);
  -webkit-backdrop-filter: var(--verre-flou-leger);
  border: 1px solid var(--cadre);
  border-radius: var(--r-sm);
  font-family: var(--parole); font-size: 12px; color: var(--gemme);
  white-space: pre;
}

.pied { padding: 2.5rem 0 3.5rem; border-top: 1px solid rgba(var(--accent-rgb), .18); font-size: 11.5px; color: var(--sourd); }
.pied a { color: var(--cendre); }

/* --- Invocation ----------------------------------------------------------- */

.porte {
  /* `svh` double le `vh` partout où une page tient en un écran : sur mobile,
     `100vh` compte la barre d'adresse retractee, donc la page dépassé le
     visible et ce qui était centre glisse vers le bas. `svh` est la petite
     hauteur, celle qui ne bouge pas. Le `vh` reste dessous pour qui ne
     connaît pas l'unité. */
  min-height: 100vh;
  min-height: 100svh;
  display: grid; place-items: center; padding: var(--gouttiere);
}
.porte-plaque { width: min(420px, 100%); }
.porte-plaque .plaque-corps { display: grid; gap: .875rem; }
.porte-lieu { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--sourd); }
.porte-note { margin: 0; font-size: 12px; font-style: italic; color: var(--cendre); }
.porte-erreur {
  padding: .625rem .75rem; font-size: 12px;
  color: #ffd9d9; background: rgba(153, 0, 0, .3);
  border: 1px solid rgba(217, 74, 43, .55);
  border-radius: var(--r-sm);
}

/* --- Le chat -------------------------------------------------------------- */

.appli {
  height: 100dvh;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-columns: minmax(0, 1fr);
}

.entete {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
  padding: .625rem .875rem;
  background: var(--entete);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  border-bottom: 1px solid var(--cadre);
  box-shadow: inset 0 1px 0 var(--filet);
}
.entete-lieu { display: flex; flex-direction: column; min-width: 0; }
.entete-lieu strong { font-size: 13px; letter-spacing: .06em; text-transform: uppercase; }
.entete-lieu small { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--cendre); }
.entete-outils { margin-left: auto; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
/* En etroit, les outils passent sous le nom du lieu plutôt que de deborder. */
@media (max-width: 620px) {
  .entete-outils { margin-left: 0; width: 100%; justify-content: space-between; }
}

/* `position: relative` : le retour au pied (.to-latest) flotte au-dessus du
   flux, et c'est la scene qui le porte. */
.scene { display: grid; grid-template-columns: minmax(0, 1fr); min-height: 0; position: relative; }
@media (min-width: 860px) { .scene { grid-template-columns: minmax(0, 1fr) 250px; } }

/* Le flux de parole */
.flux {
  overflow-y: auto; overflow-wrap: anywhere;
  padding: .75rem .875rem 1rem;
  font-family: var(--parole); font-size: 13px; line-height: 1.55;
  scrollbar-color: var(--eau) transparent;
}
.ligne { padding: .1rem 0; display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .5rem; }
.ligne-heure { color: var(--sourd); opacity: .75; font-size: 11px; padding-top: .12rem; font-variant-numeric: tabular-nums; }
.ligne-corps > b { color: var(--gemme); font-weight: 700; }
.ligne-corps > b::after { content: " :"; color: var(--sourd); font-weight: 400; }
/* Un nom qu'on peut viser se dit au survol, pas au repos : souligner tous les
   noms du flux ferait un listing de liens la ou il n'y a que des gens. */
.ligne-corps > b.nom-visable { cursor: pointer; }
.ligne-corps > b.nom-visable:hover { text-decoration: underline; text-underline-offset: 3px; }

.ligne-systeme .ligne-corps { color: var(--sourd); font-style: italic; }
.ligne-systeme .ligne-corps::before { content: "¤ "; color: var(--brume); font-style: normal; }
.ligne-sanction .ligne-corps { color: #ffbdbd; font-style: normal; }
.ligne-sanction .ligne-corps::before { content: "¤ "; color: var(--braise); }

/* L'apercu et la braise : des lignes systeme, mais du salon et non de la
   police. Le losange les distingue du signe des entrees et des sorties, sans
   les faire crier comme une sanction. */
.ligne-familier .ligne-corps { color: var(--or); }
.ligne-familier .ligne-corps::before { content: "\25c7 "; color: var(--braise); font-style: normal; }
.ligne-prive .ligne-corps { color: #ffe6b8; }
.ligne-prive .ligne-corps > b { color: var(--or); }
.ligne-prive .ligne-corps::before { content: "[privé] "; color: rgba(224, 182, 92, .6); }

/* --- Le markdown du flux --------------------------------------------------
   Le flux est déjà tout entier en `--parole` : le code littéral ne se
   distingue donc pas par sa fonte, mais par un fond. Le reste doit rester
   discret -- une ligne de chat en gras d'un bout à l'autre ne dit plus rien. */
.ligne-corps strong { color: var(--os); font-weight: 700; }
.ligne-corps em { font-style: italic; }
.ligne-corps del { color: var(--sourd); text-decoration-color: rgba(95, 159, 196, .7); }
.ligne-corps code {
  background: rgba(var(--accent-rgb), .14);
  border: 1px solid rgba(var(--accent-rgb), .28);
  border-radius: var(--r-xs);
  padding: 0 .25rem;
  color: var(--gemme);
  overflow-wrap: anywhere;
}

/* --- La visée -------------------------------------------------------------
   Viser n'est pas chuchoter. Le privé est en or et s'annonce « [prive] » ; la
   visée ne s'annonce pas du tout, parce qu'elle n'est pas un régime a part :
   c'est un message ordinaire, que tout le domaine lit, et qui dit a qui il
   s'adresse. Elle n'a donc droit qu'a une flèche.

   Sauf quand elle vous vise. La, elle doit se retrouver dans un flux qui
   défile : un filet dans la gouttière, et rien de plus -- pas de fond, qui
   ferait une boîte au milieu du listing. */
/* Le deux-points suit le nom de l'auteur (.ligne-corps > b::after). Quand la
   ligne dit aussi « a qui », il doit passer après : « Alwin → Brise : ... ».
   Le laisser devant donnerait « Alwin : → Brise ... », qui ouvre la parole
   avant d'avoir dit a qui elle s'adresse. */
.ligne-adressee .ligne-corps > b::after { content: ""; }
.visee-fleche { color: var(--sourd); }
.visee-fleche::after { content: " :"; color: var(--sourd); }
.ligne-visee .visee-fleche { color: var(--gemme); }

.ligne-visee-moi {
  border-left: 2px solid var(--gemme);
  margin-left: -2px;
  background: linear-gradient(90deg, rgba(127, 227, 255, .07), transparent 60%);
}
.ligne-visee-moi .visee-fleche { color: var(--os); }

/* Les jetons, dans la barre du bas : ce qu'on vise en ce moment. */
.visee-mot {
  font-size: 10px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--sourd); margin-right: .1rem;
}
.visee-jeton {
  font: inherit; font-size: 11px;
  color: var(--os); background: rgba(var(--accent-rgb), .16);
  border: 0; border-radius: var(--r-pilule);
  padding: .1rem .5rem; margin-left: .25rem;
  cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .38);
}
.visee-jeton i { font-style: normal; color: var(--sourd); margin-left: .3rem; }
.visee-jeton:hover { background: rgba(var(--accent-rgb), .30); }
.visee-jeton:hover i { color: var(--braise); }

/* Le viseur : il sort du champ, donc la console le porte. */
.console-saisie { position: relative; }
.viseur {
  position: absolute; bottom: calc(100% + .375rem); left: 0;
  z-index: 20; margin: 0; padding: .25rem; list-style: none;
  min-width: 12rem; max-width: min(20rem, 100%);
  background: var(--panneau);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  border-radius: var(--r-md);
  box-shadow:
    inset 0 0 0 1px var(--cadre),
    inset 0 1px 0 var(--filet),
    var(--ombre-3);
}
.viseur-choix {
  padding: .35rem .55rem; border-radius: var(--r-xs);
  font-size: 12px; color: var(--cendre); cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.viseur-choix-sur { color: var(--plaque-sombre); background: var(--gemme); }
.ligne-sort .ligne-corps { color: var(--gemme); font-style: italic; }
.ligne-sort .ligne-corps::before { content: "✦ "; font-style: normal; }
.ligne-info .ligne-corps { color: var(--cendre); white-space: pre-wrap; }
/* Un plateau de jeu n'est pas une ligne de système : ni italique, ni chevron. */
.ligne-jeu .ligne-corps { color: var(--cendre); }
.ligne-portee .ligne-corps > b { color: var(--or); }

.ligne-touche .ligne-corps > span { border-bottom: 1px dotted rgba(217, 74, 43, .6); }

/* Effets de sortilège sur une ligne */
.eff-echo .ligne-corps > span { text-shadow: 2px 0 rgba(127, 227, 255, .45), -2px 0 rgba(var(--accent-rgb), .35); }
.eff-grenouille .ligne-corps > span { color: #9fe08a; }
.eff-lueur .ligne-corps > b { text-shadow: 0 0 12px rgba(224, 182, 92, .8); color: var(--or); }

/* Le domaine entier, quand un sort le prend.
   C'est le seul endroit ou l'interface se permet un effet : un sortilège doit
   se voir, sinon le lancer ne veut rien dire. Tout revient a la normale seul. */
.appli { transition: background-color .8s ease; }
.flux { transition: filter .8s ease, background-color .8s ease; }

.appli.salle-brume .flux { filter: blur(1px) saturate(.55) contrast(.9); }

/* La braise du familier : ce n'est pas un sortilege, mais cela passe par la
   meme porte (lib/dragon.js, montreFamilier). Vingt secondes, et l'air tiedit.
   Beaucoup plus discret que la Nuit Glauque : le dragon a eternue, il n'a pas
   jete un sort. */
.appli.salle-braise { background: #1a0f0a; }
.appli.salle-braise .flux { background: rgba(217, 74, 43, .07); filter: saturate(1.12) brightness(1.04); }

.appli.salle-nuitglauque { background: #000810; }
.appli.salle-nuitglauque .flux { background: rgba(0, 0, 0, .55); filter: brightness(.72) saturate(.8); }
.appli.salle-nuitglauque .entete,
.appli.salle-nuitglauque .console { filter: brightness(.6); }
.appli.salle-nuitglauque .flux::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 5;
  background: radial-gradient(120% 90% at 50% 40%, transparent 30%, rgba(0, 0, 0, .55) 100%);
}

/* Les connectes */
.connectes {
  display: flex; flex-direction: column; min-height: 0;
  background: var(--panneau);
  border-left: 1px solid rgba(var(--accent-rgb), .25);
}
.connectes-liste { list-style: none; margin: 0; padding: .25rem; overflow-y: auto; display: grid; gap: 1px; }
.connecte {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: .5rem;
  padding: .35rem .5rem; font-size: 12px;
  border: 1px solid transparent; cursor: pointer; text-align: left;
  background: none; color: inherit; font-family: inherit; width: 100%;
}
.connecte:hover { background: rgba(var(--accent-rgb), .16); border-color: rgba(var(--accent-rgb), .3); }
.connecte-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.connecte-absent { opacity: .5; }
.connecte-ignore .connecte-nom { text-decoration: line-through; opacity: .55; }
.connecte-etoile { width: 7px; height: 7px; border-radius: 50%; background: var(--gemme); }

/* Le familier aux cotes de son proprietaire (docs/PET.md, etape 3). Il tient
   dans la case du nom et non dans une colonne a lui : la ligne est une grille
   de trois, et une quatrieme case aurait chasse le rang de tous les panneaux
   etroits pour montrer un animal. La mine porte la couleur, et rien d'autre —
   le salon n'a pas a lire les sept constantes de quelqu'un jauge par jauge. */
.connecte-familier { font-size: 11px; font-style: italic; opacity: .85; }
.connecte-familier::before { content: "\2022 "; font-style: normal; opacity: .6; }
.familier-vif { color: var(--gemme); }
.familier-calme { color: var(--cendre); }
.familier-bas { color: var(--or); }
.familier-mal { color: var(--braise); }

@media (max-width: 859px) {
  .connectes { position: fixed; inset: auto 0 0 0; max-height: 55dvh; z-index: 30;
    border-left: none; border-top: 1px solid var(--cadre); background: var(--panneau);
    backdrop-filter: var(--verre-flou); -webkit-backdrop-filter: var(--verre-flou);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    box-shadow: inset 0 1px 0 var(--filet), var(--ombre-3);
    transform: translateY(100%); transition: transform var(--lent); }
  .connectes[data-ouvert="oui"] { transform: none; }
}

/* La console */
.console {
  display: grid; gap: .5rem;
  padding: .625rem .875rem calc(.625rem + env(safe-area-inset-bottom));
  background: var(--barre);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  border-top: 1px solid var(--cadre);
  box-shadow: inset 0 1px 0 var(--filet);
}
/* La saisie ne prend plus toute la largeur. Sur un grand ecran une ligne de
   texte de 1400 px est illisible — l'oeil perd le debut de sa propre phrase —
   et elle passait sous le dragonpet, qui vit en bas a droite. On la borne, et
   ce qu'elle rend sert de place au compagnon. */
.console-saisie { display: flex; gap: .5rem; max-width: 62rem; width: 100%; }
.console-outils { max-width: 62rem; }
@media (min-width: 860px) {
  .console-saisie, .console-outils { padding-right: 5.5rem; }
}
.console-saisie .champ { font-family: var(--parole); }
.console-outils { display: flex; flex-wrap: wrap; gap: .375rem; align-items: center; }
.outil {
  padding: .28rem .6rem; font: inherit; font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--cendre); background: rgba(var(--halo-rgb), .22);
  border: 0; border-radius: var(--r-pilule); cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--cadre);
  transition: background-color var(--vite), box-shadow var(--vite), color var(--vite);
}
.outil:hover { color: var(--os); background: rgba(var(--accent-rgb), .28); box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .55); }
.outil[aria-pressed="true"] { color: var(--plaque-sombre); background: var(--gemme); box-shadow: var(--lueur); }

/* Les trois conforts (BACKLOG.md §5) : les frimousses, le son, le suivi du
   flux. Un tiroir au-dessus de la console — la console le porte, comme elle
   porte deja le viseur, et pour la meme raison : il sort du champ. */
.console { position: relative; }
.drawer {
  /* A gauche, et non a droite : le tiroir appartient a la rangee d'outils qui
     l'ouvre, et la console garde 5.5rem a droite pour le dragonpet. */
  position: absolute; bottom: calc(100% + .375rem); left: .875rem;
  z-index: 22; padding: .5rem; display: grid; gap: .375rem;
  max-width: min(22rem, calc(100vw - 2rem));
  background: var(--panneau);
  backdrop-filter: var(--verre-flou);
  -webkit-backdrop-filter: var(--verre-flou);
  border-radius: var(--r-md);
  box-shadow: inset 0 0 0 1px var(--cadre), inset 0 1px 0 var(--filet), var(--ombre-3);
}
.faces { display: grid; grid-template-columns: repeat(6, 1fr); gap: .25rem; }
.face {
  display: grid; place-items: center; padding: .35rem;
  color: var(--gemme); background: rgba(var(--halo-rgb), .22);
  border: 0; border-radius: var(--r-sm); cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--cadre);
  transition: background-color var(--vite), color var(--vite);
}
.face:hover { color: var(--os); background: rgba(var(--accent-rgb), .34); }
.face svg { width: 22px; height: 22px; }
.comfort-row { display: flex; }
.comfort-row .outil { width: 100%; text-align: left; }
.comfort-note { margin: .15rem 0 0; font-size: 10px; line-height: 1.5; color: var(--sourd); }

/* Le retour au pied. Ce qui remplace le defilement qu'on n'a plus : sans lui,
   couper le suivi voudrait dire que les lignes arrivent en dessous et que
   rien ne le dit. */
.to-latest {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: .75rem; z-index: 18;
  padding: .3rem .75rem; font: inherit; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--plaque-sombre); background: var(--gemme);
  border: 0; border-radius: var(--r-pilule); cursor: pointer;
  box-shadow: var(--ombre-2), var(--lueur);
}

/* Les frimousses. Elles sont dessinees et non ecrites : le conteneur ne porte
   aucune fonte d'emoji, et les sept boutons de soin du dragonpet ont deja
   rendu sept carres vides pour l'avoir oublie. Le raccourci tape voyage a
   cote du dessin, et c'est la feuille qui choisit lequel se montre — couper
   les frimousses atteint ainsi les lignes deja affichees. */
.emoticon { display: inline-block; vertical-align: -.2em; line-height: 1; }
.emoticon svg { width: 1.15em; height: 1.15em; }
/* Le meme dessin sert dans le flux et dans le tiroir, donc les deux repondent
   aux memes regles. Sans cette ligne, le choix rendait douze disques noirs :
   un SVG sans regle se remplit en noir et ne se trace pas. */
.emoticon circle, .emoticon path, .face circle, .face path {
  fill: none; stroke: currentColor; stroke-width: 1.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.emoticon .solid, .face .solid { fill: currentColor; stroke: none; }
.emoticon i { display: none; font-style: normal; }
.no-faces .emoticon svg { display: none; }
.no-faces .emoticon i { display: inline; }

.jauge {
  display: flex; align-items: center; gap: .4rem;
  font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--sourd);
}
/* display:block sur les deux : un <span> inline ignoré height, et la jauge
   se retrouverait vide quel que soit son remplissage. */
.jauge-barre { display: block; width: 84px; height: 7px; border-radius: var(--r-pilule); overflow: hidden; background: rgba(0, 0, 0, .45); box-shadow: inset 0 0 0 1px var(--cadre); }
.jauge-remplie { display: block; height: 100%; border-radius: var(--r-pilule); background: linear-gradient(90deg, var(--eau), var(--gemme)); transition: width .4s ease; }

.mouchard {
  padding: .3rem .6rem; font-size: 11px;
  color: #ffd9d9; background: rgba(153, 0, 0, .28); border: 1px solid rgba(217, 74, 43, .5);
  border-radius: var(--r-sm);
}
.mouchard:empty { display: none; }

/* Le plateau de jeu, pose dans le flux */
.plateau { margin: .5rem 0; display: inline-block; }
.plateau-grille { display: grid; grid-template-columns: repeat(7, 26px); gap: 3px; padding: 6px; background: var(--profond); border: 1px solid var(--plaque-sombre); }
.case { width: 26px; height: 26px; border-radius: 50%; background: rgba(1, 20, 35, .85); box-shadow: inset 0 2px 4px rgba(0,0,0,.5); }
.case-1 { background: radial-gradient(circle at 35% 30%, #ffd98a, var(--or)); }
.case-2 { background: radial-gradient(circle at 35% 30%, #ff8f7a, var(--sang)); }
.plateau-etat { font-size: 11px; color: var(--cendre); padding: .35rem 0 0; }
.essais { list-style: none; margin: .25rem 0 0; padding: 0; font-size: 12px; display: grid; gap: .15rem; }
.essais code { color: var(--gemme); letter-spacing: .2em; }

/* --- Le dragonpet ---------------------------------------------------------
   Le compagnon du salon, en bas a droite du chat. Il remplace la page du
   hamster (retiree le 2 septembre 2026) ; les sept constantes du carnet p.36
   sont les memes, `.constantes` sert donc encore et n'a pas bouge.

   `position: fixed` et non `absolute` : le chat est une grille de la hauteur
   de l'ecran dont le flux defile, et un compagnon en `absolute` remonterait
   avec la conversation. Il doit rester ou il est.

   La console lui laisse la place : voir `.console-saisie`, bornee a 62rem et
   dotee d'un `padding-right` au-dela de 860 px. Sans cela le bouton Envoyer
   passait dessous. */
.constantes { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }
.constantes li { display: grid; grid-template-columns: 5.5rem 1fr 2ch; gap: .5rem; align-items: center; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--cendre); }
.constantes .jauge-barre { width: 100%; }

.dragonpet {
  position: fixed;
  right: 1rem;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  z-index: 40;
  display: flex; flex-direction: column; align-items: flex-end; gap: .625rem;
  pointer-events: none;                 /* seuls les enfants se cliquent */
}
.dragonpet > * { pointer-events: auto; }

/* La bulle : le dragon lui-meme, toujours visible une fois adopte. */
.dragonpet-bulle {
  display: grid; place-items: center;
  width: 54px; height: 54px; padding: 0;
  border: 0; border-radius: 50%;
  cursor: pointer;
  background: var(--panneau);
  backdrop-filter: var(--verre-flou); -webkit-backdrop-filter: var(--verre-flou);
  box-shadow: inset 0 0 0 1px var(--cadre), inset 0 1px 0 var(--filet), var(--ombre-2);
  transition: transform var(--vite), box-shadow var(--vite);
}
.dragonpet-bulle:hover { transform: translateY(-2px) scale(1.04); box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .6), var(--ombre-2), var(--lueur); }
.dragonpet-bulle svg { width: 32px; height: 32px; }
.dp-corps { fill: var(--eau); }
.dp-aile { fill: var(--profond); }
.dp-corne { fill: var(--cendre); }
.dp-oeil { fill: var(--gemme); }
.dp-fele { fill: none; stroke: var(--gemme); stroke-width: 1.2; opacity: .8; }

/* La mine se lit sur la couleur, pas sur une etiquette : on doit savoir
   comment il va d'un coup d'oeil, sans ouvrir le panneau. */
.dragonpet-vif   .dp-corps { fill: var(--gemme); }
.dragonpet-vif   .dp-oeil  { fill: var(--plaque-sombre); }
.dragonpet-calme .dp-corps { fill: var(--brume); }
.dragonpet-bas   .dp-corps { fill: var(--or); }
.dragonpet-bas   .dp-oeil  { fill: var(--plaque-sombre); }
.dragonpet-mal   .dp-corps { fill: var(--braise); }
.dragonpet-mal   .dp-oeil  { fill: var(--os); }

/* --- La parure — docs/PET.md etape 5 ----------------------------------------
   La couleur de la peau, la forme des ailes, les cornes, la couleur des yeux
   et un bibelot, gagnes par le soin et par le temps. Le panneau ecrit les cinq
   codes en classes sur la racine du SVG (`public/scripts/dragonpet.js`), et
   tout le dessin est ici : le SVG portait deja `dp-corps`, `dp-aile` et
   `dp-corne`, il voulait des parametres et non une reecriture.

   **La specificite est la regle, pas l'ordre.** Une regle de parure porte deux
   classes de la racine (`.dp-parure.corps-braise`) et bat donc les regles de
   mine ci-dessus, qui n'en portent qu'une (`.dragonpet-vif .dp-corps`), ou
   qu'elles soient ecrites dans le fichier. C'est voulu, et c'est reversible :
   deplacer ce bloc ne change rien.

   **Le choix par defaut de chaque emplacement n'a aucune regle**, et c'est ce
   qui garde la mine visible. `corps-ardoise` s'ecrit bien dans la classe, mais
   ne peint rien : le corps d'un dragon dont le maitre n'a pas choisi de peau
   continue de dire comment il va. Une voie reclamee montre le choix, une voie
   libre montre l'animal. */
.dp-parure {
  /* La seule couleur que la palette n'avait pas : il n'y a pas de vert dans ce
     produit, et il n'y en a pas besoin ailleurs. Elle est declaree ici, sur le
     dragon, plutot que dans `:root` — une teinte qui ne sert qu'a une bete
     n'est pas une couleur du produit. */
  --dp-mousse: #4b8f5a;
  overflow: visible;              /* les cornes hautes sortent du viewBox */
}

.dp-parure.corps-braise .dp-corps { fill: var(--braise); }
.dp-parure.corps-mousse .dp-corps { fill: var(--dp-mousse); }
.dp-parure.corps-givre  .dp-corps { fill: var(--os); }

/* Les ailes changent de forme **et** de couleur, et la couleur n'est pas un
   ornement de plus : par defaut elles sont en `--profond`, presque la teinte du
   panneau, et a 32 px une aile invisible qui grandit reste invisible. La feuille
   de contact du 7 septembre 2026 le dit sans appel — Membrane, Voile et Faucon
   y etaient le meme dessin. Une forme qu'on ne distingue pas n'est pas une
   forme, c'est une case dans une table.

   `transform-box: view-box` est indispensable : sans lui l'origine se lit dans
   la boite de chaque chemin, et les deux ailes partent chacune de son cote. */
.dp-parure .dp-aile { transform-box: view-box; transform-origin: 20px 14px; }
.dp-parure.aile-voile    .dp-aile { transform: scale(1.15, 1.8); fill: var(--eau); }
.dp-parure.aile-faucon   .dp-aile { transform: scale(1.55, .7); fill: var(--cendre); }
.dp-parure.aile-papillon .dp-aile { transform: scale(1.45); fill: var(--violet); }

/* Les cornes tiennent dans deux triangles de cinq pixels : la meme regle vaut,
   et l'echelle doit etre franche. Le bord de la bulle ne les coupe pas — elle
   n'a pas d'`overflow: hidden`, et le SVG porte `overflow: visible`. */
.dp-parure .dp-corne { transform-box: view-box; transform-origin: 20px 9px; }
.dp-parure.corne-lyre      .dp-corne { transform: scale(1.25, 2.2); }
.dp-parure.corne-bouquetin .dp-corne { transform: scale(1.6, 2.6); fill: var(--os); }
.dp-parure.corne-couronne  .dp-corne { transform: scale(2.1, 1.5); fill: var(--or); }

.dp-parure.oeil-ambre .dp-oeil { fill: var(--or); }
.dp-parure.oeil-encre .dp-oeil { fill: var(--plaque-sombre); }
.dp-parure.oeil-astre .dp-oeil { fill: var(--violet); }

/* Le bibelot est la seule piece qui soit une forme et non une regle : le reste
   recolore ou deplace quelque chose que le dragon a deja, et il n'a rien au
   cou a deplacer. Seul le bibelot choisi est ecrit dans la page, et la racine
   ne porte donc pas de classe `bibelot-*` — elle n'aurait rien a peindre, et
   une classe qui ne style rien est du poids mort servi a chaque page. */
.dp-bibelot { fill: var(--or); }
.dp-bibelot-fil { fill: none; stroke: var(--or); stroke-width: .7; }
.dp-bibelot-anneau { fill: none; stroke: var(--or); stroke-width: 1.2; }

/* La penderie : cinq rangs, et chaque choix de chaque rang. Un choix pas
   encore gagne reste **visible** et desactive, son prix dans l'infobulle —
   « pas encore : trois jours, et ecailles a 95 ». Le cacher aurait ete une
   ligne de moins et c'est tout l'interet de l'etape : « des choses qui se
   debloquent » ne marche que si l'on voit celle qui ne l'est pas. */
.dp-penderie { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.dp-penderie li { display: grid; gap: .2rem; }
.dp-slot { font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--sourd); }
.dp-choix { display: flex; flex-wrap: wrap; gap: .25rem; }
.dp-choix .outil { font-size: 9.5px; padding: .2rem .45rem; }
.dp-choix .outil[disabled] { opacity: .38; cursor: not-allowed; }
.dp-porte { box-shadow: inset 0 0 0 1px var(--gemme); color: var(--gemme); }
.dp-vestiaire { margin-left: auto; }

/* L'oeuf respire, tres lentement : c'est ce qui dit qu'il attend quelque
   chose de vous. Rien d'autre ne bouge dans ce coin de l'ecran. */
.dragonpet-oeuf { width: auto; padding: .4rem .75rem .4rem .5rem; border-radius: var(--r-pilule); gap: .4rem; grid-auto-flow: column; }
.dragonpet-oeuf svg { width: 26px; height: 26px; }
.dragonpet-etiquette { font-size: 11px; font-weight: 600; color: var(--gemme); letter-spacing: .02em; }
@media (prefers-reduced-motion: no-preference) {
  .dragonpet-oeuf { animation: couve 3.6s ease-in-out infinite; }
}
@keyframes couve {
  0%, 100% { box-shadow: inset 0 0 0 1px var(--cadre), var(--ombre-2); }
  50%      { box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .55), var(--ombre-2), var(--lueur); }
}

.dragonpet-panneau { width: min(268px, calc(100vw - 2rem)); }
.dragonpet-panneau .plaque-corps { padding: .75rem; display: grid; gap: .625rem; }
.dragonpet-tete { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.dragonpet-fermer {
  border: 0; background: none; color: var(--sourd); cursor: pointer;
  font-size: 16px; line-height: 1; padding: 0 .15rem;
}
.dragonpet-fermer:hover { color: var(--os); }
.dragonpet-dit { margin: 0; font-size: 12px; font-style: italic; color: var(--cendre); min-height: 2.4em; }
.dragonpet-soins { display: flex; flex-wrap: wrap; gap: .3rem; }
.dragonpet-soins .outil { font-size: 9.5px; padding: .25rem .5rem; }

/* Les trois teintes de jauge du carnet : au-dessus de 60 tout va bien, entre
   30 et 60 cela se degrade, en dessous il faut faire quelque chose. */
.jauge-remplie.dp-haut  { background: linear-gradient(90deg, var(--eau), var(--gemme)); }
.jauge-remplie.dp-moyen { background: linear-gradient(90deg, #8a6a1f, var(--or)); }
.jauge-remplie.dp-bas   { background: linear-gradient(90deg, #5c1010, var(--braise)); }

/* Sous 860 px le tiroir des presences monte du bas et couvrirait le dragon :
   il se range au-dessus de la console, un peu plus haut. */
@media (max-width: 859px) {
  .dragonpet { right: .625rem; bottom: calc(4.2rem + env(safe-area-inset-bottom)); }
  .dragonpet-bulle { width: 46px; height: 46px; }
  .dragonpet-bulle svg { width: 27px; height: 27px; }
  /* A cette taille le dragon flotte juste au-dessus des outils, et non plus a
     cote de la saisie (voir la remonte plus haut) : sans reserve, sa bulle de
     46px passait par-dessus le premier rang de jetons et en avalait un
     ("CORPS", entre autres), impossible a viser du doigt. Le meme reflexe qu'a
     860px pour `.console-saisie` -- laisser au compagnon la place qu'il
     occupe -- mais applique au rang de jetons plutot qu'a la ligne de champ. */
  .console-outils { padding-right: 3.5rem; }
}

.saut { position: absolute; left: -9999px; }
.saut:focus { left: .5rem; top: .5rem; z-index: 100; padding: .5rem .75rem; background: var(--gemme); color: #001a2e; }

/* ===========================================================================
   Ce qui est venu après : les autres jeux, le corps, le forum, le pont-levis.
   Même palette, mêmes plaques biseautees — rien de nouveau cote couleurs.
   =========================================================================== */

/* --- L'échiquier ---------------------------------------------------------
   Neuf colonnes : la première porte le chiffre de la rangee, les huit autres
   les cases. La dernière ligne porte les lettres de colonnes. */
.echiquier {
  display: grid;
  grid-template-columns: 16px repeat(8, minmax(0, 30px));
  gap: 0;
  padding: 6px;
  background: var(--profond);
  border: 1px solid var(--plaque-sombre);
  width: max-content;
  max-width: 100%;
}
.ec-bord {
  display: grid; place-items: center;
  font: 9px/1 var(--chrome); color: var(--sourd);
  min-height: 14px;
}
.ec-case {
  display: grid; place-items: center;
  width: 30px; height: 30px;
  font-size: 21px; line-height: 1;
  background: #cfe4ef;   /* case claire */
  color: #f7fbfd;        /* les Blancs, sur fond clair : cernes en dessous */
  text-shadow: 0 0 1px #012, 0 1px 0 #012, 1px 0 0 #012, -1px 0 0 #012, 0 -1px 0 #012;
}
.ec-sombre { background: #4c7f9c; }
.ec-noir { color: #06131c; text-shadow: 0 0 1px rgba(255,255,255,.5); }
.ec-echec { color: var(--braise); }
.plateau-coups { font: 10px/1.5 var(--parole); color: var(--sourd); padding-top: .3rem; max-width: 32rem; }

/* --- Le damier ----------------------------------------------------------- */
.damier {
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 24px));
  padding: 6px;
  background: var(--profond);
  border: 1px solid var(--plaque-sombre);
  width: max-content;
  max-width: 100%;
}
.da-case { width: 24px; height: 24px; background: #cfe4ef; display: grid; place-items: center; }
.da-sombre { background: #2d5f7d; }
.da-num { font: 8px/1 var(--chrome); color: rgba(232, 244, 250, .35); }
.da-pion { width: 17px; height: 17px; border-radius: 50%; box-shadow: inset 0 -2px 3px rgba(0,0,0,.45); }
.da-blanc { background: radial-gradient(circle at 35% 30%, #ffffff, #b9ccd6); }
.da-noir { background: radial-gradient(circle at 35% 30%, #4a5157, #14181b); }
/* Une dame porte une couronne : un second cercle, en or. */
.da-dame { box-shadow: inset 0 -2px 3px rgba(0,0,0,.45), 0 0 0 2px var(--or); }

/* --- L'arène ------------------------------------------------------------- */
.xf-arene { display: flex; flex-wrap: wrap; gap: 1rem; padding: .5rem 0; }
.xf-combattant { min-width: 12rem; flex: 1 1 12rem; }
.xf-nom { font-size: 12px; font-weight: 700; }
.xf-pret { color: var(--gemme); font-weight: 400; font-size: 10px; }
.xf-gele { color: #9fd4ee; font-weight: 400; font-size: 10px; }
.xf-combattant .jauge { display: block; margin: .2rem 0; }
.xf-combattant .jauge i { display: block; height: 100%; transition: width .4s ease; }
.jauge-vie { background: linear-gradient(90deg, var(--sang), var(--braise)); }
.jauge-alcool { background: linear-gradient(90deg, var(--or), #ffe9b0); }
/* Les trois jauges de niveaux.txt. L'énergie prend l'or de la fiole ;
   le charisme, la brume qui va vers la gemme. */
.jauge-energie { background: linear-gradient(90deg, var(--braise), var(--or)); }
.jauge-charisme { background: linear-gradient(90deg, var(--brume), var(--gemme)); }
.xf-combattant .jauge-vie,
.xf-combattant .jauge-mana {
  height: 7px; background: rgba(0,0,0,.45); border: 1px solid rgba(var(--accent-rgb),.35);
}
.xf-combattant .jauge-vie i { background: linear-gradient(90deg, var(--sang), var(--braise)); }
.xf-combattant .jauge-mana i { background: linear-gradient(90deg, var(--eau), var(--gemme)); }
.xf-chiffres { font: 10px/1.4 var(--parole); color: var(--sourd); }
.xf-recits { list-style: none; margin: .5rem 0 0; padding: 0; font-size: 11px; color: var(--cendre); }
.xf-recits li { padding: .25rem 0; border-top: 1px solid rgba(var(--accent-rgb),.15); }

/* --- Le corps : capacites et objets --------------------------------------
   Le serveur dit de combien on y voit ; c'est ici que l'écran s'assombrit.
   Rien de tout cela n'est envoye aux autres : c'est votre vue, pas la leur. */
.appli { --noir: 0; --trouble: 0px; --couleurs: 1; }
.jauge-ivresse:has(#ivresse[hidden]) { display: none; }
.etat-corps { font-size: 11px; color: var(--or); }

.voit-trouble .flux { filter: blur(var(--trouble)) saturate(var(--couleurs)); }
.voit-noir .flux { position: relative; }
.voit-noir .flux::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: #000; opacity: calc(var(--noir) * .85);
  transition: opacity .8s ease;
}
/* Les hallucinations de user.object.alcohol.txt : "objets qui apparaissent,
   effets qui se declenchent". Une respiration lente, rien de clignotant. */
@media (prefers-reduced-motion: no-preference) {
  .hallucine .flux { animation: derive 9s ease-in-out infinite; }
}
@keyframes derive {
  0%, 100% { transform: none; }
  35% { transform: translateX(1.5px) skewY(.15deg); }
  70% { transform: translateX(-1.5px) skewY(-.15deg); }
}

/* Le fond change de teinte quand le pianiste change d'air. */
.appli { --air: transparent; }
.appli[data-air="1"] { --air: #00b0b0; }
.appli[data-air="2"] { --air: #6a8fd0; }
.appli[data-air="3"] { --air: #c08fd0; }
/* L'air se lit sur un filet, pas en repeignant le chrome : il colore le bord
   haut de l'entete et rien d'autre. */
.appli[data-air]:not([data-air="0"]) .entete { box-shadow: inset 0 2px 0 var(--air); }

.connecte-attendus {
  padding: .4rem .6rem; font-size: 10px; color: var(--sourd);
  border-top: 1px solid rgba(var(--accent-rgb), .2);
}

/* --- Le forum ------------------------------------------------------------ */
.page-forum { padding: var(--gouttiere); max-width: 60rem; margin: 0 auto; }
.forum-entete { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: flex-end; }
.forum-entete h1 { font-size: clamp(1.3rem, 4vw, 2rem); margin: .2rem 0; letter-spacing: .04em; }
.forum-retour { font-size: 11px; letter-spacing: .22em; text-transform: uppercase; color: var(--sourd); }
.forum-chemin { color: var(--cendre); font-size: 12px; margin: 0; max-width: 44rem; }
.forum-corps { margin-top: 1.5rem; display: grid; gap: 1.25rem; }
.forum-fil { font-size: 11px; color: var(--sourd); margin: 0; }
.forum-attente { color: var(--sourd); font-size: 12px; font-style: italic; }

.forum-liste { list-style: none; margin: 0; padding: 0; }
.forum-liste li { padding: .55rem .8rem; border-top: 1px solid rgba(var(--accent-rgb), .18); }
.forum-liste li:first-child { border-top: 0; }
.forum-liste a { font-weight: 700; }
.forum-liste small { display: block; color: var(--sourd); font-size: 11px; }
.forum-epingle, .forum-clos {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
  padding: 1px 5px; border: 1px solid currentColor; vertical-align: 2px;
}
.forum-epingle { color: var(--or); }
.forum-clos { color: var(--sourd); }

.forum-messages { list-style: none; margin: 0; padding: 0; }
.forum-message { padding: .8rem; border-top: 1px solid rgba(var(--accent-rgb), .18); }
.forum-message:first-child { border-top: 0; }
.forum-signature { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.forum-signature small { color: var(--sourd); font-size: 10px; }
.forum-texte { margin-top: .4rem; font-family: var(--parole); font-size: 12.5px; white-space: pre-wrap; overflow-wrap: anywhere; }
.forum-retire .forum-texte { color: var(--sourd); }
.forum-touche { font-size: 10px; color: var(--braise); margin: .3rem 0 0; }
.forum-conseil { display: flex; gap: .5rem; flex-wrap: wrap; margin: 0; }
.forum-corps textarea.champ { font-family: var(--parole); resize: vertical; }

.forum-moi { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
/* La veille est ouverte : le forum se met à jour tout seul (BACKLOG.md §5).
   Le point respire, et s'arrête pour qui a demande moins de mouvement. */
.forum-direct {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: 9px; letter-spacing: .14em; text-transform: uppercase; color: var(--sourd);
}
.forum-direct::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--gemme); box-shadow: 0 0 6px var(--gemme);
  animation: battement 2.4s ease-in-out infinite;
}
@keyframes battement { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* --- Le pont-levis ------------------------------------------------------- */
.pontlevis { display: grid; place-items: center; min-height: 100vh; padding: var(--gouttiere); }
.pontlevis { min-height: 100svh; } /* voir `.porte` : la petite hauteur, sur mobile */
.pontlevis .plaque { max-width: 30rem; padding: 1.5rem; text-align: center; }
.pontlevis h1 { font-size: 1.3rem; letter-spacing: .05em; margin: 0 0 .8rem; }
.pontlevis .message { font-family: var(--parole); color: var(--gemme); margin: 0 0 1.2rem; }
.pontlevis form { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; align-items: flex-end; }
.pontlevis label { font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: var(--sourd); display: block; }
.pontlevis input { background: rgba(0,0,0,.35); border: 1px solid var(--eau); color: var(--os); padding: .45rem .6rem; font-family: var(--parole); }
.pontlevis button { background: var(--profond); border: 1px solid var(--plaque-clair); color: var(--os); padding: .45rem 1rem; font-family: var(--chrome); cursor: pointer; }
.pontlevis .note { font-size: 11px; color: var(--sourd); margin: 1.2rem 0 0; }

/* --- La carte (404) ------------------------------------------------------ */
/*
   Le framework de 2008 nommait déjà « carte » son programme d'erreur
   (dms.lib.run.ini, [error] prog = "carte") et web.carte.inc.php ne disait
   qu'une chose : « Vous seriez vous perdu ? ». On lui donne le ciel qu'il
   n'avait pas eu le temps d'avoir.
*/
.page-carte {
  display: grid; place-items: center;
  min-height: 100vh;
  min-height: 100svh; /* voir `.porte` : la petite hauteur, sur mobile */
  background-image:
    url("/marque/fond-nuit.svg"),
    radial-gradient(120% 80% at 50% -10%, rgba(var(--halo-rgb), .45), transparent 60%);
  background-repeat: no-repeat, no-repeat;
  background-size: cover, auto;
  background-position: center;
}
.carte-perdue { max-width: 34rem; text-align: center; padding: 3rem var(--gouttiere); }
.carte-perdue .hero-actions { justify-content: center; }
.carte-perdue .prose { margin-inline: auto; }
/* L'astre dérive doucement : c'est la seule chose qui bouge sur cette page,
   et elle s'arrête si le visiteur a demande moins de mouvement. */
.carte-astre {
  width: min(240px, 60vw); height: auto; margin-bottom: 1.5rem;
  opacity: .9; animation: flotte 14s ease-in-out infinite;
}
/* `flotte` et non `derive` : `derive` existe deja pour l'hallucination de
   l'ivresse, et la seconde declaration ecrasait la premiere en silence. */
@keyframes flotte {
  0%, 100% { transform: translateY(-6px); }
  50%      { transform: translateY(6px); }
}

/* --- L'Aide -------------------------------------------------------------- */
/*
   « Site : Compte utilisateur, Aide » (structure site.txt, 2008). L'Aide était
   au menu dès le premier plan du site et n'a jamais existe ; elle est ici.
   La page reprend la mise en page de la présentation -- barre, sections,
   cartouches -- parce qu'elle est du même genre : de la prose, lue une fois.
*/
.aide-tete { padding-top: clamp(2rem, 6vw, 3.5rem); }
.aide-tete .prose { max-width: 62ch; }
.aide .forum-liste { padding: .25rem 0; }
.aide .forum-liste b { color: var(--os); }
.aide .forum-liste code { color: var(--gemme); }
.aide-deux { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(258px, 1fr)); align-items: start; }
.aide .cartouche h3 a { color: var(--gemme); }

/* --- Le filet d'ornement -------------------------------------------------- */
/*
   « divider-ornament.gif » (252x9). Servi en masque et non en image : la
   couleur vient donc de `background-color`, et le filet suit le thème au lieu
   d'être figé en noir comme l'était le GIF.
*/
.ornement {
  height: 9px; border: 0; margin: 2.5rem auto;
  width: min(252px, 60%);
  background-color: var(--brume);
  -webkit-mask: url("/marque/ornement.svg") center / contain no-repeat;
  mask: url("/marque/ornement.svg") center / contain no-repeat;
  opacity: .8;
}

/* --- Le parchemin --------------------------------------------------------- */
/*
   La nappe de 2004 (images/Nappes/N_Parchemin.jpg), pour les surfaces qui
   doivent avoir l'air écrites. Le texte y repasse en encre sombre : un creme
   pareil ne supporte pas l'os de la palette de nuit.
*/
.parchemin {
  background: #f3e6c8 url("/marque/parchemin.svg") center / cover no-repeat;
  color: #2b2114;
  border: 1px solid #b9a274;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset, 0 8px 24px rgba(0, 0, 0, .35);
}
.parchemin a { color: #6b3f10; text-decoration-color: rgba(107, 63, 16, .45); }
.parchemin .domaine-note, .parchemin small { color: #5c4a30; }

/*
   Le fil de forum est la première surface a le porter. Une plaque en parchemin
   garde son bandeau de titre, mais le bleu de `.plaque-titre` sur du creme est
   illisible : il repasse en encre sur un bandeau brûle. Tout ce qui suit ne
   fait que ramener sur le parchemin les couleurs que la palette de nuit avait
   posées pour le fond sombre.
*/
.plaque.parchemin .plaque-titre {
  background: linear-gradient(#e8d6b0, #d6c091);
  border-bottom: 1px solid #b9a274;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .6);
  color: #2b2114;
}
.parchemin .forum-message { border-top-color: rgba(120, 96, 60, .35); }
.parchemin .forum-signature b { color: #4a2f0c; }
.parchemin .rang { color: #6b4a10; border-color: rgba(107, 74, 16, .4); }
.parchemin .rang-sage { background: rgba(107, 74, 16, .1); }
.parchemin .forum-touche { color: #8c2f16; }
.parchemin .forum-retire .forum-texte { color: #6a5a42; }

/*
   La page /confirm est la seconde surface a porter le parchemin, et elle a
   une autre forme : une plaque centrée, sans bandeau de titre. Les couleurs de
   `.pontlevis` sont posées pour le fond sombre et deviennent illisibles sur du
   creme -- elles repassent donc en encre, comme au-dessus pour le fil.
*/
.pontlevis .plaque.parchemin { color: #2b2114; }
.pontlevis .parchemin h1 { color: #2b2114; }
.pontlevis .parchemin .message { color: #4a2f0c; }
.pontlevis .parchemin .note { color: #5c4a30; }
.parchemin .ornement { background-color: #8a7550; margin: 1.2rem auto; }

/*
   Le parchemin de sortilèges, déroulé dans le flux (BACKLOG.md §5). Une ligne
   du flux n'a ni chevron ni italique quand elle porte un parchemin : ce n'est
   pas le système qui parle, c'est une page qu'on regarde.
*/
.ligne-parchemin .ligne-corps { font-style: normal; }
.ligne-parchemin .ligne-corps::before { content: none; }
.parchemin-sorts {
  display: inline-block; max-width: 34rem; margin: .5rem 0;
  padding: .9rem 1.2rem 1rem; border-radius: 2px;
}
.parchemin-titre {
  font-family: var(--chrome); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: #6b4a10; text-align: center;
}
.parchemin-sorts .ornement { width: min(180px, 70%); margin: .6rem auto .8rem; }
.sorts-liste { list-style: none; margin: 0; padding: 0; }
.sorts-liste li {
  display: grid; gap: .2rem .8rem; align-items: baseline;
  grid-template-columns: minmax(9rem, auto) 5rem 4.5rem 1fr;
  padding: .22rem 0; border-top: 1px solid rgba(120, 96, 60, .25);
}
.sorts-liste li:first-child { border-top: 0; }
.sorts-liste code {
  color: #6b3f10; font-size: 12px;
  background: none; border: 0; padding: 0;
}
.sorts-cout, .sorts-rang { color: #4a2f0c; font-size: 11px; }
.sorts-portee { color: #5c4a30; font-size: 11px; font-style: italic; }
.parchemin-pied {
  margin: .8rem 0 0; font-family: var(--parole); font-size: 11px; color: #5c4a30;
}
/* Sous 30rem la grille de quatre colonnes se replie : le code garde sa ligne. */
@media (max-width: 30rem) {
  .sorts-liste li { grid-template-columns: 1fr 1fr; }
  .sorts-liste code { grid-column: 1 / -1; }
}

/* --- Le seuil (l'apex) ---------------------------------------------------- */
/*
   `dragonchat.com` ne sert plus la page de présentation depuis le 31 août
   2026 au soir : elle est passée a `dev.`. L'apex n'a plus que trois choses a
   dire -- le monogramme, le nom, le ciel -- et une seule qui bouge, l'accroche.

   Tout y est en `clamp()` et en unites relatives : cette page n'a aucune
   largeur de rupture, elle se pose pareil sur un telephone tenu debout et sur
   un écran large. C'est la seule page du système dont on puisse dire cela.

   `100svh` et non `100vh` : sur mobile, la barre d'adresse qui se retracte
   fait grandir `vh` et decale ce qui était centre. `svh` est la petite
   hauteur, celle qui ne bouge pas. Le `100vh` reste dessous pour les
   navigateurs qui ne connaissent pas l'unité.
*/
.page-seuil {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: clamp(1.5rem, 6vw, 4rem) var(--gouttiere);
  /* Le ciel du 404, mais pose plus bas et plus large : ici il n'a pas de
     texte a porter, il peut respirer. */
  background-image:
    url("/marque/fond-nuit.svg"),
    radial-gradient(115% 75% at 50% 42%, rgba(var(--halo-rgb), .38), transparent 62%),
    radial-gradient(70% 50% at 50% 118%, rgba(var(--accent-rgb), .16), transparent 70%);
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-size: cover, auto, auto;
  background-position: center, center, center;
}

.seuil {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: clamp(.5rem, 1.5vw, .9rem);
  max-width: 46rem;
  width: 100%;
}

/* L'astre. Il dérive, et son halo respire -- deux mouvements lents et
   desynchronises, pour que la page n'ait jamais l'air de pulser en mesure. */
.seuil-astre {
  width: clamp(132px, 34vw, 232px);
  margin-bottom: clamp(.25rem, 1.5vw, .6rem);
}
.seuil-astre svg { display: block; width: 100%; height: auto; overflow: visible; }
.seuil-astre .astre-etoile { fill: var(--gemme); }
.seuil-astre .astre-lune { fill: var(--cendre); opacity: .7; }
.seuil-lueur { opacity: .85; transform-origin: 35% 48%; }

@media (prefers-reduced-motion: no-preference) {
  .seuil-astre { animation: derive 16s ease-in-out infinite; }
  .seuil-lueur { animation: respire 7s ease-in-out infinite; }
}
@keyframes respire {
  0%, 100% { opacity: .55; transform: scale(.94); }
  50%      { opacity: 1;   transform: scale(1.06); }
}

/*
   Le logotype. Un dessin, plus une ligne de texte : les contours de P052
   traces une fois pour toutes (voir threshold.html). Il se met a l'échelle par sa
   seule largeur -- un SVG n'a pas de taille de corps a caler, donc il est
   juste a toute largeur sans une seule règle de rupture.

   `min()` et non `clamp()` : le rapport de la boîte est de 8.8 pour 1, et
   88vw le garde a l'aise de la gouttiere jusqu'à 320px, pendant que le
   plafond l'empêche de devenir une banniere sur un écran large.
*/
.seuil-logotype {
  width: min(88vw, 44rem);
  /* La lueur est portée par un filtre et non par une ombre de texte : elle
     suit le contour grave, pas une boîte. */
  filter: drop-shadow(0 0 clamp(10px, 2.5vw, 28px) rgba(127, 227, 255, .45));
}
.seuil-logotype svg { display: block; width: 100%; height: auto; overflow: visible; }
/* Le logotype porte sa lueur au-delà de son trace : sans ce cran, la devise
   vient s'asseoir dedans. */
.seuil-logotype { margin-bottom: clamp(.35rem, 1.2vw, .8rem); }
/*
   Grave, et non plein. Une vitre a peine teintee, un filet mince, et la nuit
   qui passe au travers.

   `vector-effect: non-scaling-stroke` est ce qui rend le filet **mince a
   toute taille** : sans lui l'epaisseur suit l'échelle du viewBox, et le
   trait qui va bien sûr un telephone devient un tuyau sur un écran large.
   Avec lui, 1.1px reste 1.1px, du 320 au 4K.

   `paint-order` peint le fond avant le trait : autrement le remplissage
   deborde d'un demi-filet et le contour perd son tranchant.
*/
.seuil-logotype path {
  fill: rgba(232, 244, 250, .13);
  stroke: var(--gemme);
  stroke-width: 1.1;
  vector-effect: non-scaling-stroke;
  paint-order: fill stroke;
}

/* Le nom reste dit aux machines, sans être montré. Pas de `display: none` ni
   de `visibility: hidden` : les deux le retireraient aussi de la parole. */
.hors-vue {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

.seuil-devise {
  margin: 0;
  font-size: clamp(9px, 2.2vw, 12px);
  letter-spacing: clamp(.2em, .9vw, .42em);
  text-transform: uppercase;
  color: var(--sourd);
  /* L'interlettrage pousse un blanc après la dernière lettre : on le reprend,
     sinon le mot n'est pas centre, il est centre plus un cran. */
  text-indent: clamp(.2em, .9vw, .42em);
}

.seuil-filet { margin: clamp(.9rem, 3vw, 1.6rem) auto clamp(.5rem, 1.5vw, .8rem); }

/*
   `display: block`, et surtout pas une grille ni une boîte flexible : les
   guillemets de `.accroche` sont des `::before` / `::after`, et dans une
   grille ils deviennent des **éléments de grille** -- ils partaient chacun
   sur sa propre ligne, au-dessus et au-dessous du texte.
*/
.seuil-accroche {
  display: block;
  min-height: 3.2em; /* la place du texte le plus long : rien ne saute quand il tourne */
  max-width: 36rem;
  font-size: clamp(.85rem, 2.6vw, 1.05rem);
  color: var(--cendre);
  transition: opacity .4s ease, transform .4s ease;
}
.accroche-part { opacity: 0; transform: translateY(-4px); }

/*
   La mention de l'attente. Elle prend la place de la porte sur
   `public/soon.html` : même aplomb, même discrétion, mais rien à cliquer.
*/
.seuil-mention {
  margin: clamp(.6rem, 2.5vw, 1.4rem) 0 0;
  font-size: clamp(9px, 2.2vw, 12px);
  letter-spacing: clamp(.14em, .6vw, .28em);
  text-transform: uppercase;
  text-indent: clamp(.14em, .6vw, .28em);
  color: var(--sourd);
}

.seuil-porte { margin: clamp(.6rem, 2.5vw, 1.4rem) 0 0; }
.seuil-porte a {
  display: inline-block;
  padding: .7rem 2.2rem;
  font-size: 11px;
  letter-spacing: .28em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--gemme);
  text-indent: .28em;
  border: 1px solid rgba(var(--accent-rgb), .55);
  background: var(--panneau);
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
.seuil-porte a:hover,
.seuil-porte a:focus-visible {
  background: rgba(var(--accent-rgb), .18);
  border-color: var(--gemme);
  color: #fff;
}

/* --- Le Scanner interstellaire --------------------------------------------
   Une planète est un rectangle de 2 pour 1 : c'est la forme d'une projection
   équirectangulaire, celle que rend lib/scanner.js. Les points y sont posés en
   pourcentages, jamais en pixels — la carte doit tenir sur un téléphone comme
   sur un écran large sans que les domaines se déplacent les uns par rapport
   aux autres. */

.ciel-univers { margin-bottom: 2.5rem; }
.ciel-univers .eyebrow { margin-bottom: .75rem; }

.ciel-planetes {
  display: grid;
  gap: 1.25rem;
  /* `auto-fill` et non `auto-fit` : une planète seule dans son univers doit
     garder la taille d'une planète, pas s'étirer en bandeau sur toute la
     largeur. XFight n'a qu'un domaine, DragonPro qu'un monde. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
}

.ciel-globe {
  position: relative;
  aspect-ratio: 2 / 1;
  margin: .75rem;
  border: 1px solid rgba(var(--accent-rgb), .35);
  background:
    radial-gradient(circle at 22% 28%, rgba(127, 227, 255, .10), transparent 55%),
    radial-gradient(circle at 74% 71%, rgba(var(--accent-rgb), .12), transparent 50%),
    repeating-linear-gradient(90deg, rgba(var(--accent-rgb), .10) 0 1px, transparent 1px 12.5%),
    repeating-linear-gradient(0deg, rgba(var(--accent-rgb), .10) 0 1px, transparent 1px 25%),
    var(--nuit);
}

/* L'équateur et le méridien, un peu plus francs que la grille : ils disent
   quel sens a la carte. */
.ciel-globe::before,
.ciel-globe::after {
  content: ""; position: absolute; background: rgba(var(--accent-rgb), .28);
}
.ciel-globe::before { left: 0; right: 0; top: 50%; height: 1px; }
.ciel-globe::after { top: 0; bottom: 0; left: 50%; width: 1px; }

.ciel-point {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: .3rem;
  text-decoration: none; color: var(--os);
  font-size: 10px; line-height: 1;
  white-space: nowrap;
  padding: 2px 3px;
  border-radius: 2px;
}
.ciel-point:hover, .ciel-point:focus-visible {
  background: rgba(0, 26, 46, .92);
  outline: 1px solid var(--brume);
  z-index: 2;
}

.ciel-astre {
  width: 8px; height: 8px; flex: 0 0 8px;
  border-radius: 50%;
  background: var(--sourd);
  box-shadow: 0 0 0 1px rgba(1, 30, 51, .9);
}
.foyer-vif  .ciel-astre { background: var(--gemme); box-shadow: 0 0 6px var(--brume); }
.foyer-plein .ciel-astre {
  background: var(--or); box-shadow: 0 0 10px var(--braise);
  width: 11px; height: 11px; flex-basis: 11px;
}
.foyer-eteint .ciel-astre { background: transparent; box-shadow: inset 0 0 0 1px var(--sourd); }

.ciel-nom { color: var(--cendre); }
.ciel-point:hover .ciel-nom, .ciel-point:focus-visible .ciel-nom { color: var(--os); }
.ciel-compte { margin-left: .3rem; color: var(--or); font-weight: 700; }

/* Une porte qui ne s'ouvrira pas au rang du regardeur reste visible : on voit
   la porte, on ne l'ouvre pas. C'est ce que fait déjà la carte de la vitrine,
   et le scanner ne cache pas ce qu'elle montre. */
.ciel-clos .ciel-nom { color: var(--sourd); font-style: italic; }
.ciel-clos .ciel-astre { opacity: .55; }

@media (prefers-reduced-motion: no-preference) {
  .foyer-plein .ciel-astre { animation: braise 3.2s ease-in-out infinite; }
}
@keyframes braise {
  0%, 100% { box-shadow: 0 0 10px var(--braise); }
  50%      { box-shadow: 0 0 18px var(--or); }
}

/* --- La page se leve ------------------------------------------------------ */
/*
   Le pendant CSS de l'observateur d'index.html. La classe `.a-lever` n'est
   posée **que par le script**, et seulement quand le visiteur n'a pas demande
   moins de mouvement : sans JavaScript, sans IntersectionObserver, ou avec
   `prefers-reduced-motion`, la page est simplement la, entière et visible.
   Rien ici ne doit pouvoir cacher du contenu pour de bon.
*/
.a-lever {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .7s cubic-bezier(.2, .7, .3, 1), transform .7s cubic-bezier(.2, .7, .3, 1);
}
.leve { opacity: 1; transform: none; }
/* Les enfants du héros montent l'un après l'autre : le titre, puis la démo.
   Deux crans, pas davantage -- au-delà cela se voit comme un effet. */
.hero > *:nth-child(2) { transition-delay: .12s; }

@media (prefers-reduced-motion: reduce) {
  .a-lever { opacity: 1; transform: none; transition: none; }
}

/*
   Les domaines répondent au survol. La transformation existait déjà ; ce qui
   manquait, c'était la lueur -- une carte qu'on désigne doit s'allumer, pas
   seulement se soulever. Sur un écran tactile, `hover` ne se déclenche pas et
   la carte reste sage : c'est le bon comportement, il n'y a rien a désigner
   avec un doigt.
*/
/* Une carte qu'on designe s'allume. La lueur n'est pas un halo pose autour de
   la boite : c'est une lumiere qui **tombe du bord haut**, comme sur du verre
   incline. Le pseudo-element porte le degrade, la transition porte son
   opacite — animer un `background` ne coute pas la meme chose qu'animer une
   opacite, et a vingt cartes cela se sent. */
.domaine::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(120% 80% at 50% -20%, rgba(var(--accent-rgb), .55), transparent 62%);
  opacity: 0;
  transition: opacity var(--lent);
}
.domaine:hover::after, .domaine:focus-visible::after { opacity: 1; }
.domaine:hover, .domaine:focus-visible {
  transform: translateY(-3px);
  border-color: rgba(var(--accent-rgb), .55);
  box-shadow: inset 0 1px 0 var(--filet), var(--ombre-3), var(--lueur);
}

/* --- Le fond et la lumière du salon ----------------------------------------
   Demandé le 2 septembre 2026. La table des thèmes est dans lib/themes.js et la
   feuille qui en sort est servie sur /styles/themes.css ; ici ne vivent que les
   règles qui s'en servent.

   Trois choses à savoir avant d'y toucher.

   1. **`--fond` est posée par le script, jamais ici.** Le chemin dépend du code
      du salon, et une feuille de style ne connaît pas le salon où l'on est.

   2. **`[data-fond]` ne paraît que si l'image a vraiment chargé.** hedwige.js
      la demande avant de la poser : sans cette précaution, un salon sans
      fichier perdrait son semis d'étoiles au profit d'un vide. C'est la seule
      façon honnête de le savoir — le serveur, lui, rend toujours le chemin.

   3. **Le voile est devant l'image, et il n'est pas décoratif.** On écrit
      par-dessus une image qu'on n'a pas choisie ; c'est lui qui garantit que la
      parole reste lisible quoi qu'on dépose dans images/fonds/. Le retirer
      rendrait le produit illisible sur la première photo claire. */

/* Les deux plans d'etoiles restent **au-dessus du voile**, donc au-dessus de
   l'image : un salon habille garde son ciel. Avant, `[data-fond]` remplacait
   toute la liste et la piece perdait ses etoiles des qu'on y deposait une
   photo — ce qui revenait a punir les salons decores. */
body[data-fond] {
  background-color: var(--nuit);
  background-image:
    url("/marque/etoiles-pres.svg"),
    url("/marque/etoiles-milieu.svg"),
    linear-gradient(var(--voile), var(--voile)),
    var(--fond),
    radial-gradient(120% 80% at 50% -10%, rgba(0, 0, 0, .35), transparent 60%);
  background-repeat: repeat, repeat, no-repeat, no-repeat, no-repeat;
  background-size: 1187px 1187px, 853px 853px, cover, cover, auto;
  background-position: 0 0, 0 0, center, center, center;
  background-attachment: fixed;
}
@media (prefers-reduced-motion: no-preference) {
  body[data-fond] { animation: derive-ciel-fond 900s linear infinite; }
}
@keyframes derive-ciel-fond {
  from { background-position: 0 0, 0 0, center, center, center; }
  to   { background-position: -3561px 1187px, -1706px 853px, center, center, center; }
}

/* Meme raison, memes tuiles reduites de moitie qu'au corps sans salon (voir la
   note pres de `derive-ciel-petit`) : sous 700 px les deux plans d'etoiles
   passent a 593.5 et 426.5 px, et la derive les suit avec une seconde paire
   de keyframes plutot qu'un calc() sur les positions animees. Les trois
   dernieres couches (voile, image du salon, degrade) restent centrees, donc
   hors de propos ici -- c'est pourquoi elles gardent `center` aux deux bouts. */
@media (max-width: 700px) {
  body[data-fond] { background-size: 593.5px 593.5px, 426.5px 426.5px, cover, cover, auto; }
}
@media (max-width: 700px) and (prefers-reduced-motion: no-preference) {
  body[data-fond] { animation-name: derive-ciel-fond-petit; }
}
@keyframes derive-ciel-fond-petit {
  from { background-position: 0 0, 0 0, center, center, center; }
  to   { background-position: -1780.5px 593.5px, -853px 426.5px, center, center, center; }
}

/* Sans fichier, la salle garde le semis d'étoiles et ne prend que la couleur du
   thème. C'est le cas de tous les salons tant que personne n'a rien déposé, et
   il doit rester beau : c'est celui qu'on voit le plus longtemps. */
body[data-theme]:not([data-fond]) { background-color: var(--nuit); }

/* Une image de fond ne doit pas coûter la lecture : sur un très petit écran on
   la laisse tomber et on garde la couleur. */
/* Sur un tres petit ecran l'image tombe — elle coute cher et ne se lit pas —
   mais **les etoiles restent**. Avant, cette regle remplacait toute la liste
   par un aplat, et le telephone se retrouvait avec un ciel vide : le seul
   ecran ou l'on regarde le produit de pres etait aussi le seul sans ciel. */
@media (max-width: 30rem) {
  body[data-fond] {
    background-image:
      url("/marque/etoiles-pres.svg"),
      url("/marque/etoiles-milieu.svg"),
      linear-gradient(var(--voile), var(--voile));
    background-repeat: repeat, repeat, no-repeat;
    /* 30rem (480px) est sous les 700px de la reduction generale des tuiles,
       ci-dessus : les deux tailles restent les memes, moitie de 1187 et 853,
       pour que la derive de `derive-ciel-fond-petit` continue de boucler
       juste -- seule la liste de couches change ici, pas leur echelle. */
    background-size: 593.5px 593.5px, 426.5px 426.5px, cover;
  }
}

/* ===========================================================================
   LE PORTAIL, VERSION 2 SEPTEMBRE 2026
   ---------------------------------------------------------------------------
   D'apres la maquette fournie ce jour-la. Ce qui change par rapport a la
   version precedente, et pourquoi :

   * **Le titre passe en casse normale.** Il etait en capitales, ce qui donnait
     une affiche ; en casse normale il donne une phrase, et une phrase se lit.
   * **La marque entre dans le titre.** « Bienvenue dans DragonChat » : le nom
     du produit est la seule chose coloree de la page a cet endroit.
   * **Les cartes portent une image.** Le fichier est celui du salon
     (`/images/fonds/<code>.jpg`, la convention de lib/themes.js) ; s'il n'y en
     a pas, la vignette reste un degrade du theme et la carte tient quand meme.
     C'est la meme regle que pour le fond des salles : rien a declarer.
   * **Les domaines defilent horizontalement.** Ils etaient en grille, et la
     grille poussait la page a 1800 px de haut avant la premiere information.
   =========================================================================== */

/* --- La barre du portail --------------------------------------------------- */
.barre-portail { gap: .5rem 2rem; }
.barre-portail .menu-principal {
  display: flex; flex-wrap: wrap; gap: .25rem 1.75rem;
  margin-inline: 1.5rem auto;
  font-size: 13px; font-weight: 500; letter-spacing: 0; text-transform: none;
  color: var(--cendre);
}
.barre-portail .menu-principal a { color: inherit; text-decoration: none; transition: color var(--vite); }
.barre-portail .menu-principal a:hover { color: var(--os); }
.barre-actions { display: flex; align-items: center; gap: .875rem; margin-left: auto; }
.lien-sobre {
  font-size: 13px; font-weight: 500; color: var(--cendre); text-decoration: none;
  transition: color var(--vite);
}
.lien-sobre:hover { color: var(--os); }
.bouton-pilule { border-radius: var(--r-pilule); padding: .5rem 1.1rem; }
/* Le bleu d'action, et non la gemme : la gemme est la couleur de la lumiere du
   produit, elle sert a lire ; l'azur est la couleur de ce sur quoi on clique. */
.bouton-azur { background: linear-gradient(180deg, #58acff, var(--azur)); color: #04101f; }
.bouton-azur:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,.45), var(--ombre-1),
                                 0 0 30px -6px rgba(59, 157, 255, .95); }

/* Sous 900 px, cinq liens et deux boutons ne tiennent plus sur un rang : le
   `flex-wrap` de `.barre` (plus haut) les faisait passer sur trois lignes, et
   la barre montait a pres de 290 px de haut sur un telephone de 390 -- toute
   la moitie superieure de l'ecran, avant la moindre information. Le menu et
   « Connexion » disparaissent donc, et Rejoindre reste seul avec le
   monogramme : rien ne se perd, chaque ancre du menu a sa section plus bas
   dans la page (`#domaines`, `#teleporteur`), et Aide est reprise au pied. */
@media (max-width: 900px) {
  .barre-portail .menu-principal,
  .barre-portail .lien-sobre { display: none; }
}

/* --- Le heros -------------------------------------------------------------- */
.hero-portail { align-items: center; padding-block: clamp(2.5rem, 7vw, 5rem) clamp(2rem, 5vw, 3.5rem); }
.hero-portail .eyebrow { color: var(--gemme); font-weight: 600; letter-spacing: .22em; font-size: 11.5px; }

.titre-accueil {
  font-family: var(--titre);
  font-size: clamp(2.25rem, 5.1vw, 4.05rem);
  font-weight: 800;
  line-height: 1.03;
  letter-spacing: -.03em;
  text-transform: none;
  text-wrap: balance;
}
/* La seconde ligne porte le nom du produit. Le degrade va du violet au cyan en
   traversant l'azur — trois couleurs, parce que deux donnent un degrade de
   presentation et trois donnent une lumiere. */
.titre-accueil em {
  display: block;
  font-style: normal;
  background: linear-gradient(96deg, var(--violet) 2%, var(--azur) 46%, var(--gemme) 96%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 38px rgba(59, 157, 255, .40));
}
.hero-portail .prose { margin-top: 1.25rem; font-size: clamp(.95rem, 1.5vw, 1.0625rem); max-width: 46ch; }

/* Les trois atouts, sous les boutons. Ce sont des reperes, pas des arguments :
   d'ou l'icone en tuile sombre et le texte en deux etages minuscules. */
.hero-atouts {
  display: flex; flex-wrap: wrap; gap: .5rem 1.25rem;
  margin-top: 2.25rem; padding-top: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, .07);
}
/* Le filet qui separe deux atouts est porte par l'atout suivant, jamais par un
   element vide : un separateur en `<span>` se retrouverait lu a voix haute. */
.atout { display: flex; align-items: center; gap: .55rem; padding-left: 1.25rem; }
.atout:first-child { padding-left: 0; }
.atout + .atout { border-left: 1px solid rgba(255, 255, 255, .08); }
@media (max-width: 1100px) { .atout { padding-left: 0; border-left: 0 !important; } }
.atout-blason {
  flex: none; display: grid; place-items: center;
  width: 31px; height: 31px; border-radius: 9px;
  background: rgba(var(--accent-rgb), .12);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .30);
  color: var(--gemme);
}
.atout-blason svg { width: 17px; height: 17px; }
.atout b { display: block; font-size: 11.5px; font-weight: 600; color: var(--gemme); letter-spacing: .01em; }
.atout span { display: block; font-size: 11px; color: var(--sourd); }

/* --- La scene, et le teleporteur qui flotte dessus ------------------------- */
.hero-scene { position: relative; min-width: 0; }
/* La scene deborde volontairement de sa colonne : dans la maquette elle touche
   le bord droit de la page et passe sous la carte du teleporteur. Bornee a sa
   colonne, elle devenait une vignette au milieu du vide. */
.hero-scene-image { display: block; width: 100%; height: auto; }
@media (min-width: 1000px) {
  .hero-scene-image {
    width: 150%;
    max-width: none;
    margin-left: -52%;
    filter: saturate(1.15) brightness(1.12);
  }
}
@media (prefers-reduced-motion: no-preference) {
  .hero-scene-image { animation: flotte 16s ease-in-out infinite; }
}

.tp-flottant {
  width: min(280px, 100%);
  padding: 1rem;
  display: grid; gap: .625rem;
}
/* En grand, la carte chevauche la scene — c'est ce qui fait qu'on la voit
   comme posee *devant* et non a cote. En petit elle repasse dans le flux :
   superposer deux blocs sur 360 px de large ne cache que du texte.

   Le chevauchement fixe (`-2.5rem`) faisait déborder la page entre 1000 et
   1180px de large : a ces largeurs, `.enveloppe` n'a pas encore atteint son
   `max-width` de 1180px et ne porte donc aucune marge automatique -- seule la
   gouttiere separe la scene du bord de l'ecran, et 2.5rem depasse toujours la
   gouttiere (au plus 2rem). La carte sortait alors du viewport et ouvrait un
   defilement lateral sur toute la page. `var(--gouttiere)` chevauche d'autant
   que la marge le permet a n'importe quelle largeur -- juste assez pour
   flotter devant la scene, jamais assez pour passer le bord. */
@media (min-width: 1000px) {
  .tp-flottant { position: absolute; right: calc(var(--gouttiere) * -1); top: 50%; transform: translateY(-50%); z-index: 2; }
}
@media (max-width: 999px) {
  .tp-flottant { margin: 1.25rem auto 0; }
}
.tp-flottant-tete {
  display: flex; align-items: center; justify-content: center; gap: .5rem;
  font-size: 10px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--gemme);
}
.tp-flottant-tete::before, .tp-flottant-tete::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .5), transparent);
}
.tp-anneau { display: block; width: 100%; height: auto; }
.tp-flottant .porte-note { text-align: center; font-size: 11.5px; }

/* --- Le rail des domaines -------------------------------------------------- */
/*
   `scroll-snap` sur le rail et non sur chaque carte : l'aimant doit tenir la
   colonne, pas la vignette. `scrollbar-width: none` retire la barre — la
   fleche et le debordement visible d'une demi-carte disent deja qu'on peut
   defiler, et une barre de defilement horizontale sous des cartes fait sale.
*/
.rail-piste { position: relative; }
.rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: 232px;
  gap: 1rem;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  padding: .25rem .25rem 1rem;
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }
.rail > * { scroll-snap-align: start; }
@media (min-width: 900px) { .rail { grid-auto-columns: 252px; } }

.rail-fleche {
  position: absolute; top: 40%; right: -6px; z-index: 3;
  display: grid; place-items: center;
  width: 38px; height: 38px; border: 0; border-radius: 50%;
  background: var(--panneau); color: var(--os); cursor: pointer;
  backdrop-filter: var(--verre-flou); -webkit-backdrop-filter: var(--verre-flou);
  box-shadow: inset 0 0 0 1px var(--cadre), var(--ombre-2);
  transition: box-shadow var(--vite), transform var(--vite);
}
.rail-fleche:hover { transform: scale(1.06); box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .6), var(--lueur); }
.rail-fleche[hidden] { display: none !important; }

/* La vignette. Sans fichier depose, le degrade du theme la porte seul — et il
   doit rester beau, parce que c'est l'etat qu'on voit le plus longtemps. */
.domaine-vue {
  position: relative;
  aspect-ratio: 16 / 10;
  background-image:
    var(--fond-vue, none),
    radial-gradient(120% 90% at 30% 0%, rgba(var(--accent-rgb), .38), transparent 62%),
    linear-gradient(160deg, var(--profond), var(--nuit));
  background-size: cover, auto, auto;
  background-position: center, center, center;
  background-repeat: no-repeat;
  border-bottom: 1px solid var(--cadre);
}
.domaine-vue::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 42%, rgba(5, 8, 15, .82));
}
.domaine-blason {
  position: absolute; left: .75rem; bottom: -17px; z-index: 2;
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--nuit); color: var(--gemme);
  box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), .45), var(--ombre-1);
  font-size: 13px;
}
.domaine-etiquette {
  position: absolute; left: .625rem; top: .625rem; z-index: 2;
  padding: .18rem .55rem; border-radius: var(--r-pilule);
  font-size: 9.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: #fff; background: rgba(139, 127, 245, .92);
}
.domaine .plaque-corps { padding: 1.5rem .875rem .875rem; }
.domaine-nom { font-family: var(--titre); font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
.domaine-genre { font-size: 11.5px; color: var(--gemme); }
.domaine-note { margin: 0; font-size: 12px; font-style: normal; color: var(--cendre); line-height: 1.5; }

/* --- Le bandeau des chiffres ---------------------------------------------- */
.bandeau-chiffres {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2.25rem;
  margin-top: 1.5rem; padding: 1rem 1.25rem;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--plaque-a), var(--plaque-b));
  border: 1px solid var(--cadre);
  box-shadow: inset 0 1px 0 var(--filet), var(--ombre-2);
  backdrop-filter: var(--verre-flou); -webkit-backdrop-filter: var(--verre-flou);
}
.chiffre { display: flex; align-items: center; gap: .55rem; font-size: 12px; color: var(--sourd); }
.chiffre b { font-family: var(--titre); font-size: 15px; font-weight: 700; color: var(--os); }
.chiffre svg { width: 16px; height: 16px; color: var(--gemme); flex: none; }
.bandeau-appel { display: flex; align-items: center; gap: .875rem; margin-left: auto; font-size: 12px; color: var(--cendre); }

/* --- Les bannieres de jeu -------------------------------------------------
   Chaque jeu montre son plateau. La vignette est en 320x120 et se sert a la
   largeur de la colonne : c'est un SVG, il ne se pixellise pas. */
.jeu { display: grid; gap: .5rem; }
.jeu-vue {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-sm);
  border: 1px solid var(--cadre);
  box-shadow: inset 0 1px 0 var(--filet);
}
.aide .forum-liste li.jeu { padding: .75rem .8rem; }
