/* Identite visuelle partagee : dashboard, claim, hall of fame, comment ca marche. */

:root {
  --bg: #0b0d12;
  --surface: #151822;
  --surface-2: #1c202c;
  --border: #2a2f3d;
  --text: #e8eaf0;
  --text-muted: #9aa0b4;
  --brand: #9146ff;
  --brand-hover: #7c2cf0;
  --brand-soft: rgba(145, 70, 255, 0.15);
  --team-a: #4da3ff;
  --team-b: #ff4d6d;
  --gold: #ffd166;
  --success: #3ddc84;
  --danger: #ff5c72;
  --radius: 12px;
  --radius-sm: 8px;
  /* Contour de case a cocher decochee : --border seul se confond avec le
     fond en theme sombre (les deux sont tres proches), d'ou un contour
     plus clair specifiquement pour ce theme -- voir les 3 blocs ci-dessous. */
  --checkbox-border: rgba(255, 255, 255, 0.35);
  /* Sans ca, Chromium dessine les commandes natives (fleche du <select>,
     etc.) avec un chrome clair par defaut : un carre blanc disgracieux
     apparait autour de la fleche du selecteur de langue en theme sombre,
     y compris a l'ouverture. Voir les memes 3 blocs data-theme/media plus
     bas pour le theme clair et le forcage explicite. */
  color-scheme: dark;
}

/* Theme clair : par defaut si le systeme le demande, ou force via
   data-theme (bouton de bascule, voir /shared/theme.js). */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #f5f6f8;
    --surface: #ffffff;
    --surface-2: #eef0f4;
    --border: #dde1e8;
    --text: #1a1d24;
    --text-muted: #5b6272;
    --brand-soft: rgba(145, 70, 255, 0.1);
    --gold: #a06600;
    --success: #1a9d5c;
    --danger: #d92d43;
    --checkbox-border: var(--border);
    color-scheme: light;
  }
}

:root[data-theme="dark"] {
  --bg: #0b0d12;
  --surface: #151822;
  --surface-2: #1c202c;
  --border: #2a2f3d;
  --text: #e8eaf0;
  --text-muted: #9aa0b4;
  --brand-soft: rgba(145, 70, 255, 0.15);
  --gold: #ffd166;
  --success: #3ddc84;
  --danger: #ff5c72;
  --checkbox-border: rgba(255, 255, 255, 0.35);
  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg: #f5f6f8;
  --surface: #ffffff;
  --surface-2: #eef0f4;
  --border: #dde1e8;
  --text: #1a1d24;
  --text-muted: #5b6272;
  --brand-soft: rgba(145, 70, 255, 0.1);
  --gold: #a06600;
  --success: #1a9d5c;
  --danger: #d92d43;
  --checkbox-border: var(--border);
  color-scheme: light;
}

* {
  box-sizing: border-box;
}

img {
  max-width: 100%;
  height: auto;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", system-ui, Arial, sans-serif;
  line-height: 1.5;
  min-height: 100vh;
}

/* Un simple plafond en px (720px, puis 1040px) redevient etroit des qu'on
   monte en resolution (4K, 5K...) : max() choisit le plus grand des deux --
   en dessous d'environ 1444px de fenetre, 1040px gagne (comportement inchange
   pour un laptop/desktop courant, et pour le mobile via le media query plus
   bas, puisque 72vw d'un petit ecran est toujours inferieur a 1040) ; au-dela,
   72vw prend le relais et la largeur continue de grandir proportionnellement
   a la fenetre, sans autre plafond -- un ecran deux fois plus large donne un
   contenu deux fois plus large, au lieu de rester bloque a une valeur fixe. */
.page {
  max-width: max(1040px, 72vw);
  margin: 0 auto;
  padding: 32px 20px 64px;
}

/* Rappel "on est en DEV" (voir viewHelpers.js#pageShell) : colle au bord
   gauche de la fenetre (pas du .page centre, qui peut laisser une marge
   large ou nulle selon la resolution -- le bord de viewport est le seul
   repere fiable a toutes les tailles). rotate(-90deg) = sens antihoraire
   (confirme par mesure DOM, pas juste a l'oeil sur des lettres pivotees en
   miniature -- trop ambigu). pointer-events: none pour ne jamais
   intercepter un clic destine au contenu en dessous.
   Centrage horizontal ET vertical robuste a la taille de police (ici
   130px) et a la longueur du texte : left/top fixent le CENTRE souhaite du
   badge (75px du bord, milieu de l'ecran), et translate(-50%, -50%)
   annule le decalage naturel de moitie largeur/hauteur de la boite AVANT
   meme que la police change de taille -- sans ce -50%, le pavage horizontal
   aurait du etre recalcule a la main a chaque changement de font-size (ce
   qui etait le cas avant ce commentaire, avec un "left" en dur suppose
   pour un texte 5x plus petit). 130 / 2 = 65px de rayon (line-height: 1
   rend la hauteur de boite egale a peu pres a la taille de police) + ~10px
   de marge = 75px choisi ci-dessous. */
