/* Ladder Libre — « ardoise & laiton ».
   ---------------------------------------------------------------------
   Le fond n'est pas noir mais ARDOISE : un noir pur écrase tout ce qu'on
   pose dessus et ne laisse qu'un seul plan de profondeur. Une ardoise
   légèrement bleutée en laisse trois — fond, panneau, carte — sans qu'on
   ait besoin d'une seule bordure.

   L'accent n'est pas doré mais LAITON : le jaune d'or saturé sonne
   « badge de récompense » ; le laiton mat, désaturé et un peu rosé, tient
   la même idée de valeur en restant un métal, pas une alerte.

   Trois règles tiennent tout le reste :
     1. Les surfaces se distinguent par leur VALEUR, pas par un trait. Les
        filets d'un pixel sont réservés aux séparations internes — lignes
        d'un tableau, pied de carte.
     2. Le laiton ne sert qu'à ce qui a de la valeur ou qui agit : kamas,
        bouton principal, état actif. Jamais en décoration.
     3. Les chiffres sont en chasse fixe et en chiffres tabulaires, partout.
        Une colonne de nombres qui ne s'aligne pas est illisible, et rien ne
        trahit davantage une interface faite à la va-vite. */

@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Manrope:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap');

:root {
  /* Trois plans d'ardoise. L'écart entre deux plans voisins est volontairement
     faible (4 à 6 points de luminance) : assez pour que l'œil sépare, trop peu
     pour que ça se voie comme un empilement de boîtes. */
  --bg:         #14181d;
  --panel:      #171c22;
  --card:       #1b2027;
  --card-2:     #212832;
  --card-hover: #262e39;
  --input:      #11151a;
  --border:     #2a323c;
  --border-sub: #232a33;
  --border-strong: #3a4450;

  /* Largeur de la barre latérale. Une variable et non un nombre en dur :
     elle sert aussi à décaler le contenu et la barre supérieure. */
  --rail: 252px;

  --text:       #e6e9ed;
  --muted:      #98a2ae;
  /* `--faint` porte TOUS les petits libellés en capitales — en-têtes de
     tableau, intitulés de champ, surtitres. À #69737f ils tombaient à 3,4:1
     sur une carte : sous le seuil AA pour du texte de 10 px, et illisibles
     sur un téléphone en plein jour. Relevé jusqu'à passer 4,5:1. */
  --faint:      #8b949f;

  /* Accent principal : laiton mat. Le ton « ink » est le brun très sombre
     qu'on pose SUR le laiton — du noir pur y ferait un trou. */
  --accent:      #d9b382;
  --accent-h:    #c9a069;
  --accent-ink:  #171310;
  --accent-soft: rgba(217,179,130,.12);
  --accent-bd:   rgba(217,179,130,.30);

  /* Accent secondaire : lilas cendré (statut technique, rôles). */
  --violet:      #a99bea;
  --violet-soft: rgba(169,155,234,.13);
  --violet-bd:   rgba(169,155,234,.32);

  /* Teintes de famille : elles ne servent QU'AU REPÉRAGE — intitulé de
     groupe, onglet courant, pastille de titre. Le laiton, lui, reste réservé
     à la valeur et à l'action. Sans cette règle, tout devient sapin de Noël. */
  --cyan:      #6fc3b4;   /* Suivi : observer, surveiller */
  --cyan-soft: rgba(111,195,180,.13);
  --cyan-bd:   rgba(111,195,180,.30);

  --ardoise:      #9fb0c6;   /* Configuration : réglages, neutre assumé */
  --ardoise-soft: rgba(159,176,198,.13);
  --ardoise-bd:   rgba(159,176,198,.28);

  /* Sémantique */
  --success:    #5fc98d;
  --success-bg: rgba(95,201,141,.13);
  --danger:     #e4695f;
  --danger-h:   #c9544b;
  --danger-plein: #b03d34;
  --danger-plein-h: #97322a;
  --danger-bg:  rgba(228,105,95,.13);
  --danger-bd:  rgba(228,105,95,.32);

  --def: #e08363;
  --atk: #71a9de;

  /* Attention : distinct de l'accent (valeur) et du danger (destruction). */
  --warn:      #e3a352;
  --warn-bg:   rgba(227,163,82,.13);
  --warn-bd:   rgba(227,163,82,.32);

  /* Une seule signature de mouvement sur tout le site (expo-out : départ vif,
     arrivée douce). La sortie est plus courte que l'entrée — une interface qui
     s'efface vite paraît vive, une qui s'efface lentement paraît lourde. */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --t-rapide: 120ms;
  --t-entree: 200ms;
  --t-sortie: 130ms;
  --t-ample:  300ms;

  /* Rayons resserrés. Le gros arrondi partout est la signature des interfaces
     bâclées ; un rayon court et constant se remarque moins et vieillit mieux. */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-xl: 18px;
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;

  /* Ombres longues et très basses en opacité : c'est ce qui remplace les
     bordures pour détacher une carte de son fond. */
  --shadow-sm: 0 1px 2px rgba(0,0,0,.32);
  --shadow: 0 20px 44px -22px rgba(0,0,0,.72), 0 2px 6px rgba(0,0,0,.28);
  --shadow-haut: 0 28px 60px -24px rgba(0,0,0,.8), 0 3px 10px rgba(0,0,0,.34);

  /* Liseré clair posé en haut des surfaces : une carte sans bordure a besoin
     de cette arête pour ne pas paraître collée au fond. */
  --arete: inset 0 1px 0 rgba(255,255,255,.045);

  --font: "Manrope", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  --display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --mono: "JetBrains Mono", "Cascadia Mono", Consolas, ui-monospace, monospace;

  /* Valeurs qui s'inversent d'un thème à l'autre. Regroupées ici plutôt que
     dispersées en rgba() dans les règles : c'est la condition pour qu'un
     second thème tienne en un bloc au lieu d'une centaine de retouches. */
  --survol:      rgba(255,255,255,.04);
  --survol-fort: rgba(255,255,255,.055);
  --zebre:       transparent;
  --rail-fond:   var(--panel);
  --topbar-fond: rgba(20,24,29,.86);
  --danger-txt:  #ffb0a8;
  --grain:       .028;
  /* Teintes des avatars : claires sur fond sombre, l'inverse en thème jour. */
  --discord-txt: #a3abff;
  --avatar-l: 74%; --avatar-s: 34%;
  --avatar-fond-l: 24%; --avatar-fond-s: 24%;
  color-scheme: dark;
}

/* ---- Thème jour -----------------------------------------------------
   Pas le thème sombre inversé. Le laiton clair est illisible sur blanc : il
   passe à un bronze soutenu pour tout ce qui est texte, et ne reste laiton
   que sur les surfaces pleines — bouton principal, onglet actif — où c'est
   le texte sombre posé dessus qui porte le contraste.
   Le fond n'est pas blanc mais un papier très légèrement bleuté, de la même
   famille que l'ardoise : c'est ce qui fait que les deux thèmes se
   reconnaissent comme un seul objet. */
:root[data-theme="clair"] {
  --bg:         #eef1f5;
  --panel:      #f7f9fb;
  --card:       #ffffff;
  --card-2:     #f2f5f8;
  --card-hover: #e9edf3;
  --input:      #ffffff;
  --border:     #dde3ea;
  --border-sub: #e7ecf1;
  --border-strong: #c3ccd7;

  --text:       #171c22;
  --muted:      #59636f;
  --faint:      #656e7a;

  /* Bronze : même famille que le laiton, assez foncé pour du texte. */
  --accent:      #7d5c2d;
  --accent-h:    #634821;
  --accent-ink:  #fffaf3;
  --accent-soft: rgba(125,92,45,.10);
  --accent-bd:   rgba(125,92,45,.32);

  --violet:      #5b4bb8;
  --violet-soft: rgba(91,75,184,.10);
  --violet-bd:   rgba(91,75,184,.28);

  --cyan:      #10756c;
  --cyan-soft: rgba(16,117,108,.10);
  --cyan-bd:   rgba(16,117,108,.26);

  --ardoise:      #4a586a;
  --ardoise-soft: rgba(74,88,106,.10);
  --ardoise-bd:   rgba(74,88,106,.24);

  --success:    #1a7a41;
  --success-bg: rgba(26,122,65,.11);
  --danger:     #b3271d;
  --danger-h:   #8f1e16;
  --danger-plein: #a8231b;
  --danger-plein-h: #8a1a13;
  --danger-bg:  rgba(179,39,29,.09);
  --danger-bd:  rgba(179,39,29,.28);
  --danger-txt: #8f1e16;

  --def: #b04a24;
  --atk: #14588f;
  --warn:      #91580a;
  --warn-bg:   rgba(145,88,10,.11);
  --warn-bd:   rgba(145,88,10,.28);

  --survol:      rgba(23,28,34,.04);
  --survol-fort: rgba(23,28,34,.06);
  --zebre:       transparent;
  --rail-fond:   var(--panel);
  --topbar-fond: rgba(247,249,251,.86);
  --grain:       .008;
  --discord-txt: #3b45c4;
  --avatar-l: 30%; --avatar-s: 44%;
  --avatar-fond-l: 92%; --avatar-fond-s: 40%;

  --shadow-sm: 0 1px 2px rgba(23,28,34,.06);
  --shadow: 0 18px 40px -22px rgba(23,28,34,.22), 0 1px 3px rgba(23,28,34,.06);
  --shadow-haut: 0 26px 56px -24px rgba(23,28,34,.28), 0 2px 6px rgba(23,28,34,.08);
  --arete: inset 0 1px 0 rgba(255,255,255,.9);
  color-scheme: light;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ---- Décor de fond --------------------------------------------------
   Une seule couche, immobile : une lumière très diffuse tombant du haut, et
   un grain photographique à 3 %.

   Ce qui a été RETIRÉ vaut d'être noté : deux nappes colorées qui dérivaient
   en boucle, des anneaux concentriques en écho au médaillon, un halo violet
   en bas à gauche. Beaucoup d'effet, zéro information — et c'est exactement
   ce genre d'accumulation décorative qui fait qu'une interface a l'air
   fabriquée en série. Le grain reste : il donne de la matière à l'ardoise
   sans rien raconter. */
html::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: -1;
  background: radial-gradient(1100px 560px at 50% -12%,
    rgba(217,179,130,.055), transparent 70%);
}
body::after {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  opacity: var(--grain);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}

/* ---- Typographie ----------------------------------------------------
   Trois fontes, trois rôles nets. Fraunces ne sert QU'AUX titres de vue et
   aux grands nombres de résumé : c'est une fonte à caractère, elle perdrait
   son effet si elle servait à tout. Manrope porte le texte courant.
   JetBrains Mono porte tout ce qui se compare : chiffres, clés, libellés en
   petites capitales. */
h1, h2, h3 { margin: 0; letter-spacing: -.02em; text-wrap: balance; }
h1, h2 { font-family: var(--display); font-optical-sizing: auto; font-weight: 600; }
h3 { font-weight: 700; }
p { margin: 0; }
button { font-family: inherit; }
[hidden] { display: none !important; }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.tabnum { font-variant-numeric: tabular-nums; font-family: var(--mono); }

/* Libellé en petites capitales : le repère de section du site. */
.surtitre {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--faint);
}

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
::selection { background: var(--accent-soft); color: var(--text); }

.skip-link { position: absolute; left: 8px; top: -48px; z-index: 100;
  background: var(--accent); color: var(--accent-ink); font-weight: 700;
  font-size: 13.5px; padding: 10px 16px; border-radius: var(--r-sm);
  transition: top .15s ease; }
.skip-link:focus { top: 8px; }

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

/* ---- Marque --------------------------------------------------------- */
.logo { width: 44px; height: 44px; flex: none; display: block; }
.topbar-brand .logo { width: 28px; height: 28px; }
.nav-panel-tete .logo { width: 30px; height: 30px; }
.credit { font-size: 12px; color: var(--faint); margin-top: 2px; }
.tb-name { display: flex; flex-direction: column; line-height: 1.15; }
.tb-name strong { font-family: var(--display); font-weight: 600; font-size: 16px;
  letter-spacing: -.01em; }
.tb-name .credit { margin-top: 1px; font-size: 10px; font-family: var(--mono);
  letter-spacing: .04em; }
.brand { display: flex; gap: var(--sp-3); align-items: center; margin-bottom: var(--sp-5); }
.brand h1 { font-size: 24px; }

/* ---- Boutons ----------------------------------------------------------
   Trois niveaux seulement, et ils se distinguent par le REMPLISSAGE, pas par
   la bordure : laiton plein pour l'action principale, ardoise claire pour le
   reste, transparent pour le tertiaire. Un bouton secondaire cerné d'un trait
   attire autant l'œil que le principal — c'est ce qui donnait à chaque écran
   trois boutons d'égale importance. */
.btn {
  --b: var(--card-2); --bh: var(--card-hover); --ink: var(--text); --bd: transparent;
  appearance: none; border: 1px solid var(--bd);
  background: var(--b); color: var(--ink);
  padding: 9px 16px; border-radius: var(--r-sm); min-height: 42px;
  font-weight: 600; font-size: 13.5px; letter-spacing: -.005em; cursor: pointer;
  transition: background var(--t-rapide) var(--ease-out),
              border-color var(--t-rapide) var(--ease-out),
              transform 60ms ease, box-shadow var(--t-rapide) var(--ease-out);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  white-space: nowrap; touch-action: manipulation;
}
.btn:hover { background: var(--bh); }
.btn:active { transform: translateY(1px); }
.btn-primary { --b: var(--accent); --bh: var(--accent-h); --ink: var(--accent-ink);
  font-weight: 700; }
.btn-primary:hover { box-shadow: 0 6px 18px -8px rgba(217,179,130,.55); }
.btn-danger  { --b: var(--danger-plein); --bh: var(--danger-plein-h); --ink: #fff;
  font-weight: 700; }
/* Bouton Discord aux couleurs de la marque (blurple officiel) : reconnu
   instantanément, ce qui rassure au moment de donner une autorisation. */
.btn-discord { --b: #5865f2; --bh: #4752c4; --ink: #fff; text-decoration: none;
  font-weight: 700; }
.btn-discord:hover { box-shadow: 0 6px 18px -8px rgba(88,101,242,.7); }
/* Séparateur « ou » entre connexion classique et Discord. */
.separateur-ou { display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-4) 0; color: var(--faint); font-size: 10.5px;
  font-family: var(--mono); text-transform: uppercase; letter-spacing: .16em; }
.separateur-ou::before, .separateur-ou::after { content: ""; flex: 1;
  height: 1px; background: var(--border-sub); }

.btn-ghost   { --b: transparent; --bh: var(--survol); color: var(--muted); }
.btn-ghost:hover { color: var(--text); }
.btn-block { width: 100%; }
.btn-icon { padding: 8px 10px; line-height: 1; min-width: 42px; }
.btn-sm { padding: 6px 12px; font-size: 12.5px; min-height: 36px; }

/* ---- Champs -------------------------------------------------------
   Les intitulés passent en chasse fixe, petites capitales : ils cessent de
   rivaliser avec le texte courant, et l'ensemble d'un formulaire se lit d'un
   coup comme une grille plutôt que comme une suite de phrases en gras. */
.field { display: flex; flex-direction: column; gap: 7px; margin-bottom: var(--sp-4); }
.field > span { font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  color: var(--faint); text-transform: uppercase; letter-spacing: .14em; }
input, select {
  appearance: none; background: var(--input); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 11px 13px; font-size: 14px; font-family: inherit; width: 100%;
  min-height: 42px;
  transition: border-color var(--t-rapide) var(--ease-out),
              box-shadow var(--t-rapide) var(--ease-out);
  touch-action: manipulation;
}

/* `appearance: none` ci-dessus efface AUSSI le dessin natif des cases à
   cocher : elles restaient des carrés vides, cochées comme décochées —
   impossible de voir qui était payé dans l'onglet Paiements. On redessine
   donc la case et sa coche. */
input[type="checkbox"] {
  min-height: 0; width: 18px; height: 18px; flex: none; padding: 0;
  border-radius: 4px; border: 1.5px solid var(--border-strong);
  background: var(--input); cursor: pointer; position: relative;
  transition: background .12s ease, border-color .12s ease;
}
input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::after {
  content: ""; position: absolute; left: 5px; top: 1px; width: 4px; height: 9px;
  border: solid var(--accent-ink); border-width: 0 2px 2px 0; transform: rotate(42deg);
}
input[type="checkbox"]:disabled { opacity: .45; cursor: not-allowed; }
input::placeholder { color: var(--faint); }
input:focus, select:focus { outline: none; border-color: var(--accent-bd);
  box-shadow: 0 0 0 3px var(--accent-soft); }
select { background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 34px;
  cursor: pointer; }
.erreur { color: var(--danger-txt); background: var(--danger-bg);
  border-left: 2px solid var(--danger); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: 9px 12px; font-size: 13px; margin: 0 0 var(--sp-4); }

/* ---- Connexion ---------------------------------------------------- */
.login-wrap { min-height: 100%; display: grid; place-items: center; padding: var(--sp-5); }
.login-card { width: 100%; max-width: 408px; background: var(--card);
  border-radius: var(--r-xl); padding: var(--sp-6); box-shadow: var(--shadow-haut), var(--arete); }
