/* socle.css — LA GRAMMAIRE D'ÉCRAN DES SITES PUBLICS, une seule fois (plan sites).
 *
 * ── POURQUOI CE FICHIER EXISTE ──────────────────────────────────────────────────────────────────
 * Elle était écrite TROIS FOIS : dans la feuille de la vitrine (apps/ea-site-eanexus), dans la
 * feuille inline du gabarit des cinq apex (packages/backend-kit/src/pages-publiques.js) et dans
 * celle de l'écran de login de l'IdP (apps/ea-id/routes-login.js). Trente-sept sélecteurs communs,
 * recopiés — et déjà divergents : le bouton mesurait 44 px de haut sur un apex et 36 px sur la
 * vitrine, son survol teintait à l'accent d'un côté et au texte de l'autre, le kicker était accent
 * ici et discret là, la colonne 1040 px puis 960 px. Aucune de ces différences n'avait été décidée.
 * Elles sont maintenant tranchées ICI, une fois, pour les six sites.
 *
 * ── CE QU'ELLE CONTIENT, ET CE QU'ELLE NE CONTIENT PAS ──────────────────────────────────────────
 * ELLE : le squelette (bande, en-tête, pied), la typographie, le bouton, la carte, la section, les
 * étapes, les repères, les listes, la page de retour — ce que TOUTE surface publique du parc rend.
 * PAS ELLE : ce qui n'appartient qu'à un site (la table de téléchargements et le sélecteur de
 * langues de la vitrine, le sommaire d'ancres d'un apex). Un consommateur ajoute SA feuille après
 * celle-ci ; il ne la fourche jamais.
 *
 * ── ZÉRO COULEUR, COMME PARTOUT ─────────────────────────────────────────────────────────────────
 * Aucune valeur de couleur n'est écrite ici : toutes sont des variables de gen/tokens.css (le
 * codegen de docs/tokens.json). Chargez tokens.css AVANT cette feuille.
 *
 * ── LA MARQUE PAR PAGE, LA GRAMMAIRE PARTAGÉE ───────────────────────────────────────────────────
 * Un conteneur déclare son produit (`data-produit`, la graphie du design system) et l'accent en
 * dérive — jamais un hex chez un consommateur. Hors de tout data-produit, l'accent NEUTRE des
 * tokens tient le chrome : la vitrine n'est aucun des produits. `--halo` est le cran 900 de la
 * rampe (le voile du haut de page) ; EA Licence n'a pas de rampe publiée (décision de tokens.json),
 * ses crans retombent sur l'accent plein et son halo sur le cran de surface. */

[data-produit='ea-castle']     { --accent: var(--accent-ea-castle);     --accent-300: var(--accent-ea-castle-300);     --accent-400: var(--accent-ea-castle-400);     --halo: var(--accent-ea-castle-900); }
[data-produit='ea-ultraderby'] { --accent: var(--accent-ea-ultraderby); --accent-300: var(--accent-ea-ultraderby-300); --accent-400: var(--accent-ea-ultraderby-400); --halo: var(--accent-ea-ultraderby-900); }
[data-produit='ea-livedeck']   { --accent: var(--accent-ea-livedeck);   --accent-300: var(--accent-ea-livedeck-300);   --accent-400: var(--accent-ea-livedeck-400);   --halo: var(--accent-ea-livedeck-900); }
[data-produit='ea-colosseum']  { --accent: var(--accent-ea-colosseum);  --accent-300: var(--accent-ea-colosseum-300);  --accent-400: var(--accent-ea-colosseum-400);  --halo: var(--accent-ea-colosseum-900); }
[data-produit='ea-faceoff']    { --accent: var(--accent-ea-faceoff);    --accent-300: var(--accent-ea-faceoff-300);    --accent-400: var(--accent-ea-faceoff-400);    --halo: var(--accent-ea-faceoff-900); }
[data-produit='ea-licence']    { --accent: var(--accent-ea-licence);    --accent-300: var(--accent-ea-licence);        --accent-400: var(--accent-ea-licence);        --halo: var(--bg-lift); }

/* ── Le corps ──────────────────────────────────────────────────────────────────────────────────
 * Fond sombre unique, halo du haut de page, et un pied qui reste EN BAS sur une page courte (la
 * 404, un pas-à-pas de trois étapes) : sans le corps en colonne flex, il flotte au milieu de
 * l'écran et la page a l'air tronquée. */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