.dev-badge {
  position: fixed;
  left: 75px;
  top: 50%;
  transform: translate(-50%, -50%) rotate(-90deg);
  transform-origin: center;
  font-size: 130px;
  line-height: 1;
  font-weight: 900;
  letter-spacing: 10px;
  color: #ff8c1a;
  text-shadow: 0 0 10px rgba(0, 0, 0, 0.45);
  pointer-events: none;
  user-select: none;
  z-index: 500;
  white-space: nowrap;
}

.brand {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 28px;
  font-weight: 700;
  font-size: 15px;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* Regroupe le bouton de menu et le nom de marque en haut a GAUCHE de l'en-tete
   (le menu etait auparavant a droite, mele au selecteur de langue/theme --
   peu visible). min-width:0 laisse le nom de marque se tronquer plutot que
   de forcer .brand a deborder sur un tres petit ecran. */
.brand-left {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.brand-name {
  display: flex;
  align-items: center;
  gap: 10px;
  color: inherit;
  text-decoration: none;
}

.brand-name:hover {
  color: var(--text);
  text-decoration: none;
}

.brand .dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 12px var(--brand);
}

/* Rappel que le service est en beta (voir pageShell) : cadre sans fond
   plutot qu'un badge plein, pour rester discret a cote du nom de marque --
   le detail (palier gratuit temporaire) est dans le title="" au survol,
   pas dans le badge lui-meme. */
.beta-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border: 1px solid var(--team-a);
  border-radius: 999px;
  color: var(--team-a);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1.4;
  cursor: help;
}

.theme-toggle {
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 15px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-transform: none;
  letter-spacing: normal;
}

.theme-toggle:hover {
  background: var(--border);
}

/* Forme pilule specifique au selecteur de langue de l'en-tete : la regle
   `select` plus bas (appearance:none + fleche dessinee + hover) s'applique
   deja a cet element (specificite de classe superieure a l'element seul,
   donc ces overrides gagnent quel que soit l'ordre dans la feuille) -- ne
   reste ici que ce qui differe reellement du style de select generique. */
.lang-select {
  color: var(--text-muted);
  border-radius: 999px;
  height: 32px;
  padding: 0 28px 0 10px;
  font-size: 13px;
}

.brand-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Garantit un alignement a droite meme quand .brand passe sur 2 lignes
     (voir le media query mobile plus bas) : sans ca, un seul flex-item
     restant seul sur sa ligne se retrouve pousse a gauche (justify-content:
     space-between ne "repartit" rien avec un seul element par ligne), ce
     qui decalait tout le menu hamburger hors-ecran a gauche sur mobile. */
  margin-left: auto;
}

/* Menu hamburger : remplace la navigation qui vivait auparavant dans une
   liste de liens en bas de page (footer-links) -- plus visible/accessible,
   surtout sur mobile ou il fallait autrefois defiler toute la page pour
   trouver "Se deconnecter". Positionnement relatif au bouton lui-meme
   (pas un panneau plein ecran) : reste simple, coherent avec le style
   compact du reste de l'en-tete. */
.hamburger-wrap {
  position: relative;
}

/* Agrandi (32px -> 42px) depuis que ce bouton est le premier element en haut
   a gauche de la page (voir .brand-left) : il doit se voir immediatement,
   contrairement a son ancienne place, mele au selecteur de langue/theme.
   --checkbox-border (plutot que --border) pour le contour : --border se
   confond avec --surface-2 en theme sombre (meme souci que documente sur
   les cases a cocher plus bas), --checkbox-border est deja la variable
   prevue pour un contour qui doit rester visible dans les deux themes. */
.hamburger-toggle {
  background: var(--surface-2);
  border: 1px solid var(--checkbox-border);
  border-radius: 999px;
  width: 42px;
  height: 42px;
  padding: 0;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  cursor: pointer;
}

.hamburger-toggle:hover {
  background: var(--border);
}

/* Bouton "actif" quand le menu est ouvert (garde son etat au lieu de
   redevenir neutre) : renforce visuellement que le cadre en dessous lui
   appartient, en plus de la fleche/pointe de .hamburger-menu::before. */
.hamburger-toggle[aria-expanded="true"] {
  background: var(--border);
}

.hamburger-toggle span {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 1px;
  background: var(--text-muted);
  transition: transform 0.2s ease, opacity 0.15s ease;
}