.login-card h1 { font-size: 24px; }
.case-souvenir { display: flex; align-items: center; gap: 10px; font-size: 13px;
  color: var(--muted); margin: 0 0 var(--sp-4); cursor: pointer; }
.case-souvenir input { width: 18px; }

/* ---- Barre latérale ------------------------------------------------
   Le menu est une colonne fixe à gauche, toujours visible : avec quinze
   onglets, une barre horizontale obligeait à défiler pour atteindre les
   derniers, et l'onglet courant se perdait dans le lot. Sous 1000 px la
   même colonne devient un tiroir (voir la requête média). */
.nav-panel {
  position: fixed; top: 0; left: 0; bottom: 0; z-index: 30; width: var(--rail);
  display: flex; flex-direction: column;
  background: var(--rail-fond);
  border-right: 1px solid var(--border-sub);
}
.nav-panel-tete {
  display: flex; align-items: center; gap: 10px; flex: none;
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
}
.nav-fermer { display: none; margin-left: auto; }

.nav { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
  gap: 1px; padding: 0 var(--sp-3) var(--sp-4); }
.nav button {
  border: 0; background: transparent; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 8px 11px; min-height: 38px; border-radius: var(--r-sm);
  font-weight: 500; font-size: 13.5px; position: relative;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out);
}
.nav button:hover { color: var(--text); background: var(--survol); }
/* L'onglet courant : un filet de 2 px à gauche et un fond à peine teinté.
   Un aplat pleine largeur écrasait tout le reste de la colonne. */
.nav button.actif { color: var(--text); background: var(--survol-fort); font-weight: 600; }
.nav button.actif::before {
  content: ""; position: absolute; left: -3px; top: 9px; bottom: 9px; width: 2px;
  border-radius: 0 2px 2px 0; background: var(--sect);
}
.nav-icone { display: inline-flex; flex: none; width: 16px; height: 16px; opacity: .65; }
.nav-icone svg { width: 100%; height: 100%; display: block; }
.nav button.actif .nav-icone { opacity: 1; color: var(--sect); }

/* Affectation des teintes de famille. Une seule table, lue à la fois par la
   barre latérale (`data-groupe` sur chaque bouton) et par la page courante
   (`data-section` sur <body>) : impossible que le menu et le titre de page
   se contredisent. */
[data-groupe="1"], body[data-section="1"] {
  --sect: var(--accent); --sect-soft: var(--accent-soft); --sect-bd: var(--accent-bd);
}
[data-groupe="2"], body[data-section="2"] {
  --sect: var(--cyan); --sect-soft: var(--cyan-soft); --sect-bd: var(--cyan-bd);
}
[data-groupe="3"], body[data-section="3"] {
  --sect: var(--violet); --sect-soft: var(--violet-soft); --sect-bd: var(--violet-bd);
}
[data-groupe="4"], body[data-section="4"] {
  --sect: var(--ardoise); --sect-soft: var(--ardoise-soft); --sect-bd: var(--ardoise-bd);
}

/* ---- Navigation : familles repliables -------------------------------
   L'intitulé de famille est devenu un BOUTON. Il porte le nombre d'onglets
   qu'il contient — replié, c'est la seule chose qui reste à l'écran, et
   « Administration 4 » en dit assez pour savoir s'il faut l'ouvrir. */
.nav-groupe {
  appearance: none; border: 0; background: transparent; width: 100%;
  font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .18em; color: var(--faint);
  padding: var(--sp-4) 11px var(--sp-2);
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  transition: color var(--t-rapide) var(--ease-out);
}
.nav-groupe:hover { color: var(--text); }
.nav-groupe > span:first-child { flex: none; }
.nav-compte {
  font-size: 9px; letter-spacing: .06em; color: var(--faint);
  background: var(--survol); border-radius: 999px; padding: 1px 6px;
  min-width: 18px; text-align: center;
}
/* Le chevron pointe vers le bas quand la famille est ouverte. Un seul
   élément qui tourne : c'est la façon la plus économique de dire « ceci
   se replie » sans écrire le mot. */
.nav-chevron { margin-left: auto; display: inline-flex; width: 12px; height: 12px;
  transition: transform var(--t-entree) var(--ease-out); }
.nav-chevron svg { width: 100%; height: 100%; }
.nav-groupe.ouvert .nav-chevron { transform: rotate(90deg); }
.nav-groupe.ouvert .nav-compte { opacity: 0; }
/* La famille courante ne se replie pas : son chevron disparaît plutôt que
   de promettre un geste qui ne fera rien. */
.nav-groupe-fige { cursor: default; }
.nav-groupe-fige:hover { color: var(--faint); }
.nav-groupe-fige .nav-chevron { opacity: 0; }
.nav-groupe:first-child { padding-top: var(--sp-2); }

.nav-sous { display: flex; flex-direction: column; gap: 1px;
  animation: nav-deplie var(--t-entree) var(--ease-out); }
@keyframes nav-deplie {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* ---- Fil d'Ariane ---------------------------------------------------- */
.fil-ariane { display: flex; align-items: baseline; gap: 8px; min-width: 0;
  font-size: 13px; }
.fil-famille { font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .16em; color: var(--sect, var(--faint)); }
.fil-sep { color: var(--border-strong); }
.fil-page { font-weight: 600; color: var(--text); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }

/* ---- Appel de la palette --------------------------------------------- */
.palette-appel {
  appearance: none; border: 0; margin-left: auto; cursor: pointer;
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--input); color: var(--faint);
  border-radius: var(--r-sm); padding: 7px 10px 7px 11px; min-height: 36px;
  font-family: inherit; font-size: 13px; min-width: 232px;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out);
}
.palette-appel:hover { background: var(--card-2); color: var(--muted); }
.palette-appel svg { width: 15px; height: 15px; flex: none; }
.palette-appel > span { flex: 1; text-align: left; }
kbd {
  font-family: var(--mono); font-size: 9.5px; line-height: 1;
  padding: 4px 5px; border-radius: 4px; background: var(--card-2);
  color: var(--faint); box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);
}

/* ---- Raccourcis clavier ---------------------------------------------
   Le témoin s'affiche pendant l'attente qui suit `g` : sans lui, on ne sait
   pas si la touche a été prise en compte, et on la retape. */
.temoin-g {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  z-index: 70; display: flex; align-items: center; gap: 9px;
  background: var(--card); color: var(--muted); border-radius: var(--r-sm);
  padding: 9px 14px; font-size: 13px;
  box-shadow: var(--shadow-haut), var(--arete);
  animation: temoin-entre var(--t-sortie) var(--ease-out) both;
}
@keyframes temoin-entre {
  from { opacity: 0; transform: translate(-50%, 6px); }
  to   { opacity: 1; transform: translateX(-50%); }
}
.raccourcis { display: flex; flex-direction: column; }
.raccourci-ligne { display: flex; align-items: center; gap: var(--sp-4);
  padding: 8px 2px; font-size: 13.5px; color: var(--muted); }
.raccourci-ligne + .raccourci-ligne { border-top: 1px solid var(--border-sub); }
.raccourci-ligne > span:first-child { flex: none; min-width: 82px;
  display: flex; gap: 4px; }
.raccourci-ligne > span:last-child { color: var(--text); }

/* ---- Palette de commandes -------------------------------------------
   Ancrée dans le tiers haut de l'écran plutôt qu'au centre : c'est là que
   le regard attend une barre de recherche, et la liste a la place de
   grandir vers le bas sans jamais bouger le champ. */
.palette-fond {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(8, 10, 13, .58); backdrop-filter: blur(3px);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh var(--sp-4) var(--sp-4);
  animation: palette-fond-entre var(--t-sortie) ease both;
}
@keyframes palette-fond-entre { from { opacity: 0; } to { opacity: 1; } }
.palette-boite {
  width: 100%; max-width: 560px; background: var(--card);
  border-radius: var(--r-lg); box-shadow: var(--shadow-haut), var(--arete);
  overflow: hidden; display: flex; flex-direction: column;
  animation: palette-entre var(--t-entree) var(--ease-out) both;
}
@keyframes palette-entre {
  from { opacity: 0; transform: translateY(-10px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
.palette-champ { display: flex; align-items: center; gap: 11px;
  padding: var(--sp-4); border-bottom: 1px solid var(--border-sub); }
.palette-champ svg { width: 17px; height: 17px; flex: none; color: var(--faint); }
.palette-champ input {
  border: 0; background: transparent; padding: 0; min-height: 0;
  font-size: 16px; font-family: inherit; color: var(--text);
}
.palette-champ input:focus { box-shadow: none; }
.palette-champ kbd { flex: none; }

.palette-liste { max-height: min(52vh, 420px); overflow-y: auto; padding: 6px; }
.palette-item {
  appearance: none; border: 0; background: transparent; width: 100%;
  display: flex; align-items: center; gap: 12px; text-align: left;
  padding: 9px 10px; border-radius: var(--r-sm); cursor: pointer;
  font-family: inherit; color: var(--muted);
}
/* Survol et sélection clavier partagent le même état : rien n'est plus
   déroutant qu'une ligne « survolée » et une autre « sélectionnée ». */
.palette-item.vise { background: var(--survol-fort); color: var(--text); }
.palette-icone { flex: none; width: 16px; height: 16px; display: inline-flex;
  color: var(--sect, var(--faint)); }
.palette-icone svg { width: 100%; height: 100%; }
.palette-texte { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.palette-texte b { font-size: 13.5px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.palette-texte span { font-size: 11.5px; color: var(--faint); }
.palette-type { flex: none; font-family: var(--mono); font-size: 9px;
  text-transform: uppercase; letter-spacing: .12em; color: var(--faint);
  background: var(--survol); border-radius: 4px; padding: 3px 6px; }
.palette-item.vise .palette-type { background: var(--card-2); }
.palette-vide { padding: var(--sp-5); text-align: center; color: var(--faint);
  font-size: 13.5px; }
.palette-pied { display: flex; gap: var(--sp-4); padding: 9px var(--sp-4);
  border-top: 1px solid var(--border-sub); background: var(--card-2);
  font-size: 11px; color: var(--faint); }
.palette-pied span { display: inline-flex; align-items: center; gap: 5px; }

@media (max-width: 900px) {
  /* Sur téléphone la marque cède la place : le fil d'Ariane dit où l'on est,
     et c'est plus utile qu'un logo déjà présent dans le tiroir. */
  .fil-ariane { flex: 1; }
  .palette-appel { min-width: 0; padding: 7px 9px; }
  .palette-appel > span, .palette-appel kbd { display: none; }
  .palette-fond { padding-top: 6vh; }
}

.nav-pied { flex: none; border-top: 1px solid var(--border-sub); padding: var(--sp-3); }
.nav-pied-lien {
  display: flex; align-items: center; gap: 11px; padding: 8px 11px;
  border-radius: var(--r-sm); color: var(--muted); text-decoration: none;
  font-weight: 500; font-size: 13.5px;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out);
}
.nav-pied-lien:hover { background: var(--survol); color: var(--text); }
.nav-pied-version { padding: 7px 11px 2px; font-size: 10px; color: var(--faint);
  font-family: var(--mono); letter-spacing: .1em; text-transform: uppercase; }

/* ---- Barre supérieure -------------------------------------------- */
/* Le décalage du contenu est porté par #ecran-app et non par chaque bloc :
   la barre latérale étant en `position: fixed`, elle ignore ce retrait et
   reste collée au bord, tandis que barre supérieure et vue s'alignent seules. */
#ecran-app { padding-left: var(--rail); }
.topbar { position: sticky; top: 0; z-index: 20; display: flex; align-items: center;
  gap: var(--sp-4); padding: 11px 36px; min-height: 60px;
  background: var(--topbar-fond); backdrop-filter: blur(18px) saturate(1.3);
  border-bottom: 1px solid var(--border-sub); }
/* La marque vit dans la barre latérale sur grand écran ; elle ne réapparaît
   en haut que lorsque cette colonne se replie en tiroir. */
.topbar-brand { display: none; align-items: center; gap: 10px; }
.nav-burger { display: none; flex: none; }
.nav-scrim { display: none; }
.topbar-user { display: flex; align-items: center; gap: 10px; flex: none; }
.user-nom { font-weight: 600; font-size: 13.5px; }
.badge { font-family: var(--mono); font-size: 9.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .12em;
  padding: 4px 9px; border-radius: var(--r-sm); background: var(--card-2);
  color: var(--muted); border: 1px solid transparent; }
.badge.superadmin { background: var(--accent-soft); color: var(--accent); }
.badge.admin { background: var(--violet-soft); color: var(--violet); }
.badge.superviseur { background: var(--cyan-soft); color: var(--cyan); }
.badge.recruteur { background: var(--success-bg); color: var(--success); }

.menu { position: relative; }
.menu-pop { position: absolute; right: 0; top: calc(100% + 8px); background: var(--card);
  border-radius: var(--r-md); padding: 6px; min-width: 224px;
  box-shadow: var(--shadow-haut), var(--arete); }
.menu-item { display: block; width: 100%; text-align: left; background: transparent; border: 0;
  color: var(--text); text-decoration: none; box-sizing: border-box;
  padding: 9px 10px; border-radius: var(--r-sm); font-size: 13.5px; font-weight: 500;
  cursor: pointer; }
.menu-item:hover { background: var(--survol-fort); }
.menu-item.danger { color: var(--danger-txt); }
/* Numéro de version affiché dans le menu ⋯ : permet de vérifier en un coup
   d'œil que la dernière mise à jour est bien celle servie (cache/upload). */
.menu-version { padding: 8px 10px 4px; font-size: 10px; color: var(--faint);
  border-top: 1px solid var(--border-sub); margin-top: 4px; font-family: var(--mono);
  letter-spacing: .1em; text-transform: uppercase; }

/* Barres de défilement discrètes, assorties au thème. */
* { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
*::-webkit-scrollbar { width: 10px; height: 8px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border); border-radius: 5px; }
*::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

/* ---- Vue / cartes -------------------------------------------------
   Les cartes n'ont PLUS DE BORDURE. Elles se détachent par leur valeur (un
   ton plus clair que le fond), une ombre longue et une arête claire en haut.
   Une page faite de rectangles cernés se lit comme un formulaire administratif ;
   les mêmes blocs posés par la lumière se lisent comme une page composée. */
.vue { max-width: 1200px; margin: 0 auto; padding: var(--sp-6) 36px var(--sp-6); }
/* Entrée en fondu-montée à chaque changement d'onglet : la transition
   remplace le « saut » brutal d'un contenu par l'autre. */
.vue > * { animation: vue-entre .32s var(--ease-out) both; }
.vue > *:nth-child(2) { animation-delay: .04s; }
.vue > *:nth-child(3) { animation-delay: .07s; }
.vue > *:nth-child(n+4) { animation-delay: .09s; }
@keyframes vue-entre {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

/* En-tête de vue : un surtitre en petites capitales qui nomme la famille,
   le titre en Fraunces, la phrase d'explication en dessous. La pastille
   d'icône perd sa bordure et son fond teinté au profit d'un simple glyphe
   coloré — c'est le surtitre qui dit désormais où l'on est. */
.vue-tete { margin-bottom: var(--sp-5);
  display: flex; align-items: center; gap: var(--sp-4); }
.vue-corps { min-width: 0; }
.vue-icone {
  width: 42px; height: 42px; border-radius: var(--r-md); flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--sect); background: var(--sect-soft);
}
.vue-icone svg { width: 20px; height: 20px; display: block; }
/* Le surtitre nomme la famille de l'onglet — « Consultation », « Suivi ».
   Il porte la couleur de cette famille : c'est le seul endroit de la page,
   avec la pastille, qui rappelle d'où l'on vient. */
.vue-surtitre { font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .18em; color: var(--sect, var(--faint));
  margin-bottom: 5px; }
.vue-tete h2 { font-size: 32px; line-height: 1.08; letter-spacing: -.025em; }
/* Les actions de la page, à droite du titre : c'est là qu'on les cherche.
   `margin-left: auto` les pousse au bord quel que soit le titre. */
.vue-actions { margin-left: auto; display: flex; align-items: center;
  gap: var(--sp-2); flex: none; }

/* ---- Sous-onglets d'une vue ------------------------------------------
   Des onglets, pas des segments : ils se posent sur un filet continu, comme
   des intercalaires. La distinction compte — un segment propose un réglage
   parmi d'autres, un onglet change ce qu'on regarde. */
.sous-onglets { display: flex; gap: var(--sp-1); margin: calc(var(--sp-5) * -1) 0 var(--sp-5);
  padding-top: var(--sp-4); border-bottom: 1px solid var(--border-sub);
  overflow-x: auto; scrollbar-width: none; }
.sous-onglets::-webkit-scrollbar { display: none; }
.sous-onglet {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--muted);
  padding: 9px 13px; min-height: 40px; white-space: nowrap; flex: none;
  display: inline-flex; align-items: center; gap: 8px;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color var(--t-rapide) var(--ease-out),
              border-color var(--t-rapide) var(--ease-out);
}
.sous-onglet:hover { color: var(--text); }
.sous-onglet.actif { color: var(--text); border-bottom-color: var(--sect, var(--accent)); }
.sous-onglet-compte { font-family: var(--mono); font-size: 10px; color: var(--faint);
  background: var(--survol); border-radius: 999px; padding: 2px 6px;
  font-variant-numeric: tabular-nums; }
.sous-onglet.actif .sous-onglet-compte { background: var(--card-2); color: var(--muted); }

@media (max-width: 640px) {
  .vue-actions { width: 100%; margin: var(--sp-3) 0 0; }
  .vue-tete { flex-wrap: wrap; }
  .vue-actions .btn { flex: 1; }
  .sous-onglet { font-size: 14px; padding: 9px 11px; }
}
.vue-tete p { color: var(--muted); font-size: 14px; margin-top: 7px; max-width: 66ch; }
@media (max-width: 640px) {
  .vue-icone { width: 36px; height: 36px; border-radius: var(--r-sm); }
  .vue-icone svg { width: 18px; height: 18px; }
  .vue-tete { gap: var(--sp-3); }
  .vue-tete h2 { font-size: 25px; }
  .vue-surtitre { font-size: 9.5px; letter-spacing: .16em; }
}

.card { background: var(--card); border-radius: var(--r-lg);
  box-shadow: var(--shadow), var(--arete); }
.card-pad { padding: var(--sp-5); }
.card + .card { margin-top: var(--sp-4); }
.card-titre { font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .16em; color: var(--faint);
  padding: var(--sp-4) var(--sp-5) 0; }
.card-sous { font-size: 13px; color: var(--muted); padding: 6px var(--sp-5) 0; }
.card-titre-pad { font-family: var(--mono); font-size: 10.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .16em; color: var(--faint);
  padding: 0 0 var(--sp-3); }

/* Sous-titre d'une section à l'intérieur d'une même vue (ex. Barème &
   Salons regroupe plusieurs blocs). Le trait vertical coloré a disparu :
   l'espace au-dessus suffit à ouvrir une section, et un filet de plus par
   sous-partie finissait par quadriller la page. */
.section-tete { margin: var(--sp-6) 0 var(--sp-4); }
.section-tete h3 { font-family: var(--display); font-weight: 600; font-size: 20px;
  letter-spacing: -.02em; }
.section-tete p { color: var(--muted); font-size: 13px; margin-top: 5px; max-width: 62ch; }

/* Utilitaires d'espacement/alignement — remplacent les style="" dispersés
   dans le HTML généré, pour un rythme visuel cohérent d'un onglet à l'autre. */
.mt-3 { margin-top: var(--sp-3); }
.mt-2 { margin-top: var(--sp-2); }
.mt-4 { margin-top: var(--sp-4); }
.mt-6 { margin-top: var(--sp-6); }
.mb-3 { margin-bottom: var(--sp-3); }
.ml-auto { margin-left: auto; }
.text-right { text-align: right; }
.w-auto { width: auto; }
.text-danger { color: var(--danger); }

.grille { display: grid; gap: var(--sp-4); }
/* `min(150px, 100%)` et non `150px` : un plancher fixe ne descend jamais
   sous sa valeur, si bien qu'une grille débordait dans tout conteneur plus
   étroit — volet latéral, écran partagé, très petit téléphone. Le `min()`
   lève ce plancher sans rien changer quand la place est là. */
.filtres { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: var(--sp-3); align-items: end; }
.filtres .field { margin: 0; }

/* ---- Barre de filtres (toolbar) -----------------------------------
   Ce n'est plus une carte mais un PLATEAU EN CREUX : légèrement plus sombre
   que le fond, sans ombre. La différence compte — une carte annonce du
   contenu, un creux annonce des commandes. */
.toolbar { background: var(--input); border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3); }
.toolbar-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.toolbar-row + .toolbar-row { padding-top: var(--sp-3); border-top: 1px solid var(--border-sub); }
.toolbar-label { font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .16em; color: var(--faint); flex: none; }

.segment { display: inline-flex; background: var(--card); border-radius: var(--r-sm);
  padding: 3px; gap: 2px; box-shadow: var(--arete); }
.segment-btn { appearance: none; border: 0; background: transparent; color: var(--muted);
  font-family: inherit; font-weight: 600; font-size: 12.5px; padding: 6px 13px;
  border-radius: 4px; min-height: 34px; cursor: pointer; white-space: nowrap;
  touch-action: manipulation;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out); }
.segment-btn:hover { color: var(--text); background: var(--survol); }
.segment-btn.actif { background: var(--accent); color: var(--accent-ink); font-weight: 700; }
.segment-btn.actif.type-def { background: var(--def); color: #1a0a03; }
.segment-btn.actif.type-atk { background: var(--atk); color: #04101c; }

.daterange { display: inline-flex; align-items: center; gap: 8px; background: var(--card);
  border-radius: var(--r-sm); padding: 4px 8px; box-shadow: var(--arete); }
.daterange input[type="date"] { border: 0; background: transparent; width: 138px; padding: 5px 2px; }
.daterange input[type="date"]:focus { box-shadow: none; }
.daterange-sep { color: var(--faint); font-size: 13px; }

.select-inline { display: inline-flex; align-items: center; gap: 9px; }
.select-inline > span { font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .14em; color: var(--faint); white-space: nowrap; }
.select-inline select { width: auto; min-width: 168px; background: var(--card);
  border-color: transparent; }

.search-box { position: relative; flex: 1 1 200px; min-width: 180px; }
.search-box svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--faint); pointer-events: none; }
.search-box input { padding-left: 34px; background: var(--card); border-color: transparent; }

/* ---- Tiroir de filtres ------------------------------------------------
   Catégorie, types et seuil se règlent une fois puis ne bougent plus. Les
   garder à l'écran en permanence coûtait trois rangs — la moitié d'un écran
   de téléphone avant le premier résultat. Le bouton porte le nombre de
   filtres actifs : un tiroir fermé ne doit jamais cacher un réglage oublié. */
.filtre-hote { position: relative; flex: none; }
.filtre-compte { font-family: var(--mono); font-size: 10px; font-weight: 500;
  background: var(--accent); color: var(--accent-ink); border-radius: 999px;
  min-width: 17px; padding: 1px 5px; text-align: center; }
#f-plus.actif { background: var(--accent-soft); color: var(--accent); }
.filtre-pop { position: absolute; right: 0; top: calc(100% + 8px); z-index: 25;
  width: min(400px, calc(100vw - 32px)); background: var(--card);
  border-radius: var(--r-md); box-shadow: var(--shadow-haut), var(--arete);
  padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4);
  animation: filtre-pop-entre var(--t-entree) var(--ease-out) both; }