html, body { margin: 0; min-height: 100%; }
body {
  background: var(--bg);
  background-image: radial-gradient(120% 90% at 50% -10%, var(--halo, var(--bg-lift)) 0%, var(--bg) 55%);
  background-repeat: no-repeat;
  color: var(--text);
  /* Inter est la police de la charte, mais aucun site n'embarque de ressource : Inter si le poste
   * l'a, system-ui sinon — jamais un CDN. */
  font-family: 'Inter', system-ui, sans-serif;
  font-size: var(--t-body);
  font-weight: var(--w-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* La bande de contenu. `width: 100%` n'est pas décoratif : sur l'axe transverse d'un conteneur
 * flex, une marge `auto` annule l'étirement, et la bande se réduirait à la largeur de son contenu
 * — le bord gauche du texte ne tomberait plus sur celui de la marque et du pied. La marge de
 * sécurité est celle de la charte (§ 5) : 24 px. */
.wrap { width: 100%; max-width: var(--wrap, 1040px); margin: 0 auto; padding: 0 var(--s5); }
main.wrap { flex: 1; }
/* Colonne de PROSE (aide, légal, 404) : une ligne de 110 caractères ne se lit pas. C'est le
 * CONTENU qui se borne, pas le <main> : borner le <main> le recentrerait dans la fenêtre et son
 * bord gauche ne tomberait plus sur celui de la marque. */
main.prose-col > * { max-width: 760px; }
main.legal { max-width: 840px; }

/* ── Typographie ───────────────────────────────────────────────────────────────────────────────
 * L'échelle des tokens, deux graisses (400/500) : la hiérarchie vient de la taille et de l'espace.
 * Les titres ne portent PAS de marge haute — c'est la section qui donne le rythme vertical. */
a { color: var(--accent-400); text-decoration: underline; text-decoration-color: var(--accent-400); text-underline-offset: 2px; }
a:hover { color: var(--text); text-decoration-color: var(--text); }
h1 { font-size: var(--t-h1); font-weight: var(--w-h1); letter-spacing: var(--tr-h1); line-height: 1.2; margin: var(--s3) 0 var(--s2); }
h2 { font-size: var(--t-h2); font-weight: var(--w-h2); line-height: 1.25; margin: 0 0 var(--s2); }
h3 { font-size: var(--t-h3); font-weight: var(--w-h3); line-height: 1.3; margin: 0 0 var(--s2); }
p { margin: 0 0 var(--s3); }
.micro, .kicker {
  font-size: var(--t-micro); font-weight: var(--w-micro);
  letter-spacing: var(--tr-micro); text-transform: var(--tt-micro); color: var(--text-faint);
}
.kicker { margin: 0 0 var(--s2); }
/* Le kicker d'un HERO annonce le produit : il prend l'accent. Celui d'une section reste discret. */
.hero .kicker { color: var(--accent); }
.note { color: var(--text-muted); font-size: var(--t-label); font-weight: var(--w-label); line-height: 1.6; max-width: 62ch; }
/* Une note qui ALERTE prend --warning : l'état, jamais l'identité (elle ne prend donc pas l'accent
 * du produit, qui dirait « marque » là où il faut dire « attention »). */
.note.alerte { color: var(--warning); }
.chapeau { color: var(--text-muted); max-width: 62ch; margin: 0 0 var(--s5); }
.lede, .desc { max-width: 62ch; }
.tagline { color: var(--text-muted); font-size: var(--t-h3); font-weight: var(--w-body); line-height: 1.5; max-width: 60ch; }
.byline { color: var(--text-muted); font-size: var(--t-h3); font-weight: var(--w-body); margin: 0 0 var(--s4); }
.date { color: var(--text-muted); font-size: var(--t-label); margin: 0 0 var(--s5); }

/* ── En-tête ───────────────────────────────────────────────────────────────────────────────────
 * Marque à gauche, la navigation du site au milieu, la sortie à droite. Ce qui remplit le milieu
 * change d'un site à l'autre (des liens de site sur la vitrine, un sommaire d'ancres sur un apex)
 * — la charpente, elle, est la même. Le chrome reste NEUTRE même sur une page de produit : les
 * sélecteurs sont ancrés sur `.entete` pour l'emporter sur la teinte de lien du périmètre. */
.entete { display: flex; align-items: center; gap: var(--s4); padding: var(--s4) 0; border-bottom: 1px solid var(--divider); }
.entete .marque { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-ui); font-weight: var(--w-ui); color: var(--text); }
.entete .marque a { color: var(--text); text-decoration: none; }
.entete .marque a:hover { color: var(--accent-400); }
.entete .liens, .entete .sommaire { display: flex; flex-wrap: wrap; gap: var(--s4); margin-left: auto; }
.entete .liens a, .entete .sommaire a { font-size: var(--t-ui); font-weight: var(--w-ui); color: var(--text-muted); text-decoration: none; }
.entete .liens a:hover, .entete .sommaire a:hover { color: var(--text); }
.entete .sortie { margin-left: auto; font-size: var(--t-ui); font-weight: var(--w-ui); color: var(--text-muted); text-decoration: none; white-space: nowrap; }
.entete .sortie:hover { color: var(--accent-400); }
.entete .liens ~ .sortie, .entete .sommaire ~ .sortie { margin-left: var(--s4); }
/* La pastille pleine de 6 px de la charte : l'état, et ici la marque de la page. */
.pastille { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }

/* ── Hero ──────────────────────────────────────────────────────────────────────────────────────
 * Le pas display est celui d'un NOM (le site, un produit). Le titre d'une page d'aide ou d'une
 * page légale est une phrase : `.hero.texte` le redescend au pas h1 de l'échelle. */
.hero { padding: var(--s7) 0 var(--s6); }
.hero h1 { font-size: var(--t-display); font-weight: var(--w-display); letter-spacing: var(--tr-display); line-height: 1.1; margin: var(--s2) 0; }
.hero.texte h1 { font-size: var(--t-h1); letter-spacing: var(--tr-h1); line-height: 1.2; }
.emoji { font-size: var(--t-display); line-height: 1; }

/* ── Boutons · le primaire est un CONTOUR d'accent, jamais un aplat ────────────────────────────
 * Trois hauteurs à la charte (36 compact / 44 action d'écran / 56 action de scène) : le défaut est
 * le compact, et une rangée d'actions — le hero, le rappel de fin — passe à l'action d'écran. Le
 * choix est ici, il n'est plus le fruit d'une recopie divergente. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: var(--h-btn-compact); padding: 0 14px;
  font-size: var(--t-ui); font-weight: var(--w-ui); line-height: 1;
  border: 1px solid var(--divider); border-radius: var(--radius-md);
  background: transparent; color: var(--text); text-decoration: none; white-space: nowrap;
  cursor: pointer;
  transition: background 90ms ease-out, border-color 90ms ease-out;   /* --m-survol du design system */
}
.btn:hover { color: var(--text); text-decoration: none; border-color: var(--accent-400); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.btn:active { background: color-mix(in srgb, var(--accent) 22%, transparent); transition: none; }
/* LE BOUTON PRINCIPAL EST PLEIN, ET IL A UNE RAISON DE L'ETRE.
   Il ne se distinguait que par la COULEUR DE SA BORDURE : dans une rangee de trois actions
   (« Telecharger », « Espace streamer », « Aide a l'installation »), l'oeil voyait trois pastilles
   identiques et rien ne disait ou cliquer. Un bouton principal qui ne se lit pas comme tel n'est
   pas un bouton principal, c'est une convention entre nous et le CSS.
   LE FOND EST --accent-400, LE TEXTE --bg : le cran 400 est le clair de chaque rampe, et le texte
   sombre dessus donne de 6,08 a 9,11 de contraste sur les SEPT accents du parc (ultraderby 8,45 ;
   castle 8,55 ; livedeck 9,11 ; colosseum 8,34 ; faceoff 8,91 ; neutre 6,08 ; licence 6,22, par son
   repli). AA demande 4,5 : le pire cas passe AAA. Zero hex, comme le reste du fichier.
   LE REPLI EST OBLIGATOIRE : ea-licence ne publie pas de rampe (« un consommateur qui demande un
   cran retombe sur l'accent plein », tokens.css), et sans lui son bouton n'aurait aucun fond.
   L'ETAT SE FAIT EN LUMINOSITE et non en changeant de cran : la rampe neutre monte a l'envers des
   rampes produit (son 300 est plus SOMBRE que son 400), donc un survol ecrit en crans s'eclaircirait
   chez les uns et s'assombrirait chez l'autre. */
.btn.primaire {
  border-color: var(--accent-400, var(--accent));
  background: var(--accent-400, var(--accent));
  color: var(--bg);
}
.btn.primaire:hover { filter: brightness(1.08); color: var(--bg); background: var(--accent-400, var(--accent)); border-color: var(--accent-400, var(--accent)); }
.btn.primaire:active { filter: brightness(0.94); background: var(--accent-400, var(--accent)); }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn.inactif { opacity: 0.45; cursor: not-allowed; }
.actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin: var(--s5) 0 var(--s4); }
.actions .btn { min-height: var(--h-btn-ecran); padding: 0 18px; }