/* Animation classique hamburger -> croix : la barre du haut et celle du bas
   se rapprochent du centre (translateY egal a l'ecartement entre barres,
   gap 4px + hauteur 2px = 6px) puis pivotent a 45/-45deg pour former un X ;
   la barre du milieu s'efface. Pilotee par aria-expanded (deja mis a jour
   par toggleHamburgerMenu, voir viewHelpers.js) plutot qu'une classe JS
   dediee -- un seul etat a synchroniser. */
.hamburger-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}

.hamburger-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.hamburger-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* Specificite [hidden] > .hamburger-menu (2 selecteurs contre 1) pour
   toujours gagner sur le display:flex ci-dessous, quel que soit l'ordre
   dans la feuille -- sans ca, l'attribut hidden (display:none du User
   Agent Stylesheet) perdrait contre une regle auteur de meme specificite
   nominale sur cet element. */
.hamburger-menu[hidden] {
  display: none;
}

/* left:0 (plutot que l'ancien right:0) : le bouton vit maintenant tout a
   gauche de l'en-tete (voir .brand-left), le panneau doit donc s'ouvrir vers
   la DROITE -- avec l'ancien ancrage, un panneau plus large que le bouton
   (min-width 220px) aurait deborde a gauche de l'ecran. */
.hamburger-menu {
  display: flex;
  flex-direction: column;
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  min-width: 220px;
  max-width: calc(100vw - 40px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  z-index: 30;
}

/* Petite pointe (carre pivote a 45deg, a moitie masque par le cadre du
   menu) qui pointe vers le bouton hamburger -- sans ca, le cadre semblait
   flotter independamment sous l'en-tete plutot que d'appartenir au bouton
   qui l'ouvre. --arrow-left : calcule en JS a chaque ouverture (voir
   toggleHamburgerMenu) a partir du centre REEL du bouton, mesure apres
   coup -- un decalage fixe en CSS ne vaudrait que pour l'ancrage "a droite
   du bouton" du mode desktop et tomberait n'importe ou une fois le panneau
   mobile pleine largeur en place (voir le media query plus bas). Repli
   right:9px (equivalent au centre d'un bouton de 32px ancre a right:0) si
   jamais la variable n'est pas encore posee. */
.hamburger-menu::before {
  content: "";
  position: absolute;
  top: -7px;
  left: var(--arrow-left, 9px);
  width: 14px;
  height: 14px;
  background: var(--surface);
  border-left: 1px solid var(--border);
  border-top: 1px solid var(--border);
  border-radius: 3px 0 0 0;
  transform: rotate(45deg);
}

.hamburger-menu a {
  display: block;
  padding: 10px 12px;
  border-radius: 6px;
  color: var(--text);
  font-size: 14px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  white-space: nowrap;
}

.hamburger-menu a:hover {
  background: var(--surface-2);
  text-decoration: none;
}

.hamburger-menu a.danger {
  color: var(--danger);
}

h1 {
  font-size: 26px;
  margin: 0 0 6px;
  color: var(--text);
}

h2 {
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 0 0 10px;
  font-weight: 600;
}

p {
  margin: 0 0 12px;
  color: var(--text);
}

p.muted,
.muted {
  color: var(--text-muted);
  font-size: 14px;
}

a {
  color: var(--brand);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 22px;
  margin-bottom: 20px;
}

/* Comparatif des paliers (voir /dashboard/settings) : deux cartes cote a
   cote, celle du palier actuellement actif est mise en avant par un
   contour dore plutot qu'une simple mention textuelle. */
.plan-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}

.plan-columns .card {
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
}

.card.plan-current {
  border: 2px solid var(--gold);
}

.plan-features {
  list-style: none;
  padding: 0;
  margin: 12px 0;
  flex-grow: 1;
}

.plan-features li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}

.plan-features li:last-child {
  border-bottom: none;
}

.plan-columns form {
  margin-top: 8px;
}

.plan-columns .badge {
  margin-top: 8px;
  align-self: flex-start;
}

.step {
  display: flex;
  gap: 14px;
  margin-bottom: 22px;
}

.step:last-child {
  margin-bottom: 0;
}

.step-number {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--brand-soft);
  color: var(--brand);
  font-weight: 700;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.step-body {
  /* Sans ca, un flex item ne retrecit jamais sous la largeur naturelle de
     son contenu (min-width:auto par defaut) : le copy-row imbrique herite
     alors d'une largeur illimitee et sa propre troncature ne sert a rien. */
  min-width: 0;
  flex: 1;
}

.step-body h2 {
  margin-bottom: 4px;
}

code,
.code {
  font-family: "Cascadia Code", "Consolas", monospace;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 2px 7px;
  font-size: 13px;
  color: var(--text);
}