@keyframes filtre-pop-entre {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}
.filtre-bloc { display: flex; flex-direction: column; gap: var(--sp-2);
  align-items: flex-start; }
.filtre-bloc .segment, .filtre-bloc .chips { width: 100%; }
.filtre-bloc .chips { flex-wrap: wrap; }
.filtre-bloc .input-compact { width: 140px; }
.filtre-bloc + .filtre-bloc { border-top: 1px solid var(--border-sub);
  padding-top: var(--sp-4); }

@media (max-width: 640px) {
  /* Le tiroir prend toute la largeur : à 402 px, un panneau ancré à droite
     dépassait du bord. */
  .filtre-hote { width: 100%; }
  .filtre-hote > .btn { width: 100%; }
  .filtre-pop { left: 0; right: auto; width: 100%; }
  /* La barre de part ne survit pas au passage en cartes : elle n'a plus de
     tableau où se comparer, et une barre isolée ne veut rien dire. */
  .col-part { display: none; }
}

.input-compact { width: 108px; }

/* ---- Rail de chiffres ---------------------------------------------
   Les compteurs d'en-tête ne sont plus une carte mais une rangée posée sur le
   fond, séparée par des filets. Ce sont des CHIFFRES, pas du contenu : les
   encadrer les mettait au même niveau que le tableau qu'ils résument. */
.resume-periode { display: flex; flex-wrap: wrap; gap: 0;
  margin-bottom: var(--sp-5); }
.resume-item { padding: 0 var(--sp-5) 0 0; min-width: 132px; }
.resume-item + .resume-item { padding-left: var(--sp-5); border-left: 1px solid var(--border-sub); }
.resume-item .valeur { font-family: var(--mono); font-size: 25px; font-weight: 600;
  color: var(--text); white-space: nowrap; line-height: 1.15;
  font-variant-numeric: tabular-nums; letter-spacing: -.03em; }
/* `--muted` et non `--faint` : ces libellés disent ce que compte le nombre
   au-dessus — sans eux, « 74 536 432 » ne veut rien dire. À 3:1 sur fond de
   carte ils étaient illisibles sur un téléphone en plein jour. */
.resume-item .libelle { font-family: var(--mono); font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .14em; margin-top: 6px; }
.resume-item.accent .valeur { color: var(--accent); }
.resume-item.alerte .valeur { color: var(--warn); }

/* ---- Tableaux -----------------------------------------------------
   Pas de zébrures, pas de bordure extérieure : des filets d'un pixel entre
   les lignes, et c'est tout. Le zébrage est une béquille — il sert quand les
   colonnes sont trop serrées, et il salit une table bien espacée. */
.scroll-x { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
thead th { text-align: left; font-family: var(--mono); font-size: 10px;
  text-transform: uppercase; letter-spacing: .14em; color: var(--faint);
  font-weight: 500; padding: 13px 16px; border-bottom: 1px solid var(--border);
  white-space: nowrap;
  /* Reste visible pendant le défilement d'un long tableau. */
  position: sticky; top: 0; background: var(--card); z-index: 1; }
tbody td { padding: 13px 16px; border-bottom: 1px solid var(--border-sub); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background var(--t-rapide) var(--ease-out); }
tbody tr:hover { background: var(--survol); }
/* En-tête de colonne triable. La flèche est toujours réservée (::after
   présent même inactif) pour que la largeur ne saute pas au tri. */
.th-tri { cursor: pointer; user-select: none; transition: color var(--t-rapide) var(--ease-out); }
.th-tri:hover { color: var(--text); }
.th-tri:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tri-fleche { display: inline-block; width: 12px; text-align: center; opacity: 0; }
.tri-fleche::after { content: "▾"; }
.th-tri:hover .tri-fleche { opacity: .45; }
.th-tri.tri-actif { color: var(--accent); }
.th-tri.tri-actif .tri-fleche { opacity: 1; }
.th-tri.tri-actif[aria-sort="ascending"] .tri-fleche::after { content: "▴"; }

.col-num { text-align: center; font-variant-numeric: tabular-nums; font-family: var(--mono); }

/* ---- Barre de part (classement) --------------------------------------
   La colonne la plus étroite du tableau et pourtant la plus lue : elle dit
   d'un regard qui décroche et qui talonne. Teinte de l'accent, très basse en
   opacité — c'est un fond de lecture, pas une donnée de plus à déchiffrer. */
.col-part { width: 116px; min-width: 88px; }
.part-piste { display: block; height: 5px; border-radius: 999px;
  background: var(--survol-fort); overflow: hidden; }
.part-part { display: block; height: 100%; border-radius: 999px;
  background: var(--accent); opacity: .55;
  transition: width var(--t-ample) var(--ease-out); }
tr.podium-1 .part-part { opacity: 1; }
tr.podium-2 .part-part, tr.podium-3 .part-part { opacity: .78; }
.rang { width: 54px; text-align: center; color: var(--faint); font-weight: 500;
  font-family: var(--mono); font-variant-numeric: tabular-nums; }
/* Pas de `color` ici : les trois premiers rangs affichent une médaille 🥇🥈🥉,
   et un émoji garde ses propres couleurs quoi qu'on déclare. */
.podium-1 .rang, .podium-2 .rang, .podium-3 .rang { font-size: 16px; }
/* Le podium ressort d'un filet coloré à gauche plutôt que d'un dégradé
   pleine largeur : trois bandes de couleur en haut d'un tableau, c'était le
   podium qui écrasait le classement au lieu de le coiffer. */
tr.podium-1 td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
tr.podium-2 td:first-child { box-shadow: inset 2px 0 0 #b9bfc9; }
tr.podium-3 td:first-child { box-shadow: inset 2px 0 0 #c08552; }
.pts { font-weight: 600; font-variant-numeric: tabular-nums; font-family: var(--mono); }
.type-def { color: var(--def); } .type-atk { color: var(--atk); }
.type-tempo { color: var(--violet); }
.vide { padding: var(--sp-6); text-align: center; color: var(--faint); font-size: 13.5px; }
/* Pied de liste paginée : centré, discret, avec le reste à afficher. */
.ligne-plus { justify-content: center; padding: var(--sp-4) 0 var(--sp-2);
  border-top: 1px solid var(--border-sub); }
.ligne-plus .faint { font-weight: 400; }

.tag { display: inline-block; font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .1em; padding: 3px 8px;
  border-radius: var(--r-sm); background: var(--card-2); color: var(--muted);
  border: 1px solid transparent; }
.tag-warn { background: var(--warn-bg); color: var(--warn); }

/* Encart d'aide entre onglets liés (ex. Salons ↔ Barème) : lilas, réservé
   au « statut technique » plutôt qu'à une action (gardée pour le laiton). */
.aide-croisee { display: flex; gap: var(--sp-3); align-items: flex-start;
  background: var(--violet-soft); border-left: 2px solid var(--violet);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4); font-size: 13px; color: var(--text);
  margin-bottom: var(--sp-4); }
.aide-croisee-icone { flex: none; width: 18px; height: 18px; color: var(--violet); margin-top: 1px; }
.aide-croisee b { color: var(--violet); }

/* Bandeau « consultation seule » : signale à un superviseur pourquoi les
   contrôles de modification sont absents, plutôt que de le laisser croire
   à un bug. Ton neutre (pas d'alerte : ce n'est pas une erreur). */
.bandeau-lecture { display: flex; gap: var(--sp-3); align-items: center;
  background: var(--card-2); border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4); font-size: 13px; color: var(--muted);
  margin-bottom: var(--sp-4); }
.bandeau-lecture svg { flex: none; width: 17px; height: 17px; }

/* Salons regroupés par catégorie : un doublon (deux rattachements actifs
   sous le même intitulé) devient visible d'un coup d'œil. */
.salon-groupe { padding: var(--sp-4) var(--sp-5); }
.salon-groupe + .salon-groupe { border-top: 1px solid var(--border-sub); }
.salon-groupe-tete { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-bottom: var(--sp-3); }
.salon-groupe-nom { font-weight: 600; font-size: 14px; }
.salon-ligne { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; padding: 9px 0; }
.salon-ligne + .salon-ligne { border-top: 1px solid var(--border-sub); }

/* Barème de paiement : une ligne par tranche (seuil_min -> seuil_max -> kamas) */
.tranche-ligne { display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap; padding: 9px 0; }
.tranche-ligne + .tranche-ligne { border-top: 1px solid var(--border-sub); }
.tranche-ligne .field { width: 130px; }
.tranche-sep { color: var(--faint); font-size: 13px; padding-bottom: 12px; }

/* Statut de paiement : pastille cliquable. La couleur ne porte jamais
   l'information seule — le libellé change aussi (« Payé » / « En attente »). */
.paye-toggle { display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: 12.5px; font-weight: 600; color: var(--muted);
  padding: 6px 12px; border-radius: var(--r-sm); border: 1px solid transparent;
  background: var(--card-2); min-height: 34px;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out); }
.paye-toggle:hover { background: var(--card-hover); color: var(--text); }
.paye-toggle input { min-height: 0; margin: 0; }
/* Dans la pastille verte, une coche laiton jurait : la case prend la couleur
   de l'état qu'elle annonce. */
.paye-toggle.paye input:checked { background: var(--success); border-color: var(--success); }
.paye-toggle.paye input:checked::after { border-color: #0b2413; }
.paye-toggle.paye { color: var(--success); background: var(--success-bg); }
.paye-toggle:has(input:disabled) { cursor: default; opacity: .75; }

/* Preuve de paiement : vignette cliquable (plein format en nouvel onglet) */
.preuve-vignette { display: flex; align-items: center; gap: var(--sp-2); }
.preuve-vignette img { width: 44px; height: 44px; object-fit: cover; border-radius: var(--r-sm);
  border: 1px solid var(--border); display: block; }

/* Zone de collage (Ctrl+V) d'une preuve : cliquer dedans puis coller. */
.preuve-zone { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  padding: 6px 8px; border: 1px dashed var(--border); border-radius: var(--r-sm);
  cursor: text; }
.preuve-zone:focus { outline: none; border-color: var(--accent); background: var(--accent-soft); }
.preuve-zone .faint { font-size: 11.5px; }

/* ---- Bloc repliable (composant unique) ------------------------------
   Trois variantes de repli coexistaient, chacune avec son balisage. Une
   seule reste, produite par repli() dans app.js.

   Pas d'animation de hauteur à l'ouverture : animer `height` force un
   recalcul de mise en page à chaque image et fait sauter tout ce qui suit.
   Seul le chevron pivote — il suffit à dire ce qui vient de se passer. */
.repli { border-radius: var(--r-lg); background: var(--card);
  box-shadow: var(--shadow), var(--arete); }
.repli + .repli, .repli + .card, .card + .repli { margin-top: var(--sp-4); }
.repli > summary {
  list-style: none; cursor: pointer; display: flex; align-items: center;
  justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); min-height: 52px;
  border-radius: var(--r-xl);
  transition: background var(--t-rapide) ease;
}
.repli > summary::-webkit-details-marker { display: none; }
.repli > summary:hover { background: var(--card-hover); }
.repli > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.repli-titre { display: flex; flex-direction: column; gap: 3px;
  font-weight: 700; font-size: 14.5px; letter-spacing: -.01em; }
