:root {
  color-scheme: light dark;
  --bg: #0f172a;
  --card: #111827;
  --text: #f8fafc;
  --muted: #cbd5e1;
  --accent: #22c55e;
  --border: #1f2937;
  --warning: #f59e0b;
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

/* Toute classe qui pose un `display` bat la règle `[hidden] { display: none }`
   de la feuille du navigateur — les styles d'auteur l'emportent sur ceux de
   l'agent, à spécificité égale. `.badge` (inline-flex) et `.pill-link`
   (inline-flex) s'affichaient donc en pastille vide alors que le code les
   avait masquées : un compteur de demandes sans demande, un lien « et N
   autres » sans autres. Le `!important` est ici la règle, pas l'exception —
   c'est la façon recommandée de rendre l'attribut `hidden` fiable. */
[hidden] { display: none !important; }

/* Skip link for keyboard users */
.skip-link {
  position: absolute;
  top: -100px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  color: #0f172a;
  padding: 12px 24px;
  border-radius: 0 0 8px 8px;
  text-decoration: none;
  font-weight: 700;
  font-size: 1rem;
  z-index: 1000;
  transition: top 0.2s ease-in-out;
}

.skip-link:focus {
  top: 0;
  color: #0f172a;
}

.skip-link:hover {
  color: #0f172a;
}

body {
  margin: 0;
  background: radial-gradient(circle at 10% 20%, #1f2937 0, #0b1223 35%, #050914 100%);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
header, footer {
  text-align: center;
  padding: 1.5rem 1rem;
  position: relative;
}
header h1 {
  margin: 0;
  font-size: 2.1rem;
  letter-spacing: 0.02em;
}
header p {
  margin: 0.35rem 0 0;
  color: var(--muted);
}
.user-menu {
  position: absolute;
  top: 1rem;
  right: 1rem;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.875rem;
}
.user-email {
  color: var(--muted);
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.logout-btn {
  background: transparent;
  border: 1px solid #374151;
  color: #cbd5e1;
  padding: 0.4rem 0.9rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.15s;
}
.logout-btn:hover {
  background: #1f2937;
  border-color: #4b5563;
  color: #fff;
}
.user-email {
  color: var(--muted);
  text-decoration: none;
}
.user-email:hover { color: #fff; }
.team-switcher {
  background: rgba(255,255,255,0.05);
  border: 1px solid #374151;
  color: #f8fafc;
  padding: 0.45rem 0.75rem;
  border-radius: 0.5rem;
  font-size: 0.9rem;
  cursor: pointer;
}
.team-switcher:hover { border-color: #22c55e; }
.team-switcher:focus { outline: none; border-color: #22c55e; }

/* Cache les éléments réservés aux coaches / dirigeants */
body.viewer-only .editor-only { display: none !important; }
/* Et, à l'intérieur de ceux-là, ce qui reste au GESTIONNAIRE de la section :
   la feuille de match, la validation des présences, les suppressions. Le
   dirigeant de club est un éditeur (il tient les fiches, les créneaux et le
   calendrier) sans être un gestionnaire — d'où deux classes et non une. */
body.club-only .coach-only { display: none !important; }
.role-badge {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75rem;
  background: rgba(34,197,94,0.15);
  color: #86efac;
  margin-left: 0.5rem;
}
@media (max-width: 600px) {
  .user-menu {
    position: static;
    justify-content: center;
    margin-bottom: 1rem;
  }
}
main {
  width: min(1100px, 95vw);
  margin: 0 auto 2rem;
  display: grid;
  gap: 1rem;
}
/* Sous-navigation. `inline-flex` + `width: fit-content` sans repli ni
   défilement imposait au <main> la largeur de TOUS les onglets : sur un iPhone
   c'est la page entière qui débordait, titres et formulaires compris, et le
   symptôme se lisait comme un bug de mise en page ailleurs.

   `max-width: max-content` garde la barre ajustée à son contenu tant qu'elle
   tient ; `overflow-x: auto` la fait défiler seule au-delà — et met accessoirement
   la taille minimale automatique de l'élément de grille à zéro, ce qui est
   précisément ce qui manquait pour qu'elle cesse de pousser ses voisins. */
.tabs {
  display: flex;
  gap: 0.5rem;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--border);
  padding: 0.35rem;
  border-radius: 12px;
  width: 100%;
  max-width: max-content;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  background: transparent;
  color: var(--muted);
  border: 1px solid transparent;
  padding: 0.6rem 1rem;
  flex: none;
  white-space: nowrap;
}
.tab.active {
  background: var(--accent);
  color: #052e12;
  border-color: var(--accent);
}
.tab-panel.hidden { display: none; }
.card {
  background: linear-gradient(145deg, rgba(31,41,55,0.8), rgba(15,23,42,0.95));
  border: 1px solid var(--border);
  padding: 1rem;
  border-radius: 16px;
  box-shadow: 0 10px 40px rgba(0,0,0,0.35);
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  /* Sans repli, le titre et ses boutons se compriment l'un l'autre sur un
     téléphone : « Liste des matchs » passait sur deux lignes derrière le bouton
     « Afficher les anciens matchs ». Ils se rangent l'un sous l'autre. */
  flex-wrap: wrap;
}
h2 { margin: 0; }
.table-wrapper { overflow-x: auto; }
table {
  width: 100%;
  border-collapse: collapse;
}
th, td {
  border-bottom: 1px solid var(--border);
  padding: 0.65rem;
  text-align: left;
  white-space: nowrap;
}
tr:hover td { background: rgba(255,255,255,0.02); }
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.form-grid .full-width {
  grid-column: 1 / -1;
}
.form-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.95rem;
  min-width: 0;
}
.inline-field {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  flex-wrap: wrap;
}
.inline-field input { flex: 1; min-width: 0; }
.inline-field button { flex-shrink: 0; white-space: nowrap; }
input, select, button {
  padding: 0.6rem 0.75rem;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: #0b1223;
  color: var(--text);
  font-size: 1rem;
}
input:focus, select:focus { outline: 2px solid var(--accent); }
button {
  cursor: pointer;
  background: var(--accent);
  color: #ffffff;
  font-weight: 700;
  border: none;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
button:hover { 
  transform: translateY(-1px); 
  box-shadow: 0 12px 30px rgba(34,197,94,0.2); 
}
button:active { 
  transform: translateY(0); 
  box-shadow: none; 
}
button:focus {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}
button.ghost {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}
.importer {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.5rem;
}
.file-label {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border: 1px dashed var(--border);
  padding: 0.65rem 0.8rem;
  border-radius: 10px;
  background: rgba(255,255,255,0.02);
  cursor: pointer;
  width: fit-content;
}
#csv-import { display: none; }
.hint { color: var(--muted); margin: 0; font-size: 0.9rem; }
.muted { color: var(--muted); margin: 0.2rem 0 0; }
.warning { color: var(--warning); margin-top: 0.6rem; }
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  background: rgba(255,255,255,0.08);
  font-size: 0.85rem;
}
.actions { display: flex; gap: 0.35rem; }
.actions.compact { align-items: center; }
.card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  flex-wrap: wrap;
  margin-top: 0.5rem;
}
.empty-row td { color: var(--muted); text-align: center; }
textarea#message-preview {
  width: 100%;
  background: #0b1223;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.8rem;
  font-size: 1rem;
  line-height: 1.5;
}
.filter-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  margin: 0.5rem 0;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--border);
}
.switch { display: inline-flex; align-items: center; gap: 0.45rem; font-weight: 600; }
.switch input { width: 18px; height: 18px; accent-color: var(--accent); }
.tag {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.45rem;
  border-radius: 999px;
  background: rgba(255,255,255,0.08);
  border: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.8rem;
  margin-left: 0.25rem;
}
.tag-conflict {
  background: rgba(251, 146, 60, 0.15);
  border-color: rgba(251, 146, 60, 0.4);
  color: #fb923c;
}
/* Au-dessus de la barre du bas (150) et du tiroir (200), sous la relance (400).
   À 20, une modale passait SOUS le chrome de l'application : sur téléphone, la
   modale tactique occupe tout l'écran, et sa barre de boutons se retrouvait
   derrière la barre d'onglets — « Enregistrer la tactique » était mesuré 40 px
   sous elle, « Fermer » 26 px sous l'en-tête, dans les deux orientations. Le
   coach composait son équipe sans pouvoir ni l'enregistrer ni refermer l'écran.
   Les modales centrées (max-height: 90vh) n'y échappaient pas non plus : à
   844 px de haut, leur pied passait 10 px sous la barre. Trois pages avaient
   déjà remonté LEUR modale (calendrier, joueurs, paiements) ; c'est la règle
   commune qui manquait. */
.modal {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 300;
}
.modal.hidden { display: none; }
.modal-content {
  background: linear-gradient(145deg, rgba(31,41,55,0.95), rgba(15,23,42,0.98));
  border: 1px solid var(--border);
  border-radius: 16px;
  width: min(1100px, 95vw);
  max-height: 90vh;
  overflow: hidden;
  box-shadow: 0 24px 80px rgba(0,0,0,0.55);
}
.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1rem 0.5rem;
}
.modal-table { max-height: 60vh; }
.modal-table table { width: 100%; }
.modal-table tbody td:nth-child(3) {
  white-space: normal;
}
.card-footer.spaced {
  padding: 0.75rem 1rem 1rem;
  border-top: 1px solid var(--border);
}