pre {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px;
  overflow-x: auto;
}

pre code {
  border: none;
  background: none;
  padding: 0;
}

.copy-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}

.copy-row .code {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 10px 12px;
}

/* Rangee d'icones reseaux sociaux (section Planification du dashboard, voir
   SOCIAL_ICON_SVG dans dashboard.routes.js). Un fond blanc fixe rendait les
   pastilles criardes en theme sombre (signale le 2026-07-25) -- meme fond
   que les autres boutons du site (--surface-2 / --checkbox-border, voir
   .hamburger-toggle) desormais. Facebook/Instagram/Bluesky/Discord gardent
   leur propre couleur de marque (fill fige dans le SVG, assez satures pour
   rester lisibles sur les deux fonds) ; seul X suit --text via
   currentColor, car il n'a pas de couleur de marque propre (noir/blanc). */
.social-icons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.social-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--checkbox-border);
  color: var(--text);
  cursor: pointer;
  transition: transform 0.12s ease;
}

.social-icon-btn:hover {
  transform: translateY(-2px);
  background: var(--surface-2);
}

button,
.btn {
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  border: none;
  border-radius: var(--radius-sm);
  padding: 10px 16px;
  cursor: pointer;
  background: var(--brand);
  color: #fff;
  transition: background 0.15s ease;
}

button:hover,
.btn:hover {
  background: var(--brand-hover);
  text-decoration: none;
}

button:disabled,
.btn:disabled,
select:disabled {
  cursor: not-allowed;
  opacity: 0.45;
  pointer-events: none;
}

button.secondary,
.btn.secondary {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
}

button.secondary:hover,
.btn.secondary:hover {
  background: var(--border);
}

button.danger,
.btn.danger {
  background: transparent;
  color: var(--danger);
  border: 1px solid var(--danger);
}

button.danger:hover,
.btn.danger:hover {
  background: rgba(255, 92, 114, 0.12);
}

form {
  margin: 0;
}

form.inline {
  display: inline-block;
  margin-right: 8px;
}

input[type="text"],
input[type="email"],
input[type="number"],
input[type="tel"],
textarea,
select {
  font-family: inherit;
  font-size: 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 9px 12px;
  width: 100%;
}

input[type="text"]:focus,
input[type="email"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--brand);
}

/* appearance:none + fleche dessinee en arriere-plan (SVG inline), meme
   principe que les cases a cocher plus bas : color-scheme seul (voir
   :root) corrige le rendu ferme/focus d'un <select> natif mais pas le
   survol -- Chromium garde un chrome natif clair sur la fleche au hover
   meme avec color-scheme:dark, d'ou un carre blanc disgracieux. S'applique
   a TOUS les select (pas seulement .lang-select, meme souci constate sur
   les select d'unite/plateforme/couleur du dashboard). */
select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%238a8f9e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 10px 7px;
  padding-right: 32px;
  cursor: pointer;
}

select:hover {
  background-color: var(--border);
}

/* Meme souci de chrome natif clair au survol, cette fois sur les fleches
   haut/bas d'un input numerique en theme sombre : plutot que de re-dessiner
   un mini spinner sur mesure pour un gain d'usage marginal (la saisie au
   clavier/pave numerique mobile suffit), on le retire simplement -- evite
   aussi un controle beaucoup trop petit pour etre tapotable sur mobile. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

input[type="number"] {
  -moz-appearance: textfield;
}

/* Meme classe de bug que le select/les cases a cocher ci-dessus (chrome
   natif qui ignore color-scheme), mais sur le bouton "Choisir un fichier"
   lui-meme : il herite color:var(--text) (blanc en theme sombre) sans que
   son fond ne suive (reste blanc/gris clair, couleur navigateur par
   defaut) -- texte blanc sur fond blanc, illisible. Style manuel du
   pseudo-element sur le modele exact de button.secondary plutot que de
   compter sur le rendu natif. ::file-selector-button est le standard
   actuel, ::-webkit-file-upload-button la variante historique encore
   necessaire pour Safari/anciens Chromium. */
input[type="file"] {
  color: var(--text-muted);
  font-size: 14px;
}

input[type="file"]::file-selector-button,
input[type="file"]::-webkit-file-upload-button {
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  margin-right: 10px;
  cursor: pointer;
  background: var(--surface-2);
  color: var(--text);
  transition: background 0.15s ease;
}

input[type="file"]::file-selector-button:hover,
input[type="file"]::-webkit-file-upload-button:hover {
  background: var(--border);
}