.repli-sous { font-weight: 500; font-size: 12.5px; color: var(--faint);
  letter-spacing: 0; }
/* Sans largeur explicite, le SVG s'affiche à sa taille naturelle et occupe
   toute la carte. */
.repli .chev { display: inline-flex; width: 14px; height: 14px; flex: none;
  color: var(--muted); transition: transform var(--t-entree) var(--ease-out); }
.repli .chev svg { width: 100%; height: 100%; display: block; }
.repli[open] .chev { transform: rotate(180deg); }
.repli-corps { padding: 0 var(--sp-5) var(--sp-5); }
/* Une fois ouvert, un trait sépare l'intitulé du contenu : sans lui, le
   titre paraît flotter au-dessus d'un bloc qui ne lui appartient pas. */
.repli[open] > summary { border-bottom: 1px solid var(--border-sub);
  border-radius: var(--r-xl) var(--r-xl) 0 0; margin-bottom: var(--sp-4); }

/* Variante compacte : à l'intérieur d'une modale ou d'une carte existante,
   le composant ne redessine pas un second cadre. */
.repli:not(.repli-carte) { border-radius: var(--r-md); }
.repli:not(.repli-carte) > summary { padding: 12px 14px; min-height: 44px;
  border-radius: var(--r-md); font-size: 13.5px; }
.repli:not(.repli-carte) > summary .repli-titre { font-size: 13.5px; }
.repli:not(.repli-carte) .repli-corps { padding: 0 14px 14px; }
.repli:not(.repli-carte)[open] > summary {
  border-radius: var(--r-md) var(--r-md) 0 0; }

/* Tableau combats : une colonne par type */
.combats-table thead th { text-align: center; }
.combats-table thead th:first-child, .combats-table tbody td:first-child { text-align: left; position: sticky; left: 0; background: var(--card); }
.combats-table .ligne-principale td { font-weight: 700; }

/* Barème matriciel (alliés × ennemis) */
.matrice { border-collapse: separate; border-spacing: 0; }
.matrice thead th { text-align: center; white-space: nowrap; color: var(--muted); font-size: 11px; padding: 8px 10px; }
.matrice thead th:first-child { text-align: left; font-size: 10.5px; }
.matrice tbody th { text-align: left; white-space: nowrap; color: var(--text); font-weight: 700; padding-right: 14px; font-size: 13px; }
.matrice td { padding: 4px; text-align: center; }
.matrice input.mx { width: 64px; text-align: center; padding: 7px 4px; font-variant-numeric: tabular-nums; font-family: var(--mono); }

/* Périodes de paie : cartes-liste */
.periode-carte { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  background: var(--card-2); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 13px 16px; }
.periode-carte + .periode-carte { margin-top: 8px; }
.periode-carte .nom { font-weight: 700; font-size: 14px; }
.periode-carte .dates { font-size: 12.5px; color: var(--muted); font-family: var(--mono); margin-top: 2px; }

/* ---- Modale / toast ---------------------------------------------- */
/* `overflow-y: auto` sur le fond ET hauteur bornée sur la boîte : sans ça,
   une fiche longue dépassait de l'écran, centrée donc rognée en haut et en
   bas, sans aucun moyen de faire défiler. */
.modale { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
  padding: var(--sp-4); overflow-y: auto; overscroll-behavior: contain; }
.modale-fond { position: absolute; inset: 0; background: rgba(5,5,6,.65); backdrop-filter: blur(3px); }
/* `max-width` bornée à la fenêtre, et non seulement à 460 px : la boîte est
   un élément de grille dimensionné par son contenu, et un tableau large —
   « Derniers combats » sur une fiche joueur — poussait sa largeur minimale à
   400 px. Sur un écran de 402 px, la boîte débordait alors de 14 px vers la
   droite, hors de portée. Le `min-width: 0` sur ses deux enfants lève le
   plancher que leur impose leur contenu ; ils défilent déjà par eux-mêmes. */
.modale-tete, .modale-corps { min-width: 0; }
.modale-boite { position: relative; width: 100%;
  max-width: min(460px, calc(100vw - var(--sp-4) * 2)); background: var(--card);
  border-radius: var(--r-lg); box-shadow: var(--shadow-haut), var(--arete);
  animation: modale-entre .3s var(--ease-spring) both;
  /* En colonne : l'en-tête reste visible, seul le corps défile — le bouton
     de fermeture ne part jamais hors de portée. */
  display: flex; flex-direction: column;
  max-height: calc(100dvh - var(--sp-5) * 2); }