/* ── Bandeau de repères : ce qu'est la chose en chiffres ───────────────────────────────────────
 * Fond creux (sunken), valeur au pas h3, libellé au pas micro — la paire des cotes du système. */
.reperes { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--s3); margin: var(--s5) 0 0; padding: 0; list-style: none; }
.repere { background: var(--sunken); border: 1px solid var(--divider); border-radius: var(--radius-md); padding: var(--s3) var(--s4); }
.repere b { display: block; font-size: var(--t-h3); font-weight: var(--w-h3); color: var(--text); font-variant-numeric: tabular-nums; }
.repere span { display: block; margin-top: var(--s1); font-size: var(--t-micro); font-weight: var(--w-micro); letter-spacing: var(--tr-micro); text-transform: var(--tt-micro); color: var(--text-faint); }

/* ── Sections : un filet les sépare, jamais un fond ────────────────────────────────────────────
 * Le contraste vient des rôles de surface, pas de la saturation. */
.section { border-top: 1px solid var(--divider); padding: var(--s7) 0; }
.section > .kicker { color: var(--text-faint); }
.section > h2 { margin: 0 0 var(--s3); }
.section > p { max-width: 72ch; margin: 0 0 var(--s3); }
.section > p:last-child { margin-bottom: 0; }
.section > h2 + .chapeau { margin-top: calc(-1 * var(--s3)); }
.section > h2 + .liste, .section > h2 + .etapes, .section > h2 + .cartes,
.section > h2 + .grid, .section > h2 + table { margin-top: var(--s5); }