/* Case a cocher dessinee sur mesure (comme les boutons/inputs : memes
   couleurs, meme rayon, memes etats hover/focus/disabled) -- accent-color
   seul ne permet pas de maitriser le rayon d'angle ni le style de coche
   entre navigateurs, d'ou le appearance:none + coche dessinee en ::after. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 18px;
  height: 18px;
  margin: 0 8px 0 0;
  border: 1px solid var(--checkbox-border);
  border-radius: 5px;
  background: var(--surface-2);
  cursor: pointer;
  position: relative;
  transition: background 0.15s ease, border-color 0.15s ease;
}

input[type="checkbox"]:hover {
  border-color: var(--brand);
}

input[type="checkbox"]:checked {
  background: var(--brand);
  border-color: var(--brand);
}

input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

input[type="checkbox"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.checkbox-row {
  display: flex;
  align-items: center;
  color: var(--text);
  font-size: 14px;
  margin-bottom: 10px;
}

.contact-field-row {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
  font-size: 14px;
  margin-bottom: 6px;
}

.contact-field-row label {
  flex: 1;
}

summary {
  cursor: pointer;
  color: var(--text);
}

summary:hover {
  color: var(--brand);
}

.drag-handle {
  font-size: 16px;
  line-height: 1;
  color: var(--text-muted);
  cursor: grab;
  user-select: none;
}

.drag-handle:active {
  cursor: grabbing;
}

.platform-choice {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 6px;
  margin-bottom: 20px;
}

.platform-choice input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.platform-choice label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 0;
  padding: 8px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
}

.platform-choice input[type="radio"]:checked + label {
  border-color: var(--brand);
  color: var(--text);
  background: var(--brand-soft);
}

.platform-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
}

.platform-badge.other { background: var(--text-muted); }

/* Logos officiels (Steam/GOG/Epic) : deux variantes par plateforme (fond
   clair / fond sombre), basculees exactement comme les variables de
   couleur du theme (prefers-color-scheme par defaut, data-theme force). */
.platform-logo {
  height: 16px;
  width: auto;
  vertical-align: middle;
}

.logo-onlight { display: inline-block; }
.logo-ondark { display: none; }

@media (prefers-color-scheme: dark) {
  .logo-onlight { display: none; }
  .logo-ondark { display: inline-block; }
}

:root[data-theme="light"] .logo-onlight { display: inline-block !important; }
:root[data-theme="light"] .logo-ondark { display: none !important; }
:root[data-theme="dark"] .logo-onlight { display: none !important; }
:root[data-theme="dark"] .logo-ondark { display: inline-block !important; }

label {
  display: block;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 14px;
}

label > input,
label > textarea {
  margin-top: 6px;
}

.field-row {
  display: flex;
  gap: 8px;
}

/* Sans flex:1 explicite, la largeur par defaut (100%) d'un select le fait
   entrer en conflit avec le calcul de retrecissement flex : son "hypothetical
   size" (100% du conteneur) depasse a lui seul la largeur disponible des que
   le field-row contient un 2e element, ce qui l'ecrase et prive l'element
   voisin (bouton) de toute la place -- observe sur "Langue de l'overlay"
   (select + bouton "Mettre a jour" coupe en deux lignes meme en plein
   desktop). Memes regles que pour input pour eviter ce souci partout. */
.field-row input,
.field-row select {
  flex: 1;
}

/* Formulaires de reglage a enregistrement automatique (attribut
   data-autosave, voir public/shared/autosave.js). Le bouton de soumission
   n'a de raison d'etre que sans JS, ou le POST classique reste le seul moyen
   d'enregistrer : la classe js-autosave est posee sur <html> par le script
   lui-meme, donc il reste visible et fonctionnel si le script n'a pas
   tourne. */
.js-autosave .autosave-submit {
  display: none;
}

/* Libelle "Enregistre" / "Echec", insere par le script a la place exacte du
   bouton masque. Largeur minimale pour que son apparition ne decale pas les
   champs voisins d'un formulaire en rangee. */
.autosave-status {
  align-self: center;
  min-width: 90px;
  font-size: 12px;
  color: var(--text-muted);
}

.autosave-status.is-error {
  color: var(--danger);
}

/* Reglette de volume des sons de l'overlay (voir
   dashboard.routes.js#overlayStepHtml). Style explicite plutot que le
   rendu natif : celui de Chromium ignore color-scheme sur ce controle et
   ressort gris clair en theme sombre. La piste est dessinee sur l'input
   lui-meme (background), seul le curseur passe par les pseudo-elements
   -- ils ne peuvent pas etre regroupes dans une meme regle, un selecteur
   inconnu invalidant tout le bloc pour le navigateur qui ne le connait
   pas. */
.field-row input[type='range'] {
  -webkit-appearance: none;
  appearance: none;
  height: 6px;
  align-self: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--surface-2);
  cursor: pointer;
}