@keyframes modale-entre {
  from { opacity: 0; transform: translateY(12px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.modale-tete { display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-4) var(--sp-4) 0; flex: none; }
.modale-tete h2 { font-size: 16.5px; }
.modale-corps { padding: var(--sp-4); overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch; }
/* Reste dans le DOM en permanence (jamais [hidden]) : un aria-live doit déjà
   être présent dans l'arbre d'accessibilité pour que son contenu soit
   annoncé de façon fiable par un lecteur d'écran. */
.toast { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(8px);
  z-index: 60; background: var(--card); border: 1px solid var(--border); color: var(--text);
  padding: 12px 18px; border-radius: var(--r-md); box-shadow: var(--shadow); font-size: 13.5px; font-weight: 600;
  opacity: 0; visibility: hidden; pointer-events: none; max-width: calc(100vw - 32px);
  transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s; }
.toast.visible { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
  transition: opacity .2s ease, transform .2s ease; }
.toast.ok { border-color: var(--accent-bd); }
.toast.ko { border-color: var(--danger-bd); }

.ligne-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }
.entre { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }

/* ---- Sous 1000 px : la colonne se replie en tiroir ------------------
   Le seuil n'est pas 640 px : entre 640 et 1000, garder à la fois un rail de
   264 px et un tableau de classement laissait moins de 400 px au contenu. */
@media (max-width: 1000px) {
  #ecran-app { padding-left: 0; }
  .topbar-brand { display: flex; }
  .nav-burger { display: inline-flex; }
  .nav-scrim { display: block; position: fixed; inset: 0; z-index: 40;
    background: rgba(5,4,3,.72); backdrop-filter: blur(3px);
    animation: scrim-entre .28s var(--ease-out); }
  .nav-scrim[hidden] { display: none; }
  @keyframes scrim-entre { from { opacity: 0; } to { opacity: 1; } }

  .nav-panel { z-index: 45; width: 82vw; max-width: 320px;
    border-right: 1px solid var(--border);
    box-shadow: 24px 0 60px -20px rgba(0,0,0,.85);
    transform: translateX(-100%); transition: transform .34s var(--ease-out);
    will-change: transform; }
  .nav-panel.ouvert { transform: translateX(0); }
  .nav-fermer { display: inline-flex; }
  .nav-panel-tete { border-bottom: 1px solid var(--border-sub); padding-bottom: var(--sp-4); }
  .nav button { min-height: 46px; }
}

@media (max-width: 640px) {
  .vue { padding: var(--sp-4) 14px var(--sp-5); }
  .login-card { padding: var(--sp-5) var(--sp-4); }

  /* Pas de backdrop-filter en mobile : cette propriété crée un bloc
     conteneur pour les descendants position:fixed, ce qui enfermait le
     tiroir dans la hauteur de la barre au lieu du viewport. */
  .topbar { padding: 10px 14px; gap: var(--sp-2); min-height: 56px;
    backdrop-filter: none; background: var(--panel); }
  .user-nom { display: none; }
  .tb-name .credit { display: none; }
  /* Sous 320 px — volet latéral, écran partagé — la marque et le fil
     d'Ariane se disputent une place qui n'existe pas, et la barre finit par
     pousser la page de quelques pixels. Le nom du site cède : il est déjà
     dans le tiroir, et le fil d'Ariane dit mieux où l'on se trouve. */
  @media (max-width: 340px) {
    .tb-name { display: none; }
    .topbar { padding-left: 10px; padding-right: 10px; gap: 6px; }
  }
  .topbar-brand .logo { width: 24px; height: 24px; }
  /* Les nombres du résumé s'empilent en grille : les séparateurs verticaux
     du mode desktop n'ont plus de sens. */
  .resume-item + .resume-item { padding-left: 0; border-left: 0; }

  /* Empêche le zoom automatique iOS au focus d'un champ (nécessite >=16px). */
  input, select { font-size: 16px; }
  input[type="checkbox"] { font-size: inherit; }

  /* Lisibilité générale : sur un écran tenu à bout de bras, les tailles
     « desktop » sont trop petites. On remonte le corps de texte et les
     tableaux, et on agrandit les valeurs chiffrées qui sont l'essentiel
     de l'information. */
  body { font-size: 16px; }
  table { font-size: 14.5px; }
  .vue-tete h2 { font-size: 25px; }
  .vue-tete p { font-size: 14px; }
  .card-titre, .card-titre-pad { font-size: 15.5px; }
  .card-sous { font-size: 13px; }
  .section-tete h3 { font-size: 19px; }
  .resume-item .valeur { font-size: 27px; }
  .resume-item .libelle { font-size: 12px; }
  .faint, .muted { font-size: 13px; }
  .btn { font-size: 14.5px; }
  .btn-sm { font-size: 13.5px; min-height: 40px; }
  .tag { font-size: 12px; }

  /* La barre de filtres du classement s'empile : chaque contrôle en pleine
     largeur plutôt que côte à côte, illisible sur un petit écran. */
  .toolbar-row { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  /* Le segment défile horizontalement plutôt que d'écraser ses boutons :
     avec 5 axes aux libellés longs, les derniers devenaient inatteignables.
     `flex: 1 0 auto` = ils s'étalent s'il y a la place, sans jamais passer
     sous la largeur de leur texte. */
  .segment { width: 100%; overflow-x: auto; scrollbar-width: none; }
  .segment::-webkit-scrollbar { display: none; }
  .segment-btn { flex: 1 0 auto; text-align: center; }

  /* Les pastilles de types reçoivent le même traitement, mais leurs règles
     sont en fin de feuille : leur déclaration de base y est postérieure. */
  .daterange { width: 100%; }
  /* `min-width: 0` sur le bloc AUSSI, pas seulement sur le champ : un
     `input[type=date]` porte une largeur intrinsèque que son parent flex
     respecte, et le bloc poussait la page de quelques pixels sur un écran
     très étroit. */
  .daterange input[type="date"] { width: auto; flex: 1; min-width: 0; }
  .daterange-bloc { min-width: 0; }
  .select-inline { width: 100%; }
  .select-inline select { width: 100%; min-width: 0; }
  /* `flex: none` est indispensable : la barre passe en colonne, et un
     flex-basis horizontal (200px) s'appliquerait alors à la HAUTEUR —
     la boîte de recherche devenait un bloc vide de 200px avec la loupe
     flottant en son milieu. */
  .search-box { flex: none !important; width: 100%; min-width: 0; }
  .input-compact { width: 100%; }
  .toolbar-row > * { flex: none; }

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

  /* `1fr` ne descend pas sous la largeur minimale de son contenu : un
     montant en chiffres monospacés, insécable, imposait une colonne de
     188 px et faisait déborder la page. `min-width: 0` lève ce plancher,
     et la taille du nombre suit celle de l'écran. */
  .resume-periode { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
  .resume-item { min-width: 0; }
  /* 5vw et non 5,4 : la colonne fait la moitié de l'écran moins la gouttière,
     elle ne grandit donc pas aussi vite que la fenêtre. Sur un écran de 402 px
     (iPhone 17 Pro), 5,4vw rognait le « k » d'un montant à neuf chiffres —
     « 181 760 676 k » demande 13 caractères monospacés. */
  .resume-item .valeur { font-size: clamp(15px, 5vw, 27px); }

  .periode-carte { flex-direction: column; align-items: stretch; gap: var(--sp-3); }
  .periode-carte .ligne-actions { justify-content: flex-end; }

  /* Ligne de salon : empilée plutôt que compressée sur une seule ligne. */
  .salon-ligne { flex-direction: column; align-items: stretch; }
  .salon-ligne input[type="text"] { max-width: none; }
  .salon-ligne .ligne-actions { margin-left: 0 !important; justify-content: flex-end; }

  .aide-croisee { font-size: 12.5px; }

  /* Tranche de paiement : empilée, chaque champ en pleine largeur. */
  .tranche-ligne { flex-direction: column; align-items: stretch; }
  .tranche-ligne .field { width: 100%; }
  .tranche-sep { display: none; }

  /* Les tableaux à colonnes fixes (matrice, combats) défilent horizontalement
     dans leur propre conteneur : on réduit juste le remplissage des cellules
     pour limiter le défilement nécessaire. */
  thead th, tbody td { padding: 10px 12px; }

  /* Le classement et les paiements deviennent une pile de cartes plutôt
     qu'un tableau à faire défiler horizontalement.
     Grille explicite (et non flex-wrap) : le placement est déterministe,
     quelles que soient les colonnes du tableau — Classement et Paiements
     n'ont pas les mêmes, mais partagent cette mise en carte.
       ligne 1 : rang · pseudo · valeur principale (à droite)
       ligne 2+ : le reste, en petits couples « libellé + valeur ». */
  .table-classement thead { position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
  .table-classement, .table-classement tbody { display: block; width: 100%; }
  .table-classement tr { display: grid; grid-template-columns: auto 1fr auto;
    align-items: center; gap: 8px 12px; padding: 15px 14px;
    border-bottom: 1px solid var(--border-sub); }
  .table-classement tr:last-child { border-bottom: 0; }
  .table-classement td { display: flex; align-items: baseline; gap: 6px;
    padding: 0; border: 0; min-width: 0; }
  /* Libellé de colonne rappelé devant chaque valeur secondaire. */
  .table-classement td::before { content: attr(data-label); font-size: 11px;
    font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--faint); white-space: nowrap; }

  /* Par défaut une cellule est une donnée secondaire : ligne 2, colonnes
     auto. Sans ce `grid-row`, la 1re cellule secondaire viendrait combler
     la case libre de la ligne 1 (celle du rang) et décalait tout. */
  .table-classement td { grid-row: 2; }
  /* Le libellé passe au-dessus de sa valeur quand la colonne est trop étroite
     pour les tenir côte à côte : sur un écran de 402 px, « Récoltes 4 » avait
     besoin de 71 px dans une colonne de 59. Sans ce repli, le texte débordait
     sans que rien ne le signale. */
  .table-classement td { flex-wrap: wrap; }
  .table-classement td.rang { grid-column: 1; grid-row: 1; font-size: 19px; }
  .table-classement td.rang::before { content: ""; }
  .table-classement td.td-nom { grid-column: 2; grid-row: 1;
    font-weight: 700; font-size: 17px; letter-spacing: -.01em;
    overflow: hidden; text-overflow: ellipsis; display: block;
    white-space: nowrap; }
  .table-classement td.td-nom::before { content: ""; }
  /* Tableaux sans colonne de rang (paiements) : le pseudo récupère la
     première colonne, sinon il resterait décalé derrière une case vide. */
  .table-classement.sans-rang td.td-nom { grid-column: 1 / 3; }
  /* Valeur principale (points, ou total en kamas) : ancrée à droite de la
     première ligne, en or — c'est l'information qu'on vient chercher. */
  .table-classement td.pts { grid-column: 3; grid-row: 1; justify-self: end;
    font-size: 17px; color: var(--accent); text-align: right; }
  .table-classement td.pts::before { font-size: 10px; align-self: center; }

  /* Payé / Preuve : pleine largeur sur leur propre ligne, ce sont des
     contrôles tactiles et non des chiffres à comparer. */
  .table-classement td[data-label="Payé"],
  .table-classement td[data-label="Preuve"] { grid-column: 1 / -1; grid-row: auto;
    align-items: center; flex-wrap: wrap; }

  /* Sur écran tactile, le collage Ctrl+V n'existe pas : on n'affiche que
     le bouton de sélection de fichier (l'appareil photo/galerie). */
  .preuve-zone { border: 0; padding: 0; }
  .preuve-zone .faint { display: none; }

  /* ---- Percepteurs et frais : six colonnes au lieu de quatre ----------
     La mise en carte générique range toutes les cellules secondaires sur
     `grid-row: 2`, qui n'a que trois emplacements. Au-delà de trois, les
     cellules se retrouvaient à se chevaucher. Ces deux tableaux reçoivent
     donc un placement explicite sur trois rangées. */
  /* `align-items: center` (hérité) décalait le montant vers le bas dès
     qu'une nature tenait sur deux lignes : on aligne en haut. */
  .table-percepteurs tr, .table-frais tr { row-gap: 10px; align-items: start; }

  .table-percepteurs td { grid-row: 2; }
  .table-percepteurs td.td-nom { grid-column: 1 / 3; grid-row: 1; }
  .table-percepteurs td.pts   { grid-column: 3;     grid-row: 1; }
  /* La colonne du milieu est un `1fr` pris en étau entre deux colonnes
     dimensionnées par leur contenu : avec un pseudo long à gauche et un net à
     droite, il ne lui restait que 20 px, et « Coffres −190 666 » débordait en
     silence. « Valeur estimée » couvre donc les deux premières colonnes, et
     les coffres prennent une ligne à eux. */
  .table-percepteurs td[data-label="Valeur estimée"] { grid-column: 1 / 3; grid-row: 2; }
  .table-percepteurs td[data-label="État"]           { grid-column: 3; grid-row: 2;
    justify-self: end; flex-wrap: wrap; justify-content: flex-end; }
  .table-percepteurs td[data-label="État"]::before   { content: ""; }
  .table-percepteurs td[data-label="Coffres"]        { grid-column: 1 / -1; grid-row: 3; }
  .table-percepteurs td[data-label="Zone"]           { grid-column: 1 / -1; grid-row: 4; }
  .table-percepteurs td[data-label="Date"]           { grid-column: 1 / -1; grid-row: 5; }

  .table-frais td { grid-row: 2; }
  .table-frais td.td-nom { grid-column: 1 / 3; grid-row: 1; white-space: normal; }
  .table-frais td.pts    { grid-column: 3;     grid-row: 1; }
  .table-frais td[data-label="Date"]        { grid-column: 1;     grid-row: 2; }
  .table-frais td[data-label="Justificatif"]{ grid-column: 2;     grid-row: 2; }
  .table-frais td[data-label="Par"]         { grid-column: 3;     grid-row: 2;
    justify-self: end; }
  .table-frais td.td-actions { grid-column: 1 / -1; grid-row: 3;
    display: flex; gap: 8px; }
  .table-frais td.td-actions .btn { flex: 1; }
  /* La note d'un frais est un bloc, pas une valeur alignée sur la ligne. */
  .table-frais td.td-nom { display: block; }
  .table-frais .module-aide { display: block; max-width: none; }

  /* Le pied « Total » n'a pas de sens en carte : le bandeau du haut porte
     déjà le cumul des frais. */
  .table-frais tfoot { display: none; }

  /* Paiements : le total porte parfois la marque « +prime », ce qui élargit
     la troisième colonne — et le « Kamas/pt » de la deuxième rangée se
     retrouvait écrasé à 32 px, son libellé débordant en silence. On lui donne
     tout ce qui reste de sa rangée. */
  .table-paiements td[data-label="Points"]   { grid-column: 1; grid-row: 2; }
  .table-paiements td[data-label="Kamas/pt"] { grid-column: 2 / -1; grid-row: 2; }

  /* Récoltes : sept colonnes. Même traitement que ci-dessus — placement
     explicite, sinon les quatre cellules secondaires se disputent les trois
     emplacements de la deuxième rangée. */
  .table-recoltes tr { row-gap: 10px; }
  .table-recoltes td[data-label="Part"]     { grid-column: 1; grid-row: 2; }
  .table-recoltes td[data-label="Récoltes"] { grid-column: 2; grid-row: 2; }
  .table-recoltes td[data-label="Moyenne"]  { grid-column: 3; grid-row: 2;
    justify-self: end; }
  .table-recoltes td[data-label="Dernière"] { grid-column: 1 / -1; grid-row: 3; }
  /* La jauge de part n'a plus de largeur utile dans un tiers de carte : le
     pourcentage seul reste lisible, le trait deviendrait décoratif. */
  .table-recoltes .part-piste { display: none; }
  .table-recoltes tfoot { display: none; }

  /* Bandeau de calcul : cinq tuiles ne tiennent pas côte à côte. */
  #pc-resume { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  #pc-resume .resume-item { padding-left: 0; border-left: 0; }

  /* Fiche d'une collecte : le champ de saisie ne doit pas pousser la ligne
     hors de la modale. */
  .calcul-ligne { gap: var(--sp-3); }
  .calcul-saisie input { width: 8ch; }
}

@media (max-width: 420px) {
  /* Très petit écran : le badge de rôle encombre plus qu'il n'informe
     (le menu ⋯ reste accessible pour tout ce qui concerne le compte). */
  .topbar .badge { display: none; }
}

/* Une seule colonne seulement sur les très petits écrans : à 375px (taille
   d'iPhone la plus répandue) deux colonnes restent lisibles et évitent
   d'allonger la page de quatre écrans. */
@media (max-width: 330px) {
  .resume-periode { grid-template-columns: 1fr; }
}

/* Compte rattaché à un Discord, dans la liste des comptes. */
.tag-discord { background: rgba(88,101,242,.16); color: var(--discord-txt);
  border-color: rgba(88,101,242,.4); }

/* Rangs d'alliance, sur les cartes de l'onglet Équipes. Trois teintes de la
   même famille plutôt que trois couleurs franches : c'est une hiérarchie, pas
   trois catégories sans rapport. */
/* Trois degrés d'appui, du plus haut gradé au plus bas — par POSITION, plus
   par nom : chaque alliance nomme ses rangs comme elle veut, mais elle a
   toujours un sommet, un milieu et une base. */
.tag-rang { border-color: var(--accent-bd); }
.tag-rang[data-niveau="0"] { background: var(--accent); color: var(--accent-ink);
  border-color: var(--accent); font-weight: 600; }
.tag-rang[data-niveau="1"] { background: var(--accent-soft); color: var(--accent);
  font-weight: 600; }
.tag-rang[data-niveau="2"] { background: transparent; color: var(--accent); }

/* Équipe d'un membre qui a quitté le serveur : conservée, mais visiblement
   mise de côté. Le contenu reste lisible — on doit pouvoir relire ses
   personnages avant de décider de les supprimer. */
.carte-equipe.est-parti { opacity: .72; border-style: dashed; }
.carte-equipe.est-parti:hover, .carte-equipe.est-parti:focus-within { opacity: 1; }
.eq-effectif-note { margin: 0 0 12px; font-size: 13px; }

/* Rôle posé à la main, que la synchronisation Discord ne touche pas. La
   mention est ambrée comme les autres marques d'exception du site, et non
   rouge : ce n'est pas une anomalie, c'est une décision. */
.role-fige { display: flex; align-items: center; gap: 8px; margin-top: 5px;
  flex-wrap: wrap; }
.tag-manuel { background: var(--accent-soft); color: var(--accent);
  border-color: var(--accent-bd); cursor: help; }

/* ---- Graphiques (SVG maison, aucune dépendance externe) ------------ */
.graph { width: 100%; }
.graph-svg { width: 100%; height: auto; display: block; overflow: visible; }
.graph-tick { fill: var(--faint); font-size: 10px; font-family: var(--mono);
  letter-spacing: .04em; }
/* Repères horizontaux de la courbe : assez visibles pour servir de règle,
   assez discrets pour ne pas rivaliser avec la courbe elle-même. */
.graph-grille { stroke: var(--border-sub); stroke-width: 1;
  vector-effect: non-scaling-stroke; }
/* Le dernier point, c'est « maintenant » : il porte un anneau permanent
   là où les autres n'ont qu'un halo au survol. */
.pt-dernier .pt-dot { r: 4.5; }
.pt-dernier .pt-halo { opacity: .28; }

.graph-barres { display: flex; align-items: flex-end; gap: 4px;
  height: var(--h, 180px); padding-top: 18px; }
.gb { display: flex; flex-direction: column; justify-content: flex-end;
  height: 100%; min-width: 0; }
/* Une seule teinte pour toute la série : ces barres mesurent la même chose.
   Le maximum passe en pleine teinte — c'est le repère qu'on cherche d'abord,
   et il se lit sans légende. */
.gb-barre { border-radius: 3px 3px 0 0; min-height: 2px;
  background: var(--accent); opacity: .45;
  transition: opacity var(--t-rapide) var(--ease-out); }
.gb-max .gb-barre { opacity: 1; }
.gb:hover .gb-barre, .gb:focus-visible .gb-barre { opacity: .85; }
.gb-max:hover .gb-barre { opacity: 1; }
.gb-valeur { font-size: 10.5px; color: var(--faint); text-align: center;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  white-space: nowrap; margin-bottom: 4px; }
.gb-max .gb-valeur { color: var(--text); font-weight: 600; }
/* Le sol du graphique : sans lui les barres flottent, et l'œil ne sait pas
   d'où elles partent. */
.graph-barres { border-bottom: 1px solid var(--border); }
.gb-libelle { font-size: 10.5px; color: var(--faint); text-align: center;
  margin-top: 5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Répartition : barres horizontales, plus lisibles qu'un camembert dès
   qu'il y a plus de 4 ou 5 catégories. */
.graph-repart { display: flex; flex-direction: column; gap: 9px; }
/* Intitulé et montant sur une ligne, jauge pleine largeur en dessous.
   La disposition en trois colonnes réservait la part du lion à la jauge —
   qui ne dit rien de plus que le pourcentage écrit juste à côté — et laissait
   60 px au nom de la catégorie, systématiquement tronqué dès que ces cartes
   étaient côte à côte. Empilé, le nom a toute la largeur, et les jauges
   partent toutes du même bord : elles se comparent mieux. */
.gr-ligne { display: grid; grid-template-columns: 1fr auto;
  align-items: baseline; gap: 5px var(--sp-3); font-size: 13px; }
.gr-nom { min-width: 0; overflow-wrap: anywhere; }
.gr-piste { grid-column: 1 / -1; }
.gr-piste { background: var(--input); border-radius: 999px; height: 10px;
  overflow: hidden; }
.gr-part { height: 100%; border-radius: 999px; transition: width .4s var(--ease-out); }
.gr-val { font-family: var(--mono); font-variant-numeric: tabular-nums;
  white-space: nowrap; font-size: 12.5px; }
.gr-val .faint { margin-left: 6px; }

/* ---- Accueil ---------------------------------------------------------
   Trois cartes de même poids : où j'en suis, ce qui attend une décision, qui
   mène. Elles se rangent en colonnes tant qu'il y a la place, et s'empilent
   sans rien perdre quand il n'y en a plus. */
.accueil-grille { display: grid; gap: var(--sp-4); align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.accueil-carte { height: 100%; }
.ac-moi-tete { display: flex; align-items: center; gap: var(--sp-3);
  margin-bottom: var(--sp-4); }
.ac-moi-nom { font-family: var(--display); font-weight: 600; font-size: 19px;
  letter-spacing: -.02em; }
.ac-paire { display: flex; gap: var(--sp-5); flex-wrap: wrap; }
.ac-valeur { font-family: var(--mono); font-size: 21px; font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.2; }
.ac-valeur.accent { color: var(--accent); }
.ac-paire .libelle { font-family: var(--mono); font-size: 9.5px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .14em; margin-top: 5px; }

/* Le cumul de toutes les quinzaines, sous les chiffres de la période en
   cours. Séparé par un filet : ce n'est pas la même échelle de temps, et
   confondre « cette quinzaine » et « depuis le début » sur un montant en
   kamas est le genre de méprise qui se paie. */
.ac-cumul { margin-top: var(--sp-4); padding-top: var(--sp-3);
  border-top: 1px solid var(--border-sub); display: flex;
  flex-direction: column; gap: 6px; }
.ac-cumul-ligne { display: flex; justify-content: space-between;
  align-items: baseline; gap: var(--sp-3); font-size: 13px; color: var(--muted); }
.ac-cumul-ligne .mono { font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--text); font-weight: 600; }
.ac-cumul-ligne.reste .mono { color: var(--warn); }

.ac-liste { display: flex; flex-direction: column; }
/* Chaque ligne est un bouton : elle mène à l'écran qui traite le sujet. Une
   liste de chiffres qu'on ne peut pas suivre ne sert qu'à inquiéter. */
.ac-ligne { appearance: none; border: 0; background: transparent; width: 100%;
  display: flex; align-items: center; gap: var(--sp-3); cursor: pointer;
  padding: 11px 8px; border-radius: var(--r-sm); text-align: left;
  font-family: inherit; color: var(--muted); min-height: 46px;
  transition: background var(--t-rapide) var(--ease-out), color var(--t-rapide) var(--ease-out); }
.ac-ligne + .ac-ligne { border-top: 1px solid var(--border-sub); border-radius: 0; }
.ac-ligne:hover { background: var(--survol); color: var(--text); }
.ac-ligne-nombre { font-family: var(--mono); font-size: 19px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--faint); min-width: 34px; }
.ac-ligne.alerte .ac-ligne-nombre { color: var(--warn); }
.ac-ligne-texte { flex: 1; font-size: 13.5px; }
.ac-ligne-fleche { color: var(--faint); font-size: 15px; opacity: 0;
  transition: opacity var(--t-rapide) var(--ease-out); }
.ac-ligne:hover .ac-ligne-fleche { opacity: 1; }

.ac-podium { display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px 8px; }
.ac-podium + .ac-podium { border-top: 1px solid var(--border-sub); }
.ac-medaille { font-size: 15px; min-width: 22px; }
.ac-podium-nom { flex: 1; font-weight: 600; font-size: 13.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ac-podium-pts { font-family: var(--mono); font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--accent); }

/* ---- Fiche joueur : deux colonnes ------------------------------------
   Gain, répartition et récolte disent la même chose sous trois angles : ils
   se lisent ensemble. Empilés, ils imposaient deux mille pixels de
   défilement pour une page qu'on consulte en dix secondes. Les derniers
   combats restent pleine largeur — c'est un tableau, il lui faut la place. */
.fiche-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr));
  gap: var(--sp-4); align-items: start; }

/* ---- Fiche joueur : gain et progression vers le palier ------------- */
.gain-ligne { display: flex; align-items: baseline; gap: var(--sp-4); flex-wrap: wrap; }
.gain-montant { font-family: var(--mono); font-size: 30px; font-weight: 700;
  color: var(--accent); letter-spacing: -.02em; }
.palier { margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--border-sub); }
.palier-texte { font-size: 13px; color: var(--muted); margin-bottom: var(--sp-3); }
.palier-texte b { color: var(--text); }
.palier-piste { background: var(--input); border-radius: 999px; height: 12px;
  overflow: hidden; border: 1px solid var(--border); }
.palier-part { height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-h), var(--accent));
  transition: width .5s var(--ease-out); }
.palier-bornes { display: flex; justify-content: space-between; margin-top: 5px;
  font-size: 11.5px; color: var(--faint); font-family: var(--mono); }

/* Pseudo cliquable ouvrant la fiche (superviseur+). */
.lien-joueur { background: none; border: 0; padding: 0; cursor: pointer;
  color: inherit; font: inherit; text-align: left;
  border-bottom: 1px dotted var(--border-strong); }
.lien-joueur:hover { color: var(--accent); border-bottom-color: var(--accent); }

@media (max-width: 640px) {
  .gain-montant { font-size: 26px; }
  .gb-valeur { display: none; }
}

/* ---- Barre de filtres repliable (téléphone) ------------------------
   Le bouton n'existe que sur petit écran : il est posé par le script, qui ne
   l'insère pas au-delà de 640 px. */
.filtres-bascule { display: flex; align-items: center; gap: 10px; width: 100%;
  font: inherit; font-size: 14px; font-weight: 700; cursor: pointer;
  padding: 12px 14px; margin-bottom: var(--sp-3);
  background: var(--card); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  transition: border-color var(--t-rapide) ease; }
.filtres-bascule:hover { border-color: var(--border-strong); }
.filtres-bascule:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.filtres-bascule .nav-icone { color: var(--muted); }
/* Combien de filtres sont réellement saisis : replier ne doit pas faire
   oublier qu'on regarde une liste restreinte. */