/* Étapes numérotées : le numéro est un CONTOUR d'accent, jamais un aplat (la règle du primaire),
 * et il est posé par la LISTE — un dictionnaire qui porterait « 1. » l'écrirait deux fois. La
 * liste reste une <ol> : l'ordre est du sens, pas de la mise en page. */
.etapes { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--s4); margin: 0; padding: 0; list-style: none; counter-reset: etape; }
.etape { background: var(--surface); border: 1px solid var(--divider); border-radius: var(--radius-md); padding: var(--s5) var(--s4) var(--s4); counter-increment: etape; }
.etape::before {
  content: counter(etape);
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; margin-bottom: var(--s3);
  border: 1px solid var(--accent); border-radius: 50%;
  color: var(--accent); font-size: var(--t-ui); font-weight: var(--w-ui); font-variant-numeric: tabular-nums;
}
.etape h3 { margin: 0 0 var(--s2); }
.etape p { margin: 0; color: var(--text-muted); }
/* Un pas-à-pas d'aide n'a pas de titre d'étape : la phrase prend alors le ton du texte courant. */
.etape > p:only-child { color: var(--text); }

/* Cartes en grille : la surface du parc, un filet d'accent en tête — la marque est un CONTOUR. */
.cartes { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s4); margin: 0; padding: 0; list-style: none; }
.cartes > li { background: var(--surface); border: 1px solid var(--divider); border-top: 2px solid var(--accent); border-radius: var(--radius-md); padding: var(--s4); }
.cartes > li h3 { margin: 0 0 var(--s2); }
.cartes > li p { margin: 0; color: var(--text-muted); }

/* Faits à puce : la pastille de 6 px de la charte, en puce de liste. */
.liste { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: var(--s6); max-width: 72ch; }
.liste li { break-inside: avoid; position: relative; padding: 0 0 var(--s3) var(--s4); color: var(--text-muted); }
.liste li::before { content: ''; position: absolute; left: 0; top: 11px; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
/* Une liste dont chaque entrée est LONGUE — une URL, une empreinte, une commande — ne se met pas
 * en deux colonnes : la ligne se casserait au milieu de la chose qu'on vient copier. */
.liste.large { columns: 1; max-width: none; }
.liste.large li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }

/* Du texte qui se copie : une adresse, une empreinte. Fond creux, jamais coupé en deux. */
code {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: var(--t-label);
  color: var(--text);
  background: var(--sunken);
  border: 1px solid var(--divider);
  border-radius: var(--radius-sm);
  padding: 2px var(--s2);
  word-break: break-all;
}

/* Questions : un filet par question, pas de cadre — une FAQ est de la prose, pas un tableau. */
.faq { margin: 0; padding: 0; list-style: none; max-width: 72ch; }
.faq li { border-top: 1px solid var(--divider); padding: var(--s4) 0; }
.faq li:first-child { border-top: 0; padding-top: 0; }
.faq h3 { margin: 0 0 var(--s2); }
.faq p { margin: 0; color: var(--text-muted); }