.field-row input[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 50%;
  background: var(--brand);
  cursor: pointer;
}

.field-row input[type='range']::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 50%;
  background: var(--brand);
  cursor: pointer;
}

/* Valeur affichee a cote de la reglette : largeur minimale figee pour que
   le curseur ne se decale pas en largeur pendant qu'on le fait glisser
   (le libelle passe de "5 %" a "100 %" a "Muet"). */
.range-value {
  min-width: 64px;
  align-self: center;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

/* Calculateur de cout de la recompense Channel Points (voir
   dashboard.routes.js#hypeRewardStepHtml) : purement indicatif, aucune
   valeur n'est envoyee au serveur tant que le streamer ne clique pas sur
   "Utiliser ce montant" -- encadre en pointilles pour le distinguer
   visuellement des vrais champs du formulaire juste en dessous. */
.hype-calc {
  margin: 10px 0;
  padding: 12px;
  border: 1px dashed var(--border);
  border-radius: 10px;
  background: var(--surface-2);
}

.hype-calc-tiers {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  flex-wrap: wrap;
}

.hype-calc-tier {
  flex: 1;
  min-width: 140px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  text-align: center;
}

.hype-calc-tier-value {
  font-size: 18px;
  font-weight: 700;
  color: var(--brand);
  margin: 4px 0;
}

.hype-calc-tier button {
  margin-top: 6px;
  width: 100%;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

.badge.status-collecting {
  color: var(--success);
  border-color: var(--success);
}

.badge.status-locked,
.badge.status-drawing {
  color: var(--gold);
  border-color: var(--gold);
}

.badge.status-complete {
  color: var(--brand);
  border-color: var(--brand);
}

.badge.status-cancelled {
  color: var(--danger);
  border-color: var(--danger);
}

.badge.overlay-online {
  color: var(--success);
  border-color: var(--success);
}

.badge.overlay-offline {
  color: var(--text-muted);
  border-color: var(--border);
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
}

.banner {
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 16px;
  font-size: 14px;
}

.banner.error {
  background: rgba(255, 92, 114, 0.12);
  border: 1px solid var(--danger);
  color: var(--danger);
}

.banner.success {
  background: rgba(61, 220, 132, 0.12);
  border: 1px solid var(--success);
  color: var(--success);
}

/* .banner.gold : notice de traduction informative sur les pages legales
   (cgu/cgv/confidentialite/mentions-legales) + .todo : encart "reste a
   completer" sur ces memes pages -- factorise ici (etait duplique en
   <style> inline dans les 4 fichiers) pour ne plus avoir besoin de nonce
   CSP sur un <style> inline, voir securityHeaders.js.
   .banner.warning partage la meme apparence sous un nom qui dit ce qu'il
   signifie plutot que sa couleur : un avertissement a lire avant d'agir, pas
   une erreur (ex. l'equite du cout d'une entree par Channel Points, voir
   dashboard.entryModeFairnessWarning). */
.banner.gold,
.banner.warning {
  background: rgba(255, 209, 102, 0.12);
  border: 1px solid var(--gold);
}

.todo {
  background: rgba(255, 209, 102, 0.12);
  border: 1px solid var(--gold);
  color: var(--gold);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 13px;
  margin: 8px 0 18px;
}

/* display:block + overflow-x:auto sur la table elle-meme (plutot que
   d'imposer un wrapper <div style="overflow-x:auto"> a chaque appelant) :
   les tables les plus larges (gagnants avec coordonnees, historique) ne
   cassent jamais la mise en page sur mobile, elles defilent horizontalement
   a la place. white-space:nowrap evite qu'une cellule etroite force un
   retour a la ligne qui romprait l'alignement des colonnes. */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  display: block;
  overflow-x: auto;
  white-space: nowrap;
}

th,
td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}

th {
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  font-size: 12px;
  letter-spacing: 0.04em;
}

tr:last-child td {
  border-bottom: none;
}

.footer-links {
  margin-top: 28px;
  font-size: 13px;
  color: var(--text-muted);
}

.footer-links a {
  color: var(--text-muted);
}

.footer-links a:hover {
  color: var(--brand);
}

/* Presente sur (quasi) toutes les pages via pageShell : distincte des
   liens de navigation propres a chaque page (moins d'espace au-dessus
   quand elle suit directement un autre .footer-links). */
.legal-footer {
  margin-top: 10px;
  font-size: 12px;
}

.sub-star {
  color: var(--gold);
}

.qr-box {
  text-align: center;
  margin-top: 14px;
}

.qr-box img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  background: #fff;
  padding: 10px;
}