.filtres-compte { min-width: 20px; height: 20px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; font-size: 11.5px; font-family: var(--mono);
  background: var(--accent); color: #1a1206; }
.filtres-compte[hidden] { display: none; }
.filtres-bascule .chev { margin-left: auto; color: var(--muted);
  display: inline-flex; transition: transform var(--t-rapide) ease; }
.filtres-bascule.replie .chev { transform: rotate(-90deg); }

/* Absence déclarée : la ligne s'efface sans disparaître. On la garde
   visible — savoir qui est en vacances fait partie du point de situation —
   mais elle ne doit pas attirer l'œil comme un joueur à relancer. */
.tag-absent { background: var(--card-2); color: var(--muted);
  border-color: var(--border-strong); }
tr.ligne-absent { opacity: .62; }
tr.ligne-absent:hover { opacity: 1; }

/* ---- Pastilles à cocher (types de combat du classement) ------------
   Un groupe de segments impose un choix unique ; ici on en veut plusieurs à
   la fois. D'où des pastilles indépendantes, éteintes ou allumées, qui se
   replient d'elles-mêmes sur plusieurs lignes quand l'écran rétrécit. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.chip { font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  padding: 7px 12px; border-radius: 999px; white-space: nowrap;
  background: var(--card-2); color: var(--muted);
  border: 1px solid var(--border);
  transition: background var(--t-rapide) ease, color var(--t-rapide) ease,
              border-color var(--t-rapide) ease; }
.chip:hover { border-color: var(--border-strong); color: var(--text); }
.chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Allumée : la teinte de la famille de combat, pour que la sélection se lise
   sans relire les libellés. */
.chip.actif { color: var(--text); border-color: currentColor; }
.chip-def.actif  { background: color-mix(in srgb, var(--def) 22%, transparent);
  border-color: var(--def); }
.chip-atk.actif  { background: color-mix(in srgb, var(--atk) 22%, transparent);
  border-color: var(--atk); }
.chip-tempo.actif { background: color-mix(in srgb, var(--violet) 22%, transparent);
  border-color: var(--violet); }
.chip-recolte.actif { background: color-mix(in srgb, var(--accent) 22%, transparent);
  border-color: var(--accent); }
/* Une ligne de barre d'outils entièrement occupée par les pastilles : elles
   prennent la place restante, les boutons d'action se tassent à droite. */
.toolbar-row-chips .chips { flex: 1 1 auto; }

/* ---- Prime de récolte : détail d'un versement ----------------------
   Le total ne dit plus d'où il vient depuis que la récolte donne droit à une
   prime en plus des points. Ce petit tableau le décompose sur la fiche ;
   dans la liste des paiements, l'infobulle dit la même chose au survol. */
.detail-gain { margin-top: var(--sp-3); border-top: 1px solid var(--border-sub);
  padding-top: var(--sp-3); font-size: 13.5px; }
.detail-gain-ligne { display: flex; justify-content: space-between;
  gap: var(--sp-4); padding: 4px 0; color: var(--muted); }
.detail-gain-ligne .mono { font-family: var(--mono); white-space: nowrap; }
.detail-gain-ligne.total { border-top: 1px solid var(--border-sub);
  margin-top: 4px; padding-top: 8px; color: var(--text); font-weight: 700; }
.detail-gain-ligne.total .mono { color: var(--accent); }

/* Marque discrète sur un total qui contient une prime : sans elle, personne
   ne penserait à survoler la cellule pour en connaître le détail. */
.pastille-prime { display: inline-block; margin-left: 6px; font-size: 10.5px;
  font-weight: 700; letter-spacing: .02em; padding: 1px 6px; border-radius: 999px;
  background: var(--card-2); color: var(--accent);
  border: 1px solid var(--accent-bd); vertical-align: middle;
  font-family: var(--sans, inherit); }
.td-total { white-space: nowrap; }

/* ---- Équipes : un membre, ses personnages --------------------------
   Une carte par joueur plutôt qu'un tableau : les équipes n'ont pas toutes
   le même nombre de lignes, et un tableau aurait imposé une colonne par
   personnage — ou une ligne par personnage, ce qui aurait éparpillé chaque
   membre sur plusieurs rangées. */
.equipes-grille { display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(min(290px, 100%), 1fr)); }
.carte-equipe { background: var(--card); border-radius: var(--r-md);
  padding: var(--sp-4); box-shadow: var(--shadow), var(--arete);
  display: flex; flex-direction: column; gap: var(--sp-3);
  transition: border-color .2s ease; }
.carte-equipe:hover { border-color: var(--border-strong); }
/* Sa propre équipe, remontée en tête : un filet doré la signale sans avoir
   à lire l'étiquette. */
.carte-equipe.est-moi { border-color: var(--accent-bd);
  box-shadow: inset 3px 0 0 var(--accent); }

.equipe-tete { display: flex; align-items: center; gap: 10px; }
.equipe-nom { font-weight: 700; font-size: 15px; letter-spacing: -.01em;
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.equipe-compte { margin-left: auto; font-family: var(--mono); font-size: 13px;
  flex: none; }
.tag-moi { background: var(--card-2); color: var(--accent);
  border-color: var(--accent-bd); }

.equipe-persos { display: flex; flex-direction: column; gap: 2px; }
.perso { display: grid; grid-template-columns: 14px 1fr auto auto;
  align-items: center; gap: 8px; padding: 6px 8px; border-radius: var(--r-sm);
  font-size: 13.5px; }
.perso[data-perso] { cursor: pointer; }
.perso[data-perso]:hover, .perso[data-perso]:focus-visible {
  background: var(--card-2); outline: none; }
.perso-etoile { color: var(--faint); text-align: center; }
.perso-principal .perso-etoile { color: var(--accent); }
.perso-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.perso-principal .perso-nom { font-weight: 700; }
.perso-classe { font-size: 11.5px; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; }
/* `--muted` et non `--faint` : un niveau (200 contre 120) et l'absence
   d'équipe sont des informations qu'on vient lire, pas de la décoration.
   `--faint` tombe à 3:1 sur fond de carte, sous le seuil de lisibilité ;
   il reste réservé au séparateur et à l'étoile éteinte. */
.perso-niveau { font-family: var(--mono); font-size: 12.5px; color: var(--muted);
  min-width: 3ch; text-align: right; }
.equipe-vide { color: var(--muted); font-size: 13px; margin: 0; padding: 4px 8px; }
.equipe-compte { color: var(--muted); }
.equipe-ajout { align-self: flex-start; }
/* Ajouter et vider une équipe, au pied de la carte. La suppression est un
   lien discret et non un bouton rouge : elle sert une fois de temps en
   temps, alors qu'on ajoute un personnage souvent. */
.equipe-actions { display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; }
.equipe-actions .equipe-ajout { align-self: auto; }
.equipe-actions .lien-mini { margin-left: auto; }

/* Deux champs sur une ligne dans un formulaire (classe + niveau) : ils se
   lisent ensemble, les séparer en deux lignes pleine largeur les éloignait. */
.champs-cote { display: grid; grid-template-columns: 2fr 1fr; gap: var(--sp-3); }
@media (max-width: 480px) { .champs-cote { grid-template-columns: 1fr; } }

/* ---- Récoltes : podium et part de chacun ---------------------------
   Trois cartes plutôt qu'un graphique en barres : le trio de tête est ce
   qu'on vient voir, il mérite d'être lu d'un coup d'œil et non déduit de
   la longueur d'un trait. Le reste de l'alliance suit dans le tableau. */
.podium-recolte { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-4);
  grid-template-columns: repeat(3, 1fr); }
.podium-carte { position: relative; text-align: center; padding: var(--sp-5) var(--sp-4);
  background: var(--card); border-radius: var(--r-md);
  box-shadow: var(--shadow), var(--arete);
  transition: transform .2s var(--ease-out), box-shadow .2s ease; }
.podium-carte:hover { transform: translateY(-2px); box-shadow: var(--shadow-haut), var(--arete); }
/* Même code que les lignes de podium des tableaux : un filet de métal sur la
   tranche. Les nappes de couleur d'avant faisaient trois cartes de trois
   couleurs différentes — le podium se lisait avant le classement. */
.podium-carte.podium-1 { box-shadow: inset 2px 0 0 var(--accent), var(--shadow), var(--arete); }
.podium-carte.podium-2 { box-shadow: inset 2px 0 0 #b9bfc9, var(--shadow), var(--arete); }
.podium-carte.podium-3 { box-shadow: inset 2px 0 0 #c08552, var(--shadow), var(--arete); }
.podium-place { font-size: 22px; line-height: 1; margin-bottom: 6px; }
.podium-carte .avatar { margin-bottom: 8px; }
.podium-nom { font-weight: 700; font-size: 15px; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.podium-montant { font-family: var(--mono); font-size: clamp(15px, 3.4vw, 21px);
  font-weight: 700; color: var(--accent); letter-spacing: -.02em;
  white-space: nowrap; margin: 4px 0 2px; }
.podium-detail { font-size: 11.5px; color: var(--muted); }

/* Part de chacun : une jauge de la largeur de la colonne, à côté du
   pourcentage. Comparer trente nombres est un effort ; comparer trente
   traits n'en est pas un. */
.td-part { display: flex; align-items: center; gap: 8px; justify-content: center; }
.part-piste { flex: 1; min-width: 34px; max-width: 90px; height: 6px;
  background: var(--input); border-radius: 999px; overflow: hidden; }
.part-part { display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--accent-h), var(--accent));
  transition: width .4s var(--ease-out); }
.part-val { font-family: var(--mono); font-size: 12.5px; color: var(--muted);
  white-space: nowrap; }

@media (max-width: 560px) {
  /* Trois cartes côte à côte deviennent trois colonnes de 90 px, où les
     montants se coupent au milieu d'un nombre ; empilées, elles repoussent
     le tableau de 500 px. Sur téléphone le podium disparaît donc : la
     liste devient une pile de cartes dont les trois premières portent déjà
     la médaille et le dégradé. Rien n'est perdu que la répétition. */
  .podium-recolte { display: none; }
}

/* ---- Avatars Discord ----------------------------------------------- */
.avatar { width: var(--a, 28px); height: var(--a, 28px); border-radius: 50%;
  flex: none; object-fit: cover; border: 1px solid var(--border);
  background: var(--card-2); display: inline-block; vertical-align: middle; }
/* Repli quand le membre n'a pas d'avatar connu ou que l'URL ne répond plus. */
/* Pastille de repli quand un joueur n'a pas d'avatar Discord. La teinte est
   tirée du pseudo (voir couleurUnPseudo dans app.js) : dans une liste de
   trente lignes, des initiales toutes grises se confondent, alors qu'une
   couleur stable devient un repère — on retrouve « son » joueur à l'œil.
   Saturation basse et luminosité haute : lisible sur fond sombre, et jamais
   assez vif pour concurrencer l'or des montants. */
.avatar-vide { display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; line-height: 1;
  font-size: calc(var(--a, 28px) * .42);
  color: hsl(var(--h, 40) var(--avatar-s) var(--avatar-l));
  background: hsl(var(--h, 40) var(--avatar-fond-s) var(--avatar-fond-l));
  box-shadow: inset 0 0 0 1px hsl(var(--h, 40) 30% 60% / .35); }
.cell-joueur { display: inline-flex; align-items: center; gap: 9px; min-width: 0; }
.cell-joueur .lien-joueur { overflow: hidden; text-overflow: ellipsis; }

.fiche-tete { display: flex; align-items: center; gap: var(--sp-4);
  margin-bottom: var(--sp-4); }
.fiche-nom { font-size: 20px; font-weight: 700; letter-spacing: -.01em; }

/* ---- Infobulles des graphiques ------------------------------------- */
/* Positionnée par transform (accéléré, sans reflow) et hors flux : elle
   suit le curseur sans jamais décaler la page. */
.graph-tip { position: fixed; top: 0; left: 0; z-index: 70; pointer-events: none;
  background: var(--panel); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); box-shadow: var(--shadow);
  padding: 7px 11px; font-size: 12.5px; line-height: 1.35;
  opacity: 0; transition: opacity .12s ease; white-space: nowrap;
  display: flex; flex-direction: column; gap: 1px; }
.graph-tip.visible { opacity: 1; }
/* Certaines bulles expliquent un montant en plusieurs phrases (le détail
   d'un versement) : le libellé honore alors les retours à la ligne et peut
   revenir à la ligne tout seul, dans une largeur bornée — sans quoi il
   sortirait de l'écran sur un téléphone. */
.graph-tip { max-width: min(340px, calc(100vw - 24px)); }
.tip-lib { color: var(--muted); font-size: 11px; white-space: pre-line; }
.tip-val { font-family: var(--mono); font-weight: 700; font-size: 14px;
  color: var(--accent); font-variant-numeric: tabular-nums; }

/* Points de la courbe : zone de survol large (16px de rayon) car une bille
   de 3,5px est intouchable au doigt, halo qui révèle le point actif. */
.pt { cursor: pointer; }
.pt-dot { fill: var(--accent); transition: r .12s ease; }
.pt-halo { fill: var(--accent); opacity: 0; transition: opacity .12s ease; }
.pt-zone { fill: transparent; }
.pt:hover .pt-halo, .pt:focus-visible .pt-halo { opacity: .28; }
.pt:hover .pt-dot, .pt:focus-visible .pt-dot { r: 5; }
.pt:focus { outline: none; }
.pt:focus-visible .pt-dot { stroke: var(--text); stroke-width: 1.5; }

/* Barres et lignes de répartition : retour visuel au survol. */
.gb { cursor: pointer; }
.gb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.gr-ligne { cursor: pointer; border-radius: var(--r-sm); padding: 2px 4px; margin: -2px -4px;
  transition: background .12s ease; }
.gr-ligne:hover { background: var(--survol); }
.gr-ligne:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }

/* ---- Camembert (anneau) -------------------------------------------- */
.camembert { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
.camembert-svg { width: var(--t, 190px); height: var(--t, 190px); flex: none; }
.part { cursor: pointer; transition: opacity .12s ease; transform-origin: 60px 60px; }
.camembert-svg:hover .part { opacity: .45; }
.camembert-svg .part:hover, .camembert-svg .part:focus-visible { opacity: 1; }
.part:focus { outline: none; }
.part:focus-visible path, .part:focus-visible circle { stroke: var(--text); stroke-width: 1.5; }
.cam-total { fill: var(--text); font-size: 15px; font-weight: 700; text-anchor: middle;
  font-family: var(--mono); }
.cam-legende-total { fill: var(--faint); font-size: 7px; text-anchor: middle;
  text-transform: uppercase; letter-spacing: .06em; }

.cam-legende { list-style: none; margin: 0; padding: 0; flex: 1 1 200px;
  display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cam-legende li { display: flex; align-items: center; gap: 9px; font-size: 13px;
  cursor: pointer; border-radius: var(--r-sm); padding: 3px 6px; margin: -3px -6px;
  transition: background .12s ease; }
.cam-legende li:hover { background: var(--survol); }
.cam-legende li:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
.cam-puce { width: 11px; height: 11px; border-radius: 3px; flex: none; }
.cam-nom { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cam-val { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 12.5px; color: var(--muted); }

@media (max-width: 640px) {
  .camembert { justify-content: center; gap: var(--sp-4); }
  .camembert-svg { width: 160px; height: 160px; }
  .cam-legende { flex: 1 1 100%; }
}

/* Ligne de répartition actionnable (ouvre le détail du joueur en dessous). */
.gr-cliquable .gr-nom { border-bottom: 1px dotted var(--border-strong); }
.gr-cliquable:hover .gr-nom { color: var(--accent); border-bottom-color: var(--accent); }
.gr-ligne.gr-actif { background: var(--accent-soft); }
.gr-ligne.gr-actif .gr-nom { color: var(--accent); }

/* Détail d'un joueur, déplié sous le graphique des meilleurs joueurs. */
.detail-joueur { margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--border-sub);
  animation: vue-entre .25s var(--ease-out) both; }
.detail-tete { display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; margin-bottom: var(--sp-4); }
.detail-nom { font-weight: 700; font-size: 16px; }

/* Sélecteur de rôle dans la liste des comptes : compact, pour ne pas
   déformer la ligne du tableau. */
.select-role { width: auto; min-width: 130px; min-height: 36px;
  padding: 5px 30px 5px 10px; font-size: 13px; }

/* ---- Bouton d'aide flottant ---------------------------------------
   Doré pour être repéré immédiatement, mais placé bas à droite pour ne
   jamais recouvrir le contenu utile. Le libellé se réduit à l'icône sur
   petit écran, où la place est comptée. */
.btn-aide {
  position: fixed; right: 18px; bottom: 18px; z-index: 55;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 18px 11px 13px; border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink); text-decoration: none;
  font-weight: 700; font-size: 13.5px; letter-spacing: -.005em;
  border: 0;
  box-shadow: 0 10px 28px -12px rgba(0,0,0,.8), 0 2px 6px rgba(0,0,0,.3);
  transition: transform .18s var(--ease-out), box-shadow .18s ease;
}
.btn-aide:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 34px -14px rgba(0,0,0,.85), 0 3px 8px rgba(0,0,0,.35);
}
.btn-aide:active { transform: translateY(0); }
.btn-aide:focus-visible { outline: 3px solid var(--text); outline-offset: 3px; }
.btn-aide-icone {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(28,20,4,.22); font-size: 14px; font-weight: 900; line-height: 1;
}

/* Le toast remonte pour ne pas passer sous le bouton d'aide. */
.toast { bottom: 78px; }

@media (max-width: 640px) {
  /* Pastille seule : sur mobile chaque pixel de largeur compte, et
     l'icône « ? » se comprend sans texte. */
  .btn-aide { right: 14px; bottom: 14px; padding: 0;
    width: 50px; height: 50px; justify-content: center; }
  .btn-aide-texte { display: none; }
  .btn-aide-icone { width: 26px; height: 26px; font-size: 17px; background: none; }
  .toast { bottom: 74px; }
}

/* ---- Calcul d'une collecte de percepteur ---------------------------
   Présentation en « addition » pour que la règle soit évidente :
   valeur estimée moins coffres, rien d'autre. */
.calcul { background: var(--card-2); border-radius: var(--r-md);
  padding: var(--sp-4); }
.calcul-ligne { display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-4); padding: 5px 0; font-size: 14px; }
.calcul-ligne .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.calcul-ligne.retire { color: var(--danger); }
.calcul-ligne.total { border-top: 1px solid var(--border); margin-top: 6px;
  padding-top: 10px; font-weight: 700; font-size: 16px; }