/* Carte : la surface posée du parc. */
.carte { background: var(--surface); border: 1px solid var(--divider); border-radius: var(--radius-lg); padding: var(--s5); }
.carte p { margin: var(--s2) 0; color: var(--text); }
.carte ul { margin: var(--s2) 0; padding-left: 20px; }
.carte li { margin: var(--s1) 0; }
.legal .carte { margin: var(--s4) 0; }

/* Rappel de fin : l'action principale se retrouve en bas d'écran, comme la barre d'état de la
 * charte la pose à droite. */
.final { border-top: 1px solid var(--divider); padding: var(--s7) 0; }
.final .carte { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4); justify-content: space-between; }
.final .carte > div { max-width: 62ch; }
.final h2 { margin: 0 0 var(--s1); }
.final p { margin: 0; color: var(--text-muted); }
.final .actions { margin: 0; }

/* ── Pied ──────────────────────────────────────────────────────────────────────────────────────
 * Une ligne : la signature d'éditeur, puis les pages légales. Sur un apex elles vivent SUR CE
 * domaine (exigence de la validation TikTok), sur la vitrine ce sont les siennes. */
.pied { border-top: 1px solid var(--divider); margin-top: var(--s7); padding: var(--s5) 0 var(--s6); color: var(--text-muted); font-size: var(--t-label); }
.pied .wrap { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); align-items: center; }
.pied a { color: var(--text-muted); text-decoration-color: var(--text-faint); }
.pied a:hover { color: var(--text); }

/* ── Page de retour ────────────────────────────────────────────────────────────────────────────
 * Une carte centrée qui dit une chose et une seule : le retour OAuth d'un jeu, l'écran de login de
 * l'IdP. L'échec prend --warning (l'état, jamais l'identité). */
.retour { flex: 1; display: flex; align-items: center; justify-content: center; padding: var(--s5) var(--s4); }
.retour .carte { max-width: 440px; width: 100%; padding: var(--s6) var(--s5); }
.retour.centre .carte { max-width: 420px; text-align: center; }
/* `text-align: center` ne centre PAS des enfants de flex : dans une carte centree, le texte l'etait
   et la rangee d'actions restait collee a gauche. Le defaut ne se voyait que la ou une carte se
   centre -- les ecrans de retour OAuth -- et c'est justement la ou il n'y a qu'un bouton a viser. */
.retour.centre .actions { justify-content: center; }
.retour .emoji { font-size: 56px; }
/* Le titre d'une carte de retour est du TEXTE : c'est l'ÉTAT qui le teinte, et seulement lui —
 * un écran de connexion (celui de l'IdP) n'annonce aucun état et reste donc au ton du texte. */
.retour .titre { font-size: var(--t-h2); font-weight: var(--w-h2); margin: var(--s4) 0 var(--s2); color: var(--text); }
.retour .titre.ok { color: var(--accent); }
.retour .titre.echec { color: var(--warning); }
.retour .msg { color: var(--text-muted); margin: 0 0 var(--s3); }
.retour .marque { margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--divider); font-size: var(--t-micro); font-weight: var(--w-micro); letter-spacing: var(--tr-micro); text-transform: var(--tt-micro); color: var(--text-faint); }

/* Champ de saisie : le seul contrôle de formulaire des surfaces publiques (le code de l'IdP). */
.champ { width: 100%; min-height: var(--h-btn-compact); padding: 0 var(--s3); font: inherit; font-size: var(--t-ui); color: var(--text); background: var(--sunken); border: 1px solid var(--divider); border-radius: var(--radius-md); }
.champ:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ── Étroit ────────────────────────────────────────────────────────────────────────────────────
 * Le sommaire disparaît (les sections restent atteignables en descendant), le display redescend
 * d'un pas, les colonnes se replient, la marge de sécurité passe au cran en dessous. */
@media (max-width: 760px) {
  .wrap { padding: 0 var(--s4); }
  .entete { flex-wrap: wrap; gap: var(--s3); }
  .entete .sommaire { display: none; }
  .hero { padding: var(--s6) 0 var(--s5); }
  .hero h1 { font-size: var(--t-h1); letter-spacing: var(--tr-h1); }
  .section, .final { padding: var(--s6) 0; }
  .liste { columns: 1; }
  .actions .btn { width: 100%; }
}