/* Modale formulaire (ajout / édition joueur) */
.modal-content.form-modal { width: min(560px, 95vw); }
.modal-content.form-modal .modal-body {
  padding: 0.5rem 1rem 0.25rem;
  overflow-y: auto;
  max-height: calc(90vh - 130px);
}
.modal-content.form-modal .card-footer.spaced {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
}
.icon-close {
  background: none;
  border: none;
  color: #94a3b8;
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  padding: 0.25rem 0.5rem;
  border-radius: 8px;
}
.icon-close:hover { color: #f8fafc; background: rgba(255, 255, 255, 0.06); }
.segmented {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
.segmented button {
  border-radius: 0;
  border: none;
  background: rgba(255,255,255,0.04);
  color: var(--muted);
  padding: 0.45rem 0.75rem;
}
.segmented button.active {
  background: var(--accent);
  color: #052e12;
}
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.chip-btn {
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.05);
  color: var(--text);
  padding: 0.35rem 0.6rem;
  border-radius: 10px;
  font-size: 0.95rem;
}
.chip-btn.active {
  background: var(--accent);
  color: #052e12;
}
.chip-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.muted-row td { color: var(--muted); }

.calendar-content {
  width: min(580px, 98vw);
  max-width: 580px;
  max-height: 85vh;
}
.calendar-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
}
.calendar-nav h3 {
  margin: 0;
  font-size: 1.1rem;
  color: var(--text);
}
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.3rem;
  padding: 0.5rem;
}
.calendar-day-header {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
  padding: 0.5rem 0;
}
.calendar-day {
  aspect-ratio: 1;
  border: 1px solid var(--border);
  background: rgba(255,255,255,0.03);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  transition: all 0.15s;
  min-height: 50px;
}
.calendar-day:hover {
  background: rgba(255,255,255,0.08);
  border-color: var(--accent);
}
.calendar-day.disabled {
  opacity: 0.3;
  cursor: not-allowed;
  background: rgba(255,255,255,0.01);
}
.calendar-day.selected {
  background: var(--accent);
  border-color: var(--accent);
  color: #052e12;
}
.calendar-day.today {
  border: 2px solid var(--accent);
}
.calendar-day-number {
  font-size: 0.95rem;
  font-weight: 500;
}
.calendar-day-badge {
  position: absolute;
  top: 4px;
  right: 4px;
  background: var(--accent);
  color: #052e12;
  font-size: 0.7rem;
  font-weight: 700;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.calendar-day.selected .calendar-day-badge {
  background: #052e12;
  color: var(--accent);
}

.staff-selectors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}

.staff-selectors label {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.95rem;
  min-width: 0;
}

/* ==================================================================
   Tactique v2 — terrain visuel, slots positionnés
   ================================================================== */
.tactic-content {
  max-width: 1200px;
  width: 95vw;
  height: 90vh;
  display: flex;
  flex-direction: column;
}