.calcul-ligne.total .mono { color: var(--accent); font-size: 19px; }

.tag-corrige { background: var(--violet-soft); color: var(--violet);
  border-color: var(--violet-bd); }
.collecte-ligne { cursor: pointer; }
.collecte-ligne:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Capture d'origine dans la fiche d'un percepteur : cliquable pour l'ouvrir
   en taille réelle, contenue dans la modale le reste du temps. */
.capture-cadre { display: block; border: 1px solid var(--border);
  border-radius: var(--r-md); overflow: hidden; background: #000;
  transition: border-color .15s ease, box-shadow .15s ease; }
.capture-cadre:hover { border-color: var(--accent); box-shadow: var(--ombre-2); }
.capture-cadre img { display: block; width: 100%; height: auto; }

/* ---- Interrupteurs de fonctionnalité ---------------------------------
   Une ligne par fonctionnalité : la case, ce qu'elle gouverne, et son état
   écrit en toutes lettres. L'étiquette n'est pas une redite de la case —
   elle survit à un coup d'œil rapide, là où une case cochée parmi huit se
   confond avec ses voisines. */
.fonc-liste { display: flex; flex-direction: column; }
.fonc-ligne { display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: 13px 4px; cursor: pointer; }
.fonc-ligne + .fonc-ligne { border-top: 1px solid var(--border-sub); }
.fonc-ligne:hover { background: var(--survol); border-radius: var(--r-sm); }
.fonc-ligne input { margin-top: 2px; }
.fonc-texte { flex: 1; display: flex; flex-direction: column; gap: 3px;
  min-width: 0; }
.fonc-texte b { font-size: 14px; font-weight: 600; }
.fonc-texte .faint { font-size: 12.5px; line-height: 1.5; }
.fonc-ligne .tag { flex: none; margin-top: 1px; }

/* ---- Grille des droits par rôle (Comptes, super-admin) --------------
   Une ligne par écran, une colonne par rôle : on lit une politique d'accès
   d'un seul coup d'œil plutôt qu'en ouvrant quatre formulaires. */
.table-droits { width: 100%; }
.table-droits th.col-droit { text-align: center; text-transform: capitalize;
  white-space: nowrap; }
.table-droits td.col-droit { text-align: center; vertical-align: middle; }
.table-droits .td-module { min-width: 220px; }
.module-nom { font-weight: 600; }
.module-aide { color: var(--txt-3); font-size: 12px; margin-top: 2px;
  max-width: 42ch; }
.segment-droit { display: inline-flex; }
.segment-droit .segment-btn { padding: 5px 10px; font-size: 12px; }
/* Le vert du niveau « modification » sert d'alerte visuelle : c'est le seul
   niveau qui laisse écrire, il doit se repérer sans lire les libellés. */
.segment-droit .segment-btn[data-niveau="2"].actif { background: var(--accent);
  color: var(--accent-ink); border-color: var(--accent); }
.segment-droit .segment-btn[data-niveau="0"].actif { background: var(--card-3);
  color: var(--txt-3); }

@media (max-width: 720px) {
  .table-droits .td-module { min-width: 150px; }
  .module-aide { display: none; }
  .segment-droit { flex-direction: column; }
  .segment-droit .segment-btn { padding: 4px 8px; }
}

/* Montant éditable à l'intérieur du calcul : le champ ressemble à la valeur
   qu'il remplace, pour que la ligne reste lisible comme une opération. */
.calcul-saisie { display: inline-flex; align-items: baseline; gap: 2px;
  font-family: var(--mono); }
.calcul-saisie input { width: 9ch; padding: 2px 6px; text-align: right;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: inherit; color: inherit; background: var(--card-3);
  border: 1px solid var(--border); border-radius: var(--r-sm); }
.calcul-saisie input:focus { border-color: var(--accent); outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft); }
/* Chrome ajoute des flèches qui écrasent un champ aussi étroit. */
.calcul-saisie input::-webkit-outer-spin-button,
.calcul-saisie input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.calcul-saisie input { -moz-appearance: textfield; }

/* Bandeau de récolte : les termes retranchés se distinguent du total, sinon
   la suite de nombres se lit comme une addition. */
.resume-item.negatif .valeur { color: var(--danger); }
.resume-item.alerte .valeur { color: var(--warn); }

/* Zone de dépôt d'un justificatif : cliquer, glisser ou coller. */
.depot-capture { border: 1px dashed var(--border); border-radius: var(--r-md);
  padding: var(--sp-4); text-align: center; cursor: pointer; background: var(--card-2);
  transition: border-color .15s ease, background .15s ease; }
.depot-capture:hover, .depot-capture:focus-visible { border-color: var(--accent);
  background: var(--card-3); outline: none; }
.depot-capture img { max-width: 100%; max-height: 220px; border-radius: var(--r-sm);
  display: block; margin: 0 auto; }

/* Doublon probable : un signalement, pas un verdict — la ligne reste comptée
   tant qu'un humain n'a pas tranché. */
.tag-doublon { background: var(--warn-bg); color: var(--warn);
  border-color: var(--warn-bd); margin-right: 4px; }
.bandeau-doublons { display: flex; align-items: center; gap: var(--sp-4);
  flex-wrap: wrap; color: var(--warn); }

/* Lien d'action à l'intérieur d'une cellule : c'est un bouton (il ouvre une
   fiche, il ne navigue pas), mais il doit se lire comme un lien. */
.lien-inline { appearance: none; border: 0; background: none; padding: 0;
  font: inherit; color: var(--accent); cursor: pointer; text-decoration: underline;
  text-underline-offset: 2px; }
.lien-inline:hover { color: var(--text); }

/* Zone d'un percepteur : information de contexte, jamais prioritaire sur les
   montants — elle se tronque plutôt que d'élargir le tableau. */
.td-zone { max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }

/* La zone se saisit en toutes lettres : le champ étroit des montants ne
   convient pas ici. */
.calcul-saisie input[type="text"] { width: 22ch; text-align: left;
  font-family: inherit; }

/* ---- Doublons de combat --------------------------------------------
   Les deux captures se lisent côte à côte : c'est la comparaison visuelle
   qui permet de trancher en une seconde, pas les métadonnées. */
.doublon-bandeau { display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; margin-bottom: var(--sp-3); }
.doublon-paire { display: grid; gap: var(--sp-4);
  /* 340 px : en dessous, les pseudos d'une capture de combat ne se lisent
     pas et il faut ouvrir la comparaison pour la moindre vérification. */
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); }
.doublon-face { background: var(--card-2); border-radius: var(--r-md);
  padding: var(--sp-3); }
/* Le message signalé se distingue de l'original, sinon on ne sait plus
   lequel des deux on s'apprête à retirer. */
.doublon-face.suspect { border-color: var(--warn-bd); background: var(--warn-bg); }
.doublon-tete { display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-3); margin-bottom: var(--sp-2); font-size: 12px; }
.doublon-role { font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  font-size: 10.5px; color: var(--faint); }
.doublon-face.suspect .doublon-role { color: var(--warn); }
.doublon-meta { margin-top: var(--sp-3); font-size: 13px; }
.doublon-meta .module-aide { margin-top: 4px; }

@media (max-width: 640px) {
  .doublon-paire { grid-template-columns: 1fr; }
  .doublon-bloc .ligne-actions { flex-direction: column; align-items: stretch; }
}

/* ---- Visionneuse d'images ------------------------------------------
   Plein écran : sur une capture de fin de combat, les noms font quelques
   pixels de haut et doivent pouvoir être agrandis sans quitter la page. */
.zoom-fond { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.92);
  display: flex; align-items: center; justify-content: center; overflow: hidden; }
.zoom-fond[hidden] { display: none; }
body.zoom-ouvert { overflow: hidden; }
.zoom-scene { position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; }
.zoom-scene img { max-width: 96vw; max-height: 92vh; transform-origin: center;
  transition: transform .12s ease-out; user-select: none; -webkit-user-drag: none; }
.zoom-barre { position: absolute; top: 12px; right: 12px; z-index: 1;
  display: flex; align-items: center; gap: 6px; padding: 6px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--ombre-2); }
.zoom-btn { appearance: none; border: 1px solid var(--border); background: var(--card-2);
  color: var(--text); border-radius: var(--r-sm); padding: 5px 10px; cursor: pointer;
  font: inherit; font-size: 13px; line-height: 1.2; text-decoration: none; }
.zoom-btn:hover { border-color: var(--accent); color: var(--accent); }
.zoom-fermer { font-size: 15px; }
.zoom-niveau { font-family: var(--mono); font-size: 12px; color: var(--muted);
  min-width: 5ch; text-align: center; }

@media (max-width: 640px) {
  /* Barre en bas sur mobile : en haut, elle tombe sous le pouce et masque
     justement la zone qu'on veut lire. */
  .zoom-barre { top: auto; bottom: 14px; right: 50%; transform: translateX(50%); }
  .zoom-scene img { max-width: 100vw; max-height: 100vh; }
}

/* Comparaison de deux captures : côte à côte, zoom et déplacement communs.
   Les regarder l'une après l'autre ne permet pas de comparer deux
   compositions d'équipe — il faut la même zone dans les deux, en même temps. */
.zoom-scene.zoom-duo { display: flex; gap: 2px; align-items: stretch;
  padding: 56px 8px 8px; }
.zoom-vue { flex: 1 1 0; min-width: 0; margin: 0; position: relative;
  display: flex; flex-direction: column; overflow: hidden; }
.zoom-vue figcaption { position: absolute; top: 0; left: 0; right: 0; z-index: 1;
  font-size: 11.5px; font-weight: 700; padding: 5px 8px; color: var(--text);
  background: rgba(0,0,0,.66); border-radius: var(--r-sm) var(--r-sm) 0 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.zoom-duo .zoom-vue img { max-width: 100%; max-height: 100%;
  object-fit: contain; margin: auto; }

@media (max-width: 700px) {
  /* Deux images côte à côte sur un téléphone donneraient deux timbres-poste :
     on empile, le zoom partagé garde l'intérêt de la comparaison. */
  .zoom-scene.zoom-duo { flex-direction: column; padding: 8px 8px 64px; }
}

/* Motifs de rejet : le décompte de ce que la détection a raté. */
.motifs-liste { display: grid; gap: 6px; }
.motif-ligne { display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-4); padding: 6px 10px; background: var(--card-2);
  border: 1px solid var(--border-sub); border-radius: var(--r-sm); font-size: 13.5px; }
.motif-ligne b { font-family: var(--mono); color: var(--warn); }
/* Une ligne de correction par message, dans la modale d'écartement. */
.correction-ligne + .correction-ligne { margin-top: var(--sp-3);
  padding-top: var(--sp-3); border-top: 1px solid var(--border-sub); }

/* ---- Trésorerie -----------------------------------------------------
   Le solde domine délibérément : c'est le seul chiffre sur lequel on peut
   s'engager. Les trois autres l'expliquent ou le nuancent, sans le
   concurrencer visuellement. */
.caisse { display: grid; grid-template-columns: minmax(220px, 300px) 1fr;
  gap: var(--sp-4); margin-bottom: var(--sp-4); }
/* Le solde de la caisse : la seule surface du site à porter un filet de
   couleur sur toute sa hauteur. C'est le chiffre qui commande tous les
   autres — il a droit à un traitement à part, et lui seul. */
.caisse-solde { background: var(--card); border-radius: var(--r-lg);
  box-shadow: inset 3px 0 0 var(--accent), var(--shadow), var(--arete);
  padding: var(--sp-5); display: flex; flex-direction: column; justify-content: center; }
.caisse-solde.negatif { box-shadow: inset 3px 0 0 var(--warn), var(--shadow), var(--arete); }
.caisse-libelle { font-family: var(--mono); font-size: 10px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .16em; color: var(--faint); }
/* La taille du montant se règle sur SA CARTE (unités `cq`) et non sur la
   largeur de la fenêtre. Le seuil en nombre de caractères qui servait avant
   était condamné à se tromper : « 65 960 000 k » fait pile 12 signes, restait
   sous le seuil, et venait coller le « k » au bord de la carte. Une taille
   proportionnelle au conteneur n'a pas de cas limite à rater. */
/* Le montant est dimensionné pour remplir SA CARTE, quel que soit le nombre
   de chiffres. Deux ingrédients :
     — `cqw` mesure la carte et non la fenêtre (100cqw = largeur utile) ;
     — `--n`, le nombre de caractères, est posé par app.js.
   La police mono avance de 0,593 em par caractère (mesuré, pas estimé) :
   pour occuper 92 % de la largeur il faut donc 0,92 × 100 / 0,593 ≈ 155cqw
   divisés par le nombre de caractères. Le seuil « au-delà de 12 signes »
   d'avant se trompait forcément un jour — il s'est trompé sur un solde de
   douze signes pile, qui collait le « k » au bord. */
.caisse-solde { container-type: inline-size; }
.caisse-montant { font-family: var(--mono); font-weight: 700; line-height: 1.05;
  letter-spacing: -.03em; color: var(--accent); margin: 4px 0 2px;
  white-space: nowrap;
  font-size: clamp(20px, 3vw, 32px);              /* repli très ancien navigateur */
  font-size: clamp(20px, calc(155cqw / var(--n, 12)), 40px);
}
.caisse-solde.negatif .caisse-montant { color: var(--warn); }
/* L'unité fait la même taille que les chiffres : réduite, elle se lisait
   comme un exposant collé au nombre. */
.caisse-detail { font-size: 12.5px; color: var(--muted); }
.caisse-cotes { display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); }
.caisse-item { background: var(--card); border-radius: var(--r-md);
  padding: var(--sp-4); box-shadow: var(--shadow), var(--arete); }
.caisse-item { min-width: 0; container-type: inline-size; }
/* La taille se règle sur LA CARTE (unités `cq`) et sur le nombre de signes,
   jamais sur la fenêtre : les tuiles font un quart de la largeur, si bien
   qu'un `vw` leur promettait une place qu'elles n'ont pas. « 1 026 183 937 k »
   demande quinze signes en chasse fixe — il débordait sur grand écran, là où
   on s'y attend le moins. Le repli en `vw` ne sert qu'aux navigateurs sans
   requêtes de conteneur (avant 2023). */
.caisse-item .valeur { font-family: var(--mono); font-weight: 700;
  letter-spacing: -.02em; white-space: nowrap;
  font-size: clamp(14px, 4.6vw, 21px);
  font-size: clamp(14px, calc(152cqw / var(--n, 13)), 21px); }
.caisse-item .libelle { font-size: 11px; color: var(--muted); text-transform: uppercase;
  letter-spacing: .05em; margin-top: 2px; }
.caisse-item.accent .valeur { color: var(--accent); }
.caisse-item.negatif .valeur { color: var(--danger); }
.caisse-item.alerte .valeur { color: var(--warn); }

.treso-graphes { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }

/* Choix entrée/sortie : deux cibles larges, on ne se trompe pas de sens. */
.segment-large { width: 100%; }
.segment-large .segment-btn { flex: 1; padding: 10px; font-size: 14px; }
.segment-large .segment-btn[data-val="depense"].actif { background: var(--danger);
  color: #fff; border-color: var(--danger); }

.case-attente { display: flex; gap: var(--sp-3); align-items: flex-start;
  background: var(--warn-bg); border: 1px solid var(--warn-bd);
  border-radius: var(--r-md); padding: var(--sp-3); font-size: 13px;
  margin-top: var(--sp-3); cursor: pointer; }
.case-attente input { margin-top: 2px; }
.case-attente[hidden] { display: none; }

@media (max-width: 720px) {
  .caisse { grid-template-columns: 1fr; }
  /* Six colonnes en carte : placement explicite, comme pour les percepteurs. */
  .table-treso td { grid-row: 2; }
  .table-treso td.td-nom { grid-column: 1 / 3; grid-row: 1; display: block;
    white-space: normal; }
  .table-treso td.pts { grid-column: 3; grid-row: 1; }
  .table-treso td[data-label="Date"]      { grid-column: 1; grid-row: 2; }
  .table-treso td[data-label="Catégorie"] { grid-column: 2 / 4; grid-row: 2; }
  .table-treso td[data-label="État"]      { grid-column: 1 / -1; grid-row: 3; }
  .table-treso td.td-actions { grid-column: 1 / -1; grid-row: 4;
    display: flex; gap: 8px; }
  .table-treso td.td-actions .btn { flex: 1; }
}

/* Mouvement produit par une autre partie du site : on le distingue d'une
   saisie, sinon personne ne comprend pourquoi il n'est pas modifiable. */
.tag-auto { background: var(--violet-soft); color: var(--violet);
  border-color: var(--violet-bd); margin-left: 4px; }

/* Proposition de reprise : une action ponctuelle qui change le solde, donc
   annoncée franchement plutôt que glissée dans un menu. */
.bandeau-reprise { display: flex; align-items: center; gap: var(--sp-4);
  flex-wrap: wrap; border-color: var(--warn-bd); background: var(--warn-bg); }

/* ---- Application installée (écran d'accueil iOS / Android) -----------
   En mode autonome, la barre de statut se superpose au contenu : sans ces
   marges, l'en-tête passe sous l'encoche et le menu devient intouchable. */
@supports (padding: env(safe-area-inset-top)) {
  .topbar { padding-top: calc(var(--sp-3) + env(safe-area-inset-top)); }
  .nav-panel { padding-top: env(safe-area-inset-top); }
  body { padding-bottom: env(safe-area-inset-bottom); }
  .btn-aide { bottom: calc(16px + env(safe-area-inset-bottom)); }
  .zoom-barre { top: calc(12px + env(safe-area-inset-top)); }
}
@media (max-width: 640px) {
  @supports (padding: env(safe-area-inset-bottom)) {
    .zoom-barre { top: auto; bottom: calc(14px + env(safe-area-inset-bottom)); }
  }
}

/* Case « rester connecté » de l'écran de connexion. */
.case-souvenir {
  display: flex; align-items: center; gap: 9px;
  font-size: 13px; color: var(--muted); cursor: pointer;
  margin: -2px 0 2px; user-select: none;
}
.case-souvenir:hover { color: var(--text); }

/* Doublon du lien « Guide » de la barre latérale : le bouton flottant ne
   sert que lorsque cette colonne est repliée en tiroir. Déclaré en fin de
   feuille — à spécificité égale, c'est la dernière règle qui l'emporte. */
@media (min-width: 1001px) { .btn-aide { display: none; } }

/* Sélecteurs à intitulés longs (période de paie, type de combat). Ils
   veulent de la place quand il y en a, mais ne doivent jamais dépasser leur
   conteneur : `min-width: 0` autorise l'élément flex à se réduire sous sa
   largeur intrinsèque, ce qu'une largeur posée en style inline empêchait. */
.select-large { width: auto; min-width: min(260px, 100%); max-width: 100%; }
.champ-identifiant { max-width: 260px; }
@media (max-width: 640px) {
  .select-large, .champ-identifiant { width: 100%; min-width: 0; max-width: none; }
}

/* ---- Squelette de chargement ---------------------------------------
   Remplace le texte « Chargement… ». Un mot au milieu du vide ne dit pas
   combien de temps ça va durer ni ce qui va arriver ; une esquisse de la
   page à venir, si — et l'attente paraît plus courte à durée égale, parce
   que l'œil a déjà de quoi s'occuper.
   Les blocs reprennent les dimensions réelles des éléments qu'ils
   remplacent : rien ne se déplace au moment où le contenu arrive. */
.sq { position: relative; overflow: hidden; border-radius: var(--r-sm);
  background: var(--card-2); }
/* Le reflet balaie le bloc au lieu de le faire clignoter : un clignotement
   attire l'œil par à-coups, un balayage suggère une progression. */
.sq::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 20%,
    rgba(255,255,255,.055) 45%, rgba(255,255,255,.055) 55%, transparent 80%);
  transform: translateX(-100%);
  animation: sq-balayage 1.5s ease-in-out infinite;
}
@keyframes sq-balayage { to { transform: translateX(100%); } }

.sq-tete { display: grid; grid-template-columns: auto 1fr; column-gap: var(--sp-4);
  align-items: center; margin-bottom: var(--sp-5); }
.sq-pastille { width: 46px; height: 46px; border-radius: 14px; grid-row: 1 / span 2; }
.sq-titre { height: 26px; width: min(280px, 60%); }
.sq-sous { height: 13px; width: min(440px, 85%); margin-top: 10px; }
.sq-barre { height: 82px; border-radius: var(--r-xl); margin-bottom: var(--sp-4); }
.sq-chiffres { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
  gap: var(--sp-4); padding: var(--sp-5); margin-bottom: var(--sp-4);
  border-radius: var(--r-lg); background: var(--card);
  box-shadow: var(--shadow), var(--arete); }
.sq-chiffre { height: 42px; }
.sq-liste { border-radius: var(--r-lg); background: var(--card);
  padding: var(--sp-5); box-shadow: var(--shadow), var(--arete); }
.sq-ligne { height: 15px; margin-bottom: 18px; }
.sq-ligne:last-child { margin-bottom: 0; }
/* Largeurs inégales : des barres toutes identiques se lisent comme un motif
   décoratif, pas comme du texte en attente. */
.sq-ligne:nth-child(1) { width: 92%; }
.sq-ligne:nth-child(2) { width: 78%; }
.sq-ligne:nth-child(3) { width: 85%; }
.sq-ligne:nth-child(4) { width: 66%; }
.sq-ligne:nth-child(5) { width: 80%; }
.sq-ligne:nth-child(6) { width: 58%; }

/* ---- Micro-interactions --------------------------------------------
   Chaque élément actionnable doit répondre au survol et à l'appui. C'est ce
   qui sépare une page « imprimée » d'une interface : sans retour, on doute
   d'avoir cliqué. Uniquement transform et opacity, jamais de dimension. */
/* Bordure seulement : une carte de tableau de bord ne se clique pas, la
   faire décoller au survol promettrait une action qui n'existe pas. */
.card { transition: border-color var(--t-entree) var(--ease-out); }
.btn:active, .segment-btn:active { transform: scale(.975); }
.btn { transition: background var(--t-rapide) ease, border-color var(--t-rapide) ease,
                   transform var(--t-rapide) var(--ease-out),
                   box-shadow var(--t-entree) var(--ease-out); }
tbody tr { transition: background var(--t-rapide) ease; }
.nav button.actif::before { animation: barre-onglet var(--t-entree) var(--ease-out); }
@keyframes barre-onglet { from { transform: scaleY(0); } to { transform: scaleY(1); } }
/* Le nom du joueur est un bouton qui ouvre sa fiche : agrandir l'avatar au
   survol annonce cette cible. Rien d'équivalent sur le titre de page, qui ne
   se clique pas — une icône qui s'anime là ne serait que de la décoration. */
.avatar { transition: transform var(--t-rapide) var(--ease-out); }
.cell-joueur:hover .avatar { transform: scale(1.12); }

/* ---- Moins d'animations (réglage système) --------------------------
   La règle générale plus haut neutralise déjà toutes les transitions. Il
   reste à supprimer ce qui n'est pas une transition : la dérive du fond et
   le balayage du squelette, qui tourneraient en boucle malgré le réglage. */
@media (prefers-reduced-motion: reduce) {
  html::before { animation: none; transform: none; }
  .sq::after { animation: none; }
}

/* ---- Choix du motif de rejet (doublons) ----------------------------
   Un bouton par motif, qui choisit ET valide. Le parcours précédent —
   ouvrir un menu, choisir, faire défiler sous trois blocs de correction,
   valider — demandait quatre gestes pour une décision qu'on répète des
   dizaines de fois d'affilée. */
.motifs-choix { display: grid; gap: var(--sp-2); }
.motif-choix {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--card-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: 12px 14px; min-height: 44px; font-family: inherit;
  transition: border-color var(--t-rapide) ease, background var(--t-rapide) ease,
              transform var(--t-rapide) var(--ease-out);
}
.motif-choix:hover { border-color: var(--accent-bd); background: var(--card-hover); }
.motif-choix:active { transform: scale(.985); }
.motif-choix.actif { border-color: var(--accent); background: var(--accent-soft); }
.motif-choix:disabled { opacity: .45; cursor: default; }
.motif-choix-titre { display: block; font-weight: 700; font-size: 14px; }
.motif-choix-aide { display: block; color: var(--muted); font-size: 12.5px;
  margin-top: 3px; line-height: 1.45; }



/* ---- Thème jour : le décor et les exceptions -------------------------
   La lumière du haut passe en bronze : posée en clair, une nappe dorée
   ternit au lieu d'éclairer. Les anneaux concentriques et le halo lilas ont
   disparu avec la refonte — il ne reste qu'une couche à retoucher. */
:root[data-theme="clair"]::before {
  background: radial-gradient(1100px 560px at 50% -12%,
    rgba(125,92,45,.07), transparent 70%);
}
/* Le voile des modales et du tiroir reste sombre dans les deux thèmes :
   c'est ce qui isole le premier plan. */
:root[data-theme="clair"] .modale-fond { background: rgba(23,28,34,.42); }
:root[data-theme="clair"] .nav-scrim { background: rgba(23,28,34,.38); }
/* Le squelette doit rester perceptible sur une carte blanche. */
:root[data-theme="clair"] .sq { background: #e6eaef; }
:root[data-theme="clair"] .sq::after {
  background: linear-gradient(100deg, transparent 20%,
    rgba(255,255,255,.9) 45%, rgba(255,255,255,.9) 55%, transparent 80%); }
/* Argent et bronze du podium, en version soutenue pour du texte sur clair. */
:root[data-theme="clair"] tr.podium-2 td:first-child { box-shadow: inset 2px 0 0 #7b8592; }
:root[data-theme="clair"] tr.podium-3 td:first-child { box-shadow: inset 2px 0 0 #9a6231; }

/* ---- Sélecteur de thème (pied de la barre latérale) ----------------- */
.theme-bascule {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 11px; min-height: 44px; border: 0; cursor: pointer;
  background: transparent; color: var(--muted); font-family: inherit;
  font-weight: 600; font-size: 13.5px; border-radius: var(--r-sm);
  transition: background var(--t-rapide) ease, color var(--t-rapide) ease;
}
.theme-bascule:hover { background: var(--survol); color: var(--text); }
.theme-bascule .nav-icone { color: currentColor; }
.theme-etat { margin-left: auto; font-size: 11px; color: var(--faint);
  text-transform: uppercase; letter-spacing: .06em; font-weight: 700; }

/* Peu de barres : on les groupe à gauche plutôt que de les étaler, sinon
   deux valeurs occupent chacune la moitié de la carte et le graphique se
   lit comme un aplat décoratif. */
.graph-barres.graph-peu { justify-content: flex-start; gap: var(--sp-4); }

/* Bandeau des doublons certains : teinté d'alerte pour se distinguer des
   cartes d'information, sans virer au rouge — l'action est proposée, pas
   subie. */
.bandeau-certains { border-color: var(--warn-bd);
  background: linear-gradient(135deg, var(--warn-bg), transparent 60%); }

/* Identifiant du message Discord sous chaque capture comparée. En moderation,
   savoir DE QUEL message on parle vaut mieux que le déduire : Discord groupe
   les messages consécutifs d'un même auteur sous un seul en-tête, deux envois
   distincts y ressemblent donc à un seul. */
.doublon-id { font-family: var(--mono); font-size: 11px; color: var(--faint);
  letter-spacing: -.02em; margin-top: 2px; }

/* ---- En-tête de colonne de profil (grille des droits) ----------------
   Chaque colonne porte son nom, le nombre de comptes rattachés, et — pour
   un profil créé sur mesure — de quoi le renommer ou le supprimer. Savoir
   combien de comptes dépendent d'un profil AVANT de le toucher évite la
   mauvaise surprise. */
.profil-tete { display: flex; flex-direction: column; align-items: center;
  gap: 2px; }
.profil-nom { font-weight: 700; }
.profil-comptes { font-size: 10.5px; color: var(--faint); font-weight: 500;
  text-transform: none; letter-spacing: 0; }
.profil-actions { display: flex; gap: 8px; margin-top: 2px; }
.lien-mini { background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 10.5px; font-weight: 600;
  color: var(--muted); text-decoration: underline; text-underline-offset: 2px;
  text-transform: none; letter-spacing: 0;
  transition: color var(--t-rapide) ease; }
.lien-mini:hover { color: var(--accent); }
.lien-mini.danger:hover { color: var(--danger); }

/* Compteur dans un bouton de segment : dit combien de lignes se cachent
   derrière chaque filtre avant même de cliquer. */
.segment-compte { font-variant-numeric: tabular-nums; opacity: .65;
  font-size: .85em; margin-left: 4px; }
.segment-btn.actif .segment-compte { opacity: .8; }

/* Paiement annulé : ni payé ni dû. Le gris dit « hors circuit » sans
   l'alarmisme du rouge, qui est réservé aux sorties d'argent. */
.tag-annule { background: var(--card-2); color: var(--faint);
  border-color: var(--border); text-decoration: line-through; }

/* ---- Règle « rôle Discord → profil » --------------------------------
   Une ligne = un rôle. Le rang est affiché en tête parce que l'ORDRE fait
   la règle : le premier rôle trouvé chez le membre décide. Sans ce numéro,
   rien ne dirait que la position compte. */
.regle-role { display: grid; align-items: end; gap: var(--sp-3);
  grid-template-columns: auto 1fr 1.3fr 1fr auto;
  padding: var(--sp-3); border: 1px solid var(--border-sub);
  border-radius: var(--r-md); background: var(--card-2); }
.regle-role + .regle-role { margin-top: var(--sp-2); }
.regle-rang { width: 26px; height: 26px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  margin-bottom: 9px; }
/* Les rangs d'alliance : quatre colonnes au lieu de cinq (pas de profil à
   choisir, seulement un nom et un rôle Discord). La flèche pointe vers la
   GAUCHE — on lit « ce rang correspond à ce rôle », dans ce sens-là. */
.regle-rang-ligne { grid-template-columns: auto 1fr auto 1.4fr auto;
  align-items: center; }
.regle-rang-ligne .regle-rang { margin-bottom: 0; }
.regle-fleche { color: var(--faint); font-size: 14px; }

.regle-actions { display: flex; gap: 4px; margin-bottom: 2px; }
.regle-rang-ligne .regle-actions { margin-bottom: 0; }
.regle-actions .btn-icon { min-width: 34px; padding: 6px 8px; }
.regle-actions .danger { color: var(--danger); }

@media (max-width: 860px) {
  /* En colonne, la pastille de rang passe en tête de ligne avec les
     actions : garder cinq colonnes écraserait chaque champ. */
  .regle-role { grid-template-columns: auto 1fr; }
  .regle-role .field { grid-column: 1 / -1; }
  .regle-actions { grid-column: 2; justify-content: flex-end; }
}

/* ---- Plage de dates avec heures --------------------------------------
   « Du [date] [heure] → Au [date] [heure] ». L'étiquette porte le sens ;
   sans elle, quatre champs alignés ne disent pas lequel borne quoi. */
.daterange { display: flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; background: var(--input); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 6px 10px; }
.daterange-bloc { display: flex; align-items: center; gap: 6px; }
.daterange-etiquette { font-size: 11px; font-weight: 700; color: var(--faint);
  text-transform: uppercase; letter-spacing: .06em; }
.daterange input[type="date"], .daterange input[type="time"] {
  border: 0; background: transparent; padding: 5px 2px; min-height: 34px; }
.daterange input[type="date"]:focus, .daterange input[type="time"]:focus {
  box-shadow: none; outline: 2px solid var(--accent-bd); outline-offset: 1px;
  border-radius: var(--r-sm); }
/* L'heure est secondaire : discrète tant qu'elle est vide, normale sinon. */
.daterange input[type="time"] { width: 82px; color: var(--muted); }
.daterange input[type="time"]:not([value=""]) { color: var(--text); }
.daterange-sep { color: var(--faint); flex: none; }

@media (max-width: 640px) {
  /* Les deux bornes s'empilent : côte à côte, quatre champs sur 375 px
     réduisent chacun à une largeur inutilisable. */
  .daterange { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .daterange-bloc { justify-content: space-between; }
  .daterange-sep { display: none; }
  .daterange input[type="date"] { flex: 1; min-width: 0; }
}

/* ---- Zones de contact au doigt --------------------------------------
   Placé en toute fin de feuille À DESSEIN : `.lien-mini` et `.lien-joueur`
   déclarent `padding: 0` plus haut, à spécificité égale. Une règle mobile
   écrite plus tôt — fût-ce dans une media query — se faisait donc écraser
   par la suite du fichier, et la cible restait minuscule.

   Le rembourrage est compensé par une marge négative de même valeur : la
   zone touchable grandit sans que rien ne bouge à l'écran. */
@media (max-width: 640px) {
  /* 14 px de haut pour « annuler le paiement », une action irréversible :
     c'était une invitation à la fausse manœuvre. */
  .lien-mini { padding: 12px 4px; margin: -12px -4px; }
  .lien-joueur { padding: 9px 0; margin: -9px 0; }
  /* « voir » dans le journal : 20 px de haut, à répétition dans une liste
     dense. Même traitement — la zone touchable grandit sans rien déplacer. */
  .lien-inline { padding: 10px 6px; margin: -10px -6px; }

  /* Pastilles de types : repliées sur sept lignes, elles mangeaient 284 px
     avant le classement — un tiers de l'écran pour un réglage qu'on touche
     rarement. Elles défilent donc sur une seule ligne, comme les segments.
     Le raccourci « Sans les récoltes » reste, lui, en pleine largeur : c'est
     le geste qu'on vient faire neuf fois sur dix. */
  .chips { flex-wrap: nowrap; width: 100%; overflow-x: auto;
    scrollbar-width: none; padding-bottom: 2px;
    /* Une pastille s'aligne sur le bord après un défilement au doigt. */
    scroll-snap-type: x proximity; }
  .chips::-webkit-scrollbar { display: none; }
  .chip { flex: 0 0 auto; scroll-snap-align: start; }
  .toolbar-row-chips .btn { width: 100%; }
}