.prize-box {
  background: var(--surface-2);
  border: 1px solid var(--gold);
  border-radius: var(--radius-sm);
  padding: 16px;
  margin: 16px 0;
  text-align: center;
}

.prize-box code {
  background: none;
  border: none;
  font-size: 18px;
  color: var(--gold);
  letter-spacing: 0.04em;
}

/* Mobile/tablette : .page reste centre par son max-width (720px) sur les
   grands ecrans, ce bloc ne resserre que ce qui deborderait ou deviendrait
   trop petit a tapoter sur un ecran etroit (telephone tenu en portrait,
   notamment pour scanner le QR code de claim). */
@media (max-width: 600px) {
  .page {
    padding: 20px 14px 48px;
  }

  .brand {
    flex-wrap: wrap;
    row-gap: 10px;
    font-size: 13px;
  }

  h1 {
    font-size: 22px;
  }

  .card {
    padding: 16px;
  }

  .plan-columns {
    grid-template-columns: 1fr;
  }

  /* Boutons d'action d'un meme formulaire (ex: "Lancer le tirage" /
     "Annuler") : empiles pleine largeur plutot que cote a cote, plus facile
     a tapoter sans viser un bouton etroit. */
  form.inline {
    display: block;
    margin-right: 0;
    margin-bottom: 8px;
  }

  form.inline:last-child {
    margin-bottom: 0;
  }

  form.inline button {
    width: 100%;
  }
}

/* Utilitaires atomiques (espacement/taille/couleur) : remplacent les
   attributs style="..." qui parsemaient les templates HTML generes cote
   serveur -- la CSP n'accepte plus 'unsafe-inline' sur style-src (voir
   securityHeaders.js), et un attribut style="" n'est de toute facon jamais
   couvrable par un nonce (contrairement a un <style nonce="...">, la spec
   CSP ne couvre pas les attributs). Volontairement limite aux valeurs
   reellement reutilisees plusieurs fois a travers le code -- pas un
   framework utilitaire complet, juste ce qui evite de dupliquer une regle
   a chaque appelant. */
.mt-4 { margin-top: 4px; }
.mt-8 { margin-top: 8px; }
.mt-12 { margin-top: 12px; }
.mt-16 { margin-top: 16px; }
.mt-neg-6 { margin-top: -6px; }
.mb-6 { margin-bottom: 6px; }
.mb-8 { margin-bottom: 8px; }
.m-0 { margin: 0; }
.max-w-100 { max-width: 100px; }
.max-w-160 { max-width: 160px; }
.fs-11 { font-size: 11px; }
.fs-12 { font-size: 12px; }
.d-inline { display: inline; }
.d-block { display: block; }
.d-none { display: none; }
.text-danger { color: var(--danger); }
.text-success { color: var(--success); }
.border-danger { border-color: var(--danger); }
.overflow-x-auto { overflow-x: auto; }
.badge-gold-soft { background: rgba(255, 209, 102, 0.18); color: var(--gold); }
.badge-danger-soft { background: rgba(255, 99, 99, 0.18); color: var(--danger); }
.pre-wrap-break { white-space: pre-wrap; word-break: break-all; font-size: 12px; }
.mb-tight-4 { margin: 0 0 4px; }
.mb-tight-8 { margin: 0 0 8px; }
.my-8 { margin: 8px 0; }
.ml-24 { margin-left: 24px; }
.valign-fix { vertical-align: -3px; }
.hr-divider { border: none; border-top: 1px solid var(--border); margin: 20px 0; }
.list-tight { margin: 8px 0 0; padding-left: 20px; }
.og-preview-img { width: 100%; max-width: 360px; border-radius: 12px; display: block; margin-bottom: 8px; }
.select-compact { display: inline-block; width: auto; padding: 6px 28px 6px 8px; }
/* Variante de .banner utilisee pour la banniere de contexte moderateur (voir
   dashboard.routes.js#contextBannerHtml) : accent de marque (violet Twitch),
   pas dore comme .banner.gold -- deux nuances differentes deja distinctes
   avant ce refactor, conservees telles quelles. */
.banner.brand-soft { background: rgba(255, 209, 102, 0.12); border: 1px solid var(--brand); }

/* Hall of Fame (/hall-of-fame/:twitchLogin) et dashboard "Derniers gagnants" :
   surligne la ligne pointee par une ancre #winner-<id> dans l'URL. Deplace
   ici depuis un <style> inline lors du retrait de 'unsafe-inline' -- :target
   ne s'active que si l'URL cible reellement un id present sur la page
   courante, sans risque a rester globale. */
tr:target { outline: 2px solid var(--brand); outline-offset: -2px; }