.tactic-scrollable {
  overflow: hidden;
  padding: 0 1rem;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.tactic-content .card-footer {
  margin-top: 0;
  padding: 1rem;
  border-top: 1px solid #1f2937;
}

.tactic-formation {
  margin-bottom: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

.tactic-formation label {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 500;
}

.tactic-formation select {
  max-width: 160px;
}

.tactic-actions {
  display: flex;
  gap: 0.4rem;
  margin-left: auto;
}

/* Ce que la formation cherche, sous le sélecteur. Le nom seul ne dit rien à
   qui n'a pas passé sa vie sur un banc — et c'est le public de
   l'application. Masquée sur téléphone couché (cf. plus bas) : là, chaque
   ligne du bandeau se paie en hauteur de terrain, et c'est une aide à
   choisir, pas une aide à placer. */
.tactic-formation-desc {
  margin: -0.2rem 0 0.4rem;
  font-size: 0.74rem;
  line-height: 1.35;
  color: #94a3b8;
}
.tactic-formation-desc[hidden] { display: none; }

/* Barre contextuelle sous les réglages : dit ce qui est « armé » et ce qu'on
   peut en faire. Elle remplace l'aide statique, qui répétait la même phrase
   quoi qu'on touche et coûtait deux lignes sur un téléphone.

   Elle est TOUJOURS rendue, même à vide : sa hauteur entre dans le calcul de
   `sizeTacticFieldContainer()`, et une barre qui apparaît/disparaît
   redimensionnerait le terrain à chaque sélection. D'où aussi le nowrap sur
   le libellé — deux lignes décaleraient le terrain sous le doigt. */
.tactic-armed-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.4rem;
  margin: 0 0 0.4rem;
  padding: 0.3rem 0.6rem;
  background: rgba(99, 102, 241, 0.08);
  border-left: 2px solid #6366f1;
  border-radius: 4px;
  flex-shrink: 0;
  box-sizing: border-box;
}
.tactic-armed-bar.is-armed {
  background: rgba(253, 224, 71, 0.12);
  border-left-color: #fde047;
}
.tarm-label {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 0.74rem;
  color: #94a3b8;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tactic-armed-bar.is-armed .tarm-label { color: #fef9c3; }
.tarm-label strong { color: #f8fafc; font-weight: 700; }
.tarm-actions {
  display: flex;
  gap: 0.3rem;
  flex-shrink: 0;
}
.tarm-actions button {
  font-size: 0.72rem;
  padding: 0.3rem 0.55rem;
  white-space: nowrap;
}
/* Bascule enfoncée (« SUB » d'un joueur déjà annoncé remplaçant) : même
   habillage que le bouton SUB des lignes du banc, pour la même bascule. */
.tarm-actions .tarm-actif {
  background: rgba(99, 102, 241, 0.3);
  border-color: #818cf8;
  color: #c7d2fe;
}
/* Bandeau d'avertissement de l'éditeur tactique.
   Il NOMME ce qui coince (« Untel n'est plus convoqué ») là où le refus
   d'enregistrement d'avant se contentait de « Un remplaçant n'est pas dans la
   convocation. ». Contrairement à la barre contextuelle il n'est rendu que
   lorsqu'il a quelque chose à dire — il ne dit rien la plupart du temps, et
   une bande vide permanente coûterait une ligne de terrain sur téléphone. */
.tactic-warning {
  margin: 0 0 0.4rem;
  padding: 0.35rem 0.6rem;
  background: rgba(251, 191, 36, 0.12);
  border-left: 2px solid #fbbf24;
  border-radius: 4px;
  flex-shrink: 0;
  box-sizing: border-box;
}
.tactic-warning[hidden] { display: none; }
.tw-line {
  margin: 0;
  font-size: 0.74rem;
  line-height: 1.35;
  color: #fde68a;
}
.tw-line + .tw-line { margin-top: 0.25rem; }
.tw-line button {
  font-size: 0.7rem;
  padding: 0.15rem 0.45rem;
  vertical-align: baseline;
}
/* Ce que le dernier enregistrement a retiré : c'est fait, plus une alerte. */
.tw-done { color: #cbd5e1; }

/* Joueur encore dans le plan mais sorti de la convocation : visible AVANT
   d'enregistrer, sur le terrain comme au banc. */
.tf-slot.hors-convocation .tf-slot-inner {
  background: #b45309;
  box-shadow: 0 0 0 2px rgba(251, 191, 36, 0.7);
}
.tf-slot.hors-convocation .tf-slot-label { color: #fde68a; }
.tf-bench-item.hors-convocation {
  border-color: #fbbf24;
  background: rgba(251, 191, 36, 0.14);
  color: #fde68a;
}
.tf-bench-flag {
  font-size: 0.6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: #fde68a;
  border: 1px solid #b45309;
  border-radius: 4px;
  padding: 0 0.25rem;
  white-space: nowrap;
}

.tactic-tabs-row { flex-shrink: 0; }
.tactic-formation { flex-shrink: 0; margin-bottom: 0.35rem; }

/* Onglets Plans A/B/C */
.tactic-tabs-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.4rem;
  flex-wrap: wrap;
}
.tactic-tabs {
  display: flex;
  gap: 0.25rem;
  flex-wrap: wrap;
}
.tt-tab {
  background: rgba(255,255,255,0.04);
  border: 1px solid #1f2937;
  color: #cbd5e1;
  padding: 0.4rem 0.75rem;
  border-radius: 6px 6px 0 0;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  border-bottom: 1px solid transparent;
}
.tt-tab:hover { background: rgba(255,255,255,0.08); color: #f1f5f9; }
.tt-tab.active {
  background: rgba(34, 197, 94, 0.18);
  color: #f1f5f9;
  border-color: #22c55e;
  border-bottom-color: #22c55e;
}
.tt-tab.principal { color: #fde047; }
.tt-tab.tt-add {
  font-size: 1.1rem;
  font-weight: 800;
  padding: 0.2rem 0.6rem;
  color: #94a3b8;
}
.tactic-tab-actions {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
  margin-left: auto;
}
.tactic-tab-actions .ghost {
  font-size: 0.72rem;
  padding: 0.3rem 0.5rem;
}

.tactic-field-container {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 1rem;
  overflow: hidden;
  /* height calculée par JS dans sizeTacticFieldContainer() */
}

/* Le terrain : ratio 2:3 portrait, dégradé vert.
   height:100% fonctionne car le conteneur a une hauteur px fixée par JS. */
.tactic-field {
  position: relative;
  flex-shrink: 0;
  /* width et height fixées par sizeTacticFieldContainer() en JS */
  background: linear-gradient(180deg, #2f7d44 0%, #226534 100%);
  border: 2px solid rgba(255,255,255,0.85);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: inset 0 0 60px rgba(0,0,0,0.25);
}

.tf-decor {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.tf-midline {
  position: absolute; left: 0; right: 0; top: 50%;
  height: 2px; background: rgba(255,255,255,0.7);
}
.tf-center-circle {
  position: absolute; left: 50%; top: 50%;
  width: 22%; aspect-ratio: 1;
  border: 2px solid rgba(255,255,255,0.7);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
.tf-center-spot {
  position: absolute; left: 50%; top: 50%;
  width: 4px; height: 4px; background: rgba(255,255,255,0.85);
  border-radius: 50%;
  transform: translate(-50%, -50%);
}
/* Grandes surfaces de réparation */
.tf-penalty-box {
  position: absolute; left: 50%; width: 60%; height: 16%;
  border: 2px solid rgba(255,255,255,0.7);
  transform: translateX(-50%);
}
.tf-penalty-box.tf-bot { bottom: 0; border-bottom: none; }
.tf-penalty-box.tf-top { top: 0;    border-top: none; }
/* Petites surfaces (5m) */
.tf-goal-box {
  position: absolute; left: 50%; width: 30%; height: 6%;
  border: 2px solid rgba(255,255,255,0.7);
  transform: translateX(-50%);
}
.tf-goal-box.tf-bot { bottom: 0; border-bottom: none; }
.tf-goal-box.tf-top { top: 0;    border-top: none; }

/* Mode paysage : redéfinition des lignes de terrain (buts gauche/droite) */
.tactic-field.landscape .tf-midline {
  top: 0; bottom: 0; left: 50%; right: auto;
  width: 2px; height: 100%;
}
.tactic-field.landscape .tf-penalty-box {
  top: 50%; left: auto; right: auto; bottom: auto;
  height: 60%; width: 16%;
  transform: translateY(-50%);
  border: 2px solid rgba(255,255,255,0.7);
}
/* tf-bot = défense = GAUCHE (côté gardien) */
.tactic-field.landscape .tf-penalty-box.tf-bot {
  left: 0; right: auto;
  border-left: none;
}
/* tf-top = attaque = DROITE */
.tactic-field.landscape .tf-penalty-box.tf-top {
  right: 0; left: auto;
  border-right: none;
}
.tactic-field.landscape .tf-goal-box {
  top: 50%; left: auto; right: auto; bottom: auto;
  height: 30%; width: 6%;
  transform: translateY(-50%);
  border: 2px solid rgba(255,255,255,0.7);
}
/* tf-bot = défense = GAUCHE */
.tactic-field.landscape .tf-goal-box.tf-bot {
  left: 0; right: auto;
  border-left: none;
}
/* tf-top = attaque = DROITE */
.tactic-field.landscape .tf-goal-box.tf-top {
  right: 0; left: auto;
  border-right: none;
}

/* Slots : pastilles cliquables positionnées en absolu */
.tf-slot {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  background: transparent;
  border: none;
  color: #f8fafc;
  cursor: pointer;
  font: inherit;
  padding: 0;
  z-index: 2;
  transition: transform 0.08s;
}
.tf-slot:active { transform: translate(-50%, -50%) scale(0.95); }
.tf-slot.armed { z-index: 3; }

/* Le cercle visuel est dans tf-slot-inner */
.tf-slot-inner {
  width: 52px; height: 52px;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.55);
  border: 2px dashed rgba(255,255,255,0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
  flex-shrink: 0;
}
.tf-slot:hover .tf-slot-inner { background: rgba(15,23,42,0.75); border-color: #fff; }
.tf-slot.filled .tf-slot-inner {
  background: #22c55e;
  border: 2px solid #fff;
  color: #052e12;
}
.tf-slot.filled.tf-gk .tf-slot-inner { background: #fbbf24; }
.tf-slot.tf-gk:not(.filled) .tf-slot-inner { border-color: #fbbf24; }
.tf-slot.armed .tf-slot-inner {
  box-shadow: 0 0 0 3px #fde047, 0 0 14px 4px rgba(253,224,71,0.6);
  animation: tf-pulse 1s infinite alternate;
}
.tf-slot.tf-dragging,
.tf-bench-item.tf-dragging {
  opacity: 0.35;
}
.tf-drag-ghost {
  position: fixed;
  pointer-events: none;
  transform: translate(-50%, -50%);
  background: #22c55e;
  color: #052e12;
  border: 2px solid #fff;
  border-radius: 999px;
  padding: 0.35rem 0.65rem;
  font-weight: 700;
  font-size: 0.78rem;
  box-shadow: 0 6px 14px rgba(0,0,0,0.4);
  z-index: 9999;
  white-space: nowrap;
}
@keyframes tf-pulse {
  from { box-shadow: 0 0 0 3px #fde047, 0 0 10px 2px rgba(253,224,71,0.5); }
  to   { box-shadow: 0 0 0 4px #fde047, 0 0 18px 6px rgba(253,224,71,0.85); }
}

.tf-slot-label {
  font-size: 0.62rem;
  font-weight: 700;
  white-space: nowrap;
  color: #f0fdf4;
  text-shadow: 0 1px 3px rgba(0,0,0,0.95), 0 0 6px rgba(0,0,0,0.8);
  pointer-events: none;
  max-width: 72px;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
  line-height: 1;
}
.tf-slot-name {
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0 4px;
}
.tf-slot-jersey {
  position: absolute;
  top: -6px;
  right: -6px;
  background: #0f172a;
  color: #fde047;
  border: 2px solid #fff;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 800;
  padding: 1px 5px;
  min-width: 18px;
  text-align: center;
  line-height: 1.2;
  pointer-events: none;
}
.tf-bench-jersey {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.4rem;
  height: 1.4rem;
  background: #0f172a;
  color: #fde047;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 800;
  padding: 0 0.35rem;
}
.section-header-row td {
  background: rgba(99, 102, 241, 0.08);
  border-top: 1px solid #1f2937;
  padding: 0.5rem 0.75rem;
}
.section-header-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #a5b4fc;
}
.match-info-cell {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.5rem 0.75rem;
}
.match-team-tag {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #a5b4fc;
}
.match-vs-label {
  font-size: 0.95rem;
}
.match-meta-label {
  font-size: 0.8rem;
}
.jersey-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  background: rgba(15, 23, 42, 0.7);
  color: #fde047;
  border: 1px solid #1f2937;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 800;
  padding: 0.1rem 0.4rem;
}
.tf-slot-code {
  font-size: 0.78rem;
  font-weight: 800;
  opacity: 1;
  letter-spacing: 0.04em;
}
.tf-slot-code-empty {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.tf-sub-btn {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 0.6rem;
  font-weight: 800;
  padding: 2px 6px;
  border-radius: 4px;
  border: 1px solid rgba(255,255,255,0.2);
  background: rgba(255,255,255,0.07);
  color: rgba(255,255,255,0.5);
  cursor: pointer;
  letter-spacing: 0.05em;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tf-sub-btn:hover {
  background: rgba(99,102,241,0.25);
  border-color: #818cf8;
  color: #818cf8;
}
.tf-sub-btn.tf-sub-active {
  background: rgba(99,102,241,0.3);
  border-color: #818cf8;
  color: #c7d2fe;
}
.tf-empty {
  text-align: center;
  color: rgba(255,255,255,0.8);
  padding: 2rem 1rem;
  font-size: 0.9rem;
}

/* Banc latéral */
.tactic-bench {
  background: rgba(255,255,255,0.03);
  border: 1px solid #1f2937;
  border-radius: 8px;
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  box-sizing: border-box;
}
.tactic-bench h4 {
  margin: 0 0 0.25rem 0;
  font-size: 0.85rem;
  color: #cbd5e1;
}
#tactic-bench-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.tf-bench-subhead {
  margin: 0.4rem 0 0.1rem;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #94a3b8;
  font-weight: 700;
}
.tf-bench-subhead.muted { color: #64748b; }
.tf-bench-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  padding: 0.35rem 0.5rem;
  background: rgba(15, 23, 42, 0.6);
  border: 1px solid #1f2937;
  border-radius: 6px;
  font-size: 0.8rem;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.tf-bench-item:hover { border-color: #22c55e; }
.tf-bench-item.armed {
  border-color: #fde047;
  background: rgba(253, 224, 71, 0.15);
  box-shadow: 0 0 0 2px rgba(253, 224, 71, 0.6);
  color: #fef9c3;
}
.tf-bench-item.on-bench {
  background: rgba(34, 197, 94, 0.18);
  border-color: #22c55e;
  color: #86efac;
}
.tf-bench-item.on-field {
  opacity: 0.45;
  cursor: not-allowed;
}
.tf-bench-name { flex: 1; overflow: hidden; text-overflow: ellipsis; }
.tf-bench-pos {
  font-size: 0.65rem;
  font-weight: 700;
  color: #94a3b8;
  padding: 0 0.3rem;
  border: 1px solid #374151;
  border-radius: 4px;
}

@media (max-width: 720px) {
  header h1 { font-size: 1.7rem; }
  main { width: 100%; padding: 0 0.4rem; }
  th, td { white-space: normal; }
  .tactic-content { height: 95svh; }
  .tactic-field-container { gap: 0.4rem; }
  /* .tactic-field dimensions fixées par JS */
  .tactic-bench { padding: 0.4rem; font-size: 0.75rem; }
  .tactic-bench h4 { font-size: 0.75rem; margin-bottom: 0.15rem; }
  .tf-bench-item { padding: 0.25rem 0.35rem; font-size: 0.72rem; }
  .tf-slot-inner { width: 36px; height: 36px; }
  .tf-slot-label { font-size: 0.5rem; max-width: 52px; }
  .tf-slot-code { font-size: 0.6rem; }
  .tf-slot-code-empty { font-size: 0.6rem; }
}

/* ==================================================================
   Tactique sur petit écran — 1/2 : la diète du bandeau
   ------------------------------------------------------------------
   La hauteur du terrain est le RESTE de la division : tout ce que le
   bandeau de réglages consomme, il le prend au terrain. Sur un téléphone
   couché, ce bandeau (titre sur deux lignes, ligne de coach, onglets
   repliés, « Formation » au-dessus de son select) ne laissait que 120 px
   de haut au terrain — un timbre-poste. D'où cette diète, appliquée dès
   que l'écran est étroit OU bas, indépendamment de l'orientation.
   ================================================================== */
@media (max-width: 820px) and (orientation: portrait),
       (max-height: 560px) and (max-width: 1024px) {
  /* Plein écran : les marges de la modale sont autant de terrain perdu. */
  #tactic-modal { padding: 0; }
  .tactic-content {
    width: 100vw;
    max-width: none;
    height: 100svh;
    max-height: none;
    border-radius: 0;
  }

  .tactic-content .modal-header { padding: 0.5rem 0.6rem 0.35rem; }
  .tactic-content .modal-header h3 { font-size: 0.92rem; line-height: 1.25; }
  /* Rappel, pas une action : l'encadrement est déjà sur la fiche du match. */
  #tactic-coach-display { display: none; }
  .tactic-scrollable { padding: 0 0.6rem; }
  .tactic-content .card-footer { padding: 0.6rem; }
  .tactic-armed-bar { min-height: 2.1rem; margin-bottom: 0.3rem; }

  /* Onglets + actions du plan sur UNE ligne défilable plutôt que deux
     lignes qui se replient. Idem pour formation + auto-placer. */
  .tactic-tabs-row,
  .tactic-formation {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 0.4rem;
    margin-bottom: 0.3rem;
  }
  .tactic-tabs,
  .tactic-tab-actions,
  .tactic-actions { flex-wrap: nowrap; flex-shrink: 0; }
  /* `label` est en colonne par défaut (règle de base) : le libellé se pose
     au-dessus du select et coûte une ligne entière au terrain. */
  .tactic-formation label {
    flex-direction: row;
    align-items: center;
    flex-shrink: 0;
    font-size: 0.78rem;
  }
  .tactic-formation select { max-width: 6.5rem; padding: 0.3rem 0.4rem; }
  .tactic-actions button { font-size: 0.72rem; padding: 0.3rem 0.5rem; }
  .tt-tab { padding: 0.3rem 0.55rem; font-size: 0.76rem; }
}

/* Écran bas (téléphone couché) : la disposition côte à côte tient, mais le
   terrain y est petit — les pastilles doivent le rester aussi, sans quoi
   elles se recouvrent. */
@media (max-height: 560px) and (max-width: 1024px) {
  /* Onglets et formation partagent une ligne : sur 390 px de haut, chaque
     ligne du bandeau vaut ~20 % de la hauteur du terrain. La largeur, elle,
     ne manque pas. (Le JS mesure la position du conteneur plutôt que la
     somme de ses frères — cf. sizeTacticFieldContainer — donc partager une
     ligne ne fausse pas son calcul.) */
  .tactic-scrollable {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, auto);
    grid-template-rows: auto auto auto minmax(0, 1fr);
    align-content: start;
  }
  .tactic-tabs-row { grid-area: 1 / 1; }
  .tactic-formation { grid-area: 1 / 2; }
  /* Sans cellule explicite, le bandeau d'avertissement se faisait
     auto-placer dans celle de la barre contextuelle, et les deux se
     superposaient. Sa ligne se referme d'elle-même quand il est masqué. */
  .tactic-warning { grid-area: 2 / 1 / 3 / -1; }
  .tactic-armed-bar { grid-area: 3 / 1 / 4 / -1; }
  .tactic-field-container { grid-area: 4 / 1 / 5 / -1; }
  /* Une aide à choisir la formation, pas à placer les joueurs : sur 390 px
     de haut elle coûterait un cinquième du terrain. */
  .tactic-formation-desc { display: none; }

  .tactic-bench { padding: 0.4rem; }
  .tactic-bench h4 { display: none; }
  .tf-bench-item { padding: 0.25rem 0.4rem; font-size: 0.72rem; }
  .tf-slot-inner { width: 32px; height: 32px; }
  .tf-slot-code { font-size: 0.6rem; }
  .tf-slot-code-empty { font-size: 0.58rem; }
  /* Un terrain de 190 px de haut serre les lignes à ~25 px : les noms des
     deux rangées se chevauchent. Le fond opaque ne les sépare pas, mais il
     garde chacun lisible — deux noms sans fond se fondaient l'un dans
     l'autre et on ne lisait plus ni l'un ni l'autre. */
  .tf-slot-label {
    font-size: 0.5rem;
    max-width: 54px;
    background: rgba(2, 6, 23, 0.75);
    padding: 1px 3px;
    border-radius: 3px;
  }
  .tf-slot.armed .tf-slot-label { background: rgba(2, 6, 23, 0.95); }
}

/* ==================================================================
   Tactique sur petit écran — 2/2 : téléphone tenu à la verticale
   ------------------------------------------------------------------
   La disposition de bureau (terrain à gauche, banc de 180 px à droite)
   ne se transpose pas : sur 390 px de large, elle laisse ~150 px au
   terrain, soit des pastilles qui se chevauchent et des noms tronqués.
   Ici le terrain prend TOUTE la largeur et le banc passe DESSOUS, en
   bande horizontale défilable. Le terrain y gagne un facteur ~5 en
   surface, et le banc reste visible en permanence.

   L'orientation compte autant que la largeur : un téléphone couché est
   large et bas, la bande sous le terrain n'y laisserait plus de hauteur.
   En paysage on garde donc la disposition côte à côte.

   ⚠️ La même requête média est répliquée en JS (LAYOUT_EMPILE dans
   static/js/pages/convocations/tactic.js), qui calcule les dimensions du
   terrain au pixel. Modifier l'une sans l'autre désaligne les deux.
   ================================================================== */
@media (max-width: 820px) and (orientation: portrait) {
  /* Le terrain dessus, le banc dessous. */
  .tactic-field-container {
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
  }
  .tactic-bench {
    /* Hauteur fixe : le JS en déduit celle du terrain. */
    height: 4rem;
    flex: 0 0 auto;
    width: 100%;
    flex-direction: row;
    align-items: center;
    gap: 0.3rem;
    padding: 0 0.4rem;
    overflow-x: auto;
    overflow-y: hidden;
  }
  .tactic-bench h4 { display: none; }
  #tactic-bench-list {
    flex-direction: row;
    align-items: center;
    gap: 0.3rem;
    flex-wrap: nowrap;
  }
  .tf-bench-item {
    flex: 0 0 auto;
    min-height: 40px;
    padding: 0.35rem 0.55rem;
    font-size: 0.76rem;
  }
  .tf-bench-name { flex: 0 0 auto; }
  /* Les joueurs déjà placés sortent de la bande : ils n'y sont pas
     cliquables, et le terrain juste au-dessus les montre déjà, nom compris.
     Les laisser, c'était neuf pastilles inertes à faire défiler avant
     d'atteindre celles sur lesquelles on peut agir.
     (`.muted` n'habille que l'intertitre « Sur le terrain ».) */
  .tf-bench-item.on-field,
  .tf-bench-subhead.muted { display: none; }
  /* Le poste et le bouton SUB doublaient la largeur de chaque pastille.
     Le poste se lit sur le terrain, et « remplaçant » se règle depuis la
     barre contextuelle une fois le joueur sélectionné. */
  .tf-bench-pos,
  .tf-sub-btn { display: none; }
  /* Les intertitres deviennent des séparateurs en ligne. */
  .tf-bench-subhead {
    flex: 0 0 auto;
    margin: 0;
    padding-left: 0.4rem;
    border-left: 1px solid #334155;
    white-space: nowrap;
    font-size: 0.6rem;
  }
  .tf-empty { flex: 0 0 auto; padding: 0 0.5rem; font-size: 0.75rem; }

  /* Cibles tactiles : 44 px est le minimum confortable au pouce. */
  .tf-slot-inner { width: 44px; height: 44px; }
  .tf-slot-label { font-size: 0.58rem; max-width: 70px; }
  .tf-slot-code { font-size: 0.72rem; }
  .tf-slot-code-empty { font-size: 0.7rem; }
}

/* Position picker : sélecteur de poste principal + secondaires.
   Premier tap = principal (chip plein vert + ★), taps suivants = secondaires
   (chip bordé vert), max 2 secondaires. */
.position-picker {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.6rem;
  background: rgba(255,255,255,0.02);
  border: 1px solid #1f2937;
  border-radius: 0.5rem;
}
.position-picker-grid {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.pp-line {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex-wrap: wrap;
}
.pp-line-label {
  font-size: 0.68rem;
  color: #94a3b8;
  width: 2.5rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
  flex-shrink: 0;
}
.pp-chip {
  background: transparent;
  border: 1px solid #374151;
  color: #cbd5e1;
  padding: 0.32rem 0.55rem;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
  min-width: 2.6rem;
  text-align: center;
}
.pp-chip:hover {
  border-color: #22c55e;
  color: #f1f5f9;
}
.pp-chip.primary {
  background: #22c55e;
  color: #052e12;
  border-color: #22c55e;
}
.pp-chip.secondary {
  background: rgba(34,197,94,0.12);
  color: #86efac;
  border-color: #22c55e;
}
.pp-hint-text {
  font-size: 0.72rem;
  color: #94a3b8;
  margin: 0.3rem 0 0;
  min-height: 1em;
}
.pp-hint-text b { color: #86efac; }

/* FFF Authentication Styles */
.btn-small {
  font-size: 0.75rem;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--text);
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}

.btn-small:hover {
  background: var(--accent);
  border-color: var(--accent);
}

.error-message {
  padding: 0.5rem;
  border-radius: 4px;
  background: rgba(245, 101, 101, 0.1);
  border: 1px solid rgba(245, 101, 101, 0.3);
}

#fffAuthStatus {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}


/* ────── Layout app (base.html) ────── */
.app-header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 1rem;
  background: rgba(15,23,42,0.85);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid #1f2937;
  position: sticky;
  top: 0;
  z-index: 100;
  text-align: left;
}
.app-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  font-size: 1.05rem;
  font-weight: 700;
  color: #f8fafc;
  text-decoration: none;
  letter-spacing: 0.01em;
  text-align: center;
}
.app-title-mark {
  width: 22px;
  height: 24px;
  flex: none;
}
.app-title:hover { color: #22c55e; }
.nav-toggle {
  background: transparent;
  border: none;
  width: 36px;
  height: 36px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  height: 2px;
  background: #f8fafc;
  border-radius: 2px;
  transition: transform 0.2s, opacity 0.2s;
}
.user-pill {
  background: rgba(34,197,94,0.12);
  border: 1px solid rgba(34,197,94,0.3);
  color: #86efac;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  font-size: 0.8rem;
  text-decoration: none;
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-pill:hover { background: rgba(34,197,94,0.22); }
.app-main {
  width: min(1100px, 95vw);
  margin: 0 auto 2rem;
  display: grid;
  /* `minmax(0, 1fr)` et non la colonne implicite : une piste `auto` prend la
     taille MIN-CONTENT de ses éléments, et il suffit d'un <select> dont l'option
     la plus longue fait 500 px pour que toute la page s'élargisse — l'iPhone la
     réduit alors d'autant, ce qui se lit comme un bug de police. Bornée à zéro,
     la piste vaut la largeur de l'écran et c'est au contenu large de défiler
     dans son propre conteneur. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
  padding-top: 1rem;
}

/* Drawer */
.nav-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
  z-index: 199;
}
.nav-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}
.nav-drawer {
  position: fixed;
  top: 0;
  left: 0;
  width: 280px;
  max-width: 85vw;
  height: 100vh;
  background: #111827;
  border-right: 1px solid #1f2937;
  transform: translateX(-100%);
  transition: transform 0.2s ease-out;
  z-index: 200;
  display: flex;
  flex-direction: column;
  padding: 0.5rem 0 1rem;
  /* Seule la LISTE défile (cf. `.nav-links`) : faire défiler tout le panneau
     emportait « Déconnexion » sous le pli dès que les liens dépassaient la
     hauteur d'écran — ce qui arrive sur un portable 900 px depuis que le menu
     est rangé en groupes. La marque reste en haut, la déconnexion en bas. */
  overflow: hidden;
}
.nav-drawer.open {
  transform: translateX(0);
}
.nav-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1.25rem 0.75rem;
  border-bottom: 1px solid #1f2937;
  color: #94a3b8;
  font-size: 0.9rem;
}
.nav-close {
  background: transparent;
  border: none;
  color: #cbd5e1;
  font-size: 1.7rem;
  cursor: pointer;
  line-height: 1;
  padding: 0 0.4rem;
}
.nav-close:hover { color: #fff; }
.nav-user {
  padding: 0.7rem 1.25rem;
  font-size: 0.85rem;
  color: #cbd5e1;
  word-break: break-all;
}
.nav-team-block {
  padding: 0 1.25rem 0.75rem;
  border-bottom: 1px solid #1f2937;
}
.nav-team-block .team-switcher {
  width: 100%;
}
.nav-label {
  display: block;
  font-size: 0.7rem;
  color: #64748b;
  margin-bottom: 0.35rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.nav-links {
  list-style: none;
  padding: 0.5rem 0;
  margin: 0;
  flex: 1;
  overflow-y: auto;
  /* Un élément de flex ne descend pas sous sa taille min-content sans ceci :
     sans lui, `overflow-y` n'a rien à faire défiler et la liste déborde. */
  min-height: 0;
}
.nav-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.85rem 1.25rem;
  color: #cbd5e1;
  text-decoration: none;
  font-size: 0.95rem;
  transition: background 0.15s;
}
.nav-link:hover { background: rgba(255,255,255,0.04); color: #fff; }
.nav-link.active {
  background: rgba(34,197,94,0.12);
  color: #86efac;
  border-left: 3px solid #22c55e;
  padding-left: calc(1.25rem - 3px);
}
.nav-icon { font-size: 1.1rem; }
.nav-logout {
  border-top: 1px solid #1f2937;
  padding: 1rem 1.25rem;
  margin: 0;
}
.nav-logout button {
  width: 100%;
  background: transparent;
  border: 1px solid #374151;
  color: #cbd5e1;
  padding: 0.55rem;
  border-radius: 0.5rem;
  cursor: pointer;
  font-size: 0.9rem;
}
.nav-logout button:hover { background: #1f2937; }

/* Titres de groupe du tiroir : « Mon espace » / « Ma section ». Le tiroir
   mélangeait les destinations du joueur et celles du coach dans une seule
   liste, où « Admin » était le seul indice qu'il y avait deux mondes. */
.nav-group-title {
  padding: 0.9rem 1.25rem 0.35rem;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #64748b;
}
.nav-group-title:first-child { padding-top: 0.35rem; }
.nav-section-name {
  margin: 0;
  font-size: 0.9rem;
  color: #e2e8f0;
}
.nav-link .badge {
  margin-left: auto;
  background: var(--accent);
  color: #052e12;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.1rem 0.5rem;
}

/* ────── Section courante ──────
   Sortie du tiroir : ce que l'écran montre ne doit pas demander d'ouvrir un
   menu pour être lu. Rendue seulement à partir de deux sections — sinon il n'y
   a rien à choisir, et la barre ne ferait qu'occuper la hauteur d'écran. */
.section-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: min(1100px, 95vw);
  margin: 0.75rem auto 0;
}
.section-bar-label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #64748b;
  flex: none;
}
.section-bar .team-switcher {
  flex: 1;
  min-width: 0;
}

/* ────── Barre d'onglets du bas ──────
   Quatre destinations pour un licencié, cinq pour un encadrant — jamais plus,
   et jamais de défilement horizontal : c'est la contrainte qui a fait sortir
   les neuf onglets admin de la page. Le reste vit dans le tiroir. */
.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 150;
  display: flex;
  background: rgba(15,23,42,0.96);
  backdrop-filter: blur(10px);
  border-top: 1px solid #1f2937;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.tabbar-link {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  padding: 0.45rem 0.2rem 0.5rem;
  color: #94a3b8;
  text-decoration: none;
  font-size: 0.68rem;
  line-height: 1.2;
}
.tabbar-icon { font-size: 1.25rem; line-height: 1; }
.tabbar-label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tabbar-link.active { color: var(--accent); }
.tabbar-link.active .tabbar-icon { transform: translateY(-1px); }
/* Le badge se pose SUR l'icône : en flux, il élargissait l'onglet et cassait
   l'alignement des cinq colonnes dès qu'une demande arrivait. */
.tabbar-badge {
  position: absolute;
  top: 0.25rem;
  left: 50%;
  margin-left: 0.35rem;
  background: var(--accent);
  color: #052e12;
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.05rem 0.35rem;
  line-height: 1.4;
}
/* La barre masquerait le bas du contenu et les liens légaux du pied de page.
   Le dégagement est une VARIABLE parce que la barre ne coûte pas la même
   hauteur partout : collée en bas elle vaut ses 52 px, en pastille flottante
   elle vaut sa hauteur PLUS son écart au bord. Une valeur unique laissait donc
   le pied de page passer sous la pastille sur ordinateur — mesuré à -11 px,
   soit « Mentions légales / Protection des données / CGU / Mes données »
   partiellement couverts, sans que rien ne permette de les dégager. */
:root { --tabbar-degagement: 3.6rem; }
body.has-tabbar {
  padding-bottom: calc(var(--tabbar-degagement) + env(safe-area-inset-bottom, 0px));
}

/* La marque, en tête de la barre latérale. Masquée tant que ce panneau est un
   tiroir : l'en-tête porte alors déjà le logo, et l'afficher deux fois
   n'apprendrait rien. */
.nav-brand { display: none; }

@media (min-width: 900px) {
  /* Sur un écran large, une barre pleine largeur collée en bas n'a pas de
     sens : elle devient une pastille centrée, à distance de lecture.
     Elle occupe alors sa hauteur (~53 px) plus son `bottom` (16 px) : 5rem
     couvre les deux, là où 3,6rem ne couvrait que la hauteur. */
  :root { --tabbar-degagement: 5rem; }
  .tabbar {
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    bottom: 1rem;
    width: min(560px, 90vw);
    border: 1px solid #1f2937;
    border-radius: 999px;
    overflow: hidden;
    box-shadow: 0 12px 40px rgba(0,0,0,0.45);
  }
}

/* ────── Barre latérale permanente (ordinateur) ──────

   Au-delà de 1100 px, la barre du bas et le tiroir sont deux gestes mobiles
   qui survivent plus qu'ils ne servent : le tiroir occupe 280 px et assombrit
   les 1160 autres pour montrer treize liens, et la pastille flotte en bas,
   là où une souris ne va pas. Le même panneau devient donc une colonne
   permanente — mêmes liens, même marquage de la page courante, aucun second
   bloc de navigation à tenir à jour.

   Le seuil est à 1100 px et non 900 : la colonne coûte 260 px, et le contenu
   (plafonné à 1100 px) doit garder de quoi respirer à côté. Entre 900 et
   1100 px, la pastille reste le bon compromis. */
@media (min-width: 1100px) {
  :root {
    --nav-latero: 260px;
    /* La barre du bas ayant disparu, le pied de page n'a plus rien à dégager. */
    --tabbar-degagement: 0rem;
  }

  /* Le contenu se décale, colonne comprise : l'en-tête est dans le flux du
     body, il suit donc le décalage sans règle propre. */
  body { padding-left: var(--nav-latero); }

  /* `95vw` se mesure sur la FENÊTRE, pas sur le conteneur : gardé tel quel,
     il produisait 95 % de 1440 px dans une zone qui n'en fait plus que 1180,
     donc un débordement horizontal — précisément ce que cette refonte a
     entrepris de supprimer. */
  main,
  .app-main,
  .section-bar { width: min(1100px, calc(100% - 3rem)); }

  .nav-drawer {
    transform: none;
    top: 0;
    bottom: 0;
    height: 100vh;
    width: var(--nav-latero);
    max-width: none;
    z-index: 101;
    padding-top: 0;
    /* Elle n'ouvre ni ne ferme plus : l'animation de glissement ferait sauter
       la colonne à chaque redimensionnement. */
    transition: none;
  }

  .nav-brand {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 1rem 1.25rem;
    font-size: 1.05rem;
    font-weight: 700;
    color: #f8fafc;
    text-decoration: none;
    border-bottom: 1px solid #1f2937;
  }
  .nav-brand:hover { color: var(--accent); }

  /* Ce qui n'a plus d'objet quand le panneau ne se ferme pas. */
  .nav-toggle,
  .nav-drawer-header,
  .nav-backdrop,
  .tabbar { display: none !important; }

  /* L'en-tête n'a plus à porter la marque — la colonne la porte — et son
     hamburger a disparu : il ne lui reste que le compte, aligné à droite.
     Une grille de trois colonnes dont deux sont masquées poserait ce reste
     à GAUCHE ; un flex à droite dit ce qu'on veut. */
  .app-header {
    display: flex;
    justify-content: flex-end;
    min-height: 3rem;
  }
  .app-header .app-title { display: none; }
}

/* ===== IMPRESSION TACTIQUE =====
   Le CSS d'impression est injecté dans la fenêtre dédiée par printTactic().
   Le @media print ci-dessous ne sert plus qu'à masquer le reste de la page
   si l'utilisateur imprime directement depuis la page principale. */
@media print {
  @page { size: A4 landscape; margin: 10mm; }

  /* Neutralise le min-height:100vh du body qui forçait une 2e page */
  html, body {
    min-height: 0 !important;
    height: auto !important;
    overflow: visible !important;
    background: white !important;
  }

  body > * { display: none !important; }
  #tactic-print-area {
    display: block !important;
    font-family: sans-serif;
    color: #000;
    width: 277mm; /* A4 landscape - 2×10mm marges */
    page-break-inside: avoid;
  }

  .tpa-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    border-bottom: 2px solid #000;
    padding-bottom: 3mm;
    margin-bottom: 3mm;
  }
  .tpa-header h1 { font-size: 13pt; margin: 0 0 1mm; }
  .tpa-header p  { font-size: 8pt;  margin: 0; color: #444; }
  #tpa-formation-badge {
    font-size: 16pt;
    font-weight: 900;
    border: 2px solid #000;
    border-radius: 6px;
    padding: 1mm 3mm;
  }

  .tpa-body {
    display: flex;
    gap: 6mm;
    align-items: flex-start;
  }

  /* Terrain : 3:2 paysage, taille réduite pour tenir sur 1 page
     A4 paysage utile : 277×190mm. Header ≈ 18mm → reste 172mm.
     Terrain : 148×99mm (ratio 3:2 exact) */
  .tpa-field-wrap {
    flex: 0 0 148mm;
    height: 99mm;
  }
  .tpa-field-inner {
    width: 148mm !important;
    height: 99mm !important;
    position: relative !important;
    background: #2f7d44 !important;
    border: 2px solid #fff !important;
    border-radius: 6px !important;
    overflow: hidden !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Slots en impression */
  .tpa-field-inner .tf-slot {
    position: absolute;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    background: transparent;
    border: none;
  }
  .tpa-field-inner .tf-slot-inner {
    width: 28px; height: 28px;
    border-radius: 50%;
    background: rgba(15,23,42,0.5);
    border: 1.5px dashed rgba(255,255,255,0.7);
    display: flex; align-items: center; justify-content: center;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .tpa-field-inner .tf-slot.filled .tf-slot-inner {
    background: #22c55e !important;
    border: 1.5px solid #fff !important;
    color: #052e12 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .tpa-field-inner .tf-slot.filled.tf-gk .tf-slot-inner {
    background: #fbbf24 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .tpa-field-inner .tf-slot-code { font-size: 6pt; font-weight: 800; }
  .tpa-field-inner .tf-slot-code-empty { font-size: 6pt; font-weight: 700; color: rgba(255,255,255,0.6); }
  .tpa-field-inner .tf-slot-label {
    font-size: 5pt; font-weight: 700; color: #fff;
    text-shadow: 0 1px 2px rgba(0,0,0,0.9);
    white-space: nowrap;
  }

  /* Lignes de terrain en impression */
  .tpa-field-inner .tf-midline {
    position: absolute; top: 0; bottom: 0; left: 50%;
    width: 1.5px; background: rgba(255,255,255,0.6);
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .tpa-field-inner .tf-center-circle {
    position: absolute; left: 50%; top: 50%;
    width: 22%; aspect-ratio: 1;
    border: 1.5px solid rgba(255,255,255,0.6); border-radius: 50%;
    transform: translate(-50%, -50%);
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .tpa-field-inner .tf-penalty-box {
    position: absolute; top: 50%; left: auto; right: auto; bottom: auto;
    height: 60%; width: 16%;
    border: 1.5px solid rgba(255,255,255,0.6);
    transform: translateY(-50%);
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .tpa-field-inner .tf-penalty-box.tf-bot { left: 0;  border-left: none; }
  .tpa-field-inner .tf-penalty-box.tf-top { right: 0; border-right: none; }
  .tpa-field-inner .tf-goal-box {
    position: absolute; top: 50%; left: auto; right: auto; bottom: auto;
    height: 30%; width: 6%;
    border: 1.5px solid rgba(255,255,255,0.6);
    transform: translateY(-50%);
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .tpa-field-inner .tf-goal-box.tf-bot { left: 0;  border-left: none; }
  .tpa-field-inner .tf-goal-box.tf-top { right: 0; border-right: none; }

  /* Liste joueurs : prend le reste de la largeur (277 - 148 - 6 = 123mm) */
  .tpa-roster { flex: 1; font-size: 8pt; max-height: 99mm; overflow: hidden; }
  .tpa-roster h3 {
    font-size: 8pt; text-transform: uppercase; letter-spacing: 0.05em;
    border-bottom: 1px solid #ccc; margin: 3mm 0 1.5mm; padding-bottom: 1mm;
  }
  .tpa-roster ul { list-style: none; margin: 0; padding: 0; }
  .tpa-roster li { padding: 0.5mm 0; border-bottom: 1px solid #eee; }
  .tpa-pos { color: #555; font-size: 7pt; }
}

/* ===== Saisie du résultat & stats ===== */
.score-badge { background: #22c55e; color: #052e12; font-weight: 800; }

.score-inputs { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.score-inputs label { display: flex; flex-direction: column; gap: 0.25rem; }
.score-inputs input { width: 6rem; }

.result-row {
  display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap;
  padding: 0.4rem 0; border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.result-row select { flex: 1 1 8rem; min-width: 7rem; }
.result-row .goal-minute, .result-row .card-minute { width: 4.5rem; flex: 0 0 auto; }
/* Le camp d'un carton, et le numéro de maillot d'en face. Courts tous les
   deux : ils ne doivent pas prendre la place du joueur, qui est l'information
   principale de la ligne. */
.result-row .card-side { flex: 0 0 auto; min-width: 7rem; }
.result-row .card-number { width: 4.5rem; flex: 0 0 auto; }
.result-row.is-csc select { opacity: 0.4; }
.result-row .csc-toggle { display: flex; align-items: center; gap: 0.25rem; white-space: nowrap; }
.result-row .goal-remove, .result-row .card-remove { flex: 0 0 auto; }

/* Convoqués restés sur le banc : une case par joueur, en grille, pour que le
   coach passe son groupe en revue d'un coup d'oeil plutot qu'en deroulant. */
.notplayed-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 0.35rem 0.75rem;
  margin: 0.25rem 0 0.5rem;
}
.notplayed-item {
  /* `flex-direction` : la règle globale sur `label` empile libellé et champ,
     ce qui poserait ici la case au-dessus du nom. */
  display: flex; flex-direction: row; align-items: center; gap: 0.4rem;
  padding: 0.3rem 0.4rem; border-radius: 6px;
  cursor: pointer;
}
.notplayed-item.is-off { background: rgba(248, 113, 113, 0.12); color: #fca5a5; }
.notplayed-item input { flex: 0 0 auto; }

/* ===== Bandeau de résultat (cf. static/js/core/resultat.js) =====
   Lu comme une feuille de résultats : les deux équipes de part et d'autre du
   score, les buteurs sous leur propre camp. */
.rs-bandeau {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid #1f2937;
  border-radius: 0.6rem;
  overflow: hidden;
  margin: 0.5rem 0;
}
.rs-score-ligne {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
}
.rs-equipe {
  display: flex; align-items: center;
  padding: 0.55rem 0.7rem;
  font-weight: 600; font-size: 0.85rem; line-height: 1.25; color: #cbd5e1;
  /* `break-word` et non `anywhere` : le second coupait « Courdimanche » en
     plein milieu dès que la colonne se resserrait. */
  min-width: 0; overflow-wrap: break-word; hyphens: none;
}
.rs-equipe-gauche { justify-content: flex-end; text-align: right; }
.rs-equipe-droite { justify-content: flex-start; text-align: left; }
/* Notre équipe ressort : sur une liste de résultats, on cherche son camp. */
.rs-nous { color: #f8fafc; }
.rs-score {
  display: flex; align-items: center; justify-content: center; gap: 0.35rem;
  padding: 0.45rem 0.9rem;
  background: #0f172a;
  font-size: 1.35rem; font-weight: 800; letter-spacing: 0.02em;
  color: #f8fafc; white-space: nowrap;
}
.rs-tiret { color: #64748b; font-weight: 600; }
.rs-victoire .rs-score { background: #14532d; color: #dcfce7; }
.rs-defaite .rs-score { background: #450a0a; color: #fecaca; }
.rs-nul .rs-score { background: #1f2937; }

.rs-buteurs {
  list-style: none; margin: 0; padding: 0.45rem 0.7rem;
  display: flex; flex-direction: column; gap: 0.2rem;
  font-size: 0.82rem; color: #cbd5e1;
  border-top: 1px solid #1f2937;
}
.rs-buteurs-gauche { align-items: flex-end; }
.rs-buteurs-droite { align-items: flex-start; }
.rs-but { display: flex; align-items: baseline; gap: 0.35rem; }
.rs-minute { color: #94a3b8; font-variant-numeric: tabular-nums; }
.rs-ballon { font-size: 0.72rem; }
.rs-csc { font-style: italic; color: #94a3b8; }

.rs-cartons {
  display: flex; gap: 0.75rem; justify-content: center;
  padding: 0.35rem 0.7rem;
  border-top: 1px solid #1f2937;
  font-size: 0.8rem; color: #94a3b8;
}
.rs-cartons-item { display: inline-flex; align-items: center; gap: 0.25rem; }
/* ⚠️ Une seule règle, sans couleur : celle-ci vient du REGISTRE des sports
   (`sanctions[].couleur`) et se pose en style en ligne. Trois classes nommées
   auraient imposé de retoucher cette feuille à chaque sport ajouté — et la
   sanction manquante ne se serait vue que sur un bandeau de résultat. */
.rs-carton {
  display: inline-block; width: 0.55rem; height: 0.8rem; border-radius: 1px;
  transform: skewX(-9deg);
}
/* Une sanction SANS couleur (les deux minutes du handball, que l'arbitre
   signale de la main) porte son libellé : ce n'est pas un carton. */
.rs-carton-texte {
  font-size: 0.72rem; font-weight: 600; color: var(--muted, #94a3b8);
}

/* Tirs au but : sous le score, plus petit que lui. La séance départage, elle
   ne remplace pas le score du match — la hiérarchie visuelle le dit. */
.rs-tab {
  text-align: center; font-size: 0.8rem; font-weight: 600;
  color: #cbd5e1; padding: 0.15rem 0.7rem 0.35rem;
  font-variant-numeric: tabular-nums;
}
.rs-tab .rs-tiret { margin: 0 0.3rem; }

.rs-issue {
  padding: 0.3rem 0.7rem; text-align: center;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: #94a3b8;
  border-top: 1px solid #1f2937;
}
.rs-victoire .rs-issue { color: #4ade80; }
.rs-defaite .rs-issue { color: #f87171; }

.ev-tag-result { background: rgba(148, 163, 184, 0.15); color: #cbd5e1; border-color: #475569; }
.match-card.match-joue .meta { color: #94a3b8; }

.stats-filters { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
/* Un <select> ne descend pas sous la largeur de sa plus longue option : celle
   de l'équipe (« ANDRESY F.C. (Vétérans) ») élargissait la page entière. */
.stats-filters label { display: flex; flex-direction: column; gap: 0.25rem; flex: 1 1 10rem; min-width: 0; }
.stats-filters select { width: 100%; min-width: 0; }

.stats-views { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; }
.stats-views .ghost.active { background: #22c55e; color: #052e12; border-color: #22c55e; }
.stat-strong { font-weight: 800; color: #22c55e; }

/* Maillots pris par l'encadrement : une liste courte, hors du tableau des
   licencies (un coach n'a pas de fiche joueur, donc pas de ligne). */
.stats-staff-list { list-style: none; padding: 0; margin: 0.5rem 0 0; }
.stats-staff-list li {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid #1f2937;
}
.stats-staff-list li:last-child { border-bottom: none; }

/* Convocation en modale. La section garde son habillage de carte, mais le
   contenu doit defiler a l'interieur : la liste des joueurs depasse largement
   la hauteur d'ecran sur un effectif complet. */
.roster-content {
  max-width: 900px;
  width: 95vw;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
}
.roster-content #roster-section {
  margin: 0;
  overflow-y: auto;
  max-height: 90vh;
}

/* Résultats d'import CSV des joueurs. Ces règles remplacent les styles inline
   qui vivaient dans le JS (static/js/pages/players.js). */
.import-line {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.45rem 0; border-top: 1px solid var(--border);
  gap: 0.5rem; flex-wrap: wrap;
}
.import-name { font-size: 0.875rem; color: var(--text); font-weight: 600; }
.import-code-wrap { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
/* Depuis qu'on affiche le lien d'inscription complet et non plus le code à
   8 caractères, la ligne doit pouvoir se tronquer : sur mobile une URL entière
   pousserait le bouton de partage hors de l'écran. */
.import-code {
  font-family: monospace; font-size: 0.72rem; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  max-width: 60vw;
}
.import-code.muted { color: #64748b; }
.copy-code-btn {
  background: rgba(34, 197, 94, 0.1);
  border: 1px solid rgba(34, 197, 94, 0.3);
  color: #86efac; border-radius: 0.3rem;
  padding: 0.2rem 0.5rem; cursor: pointer; font-size: 0.8rem;
}

/* ── Pages légales ────────────────────────────────────────────────────────── */
.legal { max-width: 60rem; line-height: 1.65; }
.legal h1 { margin-bottom: 0.3rem; }
.legal h2 { margin-top: 2rem; font-size: 1.05rem; color: var(--text); }
.legal p, .legal li { color: var(--muted); }
.legal ul { padding-left: 1.2rem; }
.legal li { margin-bottom: 0.35rem; }
.legal code {
  background: rgba(255,255,255,0.06); padding: 0.1rem 0.3rem;
  border-radius: 0.25rem; font-size: 0.9em;
}
.legal-resume { border-left: 3px solid var(--accent); padding-left: 1rem; list-style: none; }
.legal-table { width: 100%; border-collapse: collapse; margin: 0.75rem 0; font-size: 0.9rem; }
.legal-table th, .legal-table td {
  text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.legal-table th { color: var(--text); font-weight: 600; }
.legal-nav { margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--border); }

/* Marqueur visible des informations que l'éditeur doit encore renseigner. */
.a-completer {
  background: rgba(245,158,11,0.1); border-left: 3px solid var(--warning);
  padding: 0.6rem 0.9rem; border-radius: 0.3rem; font-size: 0.9rem;
}

/* ── Pied de page légal ───────────────────────────────────────────────────── */
.footer-legal { display: flex; gap: 1.2rem; flex-wrap: wrap; justify-content: center; }
.footer-legal a { color: var(--muted); font-size: 0.8rem; text-decoration: none; }
.footer-legal a:hover { color: var(--text); text-decoration: underline; }

/* ── Mes données (profil) ─────────────────────────────────────────────────── */
.zone-danger {
  border: 1px solid rgba(239,68,68,0.35); border-radius: 0.5rem;
  padding: 0.9rem; margin-top: 1rem; background: rgba(239,68,68,0.04);
}
.zone-danger h3 { margin: 0 0 0.4rem; font-size: 0.95rem; color: #fca5a5; }

/* ── Liste d'événements (matchs + entraînements) ───────────────────────────── */
/* Partagée par /calendrier (vue Liste) et l'accueil, qui rendent la même liste
   via `static/js/core/events.js`. Les régles vivaient dans calendar.html : les
   dupliquer dans home.html aurait laissé les deux pages diverger. */
.match-list-group { margin-bottom: 1.25rem; }
.match-list-group h3 {
  color: #94a3b8; font-size: 0.8rem; text-transform: uppercase;
  letter-spacing: 0.05em; margin: 0 0 0.5rem; font-weight: 600;
}
.match-card {
  background: rgba(255,255,255,0.03);
  border: 1px solid #1f2937;
  border-radius: 0.5rem;
  padding: 0.85rem 1rem;
  margin-bottom: 0.5rem;
  transition: background 0.15s;
}
/* Le curseur main et le survol ne sont posés QUE là où un clic fait quelque
   chose. Sur le calendrier la carte ouvre sa modale ; sur l'accueil elle
   n'ouvre plus rien, et un curseur main sur une carte inerte promettait un
   détail qui n'arrivait jamais. */
.match-card.clickable { cursor: pointer; }
.match-card.clickable:hover { background: rgba(255,255,255,0.05); }
.match-card .date {
  font-size: 0.75rem; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.04em;
}
.match-card .teams { font-weight: 600; color: #f8fafc; margin-top: 0.2rem; }
.match-card .meta { font-size: 0.8rem; color: #94a3b8; margin-top: 0.15rem; }
.match-card.match-event { border-left: 3px solid #22c55e; }
.match-card.training-card { border-left: 3px solid #3b82f6; }
.match-card.training-card.cancelled .teams { text-decoration: line-through; color: #94a3b8; }

/* En-tête typé : la pastille NOMME l'événement. Le liseré de couleur seul
   demandait de connaître le code couleur, et se perdait à côté de celui du
   bloc de réponses. Vert = match, bleu = entraînement, partout. */
.ev-head {
  display: flex; align-items: center; gap: 0.45rem;
  flex-wrap: wrap; margin-bottom: 0.15rem;
}
.ev-head .date { margin: 0; }
.ev-tag {
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em; padding: 0.1rem 0.45rem;
  border-radius: 0.3rem; border: 1px solid; white-space: nowrap;
}
.ev-tag-match { color: #86efac; background: rgba(34,197,94,0.15); border-color: rgba(34,197,94,0.4); }
.ev-tag-training { color: #93c5fd; background: rgba(59,130,246,0.15); border-color: rgba(59,130,246,0.4); }
.ev-tag-cancelled { color: #fca5a5; background: rgba(239,68,68,0.15); border-color: rgba(239,68,68,0.4); }

/* Dispo d'une JOURNÉE de match : rendue une seule fois par date, hors des
   cartes, parce qu'une réponse vaut pour tous les matchs du jour. */
.jour-dispo {
  border-left: 3px solid #22c55e;
  background: rgba(34,197,94,0.06);
  border-radius: 0.4rem;
  padding: 0.6rem 0.8rem;
  margin: -0.25rem 0 0.85rem;
}
.poll-hint { display: block; margin-top: 0.35rem; font-size: 0.72rem; color: #64748b; }

/* Présence d'une SÉANCE : une occasion, une réponse — d'où un bloc distinct
   de `jour-dispo`, et bleu comme la carte d'entraînement qui le porte. */
.seance-presence {
  border-left: 3px solid #3b82f6;
  background: rgba(59,130,246,0.06);
  border-radius: 0.4rem;
  padding: 0.6rem 0.8rem;
  margin-top: 0.6rem;
}
.seance-presence .poll-btns { margin-top: 0; }

/* Une ligne par répondant : le parent de deux licenciés, ou le coach qui joue,
   en ont plusieurs. En une seule rangée qui déborde, les boutons se
   retrouvaient sous le nom du répondant suivant. */
.poll-btns {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 0.4rem; margin-top: 0.5rem;
}
.poll-line { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.jour-dispo .poll-btns { margin-top: 0; }
.poll-btns .poll-name { font-size: 0.75rem; color: #94a3b8; }
.poll-btns button {
  background: rgba(255,255,255,0.05);
  border: 1px solid #374151;
  color: #94a3b8;
  padding: 0.3rem 0.7rem;
  border-radius: 0.35rem;
  cursor: pointer;
  font-size: 0.75rem;
}
.poll-btns button.sel-present { background: #16a34a; border-color: #16a34a; color: #fff; }
.poll-btns button.sel-absent { background: #dc2626; border-color: #dc2626; color: #fff; }

/* Détail nominatif réservé au coach : les noms doivent revenir à la ligne
   plutôt que d'élargir la modale sur mobile. */
.poll-detail { margin-top: 0.5rem; border-top: 1px solid #1f2937; padding-top: 0.5rem; }
.poll-detail .poll-names { color: #cbd5e1; font-size: 0.85rem; word-break: break-word; }

/* ── Écran Joueurs : état du compte lié ────────────────────────────────────── */
/* Une fiche « avec compte » a été récupérée par son licencié ; une fiche
   « sans compte » n'existe que dans l'application. La distinction commande ce
   qu'on peut en faire (suppression directe), elle est donc affichée en clair
   plutôt que déduite d'une case vide. */
.roster-filter { display: flex; gap: 0.3rem; flex-wrap: wrap; margin: 0 0 0.75rem; }
.roster-filter .ghost.active {
  background: #22c55e; color: #052e12; border-color: #22c55e; font-weight: 600;
}
.compte-mail {
  font-size: 0.8rem; color: #cbd5e1;
  display: inline-block; max-width: 16ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
.compte-lie { font-size: 0.78rem; color: #86efac; }
.compte-absent { font-size: 0.78rem; color: #fbbf24; }
button.ghost.danger { color: #fca5a5; border-color: rgba(239,68,68,0.4); }
button.ghost.danger:hover { background: rgba(239,68,68,0.1); }

/* Libellé réservé aux lecteurs d'écran. La classe était utilisée par six
   templates sans avoir jamais été définie : « Rechercher un joueur », « Seuil
   en minutes » et consorts s'affichaient donc en clair, collés au champ qu'ils
   annonçaient. Retirer la classe des templates aurait à l'inverse privé de
   libellé des champs qui n'en ont pas d'autre. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Jauge de participation ────────────────────────────────────────────────── */
/* « 8 présents, 4 absents, 8 sans réponse » demandait au coach de soustraire de
   tête pour savoir si le sondage avait pris. La barre répond avant la lecture ;
   la légende reste écrite, sans quoi l'information disparaîtrait au lecteur
   d'écran et pour qui distingue mal le vert du rouge. */
.jauge {
  display: flex; align-items: center; gap: 0.5rem;
  flex-wrap: wrap; margin-top: 0.35rem;
}
.jauge-barre {
  display: flex; overflow: hidden;
  flex: 1 1 120px; min-width: 90px; max-width: 220px;
  height: 6px; border-radius: 999px;
  background: rgba(148,163,184,0.25);
}
.jauge-barre span { display: block; height: 100%; }
.jauge-present { background: #22c55e; }
.jauge-absent { background: #ef4444; }
.jauge-texte { font-size: 0.75rem; color: #94a3b8; }
.jauge-texte strong { color: #e2e8f0; }
/* Effectif de référence inconnu (match d'une autre section en mode « Tout le
   club ») : le décompte seul, sans barre — une proportion y serait inventée. */
.jauge-sans-total { margin-top: 0.2rem; }
.jauge-portee { font-size: 0.72rem; color: #64748b; }

/* ── Relance d'un sondage ──────────────────────────────────────────────────── */
/* Modale construite par core/relance.js et ajoutée au <body> à l'ouverture :
   trois écrans l'ouvrent, et trois copies du balisage auraient divergé. */
.relance-modal {
  /* Au-dessus de TOUT : elle s'ouvre depuis la modale de détail du calendrier,
     qui monte déjà à 300. En dessous, le message se serait préparé derrière
     l'écran qui l'a demandé. */
  position: fixed; inset: 0; z-index: 400;
  background: rgba(0,0,0,0.65);
  display: flex; align-items: center; justify-content: center; padding: 1rem;
}
.relance-content {
  background: linear-gradient(145deg, rgba(31,41,55,0.97), rgba(15,23,42,0.99));
  border: 1px solid var(--border);
  border-radius: 16px;
  width: min(560px, 95vw);
  max-height: 90vh; overflow-y: auto;
  padding: 0 1rem 1rem;
  box-shadow: 0 24px 80px rgba(0,0,0,0.55);
}
.relance-content .modal-header { padding: 1rem 0 0.5rem; }
.relance-content h3 { margin: 0; color: #f8fafc; font-size: 1.05rem; }
.relance-content h4 { margin: 0 0 0.4rem; color: #cbd5e1; font-size: 0.85rem; }
.relance-label {
  display: block; margin: 0.75rem 0 0.3rem;
  font-size: 0.75rem; color: #94a3b8;
}
.relance-content textarea {
  width: 100%; box-sizing: border-box;
  background: rgba(255,255,255,0.05);
  border: 1px solid #374151; border-radius: 0.5rem;
  color: #f8fafc; font-family: inherit; font-size: 0.85rem;
  line-height: 1.45; padding: 0.6rem 0.7rem; resize: vertical;
}
.relance-note { font-size: 0.72rem; margin-top: 0.4rem; }
/* Retour de la relance par push. Défini ICI et non repris de `.fm-msg`,
   qui n'existe que dans le <style> de la fiche match : la modale s'ouvre
   depuis quatre écrans, et la ligne y serait restée sans couleur — donc
   sans distinction entre « envoyé » et « échec ». */
.relance-msg { margin-top: 0.6rem; font-size: 0.85rem; }
.relance-msg.ok { color: #86efac; }
.relance-msg.err { color: #fca5a5; }
.relance-actions { display: flex; gap: 0.5rem; margin-top: 0.75rem; flex-wrap: wrap; }
.relance-personnes {
  margin-top: 1rem; border-top: 1px solid #1f2937; padding-top: 0.75rem;
}
.relance-personnes .muted { font-size: 0.75rem; margin: 0 0 0.6rem; }
.relance-personnes ul { list-style: none; margin: 0; padding: 0; }
.relance-personnes li {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.5rem; padding: 0.35rem 0; border-top: 1px solid rgba(31,41,55,0.7);
  font-size: 0.85rem; color: #e2e8f0;
}
.relance-personnes li:first-child { border-top: none; }
/* Nom à gauche, choix d'équipe + bouton à droite. Sur mobile le sélecteur
   passe à la ligne plutôt que d'écraser le nom. */
.relance-cible { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
/* Avec un choix d'équipe, la ligne ne tient plus : le nom prend sa ligne, le
   sélecteur et le bouton la suivante. À plat, « Adam Leroy » se coupait en deux
   pour laisser passer le menu. */
.relance-personnes ul.avec-cible li { flex-direction: column; align-items: stretch; gap: 0.35rem; }
.relance-personnes ul.avec-cible .relance-cible { justify-content: flex-end; }
.relance-personnes ul.avec-cible select { flex: 1 1 auto; max-width: none; }
.relance-cible select {
  background: rgba(255,255,255,0.05);
  border: 1px solid #374151;
  border-radius: 0.35rem;
  color: #cbd5e1;
  font-size: 0.75rem;
  padding: 0.25rem 0.35rem;
  max-width: 12rem;
}
/* Bouton d'ouverture, posé sous le détail nominatif : c'est là que le coach
   constate qui manque, donc là qu'il veut relancer. */
.relance-lancer { margin-top: 0.6rem; }

/* ── Le bandeau des saisies faites sans réseau ───────────────────────────────
   Sur un terrain amateur, le réseau tombe. L'écran du direct garde alors ses
   saisies sur l'appareil et les envoie au retour de la connexion — le coach
   n'a rien à faire, mais il doit pouvoir vérifier qu'elles sont parties avant
   de fermer l'application.

   COLLÉ EN HAUT, au-dessus de l'en-tête (qui est `sticky` à z-index 100) :
   plus bas, il aurait disparu au premier défilement, et c'est justement en
   défilant la liste des buts que le coach se demande si tout est enregistré.
   Sous les modales (300), qui s'ouvrent par-dessus. */
.hl-bandeau {
  position: sticky; top: 0; z-index: 120;
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.6rem 1rem; font-size: 0.85rem; line-height: 1.4;
  border-bottom: 1px solid rgba(15, 23, 42, 0.4);
}
.hl-bandeau.attente { background: #78350f; color: #fde68a; }
.hl-bandeau.envoi { background: #1e3a5f; color: #bfdbfe; }
.hl-bandeau.refus { background: #7f1d1d; color: #fecaca; }
.hl-bandeau button {
  flex: none; min-height: 36px; padding: 0.35rem 0.75rem;
  border: 1px solid currentColor; border-radius: 8px;
  background: transparent; color: inherit; cursor: pointer; font-size: 0.8rem;
}