/* Page /verify (voir verify.routes.js) : classes utilisees par le HTML
   genere cote CLIENT (innerHTML construit par le script inline apres
   soumission du formulaire), donc necessairement globales -- deplacees ici
   depuis un <style> inline propre a cette page lors du retrait de
   'unsafe-inline' de la CSP (voir securityHeaders.js). #verify-form
   n'existe que sur cette page : la regle textarea reste bien scopee a elle
   seule, pas a tous les <textarea> de l'app. */
#verify-form textarea { font-family: "Consolas", monospace; font-size: 13px; }
.ok { color: var(--success); font-weight: 700; }
.ko { color: var(--danger); font-weight: 700; }
.result { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 16px; margin-top: 16px; }

/* Piege a bots (voir contact.routes.js) : hors ecran plutot que display:none
   -- certains lecteurs d'ecran/bots ignorent display:none, ce champ doit
   rester "techniquement visible" pour piloter un vrai navigateur mais
   jamais a l'ecran pour un humain. */
.honeypot-field { position: absolute; left: -9999px; top: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Apercu de l'overlay dans le simulateur (voir devSimulate.routes.js) :
   iframe vers /overlay/<token>?preview=1 -- la VRAIE page overlay, meme flux
   WebSocket, pas une reimplementation. Ratio 16:9 fixe pour se comporter
   comme un canvas OBS (la carte de l'overlay se dimensionne sur la hauteur
   de sa fenetre, voir availableHeight()), sur un fond sombre puisque
   l'overlay est transparent et se compose normalement par-dessus le stream. */
.overlay-preview {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin-top: 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: linear-gradient(135deg, #1b1030, #0d1a2b 55%, #12212e);
}

.overlay-preview iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* Mode simple / mode avance du dashboard (voir CLAUDE.md fonctionnalite 20) :
   un interrupteur pose au-dessus du bloc de configuration, aligne a droite.
   C'est un <form> POST et non un lien -- une navigation GET qui modifie un
   reglage serait rejouee par un simple retour arriere du navigateur. */
.dashboard-mode-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin: -4px 0 12px;
}

.mode-switch {
  display: inline-flex;
  align-items: center;
  /* L'input etant en position absolue, il n'est pas un flex item : cet ecart
     ne separe donc que le libelle de la piste. */
  gap: 7px;
  cursor: pointer;
  user-select: none;
  /* Le libelle prend la couleur du texte normal, la piste porte l'accent :
     un interrupteur doit se lire d'un coup d'oeil sans crier plus fort que
     le contenu qu'il commande. */
  color: var(--text);
  font-size: 13px;
}

/* La vraie case reste focusable et annoncee par les lecteurs d'ecran, mais
   sort du flux : c'est .mode-switch-track qui la represente. opacity:0 plutot
   que display:none, qui la retirerait de l'ordre de tabulation.
   Selecteur volontairement descendant : la regle generique input[type=
   "checkbox"] plus haut a une specificite (0,1,1) -- superieure a une simple
   classe -- et lui rendait sinon sa position:relative, sa largeur de 18px et
   sa marge de 8px. La case restait alors dans le flux et ecartait le libelle
   de l'interrupteur. */
.mode-switch .mode-switch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
  pointer-events: none;
}

.mode-switch-track {
  position: relative;
  flex: none;
  width: 40px;
  height: 22px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  transition: background 0.18s ease, border-color 0.18s ease;
}

.mode-switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--text-muted);
  transition: transform 0.18s ease, background 0.18s ease;
}

.mode-switch .mode-switch-input:checked + .mode-switch-track {
  background: var(--brand);
  border-color: var(--brand);
}

.mode-switch .mode-switch-input:checked + .mode-switch-track .mode-switch-thumb {
  background: #fff;
  transform: translateX(18px);
}

/* Le focus doit rester visible alors que la case elle-meme ne l'est pas :
   sans ca, la navigation au clavier ne montrerait plus ou elle en est. */
.mode-switch .mode-switch-input:focus-visible + .mode-switch-track {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

.mode-switch:hover .mode-switch-track {
  border-color: var(--brand);
}

/* Repli sans JS : le bouton valide la bascule que le script soumet
   autrement de lui-meme. Meme principe que .autosave-submit. */
.js-autosave .mode-switch-submit {
  display: none;
}

/* Assistant de configuration : les etapes sont les memes qu'en mode avance,
   seule celle en cours est affichee (voir setupWizardHtml). Les boutons
   restent groupes en bas, "Terminer" prenant la place de "Suivant" sur la
   derniere etape plutot que de s'ajouter a cote. */
.wizard-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

/* Mode simple, assistant termine : une seule ligne, le rappel et le bouton
   de retour a la configuration cote a cote. */
.setup-done {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
